UNPKG

@e280/authlocal

Version:

User-sovereign login system for everybody

61 lines 2.53 kB
import { Bytename, Hex, Thumbprint } from "@e280/stz"; import { deriveId, unpackKey } from "../crypto/crypto.js"; import { validLabel } from "../../../common/utils/validation.js"; /** serialize identities as seed text */ export async function seedPack(...identities) { const texts = await Promise.all(identities.map(async (identity) => JSON.stringify(identity.label) + (await dehydrate(identity.secret)) .split(" ") .map(s => `\n ${s}`) .join(""))); return texts.join("\n\n"); } /** deserialize identities from seed text. returns an array of promises, one for each seed in the text. */ export function seedRecover(seedtext) { seedtext = seedtext.trim(); const regex = /("[^"]*")([^"]+)/gm; const matches = [...seedtext.matchAll(regex)]; return matches.map(async ([, labelstring, bytename]) => { const label = labelstring ? JSON.parse(labelstring) : ""; const secret = await hydrate(bytename); const id = await deriveId(secret); return { id, secret, label: (label && validLabel(label)) ? label : Thumbprint.sigil.fromHex(id), }; }); } export class SeedError extends Error { name = this.constructor.name; } export class SeedIncompleteError extends SeedError { } export class SeedChecksumError extends SeedError { } /** convert hex key to seedling (with a 2-byte checksum) */ async function dehydrate(secret) { const secretBytes = unpackKey(secret); const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", secretBytes)); const checksumBytes = hash.slice(0, 2); const seedBytes = new Uint8Array([...secretBytes, ...checksumBytes]); if (seedBytes.length !== 34) throw new SeedIncompleteError("seed must be 34 bytes"); return Bytename.fromBytes(seedBytes); } /** convert seed to hex key (with checksum validation) */ async function hydrate(seedling) { const bytes = Bytename.toBytes(seedling); if (bytes.length !== 34) throw new SeedIncompleteError("seed must be 34 bytes"); const secretBytes = bytes.slice(0, 32); const checksumBytes = bytes.slice(32, 34); const hash = new Uint8Array(await crypto.subtle.digest("SHA-256", secretBytes)); const invalidChecksum = Hex.string(hash.slice(0, 2)) !== Hex.string(checksumBytes); if (invalidChecksum) throw new SeedChecksumError("invalid seed checksum"); return Hex.string(secretBytes); } //# sourceMappingURL=seed.js.map