@tinycloud/sdk-core 3.8.1-beta.2 → 3.9.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/held-credentials/errors.ts","../../src/held-credentials/crypto.ts","../../src/held-credentials/encoding.ts","../../src/held-credentials/did.ts","../../src/held-credentials/rows.ts","../../src/held-credentials/issuer-set.ts","../../src/held-credentials/siwe.ts","../../src/held-credentials/verify.ts","../../src/held-credentials/nonce-store.ts","../../src/held-credentials/email.ts"],"sourcesContent":["export type HeldCredentialErrorCode =\n | \"bad-config\"\n | \"bad-issuer-set\"\n | \"bad-presentation\"\n | \"bad-header\"\n | \"did-method-not-allowed\"\n | \"issuer-not-accepted\"\n | \"unknown-issuer\"\n | \"issuer-not-yet-valid\"\n | \"issuer-stopped\"\n | \"bad-signature\"\n | \"bad-payload\"\n | \"issuer-retired\"\n | \"profile-not-accepted\"\n | \"profile-mismatch\"\n | \"not-yet-valid\"\n | \"expired\"\n | \"bad-lifetime\"\n | \"bad-status\"\n | \"bad-holder-binding\"\n | \"bad-subject\"\n | \"bad-disclosure\"\n | \"bad-siwe\"\n | \"bad-siwe-signature\"\n | \"bad-nonce\"\n | \"nonce-replayed\"\n | \"nonce-account-mismatch\";\n\n/** Every refusal is one of these. The code is stable; the message is for logs and never carries a credential. */\nexport class HeldCredentialError extends Error {\n readonly code: HeldCredentialErrorCode;\n constructor(code: HeldCredentialErrorCode, message: string) {\n super(`${code}: ${message}`);\n this.name = \"HeldCredentialError\";\n this.code = code;\n }\n}\n\nexport function refuse(code: HeldCredentialErrorCode, message: string): never {\n throw new HeldCredentialError(code, message);\n}\n","import { ed25519 } from \"@noble/curves/ed25519\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { keccak_256 } from \"@noble/hashes/sha3\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { sha512 } from \"@noble/hashes/sha512\";\nimport { asciiBytes, bytesToHex } from \"./encoding\";\n\nexport { sha256, keccak_256 };\n\nconst Point = ed25519.ExtendedPoint;\n\n/** True when `bytes` is a canonical, on-curve, non-small-order Ed25519 point (public key or signature R). */\nexport function isStrictEd25519Point(bytes: Uint8Array): boolean {\n if (bytes.length !== 32) return false;\n try {\n return !Point.fromHex(bytes, false).isSmallOrder(); // zip215 = false: canonical encodings only\n } catch {\n return false;\n }\n}\n\n/**\n * RFC 8032 strict verification, matching the node's dalek `verify_strict`: canonical S, canonical\n * encodings, and no small-order public key or R.\n */\nexport function verifyEd25519Strict(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean {\n if (signature.length !== 64 || !isStrictEd25519Point(publicKey) || !isStrictEd25519Point(signature.subarray(0, 32))) return false;\n try {\n if (!ed25519.verify(signature, message, publicKey, { zip215: false })) return false;\n // noble's check is cofactored (it multiplies the difference by 8), which accepts a mixed-order R\n // that dalek's `verify_strict` refuses. Also require the exact, cofactorless equation\n // [S]B == R + [k]A with k = H(R || A || M) mod L.\n const R = Point.fromHex(signature.subarray(0, 32), false);\n const A = Point.fromHex(publicKey, false);\n const S = bytesToNumberLE(signature.subarray(32));\n const k = bytesToNumberLE(sha512(Uint8Array.from([...signature.subarray(0, 32), ...publicKey, ...message]))) % ed25519.CURVE.n;\n return Point.BASE.multiplyUnsafe(S).equals(R.add(A.multiply(k)));\n } catch {\n return false;\n }\n}\n\nfunction bytesToNumberLE(bytes: Uint8Array): bigint {\n let out = 0n;\n for (let i = bytes.length - 1; i >= 0; i -= 1) out = (out << 8n) | BigInt(bytes[i]!);\n return out;\n}\n\n/** Lowercase `0x` + 40 hex address of the key that produced an EIP-191 `personal_sign` signature, or null. */\nexport function recoverEip191Address(message: string, signature: Uint8Array): string | null {\n if (signature.length !== 65) return null;\n const v = signature[64]!;\n const recovery = v >= 27 ? v - 27 : v;\n if (recovery !== 0 && recovery !== 1) return null;\n try {\n const body = new TextEncoder().encode(message);\n const prefix = asciiBytes(`\\x19Ethereum Signed Message:\\n${body.length}`);\n const digest = keccak_256(Uint8Array.from([...prefix, ...body]));\n const publicKey = secp256k1.Signature.fromCompact(signature.subarray(0, 64)).addRecoveryBit(recovery).recoverPublicKey(digest).toRawBytes(false);\n return `0x${bytesToHex(keccak_256(publicKey.subarray(1)).subarray(12))}`;\n } catch {\n return null;\n }\n}\n","import { refuse, type HeldCredentialErrorCode } from \"./errors\";\n\nconst B64URL = /^[A-Za-z0-9_-]*$/;\n\nexport function encodeBase64Url(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Strict unpadded, canonical base64url. */\nexport function decodeBase64Url(value: string, code: HeldCredentialErrorCode = \"bad-presentation\"): Uint8Array {\n if (!B64URL.test(value) || value.length % 4 === 1) refuse(code, \"not unpadded base64url\");\n const standard = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(standard + \"=\".repeat((4 - (standard.length % 4)) % 4));\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);\n if (encodeBase64Url(bytes) !== value) refuse(code, \"non-canonical base64url\");\n return bytes;\n}\n\nexport function isAscii(value: string): boolean {\n for (let i = 0; i < value.length; i += 1) if (value.charCodeAt(i) > 0x7f) return false;\n return true;\n}\n\nexport function asciiBytes(value: string): Uint8Array {\n const bytes = new Uint8Array(value.length);\n for (let i = 0; i < value.length; i += 1) bytes[i] = value.charCodeAt(i);\n return bytes;\n}\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function hexToBytes(hex: string, code: HeldCredentialErrorCode = \"bad-presentation\"): Uint8Array {\n if (!/^(?:[0-9a-fA-F]{2})*$/.test(hex)) refuse(code, \"not hex\");\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i += 1) bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return bytes;\n}\n\nconst STRICT_UTF8 = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: true });\n\n/** Strict UTF-8: invalid bytes are refused (not replaced) and a leading BOM is kept, so JSON parsing refuses it. */\nexport function decodeUtf8(bytes: Uint8Array, code: HeldCredentialErrorCode): string {\n try { return STRICT_UTF8.decode(bytes); } catch { return refuse(code, \"not valid UTF-8\"); }\n}\n\nexport type Json = null | boolean | number | string | Json[] | { [key: string]: Json };\n\nexport function isObject(value: unknown): value is { [key: string]: Json } {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Deepest nesting `parseStrictJson` accepts. Held credentials are at most three levels deep. */\nexport const MAX_JSON_DEPTH = 8;\n\n/** Numbers are integers in plain form only: no fraction (`1.0`), no exponent (`1e3`), no `-0`. */\nconst JSON_LITERAL = /true|false|null|-?(?:0|[1-9]\\d*)(?![\\d.eE])/y;\n\n/**\n * JSON parser that refuses duplicate object keys anywhere (JSON.parse silently keeps the last).\n * Objects have a null prototype, so a `__proto__` key is an ordinary key. Nesting deeper than\n * MAX_JSON_DEPTH is refused with `code`, so the recursion is bounded and no RangeError escapes.\n */\nexport function parseStrictJson(text: string, code: HeldCredentialErrorCode): Json {\n let i = 0;\n let depth = 0;\n const fail = (what: string): never => refuse(code, `invalid JSON: ${what}`);\n const ws = () => { while (i < text.length && \" \\t\\n\\r\".includes(text[i]!)) i += 1; };\n const string = (): string => {\n const start = i;\n i += 1;\n while (i < text.length && text[i] !== '\"') i += text[i] === \"\\\\\" ? 2 : 1;\n if (i >= text.length) fail(\"unterminated string\");\n i += 1;\n try { return JSON.parse(text.slice(start, i)) as string; } catch { return fail(\"bad string\"); }\n };\n const value = (): Json => {\n ws();\n const c = text[i];\n if ((c === \"{\" || c === \"[\") && (depth += 1) > MAX_JSON_DEPTH) fail(\"nesting too deep\");\n if (c === \"{\") {\n i += 1;\n const out: { [key: string]: Json } = Object.create(null);\n ws();\n if (text[i] === \"}\") { i += 1; depth -= 1; return out; }\n for (;;) {\n ws();\n if (text[i] !== '\"') fail(\"expected key\");\n const key = string();\n if (Object.prototype.hasOwnProperty.call(out, key)) fail(\"duplicate key\");\n ws();\n if (text[i] !== \":\") fail(\"expected colon\");\n i += 1;\n out[key] = value();\n ws();\n if (text[i] === \",\") { i += 1; continue; }\n if (text[i] === \"}\") { i += 1; depth -= 1; return out; }\n fail(\"expected , or }\");\n }\n }\n if (c === \"[\") {\n i += 1;\n const out: Json[] = [];\n ws();\n if (text[i] === \"]\") { i += 1; depth -= 1; return out; }\n for (;;) {\n out.push(value());\n ws();\n if (text[i] === \",\") { i += 1; continue; }\n if (text[i] === \"]\") { i += 1; depth -= 1; return out; }\n fail(\"expected , or ]\");\n }\n }\n if (c === '\"') return string();\n JSON_LITERAL.lastIndex = i;\n const literal = JSON_LITERAL.exec(text);\n if (!literal) return fail(\"unexpected token\");\n if (literal[0] === \"-0\") fail(\"-0 is not accepted\");\n i += literal[0].length;\n return JSON.parse(literal[0]) as Json;\n };\n const result = value();\n ws();\n if (i !== text.length) fail(\"trailing data\");\n return result;\n}\n","import { keccak_256 } from \"./crypto\";\nimport { asciiBytes } from \"./encoding\";\n\nexport interface PkhDid {\n chainId: string;\n /** Lowercase `0x` + 40 hex. */\n address: string;\n}\n\n/** EIP-55 checksummed form of a `0x` + 40 hex address. */\nexport function toChecksumAddress(address: string): string {\n const lower = address.slice(2).toLowerCase();\n const hash = keccak_256(asciiBytes(lower));\n let out = \"0x\";\n for (let i = 0; i < 40; i += 1) {\n const nibble = (hash[i >> 1]! >> (i % 2 === 0 ? 4 : 0)) & 0xf;\n out += nibble >= 8 ? lower[i]!.toUpperCase() : lower[i]!;\n }\n return out;\n}\n\n/** All-lowercase or correctly checksummed. An all-uppercase address, or a mixed-case one with a wrong EIP-55 checksum, is refused. */\nexport function isValidAddress(address: string): boolean {\n if (!/^0x[0-9a-fA-F]{40}$/.test(address)) return false;\n const body = address.slice(2);\n if (body === body.toLowerCase()) return true;\n return toChecksumAddress(address) === address;\n}\n\n/** Canonical `did:pkh:eip155:<chainId>:<EIP-55 address>` of a valid pkh DID, or null. */\nexport function canonicalPkhDid(did: unknown): string | null {\n const parsed = parsePkhDid(did);\n return parsed ? `did:pkh:eip155:${parsed.chainId}:${toChecksumAddress(parsed.address)}` : null;\n}\n\n/** Parses `did:pkh:eip155:<chainId>:<address>`; null for anything else (no byte fallback). */\nexport function parsePkhDid(did: unknown): PkhDid | null {\n if (typeof did !== \"string\") return null;\n const match = /^did:pkh:eip155:(0|[1-9][0-9]{0,15}):(0x[0-9a-fA-F]{40})$/.exec(did);\n if (!match || !isValidAddress(match[2]!)) return null;\n return { chainId: match[1]!, address: match[2]!.toLowerCase() };\n}\n\n/** Principal equality: the exact eip155 chain id and the same address bytes. Never compares strings. */\nexport function pkhPrincipalsEqual(a: unknown, b: unknown): boolean {\n const left = parsePkhDid(a);\n const right = parsePkhDid(b);\n return left !== null && right !== null && left.chainId === right.chainId && left.address === right.address;\n}\n\n/**\n * The one key billing, Exo and credentials.org use for a verified subject: the lowercase 20-byte\n * address. It is chain-independent because v1 accepts only externally owned accounts (their control\n * does not depend on the chain). Accepting contract wallets or other chains would require adding\n * the chain here.\n */\nexport function registryKey(subject: string): string {\n const parsed = parsePkhDid(subject);\n if (!parsed) throw new TypeError(\"registryKey needs a did:pkh:eip155 subject\");\n return parsed.address;\n}\n","import { refuse } from \"./errors\";\nimport type { Json } from \"./encoding\";\n\n/**\n * A profile row is the complete definition of a held profile (spec §3.3). Every profile rule is\n * keyed on the row selected by the credential's signed `profile`, never on the issuer.\n */\nexport interface ProfileRow {\n profile: string;\n profileVersion: number;\n vct: string;\n holder: { kind: \"account-pkh\"; chainId: string };\n bindingDomain: string;\n /** Top-level claim names that may be selectively disclosed; nothing else is disclosable. */\n disclosable: readonly string[];\n /** Signed claims beyond the shared set that this row tolerates. */\n optionalSignedClaims: readonly string[];\n /** Largest allowed `exp - iat`, seconds. */\n maxLifetimeSeconds: number;\n /** Accepted `status.method` values. */\n statusMethods: readonly string[];\n /** Checks the disclosed claim values; throws a HeldCredentialError. */\n validateDisclosed(claims: Readonly<Record<string, Json>>): void;\n /**\n * Whether this row admits a credential signed by `issuerDid`. Fixed per shipped profile.\n * Optional for a caller-defined row. When omitted, the row admits any issuer that the issuer\n * entry permits; the entry's own key, kid, and profile checks still apply.\n */\n permitsIssuer?(issuerDid: string): boolean;\n}\n\nexport const HELD_EMAIL_TTL_SECONDS = 365 * 24 * 60 * 60;\nexport const ACCOUNT_BINDING_DOMAIN = \"tinycloud.credentials/account-binding/v1\";\n\nconst ORG_MEMBERSHIP_BINDING_DOMAIN = \"tinycloud.credentials/org-binding/v1\";\nconst ORG_MEMBERSHIP_TTL_SECONDS = 365 * 24 * 60 * 60;\nconst ORG_MEMBERSHIP_FREE_TTL_SECONDS = 30 * 24 * 60 * 60;\n/** Org-family issuer DIDs: `did:web:o` + 26 base32 characters + `.orgs.credentials.org`. */\nconst ORG_ISSUER_DID_PATTERN = /^did:web:o[a-z2-7]{26}\\.orgs\\.credentials\\.org$/;\n/** The only issuer the email row admits (held-credential-profile-v1 spec). */\nconst EMAIL_ISSUER_DID = \"did:web:issuer.credentials.org\";\nconst ORG_ROLES: readonly string[] = [\"member\", \"admin\"];\nconst MEMBERSHIP_TYPE_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;\n\n/**\n * Shipped rows are fixed definitions. Freezing each row and its nested containers keeps a caller\n * from changing a shipped rule through a reference to it.\n */\nfunction freezeRow(row: ProfileRow): ProfileRow {\n Object.freeze(row.holder);\n Object.freeze(row.disclosable);\n Object.freeze(row.optionalSignedClaims);\n Object.freeze(row.statusMethods);\n return Object.freeze(row);\n}\n\nexport const EMAIL_HELD_ROW: ProfileRow = freezeRow({\n profile: \"tinycloud.email-held/v1\",\n profileVersion: 1,\n vct: \"opencredentials.email/v1\",\n holder: { kind: \"account-pkh\", chainId: \"1\" },\n bindingDomain: ACCOUNT_BINDING_DOMAIN,\n disclosable: [\"email\", \"emailDomain\"],\n optionalSignedClaims: [],\n maxLifetimeSeconds: HELD_EMAIL_TTL_SECONDS,\n statusMethods: [\"none\"],\n // The email row admits only its literal issuer. The org family and every other DID are refused.\n permitsIssuer: (issuerDid) => issuerDid === EMAIL_ISSUER_DID,\n validateDisclosed(claims) {\n const { email, emailDomain } = claims;\n if ((typeof email === \"string\" && /[^\\x00-\\x7F]/.test(email)) || (typeof emailDomain === \"string\" && /[^\\x00-\\x7F]/.test(emailDomain))) {\n refuse(\"bad-disclosure\", \"email must be ASCII\");\n }\n if (email !== undefined) {\n if (typeof email === \"string\" && (email.indexOf(\"@\") < 1 || email.indexOf(\"@\") === email.length - 1)) {\n refuse(\"bad-disclosure\", \"email needs a non-empty local part and domain\");\n }\n if (typeof email !== \"string\" || email !== email.toLowerCase() || email.split(\"@\").length !== 2 || email.length < 3 || email.length > 320 || /[\\s\\u0000-\\u001f]/.test(email)) {\n refuse(\"bad-disclosure\", \"email is not a canonical lowercase address\");\n }\n }\n if (emailDomain !== undefined) {\n if (typeof emailDomain !== \"string\" || emailDomain === \"\" || emailDomain !== emailDomain.toLowerCase() || emailDomain.includes(\"@\")) {\n refuse(\"bad-disclosure\", \"emailDomain is not a lowercase domain\");\n }\n }\n if (typeof email === \"string\" && typeof emailDomain === \"string\" && email.slice(email.lastIndexOf(\"@\") + 1) !== emailDomain) {\n refuse(\"bad-disclosure\", \"emailDomain does not match email\");\n }\n },\n});\n\nfunction orgMembershipRow(profile: string, maxLifetimeSeconds: number): ProfileRow {\n return freezeRow({\n profile,\n profileVersion: 1,\n vct: \"opencredentials.org-membership/v1\",\n holder: { kind: \"account-pkh\", chainId: \"1\" },\n bindingDomain: ORG_MEMBERSHIP_BINDING_DOMAIN,\n disclosable: [\"role\", \"membershipType\"],\n optionalSignedClaims: [],\n maxLifetimeSeconds,\n statusMethods: [\"none\"],\n permitsIssuer: (issuerDid) => ORG_ISSUER_DID_PATTERN.test(issuerDid),\n validateDisclosed(claims) {\n const { role, membershipType } = claims;\n if (role !== undefined && (typeof role !== \"string\" || !ORG_ROLES.includes(role))) {\n refuse(\"bad-disclosure\", \"role must be member or admin\");\n }\n if (membershipType !== undefined && (typeof membershipType !== \"string\" || !MEMBERSHIP_TYPE_PATTERN.test(membershipType))) {\n refuse(\"bad-disclosure\", \"membershipType is not a canonical slug\");\n }\n },\n });\n}\n\n/** Paid org membership row (profile `tinycloud.org-membership/v1`). */\nexport const ORG_MEMBERSHIP_ROW: ProfileRow = orgMembershipRow(\"tinycloud.org-membership/v1\", ORG_MEMBERSHIP_TTL_SECONDS);\n/** Free org membership row (profile `tinycloud.org-membership-free/v1`). */\nexport const ORG_MEMBERSHIP_FREE_ROW: ProfileRow = orgMembershipRow(\"tinycloud.org-membership-free/v1\", ORG_MEMBERSHIP_FREE_TTL_SECONDS);\n/** Both org rows. Not in `DEFAULT_ROWS`: a caller opts in by passing them as `rows`. */\nexport const ORG_MEMBERSHIP_ROWS: readonly ProfileRow[] = Object.freeze([ORG_MEMBERSHIP_ROW, ORG_MEMBERSHIP_FREE_ROW]);\n\nexport const DEFAULT_ROWS: readonly ProfileRow[] = Object.freeze([EMAIL_HELD_ROW]);\n\n/**\n * Every row this package ships. The issuer set loader applies each shipped row's `permitsIssuer`\n * whatever rows the caller passes, so a caller row cannot relax a shipped profile's issuer rule.\n */\nexport const SHIPPED_ROWS: readonly ProfileRow[] = Object.freeze([EMAIL_HELD_ROW, ...ORG_MEMBERSHIP_ROWS]);\n\nexport function findRow(rows: readonly ProfileRow[], profile: string, profileVersion: number): ProfileRow | undefined {\n return rows.find((row) => row.profile === profile && row.profileVersion === profileVersion);\n}\n\n/**\n * Whether `row` admits `issuerDid`. A row without `permitsIssuer` admits every issuer its issuer\n * entry permits. A `permitsIssuer` that is present but not a function refuses with `bad-config`,\n * so a malformed rule is never read as permissive. Only a rule that returns exactly `true` admits\n * the issuer; any other result refuses it.\n */\nexport function rowPermitsIssuer(row: ProfileRow, issuerDid: string): boolean {\n const rule = row.permitsIssuer;\n if (rule === undefined) return true;\n if (typeof rule !== \"function\") refuse(\"bad-config\", \"a profile row's permitsIssuer must be a function when present\");\n return rule.call(row, issuerDid) === true;\n}\n","import { decodeBase64Url, isObject } from \"./encoding\";\nimport { isStrictEd25519Point } from \"./crypto\";\nimport { refuse } from \"./errors\";\nimport { SHIPPED_ROWS, rowPermitsIssuer } from \"./rows\";\nimport type { ProfileRow } from \"./rows\";\n\nexport const ISSUER_SET_FORMAT = \"tinycloud.credentials-issuers/v1\";\n\n/** A pinned issuer entry (spec §6). Times are epoch milliseconds. */\nexport interface IssuerEntry {\n issuerDid: string;\n kid: string;\n alg: \"EdDSA\";\n publicKey: Uint8Array;\n vcts: readonly string[];\n profiles: readonly string[];\n notBefore: number;\n /** Rotation: the key stops issuing at this instant. null = not retired. */\n retiredAt: number | null;\n /** Withdrawal: nothing verifies under the key once now >= this. null = no end. */\n notAfter: number | null;\n}\n\nconst CONSTRUCT = Symbol(\"IssuerSet.construct\");\n\n/** Module-private read of the private entry map; assigned once by the class's static block. */\nlet readEntry: (set: IssuerSet, issuerDid: string, kid: string) => IssuerEntry | undefined;\n\n/** A fresh, frozen entry: the public key is a new copy on every lookup. */\nfunction copyEntry(entry: IssuerEntry): IssuerEntry {\n return Object.freeze({ ...entry, publicKey: entry.publicKey.slice() });\n}\n\n/**\n * An issuer set that has passed every rule of `parseIssuerSet`. Only `parseIssuerSet` creates one,\n * and the verifier accepts only genuine instances (`IssuerSet.is`). The instance, its prototype and\n * the class are frozen, and the verifier reads entries through the module-private `lookupIssuerEntry`.\n * Use one build (ESM or CJS) of this package per process. Details: TC-1540\n */\nexport class IssuerSet {\n readonly version: number;\n readonly #entries: ReadonlyMap<string, IssuerEntry>;\n\n static {\n readEntry = (set, issuerDid, kid) => {\n const entry = set.#entries.get(kid);\n return entry && entry.issuerDid === issuerDid ? copyEntry(entry) : undefined;\n };\n }\n\n /** @internal Use `parseIssuerSet`. */\n constructor(token: symbol, version: number, entries: ReadonlyMap<string, IssuerEntry>) {\n if (token !== CONSTRUCT) throw new TypeError(\"IssuerSet can only be created by parseIssuerSet\");\n this.version = version;\n this.#entries = entries;\n Object.freeze(this);\n }\n\n /** True only for an instance built by `parseIssuerSet` (a real private-field brand check). */\n static is(value: unknown): value is IssuerSet {\n return typeof value === \"object\" && value !== null && #entries in value;\n }\n\n /**\n * The one lookup seam (TC-1132 would plug an allow-listed did:web resolver in here). Returns the\n * FULL entry, never just a key, so vct/profile admission and the validity window come from the\n * same entry. Exact (issuerDid, kid) match only. Every call returns a copy.\n */\n resolveIssuer(issuerDid: string, kid: string): IssuerEntry | undefined {\n return readEntry(this, issuerDid, kid);\n }\n}\nObject.freeze(IssuerSet.prototype);\nObject.freeze(IssuerSet);\n\n/** What `verifyHeldPresentation` calls. Not exported from the package index. */\nexport function lookupIssuerEntry(set: IssuerSet, issuerDid: string, kid: string): IssuerEntry | undefined {\n return readEntry(set, issuerDid, kid);\n}\n\nexport interface IssuerSetOptions {\n /** DID methods allowed for issuers (docs/principles.md principle 2), e.g. [\"web\"]. */\n didMethods: { issuer: readonly string[] };\n /** Additional rows whose permitted-issuer rules also apply to the profiles an entry lists. The shipped rows always apply. */\n rows?: readonly ProfileRow[];\n}\n\nconst ENTRY_KEYS = [\"issuerDid\", \"kid\", \"alg\", \"publicKey\", \"vcts\", \"profiles\", \"notBefore\", \"retiredAt\", \"notAfter\"].sort();\nconst RFC3339_Z = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?Z$/;\nconst DID_WEB = /^did:web:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/;\nconst DID_SELF_DESCRIBING = /^did:(?:key|jwk):[A-Za-z0-9_-]+$/;\n\nexport function didMethod(did: string): string | null {\n const match = /^did:([a-z0-9]+):/.exec(did);\n return match ? match[1]! : null;\n}\n\n/** Canonical issuer DID: lowercase, no port, path or query (did:web); `did:key`/`did:jwk` only when allowed. */\nexport function isCanonicalIssuerDid(did: string): boolean {\n return DID_WEB.test(did) || DID_SELF_DESCRIBING.test(did);\n}\n\nexport function isWellFormedKid(kid: string, issuerDid: string): boolean {\n return kid.startsWith(`${issuerDid}#`) && /^[A-Za-z0-9._-]+$/.test(kid.slice(issuerDid.length + 1));\n}\n\nfunction parseTime(value: unknown, field: string): number {\n if (typeof value !== \"string\") return refuse(\"bad-issuer-set\", `${field} must be an RFC 3339 UTC string`);\n const match = RFC3339_Z.exec(value);\n if (!match) return refuse(\"bad-issuer-set\", `${field} must be RFC 3339 UTC ending in Z`);\n const ms = Date.parse(value);\n const [, y, mo, d, h, mi, s] = match.map(Number) as number[];\n const check = new Date(ms);\n if (Number.isNaN(ms) || check.getUTCFullYear() !== y || check.getUTCMonth() + 1 !== mo || check.getUTCDate() !== d || check.getUTCHours() !== h || check.getUTCMinutes() !== mi || check.getUTCSeconds() !== s) {\n return refuse(\"bad-issuer-set\", `${field} is not a real instant`);\n }\n return ms;\n}\n\nfunction stringList(value: unknown, field: string): string[] {\n if (!Array.isArray(value) || value.length === 0 || value.some((v) => typeof v !== \"string\" || v === \"\")) {\n return refuse(\"bad-issuer-set\", `${field} must be a non-empty list of strings`);\n }\n return value as string[];\n}\n\n/** Validates a whole issuer set. Any violation makes the whole set invalid. */\nexport function parseIssuerSet(raw: unknown, options: IssuerSetOptions): IssuerSet {\n if (!isObject(raw) || Object.keys(raw).sort().join() !== [\"format\", \"issuers\", \"version\"].join()) {\n return refuse(\"bad-issuer-set\", \"unexpected shape\");\n }\n if (raw.format !== ISSUER_SET_FORMAT) refuse(\"bad-issuer-set\", \"unknown format\");\n if (typeof raw.version !== \"number\" || !Number.isInteger(raw.version) || raw.version < 1) refuse(\"bad-issuer-set\", \"version must be a positive integer\");\n if (!Array.isArray(raw.issuers) || raw.issuers.length === 0) refuse(\"bad-issuer-set\", \"issuers must be a non-empty list\");\n const rowsForLoad: readonly ProfileRow[] = [...SHIPPED_ROWS, ...(options.rows ?? [])];\n const entries = new Map<string, IssuerEntry>();\n for (const item of raw.issuers) {\n if (!isObject(item) || Object.keys(item).sort().join() !== ENTRY_KEYS.join()) refuse(\"bad-issuer-set\", \"entry has missing or unknown fields\");\n const issuerDid = item.issuerDid, kid = item.kid;\n if (typeof issuerDid !== \"string\" || !isCanonicalIssuerDid(issuerDid)) refuse(\"bad-issuer-set\", \"issuerDid is not canonical\");\n const method = didMethod(issuerDid as string);\n if (method === null || !options.didMethods.issuer.includes(method)) refuse(\"did-method-not-allowed\", `issuer DID method ${method} is not on the allow-list`);\n if (typeof kid !== \"string\" || !isWellFormedKid(kid, issuerDid as string)) refuse(\"bad-issuer-set\", \"kid must be issuerDid#fragment\");\n if (entries.has(kid as string)) refuse(\"bad-issuer-set\", \"duplicate kid\");\n if (item.alg !== \"EdDSA\") refuse(\"bad-issuer-set\", \"alg must be EdDSA\");\n if (typeof item.publicKey !== \"string\") refuse(\"bad-issuer-set\", \"publicKey must be base64url\");\n const publicKey = decodeBase64Url(item.publicKey, \"bad-issuer-set\");\n if (publicKey.length !== 32 || !isStrictEd25519Point(publicKey)) refuse(\"bad-issuer-set\", \"publicKey is not a strict 32-byte Ed25519 key\");\n const notBefore = parseTime(item.notBefore, \"notBefore\");\n const retiredAt = item.retiredAt === null ? null : parseTime(item.retiredAt, \"retiredAt\");\n const notAfter = item.notAfter === null ? null : parseTime(item.notAfter, \"notAfter\");\n if (retiredAt !== null && notAfter !== null && retiredAt > notAfter) refuse(\"bad-issuer-set\", \"retiredAt must not be after notAfter\");\n const listedProfiles = stringList(item.profiles, \"profiles\");\n for (const profile of listedProfiles) {\n for (const row of rowsForLoad) {\n if (row.profile === profile && !rowPermitsIssuer(row, issuerDid as string)) refuse(\"bad-issuer-set\", \"issuer DID is not permitted for a profile it lists\");\n }\n }\n entries.set(kid as string, Object.freeze({\n issuerDid: issuerDid as string,\n kid: kid as string,\n alg: \"EdDSA\",\n publicKey,\n vcts: Object.freeze([...stringList(item.vcts, \"vcts\")]),\n profiles: Object.freeze([...listedProfiles]),\n notBefore,\n retiredAt,\n notAfter,\n }));\n }\n return new IssuerSet(CONSTRUCT, raw.version as number, entries);\n}\n","import { toChecksumAddress } from \"./did\";\nimport { refuse } from \"./errors\";\n\nexport const PRESENTATION_STATEMENT = \"Present a TinyCloud held credential\";\nexport const SD_HASH_RESOURCE_PREFIX = \"urn:tinycloud:sd-hash:\";\n\nexport interface StrictSiwe {\n domain: string;\n address: string;\n statement: string;\n uri: string;\n version: string;\n chainId: string;\n nonce: string;\n issuedAt: string;\n expirationTime?: string;\n notBefore?: string;\n resources: string[];\n}\n\nconst RFC3339 = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,9}))?(?:Z|([+-])(\\d{2}):(\\d{2}))$/;\n\n/**\n * Exact RFC 3339 `date-time` to epoch milliseconds, or null. Every field is range-checked and the\n * date must be real (no Feb 31, hour 24 or second 60), so `Date.parse`'s leniency never decides.\n */\nexport function parseRfc3339(value: string): number | null {\n const m = RFC3339.exec(value);\n if (!m) return null;\n const [year, month, day, hour, minute, second] = [m[1], m[2], m[3], m[4], m[5], m[6]].map(Number) as [number, number, number, number, number, number];\n if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59) return null;\n const date = new Date(0);\n date.setUTCFullYear(year, month - 1, day); // unlike Date.UTC, years 0-99 are not remapped to 19xx\n if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) return null;\n let offsetMinutes = 0;\n if (m[8] !== undefined) {\n const oh = Number(m[9]), om = Number(m[10]);\n if (oh > 23 || om > 59) return null;\n offsetMinutes = (m[8] === \"-\" ? -1 : 1) * (oh * 60 + om);\n }\n const millis = m[7] === undefined ? 0 : Number(m[7].padEnd(3, \"0\").slice(0, 3));\n date.setUTCHours(hour, minute, second, millis);\n return date.getTime() - offsetMinutes * 60_000;\n}\nconst AUDIENCE = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::([1-9][0-9]{0,4}))?$/;\n\n/** An audience is a bare RFC 3986 authority: lowercase host[:port], no scheme, userinfo, path, or default port 443. */\nexport function assertValidAudience(audience: string): void {\n const match = AUDIENCE.exec(audience);\n if (!match || (match[1] !== undefined && (Number(match[1]) > 65535 || match[1] === \"443\"))) {\n refuse(\"bad-config\", \"audience must be a lowercase host[:port] authority without a default port\");\n }\n}\n\n/** Strict EIP-4361 parse in the exact layout this profile accepts. Anything else is refused. */\nexport function parseStrictSiwe(message: string): StrictSiwe {\n const bad = (why: string): never => refuse(\"bad-siwe\", why);\n const lines = message.split(\"\\n\");\n const head = /^(\\S+) wants you to sign in with your Ethereum account:$/.exec(lines[0] ?? \"\");\n if (!head) bad(\"header line\");\n const address = lines[1] ?? \"\";\n if (!/^0x[0-9a-fA-F]{40}$/.test(address)) bad(\"address\");\n if (toChecksumAddress(address) !== address) bad(\"address must be EIP-55 checksummed\");\n if (lines[2] !== \"\") bad(\"layout\");\n const statement = lines[3];\n if (statement === undefined || statement === \"\") bad(\"statement\");\n if (lines[4] !== \"\") bad(\"layout\");\n let index = 5;\n const field = (name: string, optional = false): string | undefined => {\n const line = lines[index];\n const prefix = `${name}: `;\n if (line === undefined || !line.startsWith(prefix)) {\n if (optional) return undefined;\n return bad(`missing ${name}`);\n }\n index += 1;\n const value = line.slice(prefix.length);\n if (value === \"\") bad(`empty ${name}`);\n return value;\n };\n const uri = field(\"URI\")!;\n const version = field(\"Version\")!;\n const chainId = field(\"Chain ID\")!;\n const nonce = field(\"Nonce\")!;\n const issuedAt = field(\"Issued At\")!;\n const expirationTime = field(\"Expiration Time\", true);\n const notBefore = field(\"Not Before\", true);\n if (lines[index]?.startsWith(\"Request ID: \")) bad(\"Request ID is not accepted\");\n const resources: string[] = [];\n if (lines[index] === \"Resources:\") {\n index += 1;\n while (lines[index] !== undefined && lines[index]!.startsWith(\"- \")) { resources.push(lines[index]!.slice(2)); index += 1; }\n }\n if (index !== lines.length) bad(\"trailing content\");\n if (!/^[0-9]+$/.test(chainId)) bad(\"chain id\");\n if (!/^[A-Za-z0-9]{8,}$/.test(nonce)) bad(\"nonce must be alphanumeric\");\n for (const [name, value] of [[\"Issued At\", issuedAt], [\"Expiration Time\", expirationTime], [\"Not Before\", notBefore]] as const) {\n if (value !== undefined && parseRfc3339(value) === null) bad(`${name} is not RFC 3339`);\n }\n return { domain: head![1]!, address, statement, uri, version, chainId, nonce, issuedAt, expirationTime, notBefore, resources };\n}\n","import { recoverEip191Address, sha256, verifyEd25519Strict } from \"./crypto\";\nimport { canonicalPkhDid, parsePkhDid, pkhPrincipalsEqual, registryKey } from \"./did\";\nimport { asciiBytes, decodeBase64Url, decodeUtf8, encodeBase64Url, hexToBytes, isAscii, isObject, parseStrictJson, type Json } from \"./encoding\";\nimport { refuse } from \"./errors\";\nimport { IssuerSet, didMethod, lookupIssuerEntry, isWellFormedKid, type IssuerEntry } from \"./issuer-set\";\nimport type { NonceStore } from \"./nonce-store\";\nimport { DEFAULT_ROWS, findRow, rowPermitsIssuer, type ProfileRow } from \"./rows\";\nimport { PRESENTATION_STATEMENT, SD_HASH_RESOURCE_PREFIX, assertValidAudience, parseRfc3339, parseStrictSiwe } from \"./siwe\";\n\n\nexport interface HeldPresentation {\n sdJwt: string;\n siwe: { message: string; signature: string };\n}\n\nexport interface AcceptedIssuer { issuerDid: string; kid?: string }\nexport interface AcceptedProfile { profile: string; profileVersion: number }\n\nexport interface VerifyHeldPresentationOptions {\n /** Explicit clock. The verifier never reads one. */\n now: Date;\n issuerSet: IssuerSet;\n /** From the owner-signed policy or the caller's allow-list. Empty is refused. */\n acceptedIssuers: readonly AcceptedIssuer[];\n /** Empty is refused. */\n acceptedProfiles: readonly AcceptedProfile[];\n /** docs/principles.md principle 2. Issuer methods the caller enables, e.g. [\"web\"]. */\n didMethods: { issuer: readonly string[] };\n /** This verifier's own EIP-4361 domain (an authority). One distinct audience per verifier. */\n audience: string;\n nonceStore: NonceStore;\n /**\n * The authenticated caller's account (`did:pkh:eip155:...`). Nonces are minted bound to it, and\n * `subject` must be principal-equal to it. A presentation is not authentication (spec §4).\n */\n callerAccount: string;\n /** Defaults to the email row. */\n rows?: readonly ProfileRow[];\n}\n\nexport interface VerifiedHeldCredential {\n issuer: string;\n subject: string;\n /** Lowercase 20-byte address; equals `registryKey(subject)`. */\n subjectAddress: string;\n expiresAt: Date;\n credentialId: string;\n profile: string;\n profileVersion: number;\n vct: string;\n claims: Record<string, Json>;\n}\n\nconst SHARED_SIGNED = new Set([\"iss\", \"sub\", \"iat\", \"nbf\", \"exp\", \"jti\", \"vct\", \"profile\", \"profileVersion\", \"holderBinding\", \"status\", \"_sd_alg\", \"_sd\"]);\nconst RESERVED_CLAIMS = new Set([...SHARED_SIGNED, \"...\", \"cnf\"]);\n\n/** Size limits, enforced on the raw strings before any decoding or parsing (TC-1540). */\nexport const HELD_LIMITS = {\n sdJwtChars: 16384,\n siweMessageChars: 2048,\n siweSignatureChars: 132,\n disclosures: 8,\n disclosureChars: 1024,\n headerSegmentChars: 1024,\n payloadSegmentChars: 4096,\n signatureSegmentChars: 128,\n} as const;\n\nconst HEADER_MEMBERS = new Set([\"alg\", \"typ\", \"kid\"]);\nconst JTI_UUID_V4 = /^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;\n\nfunction sdHash(sdJwt: string): string {\n return encodeBase64Url(sha256(asciiBytes(sdJwt)));\n}\n\nfunction isSafeInt(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value);\n}\n\n/**\n * Verifies a held-credential presentation (spec §4.4). Fail closed: the first failed check throws a\n * HeldCredentialError. The caller-bound nonce is spent first, on any outcome. No payload claim is read before the issuer signature has verified.\n */\nexport async function verifyHeldPresentation(presentation: unknown, options: VerifyHeldPresentationOptions): Promise<VerifiedHeldCredential> {\n // 1. shape and inputs (nothing is looked up yet)\n const { now, issuerSet, acceptedIssuers, acceptedProfiles, didMethods, audience, nonceStore, callerAccount } = options;\n const rows = options.rows ?? DEFAULT_ROWS;\n if (!(now instanceof Date) || Number.isNaN(now.getTime())) refuse(\"bad-config\", \"now must be a valid Date\");\n if (!IssuerSet.is(issuerSet)) refuse(\"bad-config\", \"issuerSet must come from parseIssuerSet\");\n if (!nonceStore || !didMethods || !Array.isArray(didMethods.issuer)) refuse(\"bad-config\", \"issuerSet, nonceStore and didMethods are required\");\n if (!Array.isArray(acceptedIssuers) || acceptedIssuers.length === 0) refuse(\"bad-config\", \"acceptedIssuers must not be empty\");\n if (!Array.isArray(acceptedProfiles) || acceptedProfiles.length === 0) refuse(\"bad-config\", \"acceptedProfiles must not be empty\");\n assertValidAudience(audience);\n if (parsePkhDid(callerAccount) === null) refuse(\"bad-config\", \"callerAccount must be a did:pkh:eip155 account\");\n if (!isObject(presentation) || Object.keys(presentation).sort().join() !== \"sdJwt,siwe\") refuse(\"bad-presentation\", \"unexpected shape\");\n const { sdJwt, siwe } = presentation as { sdJwt: unknown; siwe: unknown };\n if (typeof sdJwt !== \"string\" || !isAscii(sdJwt)) refuse(\"bad-presentation\", \"sdJwt must be an ASCII string\");\n if (!isObject(siwe) || Object.keys(siwe).sort().join() !== \"message,signature\" || typeof siwe.message !== \"string\" || typeof siwe.signature !== \"string\") refuse(\"bad-presentation\", \"siwe shape\");\n const sdJwtText = sdJwt as string;\n const siweMessage = siwe.message as string;\n const siweSignature = siwe.signature as string;\n // bounded before anything is split, decoded or parsed (a refusal here spends no nonce)\n if (sdJwtText.length > HELD_LIMITS.sdJwtChars) refuse(\"bad-presentation\", \"sdJwt is too long\");\n if (siweMessage.length > HELD_LIMITS.siweMessageChars) refuse(\"bad-presentation\", \"siwe message is too long\");\n if (siweSignature.length > HELD_LIMITS.siweSignatureChars) refuse(\"bad-presentation\", \"siwe signature is too long\");\n\n // 2. SIWE fields and the nonce. The nonce is consumed first, on any outcome, by one atomic\n // compare-and-delete that includes the authenticated caller's account; each presentation uses its\n // nonce once.\n const message = parseStrictSiwe(siweMessage);\n if (message.domain !== audience) refuse(\"bad-siwe\", \"domain is not this verifier's audience\");\n if (!/^[0-9A-Za-z]{22,}$/.test(message.nonce)) refuse(\"bad-nonce\", \"nonce must be at least 22 base62 characters (or 32 hex)\");\n const consumed = await nonceStore.consume(audience, message.nonce, callerAccount);\n if (!consumed.existed) refuse(\"nonce-replayed\", \"nonce is unknown, already used, or bound to another account\");\n\n const parts = sdJwtText.split(\"~\");\n if (parts.length < 2 || parts[parts.length - 1] !== \"\") refuse(\"bad-presentation\", \"sdJwt must end with ~ and carry no key-binding JWT\");\n const [jwt, ...disclosureParts] = parts.slice(0, -1) as [string, ...string[]];\n if (disclosureParts.some((d) => d === \"\")) refuse(\"bad-presentation\", \"empty disclosure\");\n if (disclosureParts.length > HELD_LIMITS.disclosures) refuse(\"bad-presentation\", \"too many disclosures\");\n if (disclosureParts.some((d) => d.length > HELD_LIMITS.disclosureChars)) refuse(\"bad-presentation\", \"disclosure is too long\");\n const segments = jwt.split(\".\");\n if (segments.length !== 3 || segments.some((s) => s === \"\")) refuse(\"bad-presentation\", \"JWT must have three segments\");\n const [headerB64, payloadB64, signatureB64] = segments as [string, string, string];\n if (headerB64.length > HELD_LIMITS.headerSegmentChars || payloadB64.length > HELD_LIMITS.payloadSegmentChars || signatureB64.length > HELD_LIMITS.signatureSegmentChars) refuse(\"bad-presentation\", \"JWT segment is too long\");\n\n // 2. protected header only\n const header = parseStrictJson(decodeUtf8(decodeBase64Url(headerB64, \"bad-header\"), \"bad-header\"), \"bad-header\");\n if (!isObject(header)) refuse(\"bad-header\", \"header must be an object\");\n for (const member of Object.keys(header)) if (!HEADER_MEMBERS.has(member)) refuse(\"bad-header\", `unexpected header member ${member}`);\n if (header.alg !== \"EdDSA\") refuse(\"bad-header\", \"alg must be EdDSA\");\n if (header.typ !== \"vc+sd-jwt\") refuse(\"bad-header\", \"typ must be vc+sd-jwt\");\n const kid = header.kid;\n if (typeof kid !== \"string\") refuse(\"bad-header\", \"kid is required\");\n const hash = (kid as string).indexOf(\"#\");\n if (hash < 1) refuse(\"bad-header\", \"kid must be a DID URL with a fragment\");\n const issuerDid = (kid as string).slice(0, hash);\n if (!isWellFormedKid(kid as string, issuerDid)) refuse(\"bad-header\", \"kid must be issuerDid#fragment\");\n const method = didMethod(issuerDid);\n if (method === null || !didMethods.issuer.includes(method)) refuse(\"did-method-not-allowed\", `issuer DID method ${method} is not on the allow-list`);\n\n // 3. policy selection: the presenter never chooses the issuer\n if (!acceptedIssuers.some((a) => a.issuerDid === issuerDid && (a.kid === undefined || a.kid === kid))) refuse(\"issuer-not-accepted\", \"issuer is not accepted by this policy\");\n\n // 4. entry lookup by the exact pair, then the checks that need only `now`\n const entry: IssuerEntry | undefined = lookupIssuerEntry(issuerSet, issuerDid, kid as string);\n if (!entry) refuse(\"unknown-issuer\", \"no issuer-set entry for this kid\");\n if (entry.issuerDid !== issuerDid || entry.kid !== kid) refuse(\"unknown-issuer\", \"resolved entry is not the requested (issuerDid, kid)\");\n const nowMs = now.getTime();\n if (nowMs < entry.notBefore) refuse(\"issuer-not-yet-valid\", \"issuer key is not yet valid\");\n if (entry.notAfter !== null && nowMs >= entry.notAfter) refuse(\"issuer-stopped\", \"issuer key is past its hard stop\");\n\n // 5. signature\n const signature = decodeBase64Url(signatureB64, \"bad-signature\");\n if (!verifyEd25519Strict(signature, asciiBytes(`${headerB64}.${payloadB64}`), entry.publicKey)) refuse(\"bad-signature\", \"issuer signature did not verify\");\n\n // 6. only now decode the payload\n const payload = parseStrictJson(decodeUtf8(decodeBase64Url(payloadB64, \"bad-payload\"), \"bad-payload\"), \"bad-payload\");\n if (!isObject(payload)) refuse(\"bad-payload\", \"payload must be an object\");\n if (payload.iss !== issuerDid) refuse(\"bad-payload\", \"iss must equal the DID part of kid\");\n const { iat, nbf, exp } = payload;\n if (!isSafeInt(iat)) refuse(\"bad-payload\", \"iat is required\");\n if (!isSafeInt(nbf) || nbf !== iat) refuse(\"bad-payload\", \"nbf must equal iat\");\n if (!isSafeInt(exp)) refuse(\"bad-payload\", \"exp is required\");\n if (entry.notBefore > (iat as number) * 1000) refuse(\"issuer-not-yet-valid\", \"credential predates the issuer key\");\n if (entry.retiredAt !== null && (iat as number) * 1000 >= entry.retiredAt) refuse(\"issuer-retired\", \"issued at or after the issuer key was retired\");\n\n // 7. profile row and admission\n if (typeof payload.profile !== \"string\" || !isSafeInt(payload.profileVersion) || typeof payload.vct !== \"string\") refuse(\"bad-payload\", \"profile, profileVersion and vct are required\");\n const profile = payload.profile as string, profileVersion = payload.profileVersion as number, vct = payload.vct as string;\n const row = findRow(rows, profile, profileVersion);\n if (!row) refuse(\"profile-mismatch\", \"unknown profile\");\n if (!acceptedProfiles.some((p) => p.profile === profile && p.profileVersion === profileVersion)) refuse(\"profile-not-accepted\", \"profile is not accepted by this caller\");\n if (row.vct !== vct) refuse(\"profile-mismatch\", \"vct is not the row's vct\");\n if (!entry.vcts.includes(vct) || !entry.profiles.includes(profile)) refuse(\"profile-mismatch\", \"issuer entry does not admit this vct and profile\");\n if (!rowPermitsIssuer(row, issuerDid)) refuse(\"issuer-not-accepted\", \"this profile row does not permit the issuer DID\");\n\n // 8. claims against the row\n for (const key of Object.keys(payload)) {\n if (!SHARED_SIGNED.has(key) && !row.optionalSignedClaims.includes(key)) refuse(\"bad-payload\", `unexpected claim ${key}`);\n }\n if (payload._sd_alg !== \"sha-256\") refuse(\"bad-payload\", \"_sd_alg must be sha-256\");\n if (!Array.isArray(payload._sd) || payload._sd.some((d) => typeof d !== \"string\")) refuse(\"bad-payload\", \"_sd must be a list of digests\");\n if (new Set(payload._sd as string[]).size !== payload._sd.length) refuse(\"bad-payload\", \"_sd must not repeat a digest (RFC 9901 section 7.1)\");\n const nowSeconds = nowMs / 1000;\n if (nowSeconds < (nbf as number)) refuse(\"not-yet-valid\", \"credential is not yet valid\");\n if (nowSeconds >= (exp as number)) refuse(\"expired\", \"credential has expired\");\n const lifetime = (exp as number) - (iat as number);\n if (!Number.isSafeInteger(lifetime) || lifetime <= 0 || lifetime > row.maxLifetimeSeconds) refuse(\"bad-lifetime\", \"exp - iat is outside the row's lifetime\");\n if (typeof payload.jti !== \"string\" || !JTI_UUID_V4.test(payload.jti)) refuse(\"bad-payload\", \"jti must be a urn:uuid v4\");\n const status = payload.status;\n if (!isObject(status) || Object.keys(status).join() !== \"method\" || typeof status.method !== \"string\" || !row.statusMethods.includes(status.method)) refuse(\"bad-status\", \"status method is not accepted\");\n const subject = payload.sub;\n const subjectPkh = parsePkhDid(subject);\n if (!subjectPkh || typeof subject !== \"string\" || subjectPkh.chainId !== row.holder.chainId) refuse(\"bad-subject\", \"sub is not an eip155 did:pkh on the row's chain\");\n const binding = payload.holderBinding;\n if (!isObject(binding) || Object.keys(binding).sort().join() !== \"did,signingDomain\" || binding.signingDomain !== row.bindingDomain || !pkhPrincipalsEqual(binding.did, subject)) refuse(\"bad-holder-binding\", \"holderBinding does not match the row and sub\");\n\n // 9. disclosures\n const digests = new Set(payload._sd as string[]);\n const seen = new Set<string>();\n const claims: Record<string, Json> = {};\n for (const disclosure of disclosureParts) {\n const digest = encodeBase64Url(sha256(asciiBytes(disclosure)));\n if (!digests.has(digest)) refuse(\"bad-disclosure\", \"disclosure digest is not in _sd\");\n if (seen.has(digest)) refuse(\"bad-disclosure\", \"repeated disclosure\");\n seen.add(digest);\n const decoded = parseStrictJson(decodeUtf8(decodeBase64Url(disclosure, \"bad-disclosure\"), \"bad-disclosure\"), \"bad-disclosure\");\n if (!Array.isArray(decoded) || decoded.length !== 3 || typeof decoded[0] !== \"string\" || typeof decoded[1] !== \"string\") refuse(\"bad-disclosure\", \"disclosure must be [salt, name, value]\");\n const [, name, value] = decoded as [string, string, Json];\n if (RESERVED_CLAIMS.has(name) || row.optionalSignedClaims.includes(name)) refuse(\"bad-disclosure\", \"claim name collides with a signed claim\");\n if (!row.disclosable.includes(name)) refuse(\"bad-disclosure\", \"claim is not disclosable for this profile\");\n if (typeof value !== \"string\") refuse(\"bad-disclosure\", \"disclosed value must be a string (no nested _sd or array elements)\");\n if (Object.prototype.hasOwnProperty.call(claims, name)) refuse(\"bad-disclosure\", \"claim disclosed twice\");\n claims[name] = value;\n }\n row.validateDisclosed(claims);\n\n // 10. SIWE fields against the credential\n let origin: string | undefined;\n try { const url = new URL(message.uri); origin = url.protocol === \"https:\" && !url.username && !url.password ? url.origin : undefined; } catch { origin = undefined; }\n if (origin !== `https://${audience}`) refuse(\"bad-siwe\", \"uri must be an https origin matching the audience\");\n if (message.version !== \"1\") refuse(\"bad-siwe\", \"version must be 1\");\n if (message.statement !== PRESENTATION_STATEMENT) refuse(\"bad-siwe\", \"unexpected statement\");\n if (message.chainId !== subjectPkh.chainId) refuse(\"bad-siwe\", \"chain id must equal the credential subject's\");\n if (!pkhPrincipalsEqual(`did:pkh:eip155:${message.chainId}:${message.address}`, subject)) refuse(\"bad-siwe\", \"address is not the credential subject\");\n if (message.resources.length !== 1 || message.resources.some((r) => r.toLowerCase().startsWith(\"urn:recap:\")) || message.resources[0] !== `${SD_HASH_RESOURCE_PREFIX}${sdHash(sdJwtText)}`) refuse(\"bad-siwe\", \"resources must be exactly the sd_hash of this presentation\");\n if (message.expirationTime !== undefined && nowMs >= (parseRfc3339(message.expirationTime) as number)) refuse(\"bad-siwe\", \"SIWE has expired\");\n if (message.notBefore !== undefined && nowMs < (parseRfc3339(message.notBefore) as number)) refuse(\"bad-siwe\", \"SIWE is not yet valid\");\n const signatureBytes = hexToBytes(siweSignature.startsWith(\"0x\") ? siweSignature.slice(2) : \"zz\", \"bad-siwe-signature\");\n const recovered = recoverEip191Address(siweMessage, signatureBytes);\n if (recovered === null || recovered !== subjectPkh.address) refuse(\"bad-siwe-signature\", \"signature does not recover the credential subject\");\n\n // 11. the credential's subject must be the authenticated caller (a presentation is not authentication)\n if (!pkhPrincipalsEqual(callerAccount, subject)) refuse(\"nonce-account-mismatch\", \"subject is not the authenticated caller\");\n\n return {\n issuer: issuerDid,\n subject: canonicalPkhDid(subject) as string,\n subjectAddress: registryKey(subject as string),\n expiresAt: new Date((exp as number) * 1000),\n credentialId: payload.jti as string,\n profile,\n profileVersion,\n vct,\n claims,\n };\n}\n\n","import { parsePkhDid, pkhPrincipalsEqual } from \"./did\";\n\n/**\n * Single use is a property of the store. `consume` is one atomic compare-and-delete and the only\n * nonce operation a verifier performs: it deletes the entry only where the audience, the nonce and\n * the bound account all match (for example `DELETE ... WHERE nonce = ? AND audience = ? AND\n * owner_did = ? RETURNING`). The verifier calls it before the signature and claim checks. Every\n * nonce is minted bound to the authenticated caller's account. Production stores: a Durable Object,\n * or D1/Postgres with a conditional delete; there is no stateless mode, because single use does not\n * rely on timestamps. Details: TC-1540\n */\nexport interface NonceStore {\n consume(audience: string, nonce: string, account: string): Promise<{ existed: boolean }>;\n}\n\nexport function randomNonce(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * In-memory store for tests and single-process tools. Deployments use a shared store that implements\n * `consume` atomically. Entries do not expire: a nonce lives until it is consumed or the process ends.\n */\nexport class InMemoryNonceStore implements NonceStore {\n private readonly nonces = new Map<string, string>();\n\n /** Mints a nonce for `audience`, bound to the authenticated caller's `did:pkh` account. */\n issue(audience: string, account: string): string {\n if (parsePkhDid(account) === null) throw new TypeError(\"a nonce must be bound to a did:pkh:eip155 account\");\n const nonce = randomNonce();\n this.nonces.set(`${audience}\\n${nonce}`, account);\n return nonce;\n }\n\n async consume(audience: string, nonce: string, account: string): Promise<{ existed: boolean }> {\n const key = `${audience}\\n${nonce}`;\n const bound = this.nonces.get(key);\n if (bound === undefined || !pkhPrincipalsEqual(bound, account)) return { existed: false };\n this.nonces.delete(key);\n return { existed: true };\n }\n}\n","/**\n * THE single shared email normalisation function (gap G14; the same rules as billing's\n * `src/email-normalise.ts`, TC-1439). The alias rules below are confirmed by Sam on 2026-10-19\n * (card tc-1439-email-aliases); a change to them changes only here.\n *\n * - trim and lowercase;\n * - fold a `+tag` suffix of the local part, for every domain;\n * - remove dots of the local part for gmail.com and googlemail.com only (googlemail.com maps to\n * gmail.com, as in billing's implementation).\n *\n * Unicode/IDNA handling is being defined. Until then, inputs containing a UTF-16 code unit above\n * 0x7F are refused, checked before trim and lowercase. Billing applies the same rule.\n *\n * Returns null for empty or malformed input: callers must fail closed.\n */\nexport function normaliseEmail(email: string): string | null {\n if (typeof email === \"string\" && /[^\\x00-\\x7F]/.test(email)) return null;\n const trimmed = typeof email === \"string\" ? email.trim().toLowerCase() : \"\";\n if (trimmed === \"\" || /\\s/.test(trimmed)) return null;\n const parts = trimmed.split(\"@\");\n if (parts.length !== 2) return null;\n let [local, domain] = parts as [string, string];\n if (local === \"\" || domain === \"\") return null;\n if (domain === \"googlemail.com\") domain = \"gmail.com\";\n if (!domain.includes(\".\") || domain.startsWith(\".\") || domain.endsWith(\".\") || domain.includes(\"..\")) return null;\n const plus = local.indexOf(\"+\");\n if (plus !== -1) local = local.slice(0, plus);\n if (domain === \"gmail.com\") local = local.replace(/\\./g, \"\");\n if (local === \"\") return null;\n return `${local}@${domain}`;\n}\n"],"mappings":";;;;;;;;;;AA6BO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAE7C,YAAY,MAA+B,SAAiB;AAC1D,UAAM,GAAG,IAAI,KAAK,OAAO,EAAE;AAC3B,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,OAAO,MAA+B,SAAwB;AAC5E,QAAM,IAAI,oBAAoB,MAAM,OAAO;AAC7C;;;ACxCA,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,cAAc;;;ACFvB,IAAM,SAAS;AAER,SAAS,gBAAgB,OAA2B;AACzD,MAAI,SAAS;AACb,aAAW,QAAQ,MAAO,WAAU,OAAO,aAAa,IAAI;AAC5D,SAAO,KAAK,MAAM,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AAC/E;AAGO,SAAS,gBAAgB,OAAe,OAAgC,oBAAgC;AAC7G,MAAI,CAAC,OAAO,KAAK,KAAK,KAAK,MAAM,SAAS,MAAM,EAAG,QAAO,MAAM,wBAAwB;AACxF,QAAM,WAAW,MAAM,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAC3D,QAAM,SAAS,KAAK,WAAW,IAAI,QAAQ,IAAK,SAAS,SAAS,KAAM,CAAC,CAAC;AAC1E,QAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,EAAG,OAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AACzE,MAAI,gBAAgB,KAAK,MAAM,MAAO,QAAO,MAAM,yBAAyB;AAC5E,SAAO;AACT;AAEO,SAAS,QAAQ,OAAwB;AAC9C,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,EAAG,KAAI,MAAM,WAAW,CAAC,IAAI,IAAM,QAAO;AACjF,SAAO;AACT;AAEO,SAAS,WAAW,OAA2B;AACpD,QAAM,QAAQ,IAAI,WAAW,MAAM,MAAM;AACzC,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,EAAG,OAAM,CAAC,IAAI,MAAM,WAAW,CAAC;AACvE,SAAO;AACT;AAEO,SAAS,WAAW,OAA2B;AACpD,SAAO,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC1E;AAEO,SAAS,WAAW,KAAa,OAAgC,oBAAgC;AACtG,MAAI,CAAC,wBAAwB,KAAK,GAAG,EAAG,QAAO,MAAM,SAAS;AAC9D,QAAM,QAAQ,IAAI,WAAW,IAAI,SAAS,CAAC;AAC3C,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,EAAG,OAAM,CAAC,IAAI,SAAS,IAAI,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;AAC7F,SAAO;AACT;AAEA,IAAM,cAAc,IAAI,YAAY,SAAS,EAAE,OAAO,MAAM,WAAW,KAAK,CAAC;AAGtE,SAAS,WAAW,OAAmB,MAAuC;AACnF,MAAI;AAAE,WAAO,YAAY,OAAO,KAAK;AAAA,EAAG,QAAQ;AAAE,WAAO,OAAO,MAAM,iBAAiB;AAAA,EAAG;AAC5F;AAIO,SAAS,SAAS,OAAkD;AACzE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAGO,IAAM,iBAAiB;AAG9B,IAAM,eAAe;AAOd,SAAS,gBAAgB,MAAc,MAAqC;AACjF,MAAI,IAAI;AACR,MAAI,QAAQ;AACZ,QAAM,OAAO,CAAC,SAAwB,OAAO,MAAM,iBAAiB,IAAI,EAAE;AAC1E,QAAM,KAAK,MAAM;AAAE,WAAO,IAAI,KAAK,UAAU,SAAU,SAAS,KAAK,CAAC,CAAE,EAAG,MAAK;AAAA,EAAG;AACnF,QAAM,SAAS,MAAc;AAC3B,UAAM,QAAQ;AACd,SAAK;AACL,WAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,IAAK,MAAK,KAAK,CAAC,MAAM,OAAO,IAAI;AACvE,QAAI,KAAK,KAAK,OAAQ,MAAK,qBAAqB;AAChD,SAAK;AACL,QAAI;AAAE,aAAO,KAAK,MAAM,KAAK,MAAM,OAAO,CAAC,CAAC;AAAA,IAAa,QAAQ;AAAE,aAAO,KAAK,YAAY;AAAA,IAAG;AAAA,EAChG;AACA,QAAM,QAAQ,MAAY;AACxB,OAAG;AACH,UAAM,IAAI,KAAK,CAAC;AAChB,SAAK,MAAM,OAAO,MAAM,SAAS,SAAS,KAAK,eAAgB,MAAK,kBAAkB;AACtF,QAAI,MAAM,KAAK;AACb,WAAK;AACL,YAAM,MAA+B,uBAAO,OAAO,IAAI;AACvD,SAAG;AACH,UAAI,KAAK,CAAC,MAAM,KAAK;AAAE,aAAK;AAAG,iBAAS;AAAG,eAAO;AAAA,MAAK;AACvD,iBAAS;AACP,WAAG;AACH,YAAI,KAAK,CAAC,MAAM,IAAK,MAAK,cAAc;AACxC,cAAM,MAAM,OAAO;AACnB,YAAI,OAAO,UAAU,eAAe,KAAK,KAAK,GAAG,EAAG,MAAK,eAAe;AACxE,WAAG;AACH,YAAI,KAAK,CAAC,MAAM,IAAK,MAAK,gBAAgB;AAC1C,aAAK;AACL,YAAI,GAAG,IAAI,MAAM;AACjB,WAAG;AACH,YAAI,KAAK,CAAC,MAAM,KAAK;AAAE,eAAK;AAAG;AAAA,QAAU;AACzC,YAAI,KAAK,CAAC,MAAM,KAAK;AAAE,eAAK;AAAG,mBAAS;AAAG,iBAAO;AAAA,QAAK;AACvD,aAAK,iBAAiB;AAAA,MACxB;AAAA,IACF;AACA,QAAI,MAAM,KAAK;AACb,WAAK;AACL,YAAM,MAAc,CAAC;AACrB,SAAG;AACH,UAAI,KAAK,CAAC,MAAM,KAAK;AAAE,aAAK;AAAG,iBAAS;AAAG,eAAO;AAAA,MAAK;AACvD,iBAAS;AACP,YAAI,KAAK,MAAM,CAAC;AAChB,WAAG;AACH,YAAI,KAAK,CAAC,MAAM,KAAK;AAAE,eAAK;AAAG;AAAA,QAAU;AACzC,YAAI,KAAK,CAAC,MAAM,KAAK;AAAE,eAAK;AAAG,mBAAS;AAAG,iBAAO;AAAA,QAAK;AACvD,aAAK,iBAAiB;AAAA,MACxB;AAAA,IACF;AACA,QAAI,MAAM,IAAK,QAAO,OAAO;AAC7B,iBAAa,YAAY;AACzB,UAAM,UAAU,aAAa,KAAK,IAAI;AACtC,QAAI,CAAC,QAAS,QAAO,KAAK,kBAAkB;AAC5C,QAAI,QAAQ,CAAC,MAAM,KAAM,MAAK,oBAAoB;AAClD,SAAK,QAAQ,CAAC,EAAE;AAChB,WAAO,KAAK,MAAM,QAAQ,CAAC,CAAC;AAAA,EAC9B;AACA,QAAM,SAAS,MAAM;AACrB,KAAG;AACH,MAAI,MAAM,KAAK,OAAQ,MAAK,eAAe;AAC3C,SAAO;AACT;;;ADxHA,IAAM,QAAQ,QAAQ;AAGf,SAAS,qBAAqB,OAA4B;AAC/D,MAAI,MAAM,WAAW,GAAI,QAAO;AAChC,MAAI;AACF,WAAO,CAAC,MAAM,QAAQ,OAAO,KAAK,EAAE,aAAa;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,oBAAoB,WAAuB,SAAqB,WAAgC;AAC9G,MAAI,UAAU,WAAW,MAAM,CAAC,qBAAqB,SAAS,KAAK,CAAC,qBAAqB,UAAU,SAAS,GAAG,EAAE,CAAC,EAAG,QAAO;AAC5H,MAAI;AACF,QAAI,CAAC,QAAQ,OAAO,WAAW,SAAS,WAAW,EAAE,QAAQ,MAAM,CAAC,EAAG,QAAO;AAI9E,UAAM,IAAI,MAAM,QAAQ,UAAU,SAAS,GAAG,EAAE,GAAG,KAAK;AACxD,UAAM,IAAI,MAAM,QAAQ,WAAW,KAAK;AACxC,UAAM,IAAI,gBAAgB,UAAU,SAAS,EAAE,CAAC;AAChD,UAAM,IAAI,gBAAgB,OAAO,WAAW,KAAK,CAAC,GAAG,UAAU,SAAS,GAAG,EAAE,GAAG,GAAG,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,QAAQ,MAAM;AAC7H,WAAO,MAAM,KAAK,eAAe,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBAAgB,OAA2B;AAClD,MAAI,MAAM;AACV,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,EAAG,OAAO,OAAO,KAAM,OAAO,MAAM,CAAC,CAAE;AACnF,SAAO;AACT;AAGO,SAAS,qBAAqB,SAAiB,WAAsC;AAC1F,MAAI,UAAU,WAAW,GAAI,QAAO;AACpC,QAAM,IAAI,UAAU,EAAE;AACtB,QAAM,WAAW,KAAK,KAAK,IAAI,KAAK;AACpC,MAAI,aAAa,KAAK,aAAa,EAAG,QAAO;AAC7C,MAAI;AACF,UAAM,OAAO,IAAI,YAAY,EAAE,OAAO,OAAO;AAC7C,UAAM,SAAS,WAAW;AAAA,EAAiC,KAAK,MAAM,EAAE;AACxE,UAAM,SAAS,WAAW,WAAW,KAAK,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAC,CAAC;AAC/D,UAAM,YAAY,UAAU,UAAU,YAAY,UAAU,SAAS,GAAG,EAAE,CAAC,EAAE,eAAe,QAAQ,EAAE,iBAAiB,MAAM,EAAE,WAAW,KAAK;AAC/I,WAAO,KAAK,WAAW,WAAW,UAAU,SAAS,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;AAAA,EACxE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AErDO,SAAS,kBAAkB,SAAyB;AACzD,QAAM,QAAQ,QAAQ,MAAM,CAAC,EAAE,YAAY;AAC3C,QAAM,OAAO,WAAW,WAAW,KAAK,CAAC;AACzC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC9B,UAAM,SAAU,KAAK,KAAK,CAAC,MAAO,IAAI,MAAM,IAAI,IAAI,KAAM;AAC1D,WAAO,UAAU,IAAI,MAAM,CAAC,EAAG,YAAY,IAAI,MAAM,CAAC;AAAA,EACxD;AACA,SAAO;AACT;AAGO,SAAS,eAAe,SAA0B;AACvD,MAAI,CAAC,sBAAsB,KAAK,OAAO,EAAG,QAAO;AACjD,QAAM,OAAO,QAAQ,MAAM,CAAC;AAC5B,MAAI,SAAS,KAAK,YAAY,EAAG,QAAO;AACxC,SAAO,kBAAkB,OAAO,MAAM;AACxC;AAGO,SAAS,gBAAgB,KAA6B;AAC3D,QAAM,SAAS,YAAY,GAAG;AAC9B,SAAO,SAAS,kBAAkB,OAAO,OAAO,IAAI,kBAAkB,OAAO,OAAO,CAAC,KAAK;AAC5F;AAGO,SAAS,YAAY,KAA6B;AACvD,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,QAAQ,4DAA4D,KAAK,GAAG;AAClF,MAAI,CAAC,SAAS,CAAC,eAAe,MAAM,CAAC,CAAE,EAAG,QAAO;AACjD,SAAO,EAAE,SAAS,MAAM,CAAC,GAAI,SAAS,MAAM,CAAC,EAAG,YAAY,EAAE;AAChE;AAGO,SAAS,mBAAmB,GAAY,GAAqB;AAClE,QAAM,OAAO,YAAY,CAAC;AAC1B,QAAM,QAAQ,YAAY,CAAC;AAC3B,SAAO,SAAS,QAAQ,UAAU,QAAQ,KAAK,YAAY,MAAM,WAAW,KAAK,YAAY,MAAM;AACrG;AAQO,SAAS,YAAY,SAAyB;AACnD,QAAM,SAAS,YAAY,OAAO;AAClC,MAAI,CAAC,OAAQ,OAAM,IAAI,UAAU,4CAA4C;AAC7E,SAAO,OAAO;AAChB;;;AC7BO,IAAM,yBAAyB,MAAM,KAAK,KAAK;AAC/C,IAAM,yBAAyB;AAEtC,IAAM,gCAAgC;AACtC,IAAM,6BAA6B,MAAM,KAAK,KAAK;AACnD,IAAM,kCAAkC,KAAK,KAAK,KAAK;AAEvD,IAAM,yBAAyB;AAE/B,IAAM,mBAAmB;AACzB,IAAM,YAA+B,CAAC,UAAU,OAAO;AACvD,IAAM,0BAA0B;AAMhC,SAAS,UAAU,KAA6B;AAC9C,SAAO,OAAO,IAAI,MAAM;AACxB,SAAO,OAAO,IAAI,WAAW;AAC7B,SAAO,OAAO,IAAI,oBAAoB;AACtC,SAAO,OAAO,IAAI,aAAa;AAC/B,SAAO,OAAO,OAAO,GAAG;AAC1B;AAEO,IAAM,iBAA6B,UAAU;AAAA,EAClD,SAAS;AAAA,EACT,gBAAgB;AAAA,EAChB,KAAK;AAAA,EACL,QAAQ,EAAE,MAAM,eAAe,SAAS,IAAI;AAAA,EAC5C,eAAe;AAAA,EACf,aAAa,CAAC,SAAS,aAAa;AAAA,EACpC,sBAAsB,CAAC;AAAA,EACvB,oBAAoB;AAAA,EACpB,eAAe,CAAC,MAAM;AAAA;AAAA,EAEtB,eAAe,CAAC,cAAc,cAAc;AAAA,EAC5C,kBAAkB,QAAQ;AACxB,UAAM,EAAE,OAAO,YAAY,IAAI;AAC/B,QAAK,OAAO,UAAU,YAAY,eAAe,KAAK,KAAK,KAAO,OAAO,gBAAgB,YAAY,eAAe,KAAK,WAAW,GAAI;AACtI,aAAO,kBAAkB,qBAAqB;AAAA,IAChD;AACA,QAAI,UAAU,QAAW;AACvB,UAAI,OAAO,UAAU,aAAa,MAAM,QAAQ,GAAG,IAAI,KAAK,MAAM,QAAQ,GAAG,MAAM,MAAM,SAAS,IAAI;AACpG,eAAO,kBAAkB,+CAA+C;AAAA,MAC1E;AACA,UAAI,OAAO,UAAU,YAAY,UAAU,MAAM,YAAY,KAAK,MAAM,MAAM,GAAG,EAAE,WAAW,KAAK,MAAM,SAAS,KAAK,MAAM,SAAS,OAAO,oBAAoB,KAAK,KAAK,GAAG;AAC5K,eAAO,kBAAkB,4CAA4C;AAAA,MACvE;AAAA,IACF;AACA,QAAI,gBAAgB,QAAW;AAC7B,UAAI,OAAO,gBAAgB,YAAY,gBAAgB,MAAM,gBAAgB,YAAY,YAAY,KAAK,YAAY,SAAS,GAAG,GAAG;AACnI,eAAO,kBAAkB,uCAAuC;AAAA,MAClE;AAAA,IACF;AACA,QAAI,OAAO,UAAU,YAAY,OAAO,gBAAgB,YAAY,MAAM,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,aAAa;AAC3H,aAAO,kBAAkB,kCAAkC;AAAA,IAC7D;AAAA,EACF;AACF,CAAC;AAED,SAAS,iBAAiB,SAAiB,oBAAwC;AACjF,SAAO,UAAU;AAAA,IACf;AAAA,IACA,gBAAgB;AAAA,IAChB,KAAK;AAAA,IACL,QAAQ,EAAE,MAAM,eAAe,SAAS,IAAI;AAAA,IAC5C,eAAe;AAAA,IACf,aAAa,CAAC,QAAQ,gBAAgB;AAAA,IACtC,sBAAsB,CAAC;AAAA,IACvB;AAAA,IACA,eAAe,CAAC,MAAM;AAAA,IACtB,eAAe,CAAC,cAAc,uBAAuB,KAAK,SAAS;AAAA,IACnE,kBAAkB,QAAQ;AACxB,YAAM,EAAE,MAAM,eAAe,IAAI;AACjC,UAAI,SAAS,WAAc,OAAO,SAAS,YAAY,CAAC,UAAU,SAAS,IAAI,IAAI;AACjF,eAAO,kBAAkB,8BAA8B;AAAA,MACzD;AACA,UAAI,mBAAmB,WAAc,OAAO,mBAAmB,YAAY,CAAC,wBAAwB,KAAK,cAAc,IAAI;AACzH,eAAO,kBAAkB,wCAAwC;AAAA,MACnE;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAGO,IAAM,qBAAiC,iBAAiB,+BAA+B,0BAA0B;AAEjH,IAAM,0BAAsC,iBAAiB,oCAAoC,+BAA+B;AAEhI,IAAM,sBAA6C,OAAO,OAAO,CAAC,oBAAoB,uBAAuB,CAAC;AAE9G,IAAM,eAAsC,OAAO,OAAO,CAAC,cAAc,CAAC;AAM1E,IAAM,eAAsC,OAAO,OAAO,CAAC,gBAAgB,GAAG,mBAAmB,CAAC;AAElG,SAAS,QAAQ,MAA6B,SAAiB,gBAAgD;AACpH,SAAO,KAAK,KAAK,CAAC,QAAQ,IAAI,YAAY,WAAW,IAAI,mBAAmB,cAAc;AAC5F;AAQO,SAAS,iBAAiB,KAAiB,WAA4B;AAC5E,QAAM,OAAO,IAAI;AACjB,MAAI,SAAS,OAAW,QAAO;AAC/B,MAAI,OAAO,SAAS,WAAY,QAAO,cAAc,+DAA+D;AACpH,SAAO,KAAK,KAAK,KAAK,SAAS,MAAM;AACvC;;;AC5IO,IAAM,oBAAoB;AAiBjC,IAAM,YAAY,uBAAO,qBAAqB;AAG9C,IAAI;AAGJ,SAAS,UAAU,OAAiC;AAClD,SAAO,OAAO,OAAO,EAAE,GAAG,OAAO,WAAW,MAAM,UAAU,MAAM,EAAE,CAAC;AACvE;AA/BA;AAuCO,IAAM,YAAN,MAAgB;AAAA;AAAA,EAYrB,YAAY,OAAe,SAAiB,SAA2C;AAVvF,uBAAS;AAWP,QAAI,UAAU,UAAW,OAAM,IAAI,UAAU,iDAAiD;AAC9F,SAAK,UAAU;AACf,uBAAK,UAAW;AAChB,WAAO,OAAO,IAAI;AAAA,EACpB;AAAA;AAAA,EAGA,OAAO,GAAG,OAAoC;AAC5C,WAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,sBAAY;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,WAAmB,KAAsC;AACrE,WAAO,UAAU,MAAM,WAAW,GAAG;AAAA,EACvC;AACF;AA9BW;AAGP,YAAY,CAAC,KAAK,WAAW,QAAQ;AACnC,QAAM,QAAQ,kBAAI,UAAS,IAAI,GAAG;AAClC,SAAO,SAAS,MAAM,cAAc,YAAY,UAAU,KAAK,IAAI;AACrE;AAyBJ,OAAO,OAAO,UAAU,SAAS;AACjC,OAAO,OAAO,SAAS;AAGhB,SAAS,kBAAkB,KAAgB,WAAmB,KAAsC;AACzG,SAAO,UAAU,KAAK,WAAW,GAAG;AACtC;AASA,IAAM,aAAa,CAAC,aAAa,OAAO,OAAO,aAAa,QAAQ,YAAY,aAAa,aAAa,UAAU,EAAE,KAAK;AAC3H,IAAM,YAAY;AAClB,IAAM,UAAU;AAChB,IAAM,sBAAsB;AAErB,SAAS,UAAU,KAA4B;AACpD,QAAM,QAAQ,oBAAoB,KAAK,GAAG;AAC1C,SAAO,QAAQ,MAAM,CAAC,IAAK;AAC7B;AAGO,SAAS,qBAAqB,KAAsB;AACzD,SAAO,QAAQ,KAAK,GAAG,KAAK,oBAAoB,KAAK,GAAG;AAC1D;AAEO,SAAS,gBAAgB,KAAa,WAA4B;AACvE,SAAO,IAAI,WAAW,GAAG,SAAS,GAAG,KAAK,oBAAoB,KAAK,IAAI,MAAM,UAAU,SAAS,CAAC,CAAC;AACpG;AAEA,SAAS,UAAU,OAAgB,OAAuB;AACxD,MAAI,OAAO,UAAU,SAAU,QAAO,OAAO,kBAAkB,GAAG,KAAK,iCAAiC;AACxG,QAAM,QAAQ,UAAU,KAAK,KAAK;AAClC,MAAI,CAAC,MAAO,QAAO,OAAO,kBAAkB,GAAG,KAAK,mCAAmC;AACvF,QAAM,KAAK,KAAK,MAAM,KAAK;AAC3B,QAAM,CAAC,EAAE,GAAG,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,MAAM,IAAI,MAAM;AAC/C,QAAM,QAAQ,IAAI,KAAK,EAAE;AACzB,MAAI,OAAO,MAAM,EAAE,KAAK,MAAM,eAAe,MAAM,KAAK,MAAM,YAAY,IAAI,MAAM,MAAM,MAAM,WAAW,MAAM,KAAK,MAAM,YAAY,MAAM,KAAK,MAAM,cAAc,MAAM,MAAM,MAAM,cAAc,MAAM,GAAG;AAC9M,WAAO,OAAO,kBAAkB,GAAG,KAAK,wBAAwB;AAAA,EAClE;AACA,SAAO;AACT;AAEA,SAAS,WAAW,OAAgB,OAAyB;AAC3D,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,KAAK,MAAM,KAAK,CAAC,MAAM,OAAO,MAAM,YAAY,MAAM,EAAE,GAAG;AACvG,WAAO,OAAO,kBAAkB,GAAG,KAAK,sCAAsC;AAAA,EAChF;AACA,SAAO;AACT;AAGO,SAAS,eAAe,KAAc,SAAsC;AACjF,MAAI,CAAC,SAAS,GAAG,KAAK,OAAO,KAAK,GAAG,EAAE,KAAK,EAAE,KAAK,MAAM,CAAC,UAAU,WAAW,SAAS,EAAE,KAAK,GAAG;AAChG,WAAO,OAAO,kBAAkB,kBAAkB;AAAA,EACpD;AACA,MAAI,IAAI,WAAW,kBAAmB,QAAO,kBAAkB,gBAAgB;AAC/E,MAAI,OAAO,IAAI,YAAY,YAAY,CAAC,OAAO,UAAU,IAAI,OAAO,KAAK,IAAI,UAAU,EAAG,QAAO,kBAAkB,oCAAoC;AACvJ,MAAI,CAAC,MAAM,QAAQ,IAAI,OAAO,KAAK,IAAI,QAAQ,WAAW,EAAG,QAAO,kBAAkB,kCAAkC;AACxH,QAAM,cAAqC,CAAC,GAAG,cAAc,GAAI,QAAQ,QAAQ,CAAC,CAAE;AACpF,QAAM,UAAU,oBAAI,IAAyB;AAC7C,aAAW,QAAQ,IAAI,SAAS;AAC9B,QAAI,CAAC,SAAS,IAAI,KAAK,OAAO,KAAK,IAAI,EAAE,KAAK,EAAE,KAAK,MAAM,WAAW,KAAK,EAAG,QAAO,kBAAkB,qCAAqC;AAC5I,UAAM,YAAY,KAAK,WAAW,MAAM,KAAK;AAC7C,QAAI,OAAO,cAAc,YAAY,CAAC,qBAAqB,SAAS,EAAG,QAAO,kBAAkB,4BAA4B;AAC5H,UAAM,SAAS,UAAU,SAAmB;AAC5C,QAAI,WAAW,QAAQ,CAAC,QAAQ,WAAW,OAAO,SAAS,MAAM,EAAG,QAAO,0BAA0B,qBAAqB,MAAM,2BAA2B;AAC3J,QAAI,OAAO,QAAQ,YAAY,CAAC,gBAAgB,KAAK,SAAmB,EAAG,QAAO,kBAAkB,gCAAgC;AACpI,QAAI,QAAQ,IAAI,GAAa,EAAG,QAAO,kBAAkB,eAAe;AACxE,QAAI,KAAK,QAAQ,QAAS,QAAO,kBAAkB,mBAAmB;AACtE,QAAI,OAAO,KAAK,cAAc,SAAU,QAAO,kBAAkB,6BAA6B;AAC9F,UAAM,YAAY,gBAAgB,KAAK,WAAW,gBAAgB;AAClE,QAAI,UAAU,WAAW,MAAM,CAAC,qBAAqB,SAAS,EAAG,QAAO,kBAAkB,+CAA+C;AACzI,UAAM,YAAY,UAAU,KAAK,WAAW,WAAW;AACvD,UAAM,YAAY,KAAK,cAAc,OAAO,OAAO,UAAU,KAAK,WAAW,WAAW;AACxF,UAAM,WAAW,KAAK,aAAa,OAAO,OAAO,UAAU,KAAK,UAAU,UAAU;AACpF,QAAI,cAAc,QAAQ,aAAa,QAAQ,YAAY,SAAU,QAAO,kBAAkB,sCAAsC;AACpI,UAAM,iBAAiB,WAAW,KAAK,UAAU,UAAU;AAC3D,eAAW,WAAW,gBAAgB;AACpC,iBAAW,OAAO,aAAa;AAC7B,YAAI,IAAI,YAAY,WAAW,CAAC,iBAAiB,KAAK,SAAmB,EAAG,QAAO,kBAAkB,oDAAoD;AAAA,MAC3J;AAAA,IACF;AACA,YAAQ,IAAI,KAAe,OAAO,OAAO;AAAA,MACvC;AAAA,MACA;AAAA,MACA,KAAK;AAAA,MACL;AAAA,MACA,MAAM,OAAO,OAAO,CAAC,GAAG,WAAW,KAAK,MAAM,MAAM,CAAC,CAAC;AAAA,MACtD,UAAU,OAAO,OAAO,CAAC,GAAG,cAAc,CAAC;AAAA,MAC3C;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC,CAAC;AAAA,EACJ;AACA,SAAO,IAAI,UAAU,WAAW,IAAI,SAAmB,OAAO;AAChE;;;ACxKO,IAAM,yBAAyB;AAC/B,IAAM,0BAA0B;AAgBvC,IAAM,UAAU;AAMT,SAAS,aAAa,OAA8B;AACzD,QAAM,IAAI,QAAQ,KAAK,KAAK;AAC5B,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,CAAC,MAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,MAAM;AAChG,MAAI,QAAQ,KAAK,QAAQ,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,GAAI,QAAO;AAC/E,QAAM,OAAO,oBAAI,KAAK,CAAC;AACvB,OAAK,eAAe,MAAM,QAAQ,GAAG,GAAG;AACxC,MAAI,KAAK,eAAe,MAAM,QAAQ,KAAK,YAAY,MAAM,QAAQ,KAAK,KAAK,WAAW,MAAM,IAAK,QAAO;AAC5G,MAAI,gBAAgB;AACpB,MAAI,EAAE,CAAC,MAAM,QAAW;AACtB,UAAM,KAAK,OAAO,EAAE,CAAC,CAAC,GAAG,KAAK,OAAO,EAAE,EAAE,CAAC;AAC1C,QAAI,KAAK,MAAM,KAAK,GAAI,QAAO;AAC/B,qBAAiB,EAAE,CAAC,MAAM,MAAM,KAAK,MAAM,KAAK,KAAK;AAAA,EACvD;AACA,QAAM,SAAS,EAAE,CAAC,MAAM,SAAY,IAAI,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,GAAG,EAAE,MAAM,GAAG,CAAC,CAAC;AAC9E,OAAK,YAAY,MAAM,QAAQ,QAAQ,MAAM;AAC7C,SAAO,KAAK,QAAQ,IAAI,gBAAgB;AAC1C;AACA,IAAM,WAAW;AAGV,SAAS,oBAAoB,UAAwB;AAC1D,QAAM,QAAQ,SAAS,KAAK,QAAQ;AACpC,MAAI,CAAC,SAAU,MAAM,CAAC,MAAM,WAAc,OAAO,MAAM,CAAC,CAAC,IAAI,SAAS,MAAM,CAAC,MAAM,QAAS;AAC1F,WAAO,cAAc,2EAA2E;AAAA,EAClG;AACF;AAGO,SAAS,gBAAgB,SAA6B;AAC3D,QAAM,MAAM,CAAC,QAAuB,OAAO,YAAY,GAAG;AAC1D,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,OAAO,2DAA2D,KAAK,MAAM,CAAC,KAAK,EAAE;AAC3F,MAAI,CAAC,KAAM,KAAI,aAAa;AAC5B,QAAM,UAAU,MAAM,CAAC,KAAK;AAC5B,MAAI,CAAC,sBAAsB,KAAK,OAAO,EAAG,KAAI,SAAS;AACvD,MAAI,kBAAkB,OAAO,MAAM,QAAS,KAAI,oCAAoC;AACpF,MAAI,MAAM,CAAC,MAAM,GAAI,KAAI,QAAQ;AACjC,QAAM,YAAY,MAAM,CAAC;AACzB,MAAI,cAAc,UAAa,cAAc,GAAI,KAAI,WAAW;AAChE,MAAI,MAAM,CAAC,MAAM,GAAI,KAAI,QAAQ;AACjC,MAAI,QAAQ;AACZ,QAAM,QAAQ,CAAC,MAAc,WAAW,UAA8B;AACpE,UAAM,OAAO,MAAM,KAAK;AACxB,UAAM,SAAS,GAAG,IAAI;AACtB,QAAI,SAAS,UAAa,CAAC,KAAK,WAAW,MAAM,GAAG;AAClD,UAAI,SAAU,QAAO;AACrB,aAAO,IAAI,WAAW,IAAI,EAAE;AAAA,IAC9B;AACA,aAAS;AACT,UAAM,QAAQ,KAAK,MAAM,OAAO,MAAM;AACtC,QAAI,UAAU,GAAI,KAAI,SAAS,IAAI,EAAE;AACrC,WAAO;AAAA,EACT;AACA,QAAM,MAAM,MAAM,KAAK;AACvB,QAAM,UAAU,MAAM,SAAS;AAC/B,QAAM,UAAU,MAAM,UAAU;AAChC,QAAM,QAAQ,MAAM,OAAO;AAC3B,QAAM,WAAW,MAAM,WAAW;AAClC,QAAM,iBAAiB,MAAM,mBAAmB,IAAI;AACpD,QAAM,YAAY,MAAM,cAAc,IAAI;AAC1C,MAAI,MAAM,KAAK,GAAG,WAAW,cAAc,EAAG,KAAI,4BAA4B;AAC9E,QAAM,YAAsB,CAAC;AAC7B,MAAI,MAAM,KAAK,MAAM,cAAc;AACjC,aAAS;AACT,WAAO,MAAM,KAAK,MAAM,UAAa,MAAM,KAAK,EAAG,WAAW,IAAI,GAAG;AAAE,gBAAU,KAAK,MAAM,KAAK,EAAG,MAAM,CAAC,CAAC;AAAG,eAAS;AAAA,IAAG;AAAA,EAC7H;AACA,MAAI,UAAU,MAAM,OAAQ,KAAI,kBAAkB;AAClD,MAAI,CAAC,WAAW,KAAK,OAAO,EAAG,KAAI,UAAU;AAC7C,MAAI,CAAC,oBAAoB,KAAK,KAAK,EAAG,KAAI,4BAA4B;AACtE,aAAW,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,aAAa,QAAQ,GAAG,CAAC,mBAAmB,cAAc,GAAG,CAAC,cAAc,SAAS,CAAC,GAAY;AAC9H,QAAI,UAAU,UAAa,aAAa,KAAK,MAAM,KAAM,KAAI,GAAG,IAAI,kBAAkB;AAAA,EACxF;AACA,SAAO,EAAE,QAAQ,KAAM,CAAC,GAAI,SAAS,WAAW,KAAK,SAAS,SAAS,OAAO,UAAU,gBAAgB,WAAW,UAAU;AAC/H;;;AC/CA,IAAM,gBAAgB,oBAAI,IAAI,CAAC,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,WAAW,kBAAkB,iBAAiB,UAAU,WAAW,KAAK,CAAC;AACzJ,IAAM,kBAAkB,oBAAI,IAAI,CAAC,GAAG,eAAe,OAAO,KAAK,CAAC;AAGzD,IAAM,cAAc;AAAA,EACzB,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,uBAAuB;AACzB;AAEA,IAAM,iBAAiB,oBAAI,IAAI,CAAC,OAAO,OAAO,KAAK,CAAC;AACpD,IAAM,cAAc;AAEpB,SAAS,OAAO,OAAuB;AACrC,SAAO,gBAAgB,OAAO,WAAW,KAAK,CAAC,CAAC;AAClD;AAEA,SAAS,UAAU,OAAiC;AAClD,SAAO,OAAO,UAAU,YAAY,OAAO,cAAc,KAAK;AAChE;AAMA,eAAsB,uBAAuB,cAAuB,SAAyE;AAE3I,QAAM,EAAE,KAAK,WAAW,iBAAiB,kBAAkB,YAAY,UAAU,YAAY,cAAc,IAAI;AAC/G,QAAM,OAAO,QAAQ,QAAQ;AAC7B,MAAI,EAAE,eAAe,SAAS,OAAO,MAAM,IAAI,QAAQ,CAAC,EAAG,QAAO,cAAc,0BAA0B;AAC1G,MAAI,CAAC,UAAU,GAAG,SAAS,EAAG,QAAO,cAAc,yCAAyC;AAC5F,MAAI,CAAC,cAAc,CAAC,cAAc,CAAC,MAAM,QAAQ,WAAW,MAAM,EAAG,QAAO,cAAc,mDAAmD;AAC7I,MAAI,CAAC,MAAM,QAAQ,eAAe,KAAK,gBAAgB,WAAW,EAAG,QAAO,cAAc,mCAAmC;AAC7H,MAAI,CAAC,MAAM,QAAQ,gBAAgB,KAAK,iBAAiB,WAAW,EAAG,QAAO,cAAc,oCAAoC;AAChI,sBAAoB,QAAQ;AAC5B,MAAI,YAAY,aAAa,MAAM,KAAM,QAAO,cAAc,gDAAgD;AAC9G,MAAI,CAAC,SAAS,YAAY,KAAK,OAAO,KAAK,YAAY,EAAE,KAAK,EAAE,KAAK,MAAM,aAAc,QAAO,oBAAoB,kBAAkB;AACtI,QAAM,EAAE,OAAO,KAAK,IAAI;AACxB,MAAI,OAAO,UAAU,YAAY,CAAC,QAAQ,KAAK,EAAG,QAAO,oBAAoB,+BAA+B;AAC5G,MAAI,CAAC,SAAS,IAAI,KAAK,OAAO,KAAK,IAAI,EAAE,KAAK,EAAE,KAAK,MAAM,uBAAuB,OAAO,KAAK,YAAY,YAAY,OAAO,KAAK,cAAc,SAAU,QAAO,oBAAoB,YAAY;AACjM,QAAM,YAAY;AAClB,QAAM,cAAc,KAAK;AACzB,QAAM,gBAAgB,KAAK;AAE3B,MAAI,UAAU,SAAS,YAAY,WAAY,QAAO,oBAAoB,mBAAmB;AAC7F,MAAI,YAAY,SAAS,YAAY,iBAAkB,QAAO,oBAAoB,0BAA0B;AAC5G,MAAI,cAAc,SAAS,YAAY,mBAAoB,QAAO,oBAAoB,4BAA4B;AAKlH,QAAM,UAAU,gBAAgB,WAAW;AAC3C,MAAI,QAAQ,WAAW,SAAU,QAAO,YAAY,wCAAwC;AAC5F,MAAI,CAAC,qBAAqB,KAAK,QAAQ,KAAK,EAAG,QAAO,aAAa,yDAAyD;AAC5H,QAAM,WAAW,MAAM,WAAW,QAAQ,UAAU,QAAQ,OAAO,aAAa;AAChF,MAAI,CAAC,SAAS,QAAS,QAAO,kBAAkB,6DAA6D;AAE7G,QAAM,QAAQ,UAAU,MAAM,GAAG;AACjC,MAAI,MAAM,SAAS,KAAK,MAAM,MAAM,SAAS,CAAC,MAAM,GAAI,QAAO,oBAAoB,oDAAoD;AACvI,QAAM,CAAC,KAAK,GAAG,eAAe,IAAI,MAAM,MAAM,GAAG,EAAE;AACnD,MAAI,gBAAgB,KAAK,CAAC,MAAM,MAAM,EAAE,EAAG,QAAO,oBAAoB,kBAAkB;AACxF,MAAI,gBAAgB,SAAS,YAAY,YAAa,QAAO,oBAAoB,sBAAsB;AACvG,MAAI,gBAAgB,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY,eAAe,EAAG,QAAO,oBAAoB,wBAAwB;AAC5H,QAAM,WAAW,IAAI,MAAM,GAAG;AAC9B,MAAI,SAAS,WAAW,KAAK,SAAS,KAAK,CAAC,MAAM,MAAM,EAAE,EAAG,QAAO,oBAAoB,8BAA8B;AACtH,QAAM,CAAC,WAAW,YAAY,YAAY,IAAI;AAC9C,MAAI,UAAU,SAAS,YAAY,sBAAsB,WAAW,SAAS,YAAY,uBAAuB,aAAa,SAAS,YAAY,sBAAuB,QAAO,oBAAoB,yBAAyB;AAG7N,QAAM,SAAS,gBAAgB,WAAW,gBAAgB,WAAW,YAAY,GAAG,YAAY,GAAG,YAAY;AAC/G,MAAI,CAAC,SAAS,MAAM,EAAG,QAAO,cAAc,0BAA0B;AACtE,aAAW,UAAU,OAAO,KAAK,MAAM,EAAG,KAAI,CAAC,eAAe,IAAI,MAAM,EAAG,QAAO,cAAc,4BAA4B,MAAM,EAAE;AACpI,MAAI,OAAO,QAAQ,QAAS,QAAO,cAAc,mBAAmB;AACpE,MAAI,OAAO,QAAQ,YAAa,QAAO,cAAc,uBAAuB;AAC5E,QAAM,MAAM,OAAO;AACnB,MAAI,OAAO,QAAQ,SAAU,QAAO,cAAc,iBAAiB;AACnE,QAAM,OAAQ,IAAe,QAAQ,GAAG;AACxC,MAAI,OAAO,EAAG,QAAO,cAAc,uCAAuC;AAC1E,QAAM,YAAa,IAAe,MAAM,GAAG,IAAI;AAC/C,MAAI,CAAC,gBAAgB,KAAe,SAAS,EAAG,QAAO,cAAc,gCAAgC;AACrG,QAAM,SAAS,UAAU,SAAS;AAClC,MAAI,WAAW,QAAQ,CAAC,WAAW,OAAO,SAAS,MAAM,EAAG,QAAO,0BAA0B,qBAAqB,MAAM,2BAA2B;AAGnJ,MAAI,CAAC,gBAAgB,KAAK,CAAC,MAAM,EAAE,cAAc,cAAc,EAAE,QAAQ,UAAa,EAAE,QAAQ,IAAI,EAAG,QAAO,uBAAuB,uCAAuC;AAG5K,QAAM,QAAiC,kBAAkB,WAAW,WAAW,GAAa;AAC5F,MAAI,CAAC,MAAO,QAAO,kBAAkB,kCAAkC;AACvE,MAAI,MAAM,cAAc,aAAa,MAAM,QAAQ,IAAK,QAAO,kBAAkB,sDAAsD;AACvI,QAAM,QAAQ,IAAI,QAAQ;AAC1B,MAAI,QAAQ,MAAM,UAAW,QAAO,wBAAwB,6BAA6B;AACzF,MAAI,MAAM,aAAa,QAAQ,SAAS,MAAM,SAAU,QAAO,kBAAkB,kCAAkC;AAGnH,QAAM,YAAY,gBAAgB,cAAc,eAAe;AAC/D,MAAI,CAAC,oBAAoB,WAAW,WAAW,GAAG,SAAS,IAAI,UAAU,EAAE,GAAG,MAAM,SAAS,EAAG,QAAO,iBAAiB,iCAAiC;AAGzJ,QAAM,UAAU,gBAAgB,WAAW,gBAAgB,YAAY,aAAa,GAAG,aAAa,GAAG,aAAa;AACpH,MAAI,CAAC,SAAS,OAAO,EAAG,QAAO,eAAe,2BAA2B;AACzE,MAAI,QAAQ,QAAQ,UAAW,QAAO,eAAe,oCAAoC;AACzF,QAAM,EAAE,KAAK,KAAK,IAAI,IAAI;AAC1B,MAAI,CAAC,UAAU,GAAG,EAAG,QAAO,eAAe,iBAAiB;AAC5D,MAAI,CAAC,UAAU,GAAG,KAAK,QAAQ,IAAK,QAAO,eAAe,oBAAoB;AAC9E,MAAI,CAAC,UAAU,GAAG,EAAG,QAAO,eAAe,iBAAiB;AAC5D,MAAI,MAAM,YAAa,MAAiB,IAAM,QAAO,wBAAwB,oCAAoC;AACjH,MAAI,MAAM,cAAc,QAAS,MAAiB,OAAQ,MAAM,UAAW,QAAO,kBAAkB,+CAA+C;AAGnJ,MAAI,OAAO,QAAQ,YAAY,YAAY,CAAC,UAAU,QAAQ,cAAc,KAAK,OAAO,QAAQ,QAAQ,SAAU,QAAO,eAAe,8CAA8C;AACtL,QAAM,UAAU,QAAQ,SAAmB,iBAAiB,QAAQ,gBAA0B,MAAM,QAAQ;AAC5G,QAAM,MAAM,QAAQ,MAAM,SAAS,cAAc;AACjD,MAAI,CAAC,IAAK,QAAO,oBAAoB,iBAAiB;AACtD,MAAI,CAAC,iBAAiB,KAAK,CAAC,MAAM,EAAE,YAAY,WAAW,EAAE,mBAAmB,cAAc,EAAG,QAAO,wBAAwB,wCAAwC;AACxK,MAAI,IAAI,QAAQ,IAAK,QAAO,oBAAoB,0BAA0B;AAC1E,MAAI,CAAC,MAAM,KAAK,SAAS,GAAG,KAAK,CAAC,MAAM,SAAS,SAAS,OAAO,EAAG,QAAO,oBAAoB,kDAAkD;AACjJ,MAAI,CAAC,iBAAiB,KAAK,SAAS,EAAG,QAAO,uBAAuB,iDAAiD;AAGtH,aAAW,OAAO,OAAO,KAAK,OAAO,GAAG;AACtC,QAAI,CAAC,cAAc,IAAI,GAAG,KAAK,CAAC,IAAI,qBAAqB,SAAS,GAAG,EAAG,QAAO,eAAe,oBAAoB,GAAG,EAAE;AAAA,EACzH;AACA,MAAI,QAAQ,YAAY,UAAW,QAAO,eAAe,yBAAyB;AAClF,MAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,OAAO,MAAM,QAAQ,EAAG,QAAO,eAAe,+BAA+B;AACxI,MAAI,IAAI,IAAI,QAAQ,GAAe,EAAE,SAAS,QAAQ,IAAI,OAAQ,QAAO,eAAe,qDAAqD;AAC7I,QAAM,aAAa,QAAQ;AAC3B,MAAI,aAAc,IAAgB,QAAO,iBAAiB,6BAA6B;AACvF,MAAI,cAAe,IAAgB,QAAO,WAAW,wBAAwB;AAC7E,QAAM,WAAY,MAAkB;AACpC,MAAI,CAAC,OAAO,cAAc,QAAQ,KAAK,YAAY,KAAK,WAAW,IAAI,mBAAoB,QAAO,gBAAgB,yCAAyC;AAC3J,MAAI,OAAO,QAAQ,QAAQ,YAAY,CAAC,YAAY,KAAK,QAAQ,GAAG,EAAG,QAAO,eAAe,2BAA2B;AACxH,QAAM,SAAS,QAAQ;AACvB,MAAI,CAAC,SAAS,MAAM,KAAK,OAAO,KAAK,MAAM,EAAE,KAAK,MAAM,YAAY,OAAO,OAAO,WAAW,YAAY,CAAC,IAAI,cAAc,SAAS,OAAO,MAAM,EAAG,QAAO,cAAc,+BAA+B;AACzM,QAAM,UAAU,QAAQ;AACxB,QAAM,aAAa,YAAY,OAAO;AACtC,MAAI,CAAC,cAAc,OAAO,YAAY,YAAY,WAAW,YAAY,IAAI,OAAO,QAAS,QAAO,eAAe,iDAAiD;AACpK,QAAM,UAAU,QAAQ;AACxB,MAAI,CAAC,SAAS,OAAO,KAAK,OAAO,KAAK,OAAO,EAAE,KAAK,EAAE,KAAK,MAAM,uBAAuB,QAAQ,kBAAkB,IAAI,iBAAiB,CAAC,mBAAmB,QAAQ,KAAK,OAAO,EAAG,QAAO,sBAAsB,8CAA8C;AAG7P,QAAM,UAAU,IAAI,IAAI,QAAQ,GAAe;AAC/C,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,SAA+B,CAAC;AACtC,aAAW,cAAc,iBAAiB;AACxC,UAAM,SAAS,gBAAgB,OAAO,WAAW,UAAU,CAAC,CAAC;AAC7D,QAAI,CAAC,QAAQ,IAAI,MAAM,EAAG,QAAO,kBAAkB,iCAAiC;AACpF,QAAI,KAAK,IAAI,MAAM,EAAG,QAAO,kBAAkB,qBAAqB;AACpE,SAAK,IAAI,MAAM;AACf,UAAM,UAAU,gBAAgB,WAAW,gBAAgB,YAAY,gBAAgB,GAAG,gBAAgB,GAAG,gBAAgB;AAC7H,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,KAAK,OAAO,QAAQ,CAAC,MAAM,YAAY,OAAO,QAAQ,CAAC,MAAM,SAAU,QAAO,kBAAkB,wCAAwC;AAC1L,UAAM,CAAC,EAAE,MAAM,KAAK,IAAI;AACxB,QAAI,gBAAgB,IAAI,IAAI,KAAK,IAAI,qBAAqB,SAAS,IAAI,EAAG,QAAO,kBAAkB,yCAAyC;AAC5I,QAAI,CAAC,IAAI,YAAY,SAAS,IAAI,EAAG,QAAO,kBAAkB,2CAA2C;AACzG,QAAI,OAAO,UAAU,SAAU,QAAO,kBAAkB,oEAAoE;AAC5H,QAAI,OAAO,UAAU,eAAe,KAAK,QAAQ,IAAI,EAAG,QAAO,kBAAkB,uBAAuB;AACxG,WAAO,IAAI,IAAI;AAAA,EACjB;AACA,MAAI,kBAAkB,MAAM;AAG5B,MAAI;AACJ,MAAI;AAAE,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAAG,aAAS,IAAI,aAAa,YAAY,CAAC,IAAI,YAAY,CAAC,IAAI,WAAW,IAAI,SAAS;AAAA,EAAW,QAAQ;AAAE,aAAS;AAAA,EAAW;AACrK,MAAI,WAAW,WAAW,QAAQ,GAAI,QAAO,YAAY,mDAAmD;AAC5G,MAAI,QAAQ,YAAY,IAAK,QAAO,YAAY,mBAAmB;AACnE,MAAI,QAAQ,cAAc,uBAAwB,QAAO,YAAY,sBAAsB;AAC3F,MAAI,QAAQ,YAAY,WAAW,QAAS,QAAO,YAAY,8CAA8C;AAC7G,MAAI,CAAC,mBAAmB,kBAAkB,QAAQ,OAAO,IAAI,QAAQ,OAAO,IAAI,OAAO,EAAG,QAAO,YAAY,uCAAuC;AACpJ,MAAI,QAAQ,UAAU,WAAW,KAAK,QAAQ,UAAU,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,WAAW,YAAY,CAAC,KAAK,QAAQ,UAAU,CAAC,MAAM,GAAG,uBAAuB,GAAG,OAAO,SAAS,CAAC,GAAI,QAAO,YAAY,4DAA4D;AAC3Q,MAAI,QAAQ,mBAAmB,UAAa,SAAU,aAAa,QAAQ,cAAc,EAAc,QAAO,YAAY,kBAAkB;AAC5I,MAAI,QAAQ,cAAc,UAAa,QAAS,aAAa,QAAQ,SAAS,EAAc,QAAO,YAAY,uBAAuB;AACtI,QAAM,iBAAiB,WAAW,cAAc,WAAW,IAAI,IAAI,cAAc,MAAM,CAAC,IAAI,MAAM,oBAAoB;AACtH,QAAM,YAAY,qBAAqB,aAAa,cAAc;AAClE,MAAI,cAAc,QAAQ,cAAc,WAAW,QAAS,QAAO,sBAAsB,mDAAmD;AAG5I,MAAI,CAAC,mBAAmB,eAAe,OAAO,EAAG,QAAO,0BAA0B,yCAAyC;AAE3H,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS,gBAAgB,OAAO;AAAA,IAChC,gBAAgB,YAAY,OAAiB;AAAA,IAC7C,WAAW,IAAI,KAAM,MAAiB,GAAI;AAAA,IAC1C,cAAc,QAAQ;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACxOO,SAAS,cAAsB;AACpC,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,gBAAgB,KAAK;AAC5B,SAAO,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC1E;AAMO,IAAM,qBAAN,MAA+C;AAAA,EAA/C;AACL,SAAiB,SAAS,oBAAI,IAAoB;AAAA;AAAA;AAAA,EAGlD,MAAM,UAAkB,SAAyB;AAC/C,QAAI,YAAY,OAAO,MAAM,KAAM,OAAM,IAAI,UAAU,mDAAmD;AAC1G,UAAM,QAAQ,YAAY;AAC1B,SAAK,OAAO,IAAI,GAAG,QAAQ;AAAA,EAAK,KAAK,IAAI,OAAO;AAChD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAQ,UAAkB,OAAe,SAAgD;AAC7F,UAAM,MAAM,GAAG,QAAQ;AAAA,EAAK,KAAK;AACjC,UAAM,QAAQ,KAAK,OAAO,IAAI,GAAG;AACjC,QAAI,UAAU,UAAa,CAAC,mBAAmB,OAAO,OAAO,EAAG,QAAO,EAAE,SAAS,MAAM;AACxF,SAAK,OAAO,OAAO,GAAG;AACtB,WAAO,EAAE,SAAS,KAAK;AAAA,EACzB;AACF;;;AC5BO,SAAS,eAAe,OAA8B;AAC3D,MAAI,OAAO,UAAU,YAAY,eAAe,KAAK,KAAK,EAAG,QAAO;AACpE,QAAM,UAAU,OAAO,UAAU,WAAW,MAAM,KAAK,EAAE,YAAY,IAAI;AACzE,MAAI,YAAY,MAAM,KAAK,KAAK,OAAO,EAAG,QAAO;AACjD,QAAM,QAAQ,QAAQ,MAAM,GAAG;AAC/B,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,CAAC,OAAO,MAAM,IAAI;AACtB,MAAI,UAAU,MAAM,WAAW,GAAI,QAAO;AAC1C,MAAI,WAAW,iBAAkB,UAAS;AAC1C,MAAI,CAAC,OAAO,SAAS,GAAG,KAAK,OAAO,WAAW,GAAG,KAAK,OAAO,SAAS,GAAG,KAAK,OAAO,SAAS,IAAI,EAAG,QAAO;AAC7G,QAAM,OAAO,MAAM,QAAQ,GAAG;AAC9B,MAAI,SAAS,GAAI,SAAQ,MAAM,MAAM,GAAG,IAAI;AAC5C,MAAI,WAAW,YAAa,SAAQ,MAAM,QAAQ,OAAO,EAAE;AAC3D,MAAI,UAAU,GAAI,QAAO;AACzB,SAAO,GAAG,KAAK,IAAI,MAAM;AAC3B;","names":[]}
1
+ {"version":3,"sources":["../../src/held-credentials/errors.ts","../../src/held-credentials/crypto.ts","../../src/held-credentials/encoding.ts","../../src/held-credentials/did.ts","../../src/held-credentials/rows.ts","../../src/held-credentials/issuer-set.ts","../../src/held-credentials/credential.ts","../../src/held-credentials/siwe.ts","../../src/held-credentials/verify.ts","../../src/held-credentials/nonce-store.ts","../../src/held-credentials/email.ts","../../src/held-credentials/presentation.ts"],"sourcesContent":["export type HeldCredentialErrorCode =\n | \"bad-config\"\n | \"bad-issuer-set\"\n | \"bad-presentation\"\n | \"bad-header\"\n | \"did-method-not-allowed\"\n | \"issuer-not-accepted\"\n | \"unknown-issuer\"\n | \"issuer-not-yet-valid\"\n | \"issuer-stopped\"\n | \"bad-signature\"\n | \"bad-payload\"\n | \"issuer-retired\"\n | \"profile-not-accepted\"\n | \"profile-mismatch\"\n | \"not-yet-valid\"\n | \"expired\"\n | \"bad-lifetime\"\n | \"bad-status\"\n | \"bad-holder-binding\"\n | \"bad-subject\"\n | \"bad-disclosure\"\n | \"bad-siwe\"\n | \"bad-siwe-signature\"\n | \"bad-nonce\"\n | \"nonce-replayed\"\n | \"nonce-account-mismatch\";\n\n/** Every refusal is one of these. The code is stable; the message is for logs and never carries a credential. */\nexport class HeldCredentialError extends Error {\n readonly code: HeldCredentialErrorCode;\n constructor(code: HeldCredentialErrorCode, message: string) {\n super(`${code}: ${message}`);\n this.name = \"HeldCredentialError\";\n this.code = code;\n }\n}\n\nexport function refuse(code: HeldCredentialErrorCode, message: string): never {\n throw new HeldCredentialError(code, message);\n}\n","import { ed25519 } from \"@noble/curves/ed25519\";\nimport { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { keccak_256 } from \"@noble/hashes/sha3\";\nimport { sha256 } from \"@noble/hashes/sha256\";\nimport { sha512 } from \"@noble/hashes/sha512\";\nimport { asciiBytes, bytesToHex } from \"./encoding\";\n\nexport { sha256, keccak_256 };\n\nconst Point = ed25519.ExtendedPoint;\n\n/** True when `bytes` is a canonical, on-curve, non-small-order Ed25519 point (public key or signature R). */\nexport function isStrictEd25519Point(bytes: Uint8Array): boolean {\n if (bytes.length !== 32) return false;\n try {\n return !Point.fromHex(bytes, false).isSmallOrder(); // zip215 = false: canonical encodings only\n } catch {\n return false;\n }\n}\n\n/**\n * RFC 8032 strict verification, matching the node's dalek `verify_strict`: canonical S, canonical\n * encodings, and no small-order public key or R.\n */\nexport function verifyEd25519Strict(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean {\n if (signature.length !== 64 || !isStrictEd25519Point(publicKey) || !isStrictEd25519Point(signature.subarray(0, 32))) return false;\n try {\n if (!ed25519.verify(signature, message, publicKey, { zip215: false })) return false;\n // noble's check is cofactored (it multiplies the difference by 8), which accepts a mixed-order R\n // that dalek's `verify_strict` refuses. Also require the exact, cofactorless equation\n // [S]B == R + [k]A with k = H(R || A || M) mod L.\n const R = Point.fromHex(signature.subarray(0, 32), false);\n const A = Point.fromHex(publicKey, false);\n const S = bytesToNumberLE(signature.subarray(32));\n const k = bytesToNumberLE(sha512(Uint8Array.from([...signature.subarray(0, 32), ...publicKey, ...message]))) % ed25519.CURVE.n;\n return Point.BASE.multiplyUnsafe(S).equals(R.add(A.multiply(k)));\n } catch {\n return false;\n }\n}\n\nfunction bytesToNumberLE(bytes: Uint8Array): bigint {\n let out = 0n;\n for (let i = bytes.length - 1; i >= 0; i -= 1) out = (out << 8n) | BigInt(bytes[i]!);\n return out;\n}\n\n/** Lowercase `0x` + 40 hex address of the key that produced an EIP-191 `personal_sign` signature, or null. */\nexport function recoverEip191Address(message: string, signature: Uint8Array): string | null {\n if (signature.length !== 65) return null;\n const v = signature[64]!;\n const recovery = v >= 27 ? v - 27 : v;\n if (recovery !== 0 && recovery !== 1) return null;\n try {\n const body = new TextEncoder().encode(message);\n const prefix = asciiBytes(`\\x19Ethereum Signed Message:\\n${body.length}`);\n const digest = keccak_256(Uint8Array.from([...prefix, ...body]));\n const publicKey = secp256k1.Signature.fromCompact(signature.subarray(0, 64)).addRecoveryBit(recovery).recoverPublicKey(digest).toRawBytes(false);\n return `0x${bytesToHex(keccak_256(publicKey.subarray(1)).subarray(12))}`;\n } catch {\n return null;\n }\n}\n","import { refuse, type HeldCredentialErrorCode } from \"./errors\";\n\nconst B64URL = /^[A-Za-z0-9_-]*$/;\n\nexport function encodeBase64Url(bytes: Uint8Array): string {\n let binary = \"\";\n for (const byte of bytes) binary += String.fromCharCode(byte);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\n/** Strict unpadded, canonical base64url. */\nexport function decodeBase64Url(value: string, code: HeldCredentialErrorCode = \"bad-presentation\"): Uint8Array {\n if (!B64URL.test(value) || value.length % 4 === 1) refuse(code, \"not unpadded base64url\");\n const standard = value.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const binary = atob(standard + \"=\".repeat((4 - (standard.length % 4)) % 4));\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);\n if (encodeBase64Url(bytes) !== value) refuse(code, \"non-canonical base64url\");\n return bytes;\n}\n\nexport function isAscii(value: string): boolean {\n for (let i = 0; i < value.length; i += 1) if (value.charCodeAt(i) > 0x7f) return false;\n return true;\n}\n\nexport function asciiBytes(value: string): Uint8Array {\n const bytes = new Uint8Array(value.length);\n for (let i = 0; i < value.length; i += 1) bytes[i] = value.charCodeAt(i);\n return bytes;\n}\n\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function hexToBytes(hex: string, code: HeldCredentialErrorCode = \"bad-presentation\"): Uint8Array {\n if (!/^(?:[0-9a-fA-F]{2})*$/.test(hex)) refuse(code, \"not hex\");\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i += 1) bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);\n return bytes;\n}\n\nconst STRICT_UTF8 = new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: true });\n\n/** Strict UTF-8: invalid bytes are refused (not replaced) and a leading BOM is kept, so JSON parsing refuses it. */\nexport function decodeUtf8(bytes: Uint8Array, code: HeldCredentialErrorCode): string {\n try { return STRICT_UTF8.decode(bytes); } catch { return refuse(code, \"not valid UTF-8\"); }\n}\n\nexport type Json = null | boolean | number | string | Json[] | { [key: string]: Json };\n\nexport function isObject(value: unknown): value is { [key: string]: Json } {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Deepest nesting `parseStrictJson` accepts. Held credentials are at most three levels deep. */\nexport const MAX_JSON_DEPTH = 8;\n\n/** Numbers are integers in plain form only: no fraction (`1.0`), no exponent (`1e3`), no `-0`. */\nconst JSON_LITERAL = /true|false|null|-?(?:0|[1-9]\\d*)(?![\\d.eE])/y;\n\n/**\n * JSON parser that refuses duplicate object keys anywhere (JSON.parse silently keeps the last).\n * Objects have a null prototype, so a `__proto__` key is an ordinary key. Nesting deeper than\n * MAX_JSON_DEPTH is refused with `code`, so the recursion is bounded and no RangeError escapes.\n */\nexport function parseStrictJson(text: string, code: HeldCredentialErrorCode): Json {\n let i = 0;\n let depth = 0;\n const fail = (what: string): never => refuse(code, `invalid JSON: ${what}`);\n const ws = () => { while (i < text.length && \" \\t\\n\\r\".includes(text[i]!)) i += 1; };\n const string = (): string => {\n const start = i;\n i += 1;\n while (i < text.length && text[i] !== '\"') i += text[i] === \"\\\\\" ? 2 : 1;\n if (i >= text.length) fail(\"unterminated string\");\n i += 1;\n try { return JSON.parse(text.slice(start, i)) as string; } catch { return fail(\"bad string\"); }\n };\n const value = (): Json => {\n ws();\n const c = text[i];\n if ((c === \"{\" || c === \"[\") && (depth += 1) > MAX_JSON_DEPTH) fail(\"nesting too deep\");\n if (c === \"{\") {\n i += 1;\n const out: { [key: string]: Json } = Object.create(null);\n ws();\n if (text[i] === \"}\") { i += 1; depth -= 1; return out; }\n for (;;) {\n ws();\n if (text[i] !== '\"') fail(\"expected key\");\n const key = string();\n if (Object.prototype.hasOwnProperty.call(out, key)) fail(\"duplicate key\");\n ws();\n if (text[i] !== \":\") fail(\"expected colon\");\n i += 1;\n out[key] = value();\n ws();\n if (text[i] === \",\") { i += 1; continue; }\n if (text[i] === \"}\") { i += 1; depth -= 1; return out; }\n fail(\"expected , or }\");\n }\n }\n if (c === \"[\") {\n i += 1;\n const out: Json[] = [];\n ws();\n if (text[i] === \"]\") { i += 1; depth -= 1; return out; }\n for (;;) {\n out.push(value());\n ws();\n if (text[i] === \",\") { i += 1; continue; }\n if (text[i] === \"]\") { i += 1; depth -= 1; return out; }\n fail(\"expected , or ]\");\n }\n }\n if (c === '\"') return string();\n JSON_LITERAL.lastIndex = i;\n const literal = JSON_LITERAL.exec(text);\n if (!literal) return fail(\"unexpected token\");\n if (literal[0] === \"-0\") fail(\"-0 is not accepted\");\n i += literal[0].length;\n return JSON.parse(literal[0]) as Json;\n };\n const result = value();\n ws();\n if (i !== text.length) fail(\"trailing data\");\n return result;\n}\n","import { keccak_256 } from \"./crypto\";\nimport { asciiBytes } from \"./encoding\";\n\nexport interface PkhDid {\n chainId: string;\n /** Lowercase `0x` + 40 hex. */\n address: string;\n}\n\n/** EIP-55 checksummed form of a `0x` + 40 hex address. */\nexport function toChecksumAddress(address: string): string {\n const lower = address.slice(2).toLowerCase();\n const hash = keccak_256(asciiBytes(lower));\n let out = \"0x\";\n for (let i = 0; i < 40; i += 1) {\n const nibble = (hash[i >> 1]! >> (i % 2 === 0 ? 4 : 0)) & 0xf;\n out += nibble >= 8 ? lower[i]!.toUpperCase() : lower[i]!;\n }\n return out;\n}\n\n/** All-lowercase or correctly checksummed. An all-uppercase address, or a mixed-case one with a wrong EIP-55 checksum, is refused. */\nexport function isValidAddress(address: string): boolean {\n if (!/^0x[0-9a-fA-F]{40}$/.test(address)) return false;\n const body = address.slice(2);\n if (body === body.toLowerCase()) return true;\n return toChecksumAddress(address) === address;\n}\n\n/** Canonical `did:pkh:eip155:<chainId>:<EIP-55 address>` of a valid pkh DID, or null. */\nexport function canonicalPkhDid(did: unknown): string | null {\n const parsed = parsePkhDid(did);\n return parsed ? `did:pkh:eip155:${parsed.chainId}:${toChecksumAddress(parsed.address)}` : null;\n}\n\n/** Parses `did:pkh:eip155:<chainId>:<address>`; null for anything else (no byte fallback). */\nexport function parsePkhDid(did: unknown): PkhDid | null {\n if (typeof did !== \"string\") return null;\n const match = /^did:pkh:eip155:(0|[1-9][0-9]{0,15}):(0x[0-9a-fA-F]{40})$/.exec(did);\n if (!match || !isValidAddress(match[2]!)) return null;\n return { chainId: match[1]!, address: match[2]!.toLowerCase() };\n}\n\n/** Principal equality: the exact eip155 chain id and the same address bytes. Never compares strings. */\nexport function pkhPrincipalsEqual(a: unknown, b: unknown): boolean {\n const left = parsePkhDid(a);\n const right = parsePkhDid(b);\n return left !== null && right !== null && left.chainId === right.chainId && left.address === right.address;\n}\n\n/**\n * The one key billing, Exo and credentials.org use for a verified subject: the lowercase 20-byte\n * address. It is chain-independent because v1 accepts only externally owned accounts (their control\n * does not depend on the chain). Accepting contract wallets or other chains would require adding\n * the chain here.\n */\nexport function registryKey(subject: string): string {\n const parsed = parsePkhDid(subject);\n if (!parsed) throw new TypeError(\"registryKey needs a did:pkh:eip155 subject\");\n return parsed.address;\n}\n","import { refuse } from \"./errors\";\nimport type { Json } from \"./encoding\";\n\n/**\n * A profile row is the complete definition of a held profile (spec §3.3). Every profile rule is\n * keyed on the row selected by the credential's signed `profile`, never on the issuer.\n */\nexport interface ProfileRow {\n profile: string;\n profileVersion: number;\n vct: string;\n holder: { kind: \"account-pkh\"; chainId: string };\n bindingDomain: string;\n /** Top-level claim names that may be selectively disclosed; nothing else is disclosable. */\n disclosable: readonly string[];\n /** Signed claims beyond the shared set that this row tolerates. */\n optionalSignedClaims: readonly string[];\n /** Largest allowed `exp - iat`, seconds. */\n maxLifetimeSeconds: number;\n /** Accepted `status.method` values. */\n statusMethods: readonly string[];\n /** Checks the disclosed claim values; throws a HeldCredentialError. */\n validateDisclosed(claims: Readonly<Record<string, Json>>): void;\n /**\n * Whether this row admits a credential signed by `issuerDid`. Fixed per shipped profile.\n * Optional for a caller-defined row. When omitted, the row admits any issuer that the issuer\n * entry permits; the entry's own key, kid, and profile checks still apply.\n */\n permitsIssuer?(issuerDid: string): boolean;\n}\n\nexport const HELD_EMAIL_TTL_SECONDS = 365 * 24 * 60 * 60;\nexport const ACCOUNT_BINDING_DOMAIN = \"tinycloud.credentials/account-binding/v1\";\n\nconst ORG_MEMBERSHIP_BINDING_DOMAIN = \"tinycloud.credentials/org-binding/v1\";\nconst ORG_MEMBERSHIP_TTL_SECONDS = 365 * 24 * 60 * 60;\nconst ORG_MEMBERSHIP_FREE_TTL_SECONDS = 30 * 24 * 60 * 60;\n/** Org-family issuer DIDs: `did:web:o` + 26 base32 characters + `.orgs.credentials.org`. */\nconst ORG_ISSUER_DID_PATTERN = /^did:web:o[a-z2-7]{26}\\.orgs\\.credentials\\.org$/;\n/** The only issuer the email row admits (held-credential-profile-v1 spec). */\nconst EMAIL_ISSUER_DID = \"did:web:issuer.credentials.org\";\nconst ORG_ROLES: readonly string[] = [\"member\", \"admin\"];\nconst MEMBERSHIP_TYPE_PATTERN = /^[a-z0-9][a-z0-9-]{0,62}$/;\n\n/**\n * Shipped rows are fixed definitions. Freezing each row and its nested containers keeps a caller\n * from changing a shipped rule through a reference to it.\n */\nfunction freezeRow(row: ProfileRow): ProfileRow {\n Object.freeze(row.holder);\n Object.freeze(row.disclosable);\n Object.freeze(row.optionalSignedClaims);\n Object.freeze(row.statusMethods);\n return Object.freeze(row);\n}\n\nexport const EMAIL_HELD_ROW: ProfileRow = freezeRow({\n profile: \"tinycloud.email-held/v1\",\n profileVersion: 1,\n vct: \"opencredentials.email/v1\",\n holder: { kind: \"account-pkh\", chainId: \"1\" },\n bindingDomain: ACCOUNT_BINDING_DOMAIN,\n disclosable: [\"email\", \"emailDomain\"],\n optionalSignedClaims: [],\n maxLifetimeSeconds: HELD_EMAIL_TTL_SECONDS,\n statusMethods: [\"none\"],\n // The email row admits only its literal issuer. The org family and every other DID are refused.\n permitsIssuer: (issuerDid) => issuerDid === EMAIL_ISSUER_DID,\n validateDisclosed(claims) {\n const { email, emailDomain } = claims;\n if ((typeof email === \"string\" && /[^\\x00-\\x7F]/.test(email)) || (typeof emailDomain === \"string\" && /[^\\x00-\\x7F]/.test(emailDomain))) {\n refuse(\"bad-disclosure\", \"email must be ASCII\");\n }\n if (email !== undefined) {\n if (typeof email === \"string\" && (email.indexOf(\"@\") < 1 || email.indexOf(\"@\") === email.length - 1)) {\n refuse(\"bad-disclosure\", \"email needs a non-empty local part and domain\");\n }\n if (typeof email !== \"string\" || email !== email.toLowerCase() || email.split(\"@\").length !== 2 || email.length < 3 || email.length > 320 || /[\\s\\u0000-\\u001f]/.test(email)) {\n refuse(\"bad-disclosure\", \"email is not a canonical lowercase address\");\n }\n }\n if (emailDomain !== undefined) {\n if (typeof emailDomain !== \"string\" || emailDomain === \"\" || emailDomain !== emailDomain.toLowerCase() || emailDomain.includes(\"@\")) {\n refuse(\"bad-disclosure\", \"emailDomain is not a lowercase domain\");\n }\n }\n if (typeof email === \"string\" && typeof emailDomain === \"string\" && email.slice(email.lastIndexOf(\"@\") + 1) !== emailDomain) {\n refuse(\"bad-disclosure\", \"emailDomain does not match email\");\n }\n },\n});\n\nfunction orgMembershipRow(profile: string, maxLifetimeSeconds: number): ProfileRow {\n return freezeRow({\n profile,\n profileVersion: 1,\n vct: \"opencredentials.org-membership/v1\",\n holder: { kind: \"account-pkh\", chainId: \"1\" },\n bindingDomain: ORG_MEMBERSHIP_BINDING_DOMAIN,\n disclosable: [\"role\", \"membershipType\"],\n optionalSignedClaims: [],\n maxLifetimeSeconds,\n statusMethods: [\"none\"],\n permitsIssuer: (issuerDid) => ORG_ISSUER_DID_PATTERN.test(issuerDid),\n validateDisclosed(claims) {\n const { role, membershipType } = claims;\n if (role !== undefined && (typeof role !== \"string\" || !ORG_ROLES.includes(role))) {\n refuse(\"bad-disclosure\", \"role must be member or admin\");\n }\n if (membershipType !== undefined && (typeof membershipType !== \"string\" || !MEMBERSHIP_TYPE_PATTERN.test(membershipType))) {\n refuse(\"bad-disclosure\", \"membershipType is not a canonical slug\");\n }\n },\n });\n}\n\n/** Paid org membership row (profile `tinycloud.org-membership/v1`). */\nexport const ORG_MEMBERSHIP_ROW: ProfileRow = orgMembershipRow(\"tinycloud.org-membership/v1\", ORG_MEMBERSHIP_TTL_SECONDS);\n/** Free org membership row (profile `tinycloud.org-membership-free/v1`). */\nexport const ORG_MEMBERSHIP_FREE_ROW: ProfileRow = orgMembershipRow(\"tinycloud.org-membership-free/v1\", ORG_MEMBERSHIP_FREE_TTL_SECONDS);\n/** Both org rows. Not in `DEFAULT_ROWS`: a caller opts in by passing them as `rows`. */\nexport const ORG_MEMBERSHIP_ROWS: readonly ProfileRow[] = Object.freeze([ORG_MEMBERSHIP_ROW, ORG_MEMBERSHIP_FREE_ROW]);\n\nexport const DEFAULT_ROWS: readonly ProfileRow[] = Object.freeze([EMAIL_HELD_ROW]);\n\n/**\n * Every row this package ships. The issuer set loader applies each shipped row's `permitsIssuer`\n * whatever rows the caller passes, so a caller row cannot relax a shipped profile's issuer rule.\n */\nexport const SHIPPED_ROWS: readonly ProfileRow[] = Object.freeze([EMAIL_HELD_ROW, ...ORG_MEMBERSHIP_ROWS]);\n\nexport function findRow(rows: readonly ProfileRow[], profile: string, profileVersion: number): ProfileRow | undefined {\n return rows.find((row) => row.profile === profile && row.profileVersion === profileVersion);\n}\n\n/**\n * Whether `row` admits `issuerDid`. A row without `permitsIssuer` admits every issuer its issuer\n * entry permits. A `permitsIssuer` that is present but not a function refuses with `bad-config`,\n * so a malformed rule is never read as permissive. Only a rule that returns exactly `true` admits\n * the issuer; any other result refuses it.\n */\nexport function rowPermitsIssuer(row: ProfileRow, issuerDid: string): boolean {\n const rule = row.permitsIssuer;\n if (rule === undefined) return true;\n if (typeof rule !== \"function\") refuse(\"bad-config\", \"a profile row's permitsIssuer must be a function when present\");\n return rule.call(row, issuerDid) === true;\n}\n","import { decodeBase64Url, isObject } from \"./encoding\";\nimport { isStrictEd25519Point } from \"./crypto\";\nimport { refuse } from \"./errors\";\nimport { SHIPPED_ROWS, rowPermitsIssuer } from \"./rows\";\nimport type { ProfileRow } from \"./rows\";\n\nexport const ISSUER_SET_FORMAT = \"tinycloud.credentials-issuers/v1\";\n\n/** A pinned issuer entry (spec §6). Times are epoch milliseconds. */\nexport interface IssuerEntry {\n issuerDid: string;\n kid: string;\n alg: \"EdDSA\";\n publicKey: Uint8Array;\n vcts: readonly string[];\n profiles: readonly string[];\n notBefore: number;\n /** Rotation: the key stops issuing at this instant. null = not retired. */\n retiredAt: number | null;\n /** Withdrawal: nothing verifies under the key once now >= this. null = no end. */\n notAfter: number | null;\n}\n\nconst CONSTRUCT = Symbol(\"IssuerSet.construct\");\n\n/** Module-private read of the private entry map; assigned once by the class's static block. */\nlet readEntry: (set: IssuerSet, issuerDid: string, kid: string) => IssuerEntry | undefined;\n\n/** A fresh, frozen entry: the public key is a new copy on every lookup. */\nfunction copyEntry(entry: IssuerEntry): IssuerEntry {\n return Object.freeze({ ...entry, publicKey: entry.publicKey.slice() });\n}\n\n/**\n * An issuer set that has passed every rule of `parseIssuerSet`. Only `parseIssuerSet` creates one,\n * and the verifier accepts only genuine instances (`IssuerSet.is`). The instance, its prototype and\n * the class are frozen, and the verifier reads entries through the module-private `lookupIssuerEntry`.\n * Use one build (ESM or CJS) of this package per process. Details: TC-1540\n */\nexport class IssuerSet {\n readonly version: number;\n readonly #entries: ReadonlyMap<string, IssuerEntry>;\n\n static {\n readEntry = (set, issuerDid, kid) => {\n const entry = set.#entries.get(kid);\n return entry && entry.issuerDid === issuerDid ? copyEntry(entry) : undefined;\n };\n }\n\n /** @internal Use `parseIssuerSet`. */\n constructor(token: symbol, version: number, entries: ReadonlyMap<string, IssuerEntry>) {\n if (token !== CONSTRUCT) throw new TypeError(\"IssuerSet can only be created by parseIssuerSet\");\n this.version = version;\n this.#entries = entries;\n Object.freeze(this);\n }\n\n /** True only for an instance built by `parseIssuerSet` (a real private-field brand check). */\n static is(value: unknown): value is IssuerSet {\n return typeof value === \"object\" && value !== null && #entries in value;\n }\n\n /**\n * The one lookup seam (TC-1132 would plug an allow-listed did:web resolver in here). Returns the\n * FULL entry, never just a key, so vct/profile admission and the validity window come from the\n * same entry. Exact (issuerDid, kid) match only. Every call returns a copy.\n */\n resolveIssuer(issuerDid: string, kid: string): IssuerEntry | undefined {\n return readEntry(this, issuerDid, kid);\n }\n}\nObject.freeze(IssuerSet.prototype);\nObject.freeze(IssuerSet);\n\n/** What `verifyHeldPresentation` calls. Not exported from the package index. */\nexport function lookupIssuerEntry(set: IssuerSet, issuerDid: string, kid: string): IssuerEntry | undefined {\n return readEntry(set, issuerDid, kid);\n}\n\nexport interface IssuerSetOptions {\n /** DID methods allowed for issuers (docs/principles.md principle 2), e.g. [\"web\"]. */\n didMethods: { issuer: readonly string[] };\n /** Additional rows whose permitted-issuer rules also apply to the profiles an entry lists. The shipped rows always apply. */\n rows?: readonly ProfileRow[];\n}\n\nconst ENTRY_KEYS = [\"issuerDid\", \"kid\", \"alg\", \"publicKey\", \"vcts\", \"profiles\", \"notBefore\", \"retiredAt\", \"notAfter\"].sort();\nconst RFC3339_Z = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(\\.\\d+)?Z$/;\nconst DID_WEB = /^did:web:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/;\nconst DID_SELF_DESCRIBING = /^did:(?:key|jwk):[A-Za-z0-9_-]+$/;\n\nexport function didMethod(did: string): string | null {\n const match = /^did:([a-z0-9]+):/.exec(did);\n return match ? match[1]! : null;\n}\n\n/** Canonical issuer DID: lowercase, no port, path or query (did:web); `did:key`/`did:jwk` only when allowed. */\nexport function isCanonicalIssuerDid(did: string): boolean {\n return DID_WEB.test(did) || DID_SELF_DESCRIBING.test(did);\n}\n\nexport function isWellFormedKid(kid: string, issuerDid: string): boolean {\n return kid.startsWith(`${issuerDid}#`) && /^[A-Za-z0-9._-]+$/.test(kid.slice(issuerDid.length + 1));\n}\n\nfunction parseTime(value: unknown, field: string): number {\n if (typeof value !== \"string\") return refuse(\"bad-issuer-set\", `${field} must be an RFC 3339 UTC string`);\n const match = RFC3339_Z.exec(value);\n if (!match) return refuse(\"bad-issuer-set\", `${field} must be RFC 3339 UTC ending in Z`);\n const ms = Date.parse(value);\n const [, y, mo, d, h, mi, s] = match.map(Number) as number[];\n const check = new Date(ms);\n if (Number.isNaN(ms) || check.getUTCFullYear() !== y || check.getUTCMonth() + 1 !== mo || check.getUTCDate() !== d || check.getUTCHours() !== h || check.getUTCMinutes() !== mi || check.getUTCSeconds() !== s) {\n return refuse(\"bad-issuer-set\", `${field} is not a real instant`);\n }\n return ms;\n}\n\nfunction stringList(value: unknown, field: string): string[] {\n if (!Array.isArray(value) || value.length === 0 || value.some((v) => typeof v !== \"string\" || v === \"\")) {\n return refuse(\"bad-issuer-set\", `${field} must be a non-empty list of strings`);\n }\n return value as string[];\n}\n\n/** Validates a whole issuer set. Any violation makes the whole set invalid. */\nexport function parseIssuerSet(raw: unknown, options: IssuerSetOptions): IssuerSet {\n if (!isObject(raw) || Object.keys(raw).sort().join() !== [\"format\", \"issuers\", \"version\"].join()) {\n return refuse(\"bad-issuer-set\", \"unexpected shape\");\n }\n if (raw.format !== ISSUER_SET_FORMAT) refuse(\"bad-issuer-set\", \"unknown format\");\n if (typeof raw.version !== \"number\" || !Number.isInteger(raw.version) || raw.version < 1) refuse(\"bad-issuer-set\", \"version must be a positive integer\");\n if (!Array.isArray(raw.issuers) || raw.issuers.length === 0) refuse(\"bad-issuer-set\", \"issuers must be a non-empty list\");\n const rowsForLoad: readonly ProfileRow[] = [...SHIPPED_ROWS, ...(options.rows ?? [])];\n const entries = new Map<string, IssuerEntry>();\n for (const item of raw.issuers) {\n if (!isObject(item) || Object.keys(item).sort().join() !== ENTRY_KEYS.join()) refuse(\"bad-issuer-set\", \"entry has missing or unknown fields\");\n const issuerDid = item.issuerDid, kid = item.kid;\n if (typeof issuerDid !== \"string\" || !isCanonicalIssuerDid(issuerDid)) refuse(\"bad-issuer-set\", \"issuerDid is not canonical\");\n const method = didMethod(issuerDid as string);\n if (method === null || !options.didMethods.issuer.includes(method)) refuse(\"did-method-not-allowed\", `issuer DID method ${method} is not on the allow-list`);\n if (typeof kid !== \"string\" || !isWellFormedKid(kid, issuerDid as string)) refuse(\"bad-issuer-set\", \"kid must be issuerDid#fragment\");\n if (entries.has(kid as string)) refuse(\"bad-issuer-set\", \"duplicate kid\");\n if (item.alg !== \"EdDSA\") refuse(\"bad-issuer-set\", \"alg must be EdDSA\");\n if (typeof item.publicKey !== \"string\") refuse(\"bad-issuer-set\", \"publicKey must be base64url\");\n const publicKey = decodeBase64Url(item.publicKey, \"bad-issuer-set\");\n if (publicKey.length !== 32 || !isStrictEd25519Point(publicKey)) refuse(\"bad-issuer-set\", \"publicKey is not a strict 32-byte Ed25519 key\");\n const notBefore = parseTime(item.notBefore, \"notBefore\");\n const retiredAt = item.retiredAt === null ? null : parseTime(item.retiredAt, \"retiredAt\");\n const notAfter = item.notAfter === null ? null : parseTime(item.notAfter, \"notAfter\");\n if (retiredAt !== null && notAfter !== null && retiredAt > notAfter) refuse(\"bad-issuer-set\", \"retiredAt must not be after notAfter\");\n const listedProfiles = stringList(item.profiles, \"profiles\");\n for (const profile of listedProfiles) {\n for (const row of rowsForLoad) {\n if (row.profile === profile && !rowPermitsIssuer(row, issuerDid as string)) refuse(\"bad-issuer-set\", \"issuer DID is not permitted for a profile it lists\");\n }\n }\n entries.set(kid as string, Object.freeze({\n issuerDid: issuerDid as string,\n kid: kid as string,\n alg: \"EdDSA\",\n publicKey,\n vcts: Object.freeze([...stringList(item.vcts, \"vcts\")]),\n profiles: Object.freeze([...listedProfiles]),\n notBefore,\n retiredAt,\n notAfter,\n }));\n }\n return new IssuerSet(CONSTRUCT, raw.version as number, entries);\n}\n","import { sha256, verifyEd25519Strict } from \"./crypto\";\nimport { canonicalPkhDid, parsePkhDid, pkhPrincipalsEqual, registryKey, type PkhDid } from \"./did\";\nimport { asciiBytes, decodeBase64Url, decodeUtf8, encodeBase64Url, isAscii, isObject, parseStrictJson, type Json } from \"./encoding\";\nimport { refuse } from \"./errors\";\nimport { IssuerSet, didMethod, isWellFormedKid, lookupIssuerEntry, type IssuerEntry } from \"./issuer-set\";\nimport { EMAIL_HELD_ROW, findRow, rowPermitsIssuer, type ProfileRow } from \"./rows\";\n\nexport interface AcceptedIssuer { issuerDid: string; kid?: string }\nexport interface AcceptedProfile { profile: string; profileVersion: number }\n\nexport interface VerifiedHeldCredential {\n issuer: string;\n subject: string;\n /** Lowercase 20-byte address; equals `registryKey(subject)`. */\n subjectAddress: string;\n expiresAt: Date;\n credentialId: string;\n profile: string;\n profileVersion: number;\n vct: string;\n claims: Record<string, Json>;\n}\n\nconst SHARED_SIGNED = new Set([\"iss\", \"sub\", \"iat\", \"nbf\", \"exp\", \"jti\", \"vct\", \"profile\", \"profileVersion\", \"holderBinding\", \"status\", \"_sd_alg\", \"_sd\"]);\nconst RESERVED_CLAIMS = new Set([...SHARED_SIGNED, \"...\", \"cnf\"]);\n\n/** Size limits, enforced on the raw strings before any decoding or parsing (TC-1540). */\nexport const HELD_LIMITS = {\n sdJwtChars: 16384,\n siweMessageChars: 2048,\n siweSignatureChars: 132,\n disclosures: 8,\n disclosureChars: 1024,\n headerSegmentChars: 1024,\n payloadSegmentChars: 4096,\n signatureSegmentChars: 128,\n} as const;\n\nconst HEADER_MEMBERS = new Set([\"alg\", \"typ\", \"kid\"]);\nconst JTI_UUID_V4 = /^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;\n\n/** Base64url of SHA-256 over the ASCII bytes of the whole SD-JWT text (the sd_hash the SIWE resource binds). */\nexport function sdHash(sdJwt: string): string {\n if (typeof sdJwt !== \"string\" || !isAscii(sdJwt)) refuse(\"bad-presentation\", \"sdJwt must be an ASCII string\");\n return encodeBase64Url(sha256(asciiBytes(sdJwt)));\n}\n\nfunction isSafeInt(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value);\n}\n\n/** Already-validated policy. The caller checks `now`, `issuerSet`, and the list shapes first. */\nexport interface HeldCredentialPolicy {\n now: Date;\n issuerSet: IssuerSet;\n acceptedIssuers: readonly AcceptedIssuer[];\n acceptedProfiles: readonly AcceptedProfile[];\n didMethods: { issuer: readonly string[] };\n rows: readonly ProfileRow[];\n}\n\n/** Output of the signed-credential checks. The subject is not yet tied to a SIWE proof or a caller. */\nexport interface CheckedHeldCredential {\n issuer: string;\n /** The `sub` claim as signed (already a valid eip155 did:pkh on the row's chain). */\n subject: string;\n subjectPkh: PkhDid;\n /** The signed `iat`. */\n issuedAt: Date;\n expiresAt: Date;\n credentialId: string;\n profile: string;\n profileVersion: number;\n vct: string;\n claims: Record<string, Json>;\n}\n\n/**\n * Spec §4.4 steps 2 to 9 for an SD-JWT that has already passed the caller's size checks: header,\n * policy selection, issuer signature, claims, profile row, and disclosures. Does not spend nonces,\n * read SIWE, or compare the subject with a caller. Throws a HeldCredentialError on the first failure.\n */\nexport function checkHeldCredential(sdJwtText: string, policy: HeldCredentialPolicy): CheckedHeldCredential {\n const { now, issuerSet, acceptedIssuers, acceptedProfiles, didMethods, rows } = policy;\n const parts = sdJwtText.split(\"~\");\n if (parts.length < 2 || parts[parts.length - 1] !== \"\") refuse(\"bad-presentation\", \"sdJwt must end with ~ and carry no key-binding JWT\");\n const [jwt, ...disclosureParts] = parts.slice(0, -1) as [string, ...string[]];\n if (disclosureParts.some((d) => d === \"\")) refuse(\"bad-presentation\", \"empty disclosure\");\n if (disclosureParts.length > HELD_LIMITS.disclosures) refuse(\"bad-presentation\", \"too many disclosures\");\n if (disclosureParts.some((d) => d.length > HELD_LIMITS.disclosureChars)) refuse(\"bad-presentation\", \"disclosure is too long\");\n const segments = jwt.split(\".\");\n if (segments.length !== 3 || segments.some((s) => s === \"\")) refuse(\"bad-presentation\", \"JWT must have three segments\");\n const [headerB64, payloadB64, signatureB64] = segments as [string, string, string];\n if (headerB64.length > HELD_LIMITS.headerSegmentChars || payloadB64.length > HELD_LIMITS.payloadSegmentChars || signatureB64.length > HELD_LIMITS.signatureSegmentChars) refuse(\"bad-presentation\", \"JWT segment is too long\");\n\n // protected header only\n const header = parseStrictJson(decodeUtf8(decodeBase64Url(headerB64, \"bad-header\"), \"bad-header\"), \"bad-header\");\n if (!isObject(header)) refuse(\"bad-header\", \"header must be an object\");\n for (const member of Object.keys(header)) if (!HEADER_MEMBERS.has(member)) refuse(\"bad-header\", `unexpected header member ${member}`);\n if (header.alg !== \"EdDSA\") refuse(\"bad-header\", \"alg must be EdDSA\");\n if (header.typ !== \"vc+sd-jwt\") refuse(\"bad-header\", \"typ must be vc+sd-jwt\");\n const kid = header.kid;\n if (typeof kid !== \"string\") refuse(\"bad-header\", \"kid is required\");\n const hash = (kid as string).indexOf(\"#\");\n if (hash < 1) refuse(\"bad-header\", \"kid must be a DID URL with a fragment\");\n const issuerDid = (kid as string).slice(0, hash);\n if (!isWellFormedKid(kid as string, issuerDid)) refuse(\"bad-header\", \"kid must be issuerDid#fragment\");\n const method = didMethod(issuerDid);\n if (method === null || !didMethods.issuer.includes(method)) refuse(\"did-method-not-allowed\", `issuer DID method ${method} is not on the allow-list`);\n\n // policy selection: the presenter never chooses the issuer\n if (!acceptedIssuers.some((a) => a.issuerDid === issuerDid && (a.kid === undefined || a.kid === kid))) refuse(\"issuer-not-accepted\", \"issuer is not accepted by this policy\");\n\n // entry lookup by the exact pair, then the checks that need only `now`\n const entry: IssuerEntry | undefined = lookupIssuerEntry(issuerSet, issuerDid, kid as string);\n if (!entry) refuse(\"unknown-issuer\", \"no issuer-set entry for this kid\");\n if (entry.issuerDid !== issuerDid || entry.kid !== kid) refuse(\"unknown-issuer\", \"resolved entry is not the requested (issuerDid, kid)\");\n const nowMs = now.getTime();\n if (nowMs < entry.notBefore) refuse(\"issuer-not-yet-valid\", \"issuer key is not yet valid\");\n if (entry.notAfter !== null && nowMs >= entry.notAfter) refuse(\"issuer-stopped\", \"issuer key is past its hard stop\");\n\n // signature\n const signature = decodeBase64Url(signatureB64, \"bad-signature\");\n if (!verifyEd25519Strict(signature, asciiBytes(`${headerB64}.${payloadB64}`), entry.publicKey)) refuse(\"bad-signature\", \"issuer signature did not verify\");\n\n // only now decode the payload\n const payload = parseStrictJson(decodeUtf8(decodeBase64Url(payloadB64, \"bad-payload\"), \"bad-payload\"), \"bad-payload\");\n if (!isObject(payload)) refuse(\"bad-payload\", \"payload must be an object\");\n if (payload.iss !== issuerDid) refuse(\"bad-payload\", \"iss must equal the DID part of kid\");\n const { iat, nbf, exp } = payload;\n if (!isSafeInt(iat)) refuse(\"bad-payload\", \"iat is required\");\n if (!isSafeInt(nbf) || nbf !== iat) refuse(\"bad-payload\", \"nbf must equal iat\");\n if (!isSafeInt(exp)) refuse(\"bad-payload\", \"exp is required\");\n if (entry.notBefore > (iat as number) * 1000) refuse(\"issuer-not-yet-valid\", \"credential predates the issuer key\");\n if (entry.retiredAt !== null && (iat as number) * 1000 >= entry.retiredAt) refuse(\"issuer-retired\", \"issued at or after the issuer key was retired\");\n\n // profile row and admission\n if (typeof payload.profile !== \"string\" || !isSafeInt(payload.profileVersion) || typeof payload.vct !== \"string\") refuse(\"bad-payload\", \"profile, profileVersion and vct are required\");\n const profile = payload.profile as string, profileVersion = payload.profileVersion as number, vct = payload.vct as string;\n const row = findRow(rows, profile, profileVersion);\n if (!row) refuse(\"profile-mismatch\", \"unknown profile\");\n if (!acceptedProfiles.some((p) => p.profile === profile && p.profileVersion === profileVersion)) refuse(\"profile-not-accepted\", \"profile is not accepted by this caller\");\n if (row.vct !== vct) refuse(\"profile-mismatch\", \"vct is not the row's vct\");\n if (!entry.vcts.includes(vct) || !entry.profiles.includes(profile)) refuse(\"profile-mismatch\", \"issuer entry does not admit this vct and profile\");\n if (!rowPermitsIssuer(row, issuerDid)) refuse(\"issuer-not-accepted\", \"this profile row does not permit the issuer DID\");\n\n // claims against the row\n for (const key of Object.keys(payload)) {\n if (!SHARED_SIGNED.has(key) && !row.optionalSignedClaims.includes(key)) refuse(\"bad-payload\", `unexpected claim ${key}`);\n }\n if (payload._sd_alg !== \"sha-256\") refuse(\"bad-payload\", \"_sd_alg must be sha-256\");\n if (!Array.isArray(payload._sd) || payload._sd.some((d) => typeof d !== \"string\")) refuse(\"bad-payload\", \"_sd must be a list of digests\");\n if (new Set(payload._sd as string[]).size !== payload._sd.length) refuse(\"bad-payload\", \"_sd must not repeat a digest (RFC 9901 section 7.1)\");\n const nowSeconds = nowMs / 1000;\n if (nowSeconds < (nbf as number)) refuse(\"not-yet-valid\", \"credential is not yet valid\");\n if (nowSeconds >= (exp as number)) refuse(\"expired\", \"credential has expired\");\n const lifetime = (exp as number) - (iat as number);\n if (!Number.isSafeInteger(lifetime) || lifetime <= 0 || lifetime > row.maxLifetimeSeconds) refuse(\"bad-lifetime\", \"exp - iat is outside the row's lifetime\");\n if (typeof payload.jti !== \"string\" || !JTI_UUID_V4.test(payload.jti)) refuse(\"bad-payload\", \"jti must be a urn:uuid v4\");\n const status = payload.status;\n if (!isObject(status) || Object.keys(status).join() !== \"method\" || typeof status.method !== \"string\" || !row.statusMethods.includes(status.method)) refuse(\"bad-status\", \"status method is not accepted\");\n const subject = payload.sub;\n const subjectPkh = parsePkhDid(subject);\n if (!subjectPkh || typeof subject !== \"string\" || subjectPkh.chainId !== row.holder.chainId) refuse(\"bad-subject\", \"sub is not an eip155 did:pkh on the row's chain\");\n const binding = payload.holderBinding;\n if (!isObject(binding) || Object.keys(binding).sort().join() !== \"did,signingDomain\" || binding.signingDomain !== row.bindingDomain || !pkhPrincipalsEqual(binding.did, subject)) refuse(\"bad-holder-binding\", \"holderBinding does not match the row and sub\");\n\n // disclosures\n const digests = new Set(payload._sd as string[]);\n const seen = new Set<string>();\n const claims: Record<string, Json> = {};\n for (const disclosure of disclosureParts) {\n const digest = encodeBase64Url(sha256(asciiBytes(disclosure)));\n if (!digests.has(digest)) refuse(\"bad-disclosure\", \"disclosure digest is not in _sd\");\n if (seen.has(digest)) refuse(\"bad-disclosure\", \"repeated disclosure\");\n seen.add(digest);\n const decoded = parseStrictJson(decodeUtf8(decodeBase64Url(disclosure, \"bad-disclosure\"), \"bad-disclosure\"), \"bad-disclosure\");\n if (!Array.isArray(decoded) || decoded.length !== 3 || typeof decoded[0] !== \"string\" || typeof decoded[1] !== \"string\") refuse(\"bad-disclosure\", \"disclosure must be [salt, name, value]\");\n const [, name, value] = decoded as [string, string, Json];\n if (RESERVED_CLAIMS.has(name) || row.optionalSignedClaims.includes(name)) refuse(\"bad-disclosure\", \"claim name collides with a signed claim\");\n if (!row.disclosable.includes(name)) refuse(\"bad-disclosure\", \"claim is not disclosable for this profile\");\n if (typeof value !== \"string\") refuse(\"bad-disclosure\", \"disclosed value must be a string (no nested _sd or array elements)\");\n if (Object.prototype.hasOwnProperty.call(claims, name)) refuse(\"bad-disclosure\", \"claim disclosed twice\");\n claims[name] = value;\n }\n row.validateDisclosed(claims);\n\n return {\n issuer: issuerDid,\n subject,\n subjectPkh,\n issuedAt: new Date((iat as number) * 1000),\n expiresAt: new Date((exp as number) * 1000),\n credentialId: payload.jti as string,\n profile,\n profileVersion,\n vct,\n claims,\n };\n}\n\n/** Public result shape. The subject is canonical and the address is the registry key. */\nexport function toVerifiedCredential(checked: CheckedHeldCredential): VerifiedHeldCredential {\n return {\n issuer: checked.issuer,\n subject: canonicalPkhDid(checked.subject) as string,\n subjectAddress: registryKey(checked.subject),\n expiresAt: checked.expiresAt,\n credentialId: checked.credentialId,\n profile: checked.profile,\n profileVersion: checked.profileVersion,\n vct: checked.vct,\n claims: checked.claims,\n };\n}\n\n/** `verifyHeldEmailCredential` output: the shared verified shape plus the signed issuance time (`iat`). The presentation verifier's output has no `issuedAt`. */\nexport interface VerifiedHeldEmailCredential extends VerifiedHeldCredential {\n issuedAt: Date;\n}\n\nexport interface HeldEmailCredentialOptions {\n /** Explicit clock. The verifier never reads one. */\n now: Date;\n issuerSet: IssuerSet;\n /** From the owner-signed policy or the caller's allow-list. Empty is refused. */\n acceptedIssuers: readonly AcceptedIssuer[];\n /** Issuer methods the caller enables, e.g. [\"web\"]. */\n didMethods: { issuer: readonly string[] };\n /** The authenticated owner (`did:pkh:eip155:...`). The credential subject must be principal-equal to it. */\n owner: string;\n}\n\n/**\n * Verifies a held email credential on its own. No presentation proof and no nonce: the caller must\n * already hold the SD-JWT and have authenticated `owner`. Refuses an owner mismatch with the same\n * code as the presentation path. Synchronous; the email row is fixed.\n * The code `nonce-account-mismatch` is kept for parity with the presentation path. This path spends no nonce.\n */\nexport function verifyHeldEmailCredential(sdJwt: unknown, options: HeldEmailCredentialOptions): VerifiedHeldEmailCredential {\n const { now, issuerSet, acceptedIssuers, didMethods, owner } = options;\n if (!(now instanceof Date) || Number.isNaN(now.getTime())) refuse(\"bad-config\", \"now must be a valid Date\");\n if (!IssuerSet.is(issuerSet)) refuse(\"bad-config\", \"issuerSet must come from parseIssuerSet\");\n if (!didMethods || !Array.isArray(didMethods.issuer)) refuse(\"bad-config\", \"didMethods is required\");\n if (!Array.isArray(acceptedIssuers) || acceptedIssuers.length === 0) refuse(\"bad-config\", \"acceptedIssuers must not be empty\");\n if (parsePkhDid(owner) === null) refuse(\"bad-config\", \"owner must be a did:pkh:eip155 account\");\n if (typeof sdJwt !== \"string\" || !isAscii(sdJwt)) refuse(\"bad-presentation\", \"sdJwt must be an ASCII string\");\n if (sdJwt.length > HELD_LIMITS.sdJwtChars) refuse(\"bad-presentation\", \"sdJwt is too long\");\n\n const checked = checkHeldCredential(sdJwt, {\n now, issuerSet, acceptedIssuers, didMethods,\n acceptedProfiles: [{ profile: EMAIL_HELD_ROW.profile, profileVersion: EMAIL_HELD_ROW.profileVersion }],\n rows: [EMAIL_HELD_ROW],\n });\n if (!pkhPrincipalsEqual(owner, checked.subject)) refuse(\"nonce-account-mismatch\", \"subject is not the authenticated owner\");\n return { ...toVerifiedCredential(checked), issuedAt: checked.issuedAt };\n}\n\n/**\n * Structural selective disclosure: keeps only the disclosures named in `names`, in their original\n * order, and drops any key-binding suffix. It does not verify anything. Verify the full SD-JWT\n * first; this function only re-shapes a verified presentation.\n */\nexport function selectDisclosures(sdJwt: string, names: readonly string[]): string {\n if (typeof sdJwt !== \"string\") refuse(\"bad-presentation\", \"sdJwt must be a string\");\n if (names.length === 0 || new Set(names).size !== names.length) refuse(\"bad-config\", \"names must be non-empty and unique\");\n const parts = sdJwt.split(\"~\");\n if (parts.length < 2 || parts[parts.length - 1] !== \"\") refuse(\"bad-presentation\", \"sdJwt must end with ~ and carry no key-binding JWT\");\n const [jwt, ...disclosures] = parts.slice(0, -1) as [string, ...string[]];\n const kept: string[] = [];\n const found = new Set<string>();\n for (const disclosure of disclosures) {\n const decoded = parseStrictJson(decodeUtf8(decodeBase64Url(disclosure, \"bad-disclosure\"), \"bad-disclosure\"), \"bad-disclosure\");\n if (!Array.isArray(decoded) || decoded.length !== 3 || typeof decoded[1] !== \"string\") refuse(\"bad-disclosure\", \"disclosure must be [salt, name, value]\");\n const name = decoded[1] as string;\n if (names.includes(name)) {\n if (found.has(name)) refuse(\"bad-disclosure\", `duplicate disclosure for ${name}`);\n kept.push(disclosure);\n found.add(name);\n }\n }\n for (const name of names) if (!found.has(name)) refuse(\"bad-disclosure\", `no disclosure for ${name}`);\n return [jwt, ...kept, \"\"].join(\"~\");\n}\n","import { toChecksumAddress } from \"./did\";\nimport { refuse } from \"./errors\";\n\nexport const PRESENTATION_STATEMENT = \"Present a TinyCloud held credential\";\nexport const SD_HASH_RESOURCE_PREFIX = \"urn:tinycloud:sd-hash:\";\n\nexport interface StrictSiwe {\n domain: string;\n address: string;\n statement: string;\n uri: string;\n version: string;\n chainId: string;\n nonce: string;\n issuedAt: string;\n expirationTime?: string;\n notBefore?: string;\n resources: string[];\n}\n\nconst RFC3339 = /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,9}))?(?:Z|([+-])(\\d{2}):(\\d{2}))$/;\n\n/**\n * Exact RFC 3339 `date-time` to epoch milliseconds, or null. Every field is range-checked and the\n * date must be real (no Feb 31, hour 24 or second 60), so `Date.parse`'s leniency never decides.\n */\nexport function parseRfc3339(value: string): number | null {\n const m = RFC3339.exec(value);\n if (!m) return null;\n const [year, month, day, hour, minute, second] = [m[1], m[2], m[3], m[4], m[5], m[6]].map(Number) as [number, number, number, number, number, number];\n if (month < 1 || month > 12 || hour > 23 || minute > 59 || second > 59) return null;\n const date = new Date(0);\n date.setUTCFullYear(year, month - 1, day); // unlike Date.UTC, years 0-99 are not remapped to 19xx\n if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) return null;\n let offsetMinutes = 0;\n if (m[8] !== undefined) {\n const oh = Number(m[9]), om = Number(m[10]);\n if (oh > 23 || om > 59) return null;\n offsetMinutes = (m[8] === \"-\" ? -1 : 1) * (oh * 60 + om);\n }\n const millis = m[7] === undefined ? 0 : Number(m[7].padEnd(3, \"0\").slice(0, 3));\n date.setUTCHours(hour, minute, second, millis);\n return date.getTime() - offsetMinutes * 60_000;\n}\nconst AUDIENCE = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?(?::([1-9][0-9]{0,4}))?$/;\n\n/** An audience is a bare RFC 3986 authority: lowercase host[:port], no scheme, userinfo, path, or default port 443. */\nexport function assertValidAudience(audience: string): void {\n const match = AUDIENCE.exec(audience);\n if (!match || (match[1] !== undefined && (Number(match[1]) > 65535 || match[1] === \"443\"))) {\n refuse(\"bad-config\", \"audience must be a lowercase host[:port] authority without a default port\");\n }\n}\n\n/** Strict EIP-4361 parse in the exact layout this profile accepts. Anything else is refused. */\nexport function parseStrictSiwe(message: string): StrictSiwe {\n const bad = (why: string): never => refuse(\"bad-siwe\", why);\n const lines = message.split(\"\\n\");\n const head = /^(\\S+) wants you to sign in with your Ethereum account:$/.exec(lines[0] ?? \"\");\n if (!head) bad(\"header line\");\n const address = lines[1] ?? \"\";\n if (!/^0x[0-9a-fA-F]{40}$/.test(address)) bad(\"address\");\n if (toChecksumAddress(address) !== address) bad(\"address must be EIP-55 checksummed\");\n if (lines[2] !== \"\") bad(\"layout\");\n const statement = lines[3];\n if (statement === undefined || statement === \"\") bad(\"statement\");\n if (lines[4] !== \"\") bad(\"layout\");\n let index = 5;\n const field = (name: string, optional = false): string | undefined => {\n const line = lines[index];\n const prefix = `${name}: `;\n if (line === undefined || !line.startsWith(prefix)) {\n if (optional) return undefined;\n return bad(`missing ${name}`);\n }\n index += 1;\n const value = line.slice(prefix.length);\n if (value === \"\") bad(`empty ${name}`);\n return value;\n };\n const uri = field(\"URI\")!;\n const version = field(\"Version\")!;\n const chainId = field(\"Chain ID\")!;\n const nonce = field(\"Nonce\")!;\n const issuedAt = field(\"Issued At\")!;\n const expirationTime = field(\"Expiration Time\", true);\n const notBefore = field(\"Not Before\", true);\n if (lines[index]?.startsWith(\"Request ID: \")) bad(\"Request ID is not accepted\");\n const resources: string[] = [];\n if (lines[index] === \"Resources:\") {\n index += 1;\n while (lines[index] !== undefined && lines[index]!.startsWith(\"- \")) { resources.push(lines[index]!.slice(2)); index += 1; }\n }\n if (index !== lines.length) bad(\"trailing content\");\n if (!/^[0-9]+$/.test(chainId)) bad(\"chain id\");\n if (!/^[A-Za-z0-9]{8,}$/.test(nonce)) bad(\"nonce must be alphanumeric\");\n for (const [name, value] of [[\"Issued At\", issuedAt], [\"Expiration Time\", expirationTime], [\"Not Before\", notBefore]] as const) {\n if (value !== undefined && parseRfc3339(value) === null) bad(`${name} is not RFC 3339`);\n }\n return { domain: head![1]!, address, statement, uri, version, chainId, nonce, issuedAt, expirationTime, notBefore, resources };\n}\n","import { recoverEip191Address } from \"./crypto\";\nimport { parsePkhDid, pkhPrincipalsEqual } from \"./did\";\nimport { hexToBytes, isAscii, isObject } from \"./encoding\";\nimport { refuse } from \"./errors\";\nimport { HELD_LIMITS, checkHeldCredential, sdHash, toVerifiedCredential, type AcceptedIssuer, type AcceptedProfile, type VerifiedHeldCredential } from \"./credential\";\nimport { IssuerSet } from \"./issuer-set\";\nimport type { NonceStore } from \"./nonce-store\";\nimport { DEFAULT_ROWS, type ProfileRow } from \"./rows\";\nimport { PRESENTATION_STATEMENT, SD_HASH_RESOURCE_PREFIX, assertValidAudience, parseRfc3339, parseStrictSiwe } from \"./siwe\";\n\nexport { HELD_LIMITS, type AcceptedIssuer, type AcceptedProfile, type VerifiedHeldCredential } from \"./credential\";\n\nexport interface HeldPresentation {\n sdJwt: string;\n siwe: { message: string; signature: string };\n}\n\nexport interface VerifyHeldPresentationOptions {\n /** Explicit clock. The verifier never reads one. */\n now: Date;\n issuerSet: IssuerSet;\n /** From the owner-signed policy or the caller's allow-list. Empty is refused. */\n acceptedIssuers: readonly AcceptedIssuer[];\n /** Empty is refused. */\n acceptedProfiles: readonly AcceptedProfile[];\n /** docs/principles.md principle 2. Issuer methods the caller enables, e.g. [\"web\"]. */\n didMethods: { issuer: readonly string[] };\n /** This verifier's own EIP-4361 domain (an authority). One distinct audience per verifier. */\n audience: string;\n nonceStore: NonceStore;\n /**\n * The authenticated caller's account (`did:pkh:eip155:...`). Nonces are minted bound to it, and\n * `subject` must be principal-equal to it. A presentation is not authentication (spec §4).\n */\n callerAccount: string;\n /** Defaults to the email row. */\n rows?: readonly ProfileRow[];\n}\n\n/**\n * Verifies a held-credential presentation (spec §4.4). Fail closed: the first failed check throws a\n * HeldCredentialError. The caller-bound nonce is spent first, on any outcome. No payload claim is read before the issuer signature has verified.\n */\nexport async function verifyHeldPresentation(presentation: unknown, options: VerifyHeldPresentationOptions): Promise<VerifiedHeldCredential> {\n // 1. shape and inputs (nothing is looked up yet)\n const { now, issuerSet, acceptedIssuers, acceptedProfiles, didMethods, audience, nonceStore, callerAccount } = options;\n const rows = options.rows ?? DEFAULT_ROWS;\n if (!(now instanceof Date) || Number.isNaN(now.getTime())) refuse(\"bad-config\", \"now must be a valid Date\");\n if (!IssuerSet.is(issuerSet)) refuse(\"bad-config\", \"issuerSet must come from parseIssuerSet\");\n if (!nonceStore || !didMethods || !Array.isArray(didMethods.issuer)) refuse(\"bad-config\", \"issuerSet, nonceStore and didMethods are required\");\n if (!Array.isArray(acceptedIssuers) || acceptedIssuers.length === 0) refuse(\"bad-config\", \"acceptedIssuers must not be empty\");\n if (!Array.isArray(acceptedProfiles) || acceptedProfiles.length === 0) refuse(\"bad-config\", \"acceptedProfiles must not be empty\");\n assertValidAudience(audience);\n if (parsePkhDid(callerAccount) === null) refuse(\"bad-config\", \"callerAccount must be a did:pkh:eip155 account\");\n if (!isObject(presentation) || Object.keys(presentation).sort().join() !== \"sdJwt,siwe\") refuse(\"bad-presentation\", \"unexpected shape\");\n const { sdJwt, siwe } = presentation as { sdJwt: unknown; siwe: unknown };\n if (typeof sdJwt !== \"string\" || !isAscii(sdJwt)) refuse(\"bad-presentation\", \"sdJwt must be an ASCII string\");\n if (!isObject(siwe) || Object.keys(siwe).sort().join() !== \"message,signature\" || typeof siwe.message !== \"string\" || typeof siwe.signature !== \"string\") refuse(\"bad-presentation\", \"siwe shape\");\n const sdJwtText = sdJwt as string;\n const siweMessage = siwe.message as string;\n const siweSignature = siwe.signature as string;\n // bounded before anything is split, decoded or parsed (a refusal here spends no nonce)\n if (sdJwtText.length > HELD_LIMITS.sdJwtChars) refuse(\"bad-presentation\", \"sdJwt is too long\");\n if (siweMessage.length > HELD_LIMITS.siweMessageChars) refuse(\"bad-presentation\", \"siwe message is too long\");\n if (siweSignature.length > HELD_LIMITS.siweSignatureChars) refuse(\"bad-presentation\", \"siwe signature is too long\");\n\n // 2. SIWE fields and the nonce. The nonce is consumed first, on any outcome, by one atomic\n // compare-and-delete that includes the authenticated caller's account; each presentation uses its\n // nonce once.\n const message = parseStrictSiwe(siweMessage);\n if (message.domain !== audience) refuse(\"bad-siwe\", \"domain is not this verifier's audience\");\n if (!/^[0-9A-Za-z]{22,}$/.test(message.nonce)) refuse(\"bad-nonce\", \"nonce must be at least 22 base62 characters (or 32 hex)\");\n const consumed = await nonceStore.consume(audience, message.nonce, callerAccount);\n if (!consumed.existed) refuse(\"nonce-replayed\", \"nonce is unknown, already used, or bound to another account\");\n\n // 3. the signed credential (spec §4.4 steps 2 to 9), shared with verifyHeldEmailCredential\n const checked = checkHeldCredential(sdJwtText, { now, issuerSet, acceptedIssuers, acceptedProfiles, didMethods, rows });\n const subject = checked.subject;\n const subjectPkh = checked.subjectPkh;\n\n // 4. SIWE fields against the credential\n const nowMs = now.getTime();\n let origin: string | undefined;\n try { const url = new URL(message.uri); origin = url.protocol === \"https:\" && !url.username && !url.password ? url.origin : undefined; } catch { origin = undefined; }\n if (origin !== `https://${audience}`) refuse(\"bad-siwe\", \"uri must be an https origin matching the audience\");\n if (message.version !== \"1\") refuse(\"bad-siwe\", \"version must be 1\");\n if (message.statement !== PRESENTATION_STATEMENT) refuse(\"bad-siwe\", \"unexpected statement\");\n if (message.chainId !== subjectPkh.chainId) refuse(\"bad-siwe\", \"chain id must equal the credential subject's\");\n if (!pkhPrincipalsEqual(`did:pkh:eip155:${message.chainId}:${message.address}`, subject)) refuse(\"bad-siwe\", \"address is not the credential subject\");\n if (message.resources.length !== 1 || message.resources.some((r) => r.toLowerCase().startsWith(\"urn:recap:\")) || message.resources[0] !== `${SD_HASH_RESOURCE_PREFIX}${sdHash(sdJwtText)}`) refuse(\"bad-siwe\", \"resources must be exactly the sd_hash of this presentation\");\n if (message.expirationTime !== undefined && nowMs >= (parseRfc3339(message.expirationTime) as number)) refuse(\"bad-siwe\", \"SIWE has expired\");\n if (message.notBefore !== undefined && nowMs < (parseRfc3339(message.notBefore) as number)) refuse(\"bad-siwe\", \"SIWE is not yet valid\");\n const signatureBytes = hexToBytes(siweSignature.startsWith(\"0x\") ? siweSignature.slice(2) : \"zz\", \"bad-siwe-signature\");\n const recovered = recoverEip191Address(siweMessage, signatureBytes);\n if (recovered === null || recovered !== subjectPkh.address) refuse(\"bad-siwe-signature\", \"signature does not recover the credential subject\");\n\n // 5. the credential's subject must be the authenticated caller (a presentation is not authentication)\n if (!pkhPrincipalsEqual(callerAccount, subject)) refuse(\"nonce-account-mismatch\", \"subject is not the authenticated caller\");\n\n return toVerifiedCredential(checked);\n}\n","import { parsePkhDid, pkhPrincipalsEqual } from \"./did\";\n\n/**\n * Single use is a property of the store. `consume` is one atomic compare-and-delete and the only\n * nonce operation a verifier performs: it deletes the entry only where the audience, the nonce and\n * the bound account all match (for example `DELETE ... WHERE nonce = ? AND audience = ? AND\n * owner_did = ? RETURNING`). The verifier calls it before the signature and claim checks. Every\n * nonce is minted bound to the authenticated caller's account. Production stores: a Durable Object,\n * or D1/Postgres with a conditional delete; there is no stateless mode, because single use does not\n * rely on timestamps. Details: TC-1540\n */\nexport interface NonceStore {\n consume(audience: string, nonce: string, account: string): Promise<{ existed: boolean }>;\n}\n\nexport function randomNonce(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * In-memory store for tests and single-process tools. Deployments use a shared store that implements\n * `consume` atomically. Entries do not expire: a nonce lives until it is consumed or the process ends.\n */\nexport class InMemoryNonceStore implements NonceStore {\n private readonly nonces = new Map<string, string>();\n\n /** Mints a nonce for `audience`, bound to the authenticated caller's `did:pkh` account. */\n issue(audience: string, account: string): string {\n if (parsePkhDid(account) === null) throw new TypeError(\"a nonce must be bound to a did:pkh:eip155 account\");\n const nonce = randomNonce();\n this.nonces.set(`${audience}\\n${nonce}`, account);\n return nonce;\n }\n\n async consume(audience: string, nonce: string, account: string): Promise<{ existed: boolean }> {\n const key = `${audience}\\n${nonce}`;\n const bound = this.nonces.get(key);\n if (bound === undefined || !pkhPrincipalsEqual(bound, account)) return { existed: false };\n this.nonces.delete(key);\n return { existed: true };\n }\n}\n","/**\n * THE single shared email normalisation function (gap G14; the same rules as billing's\n * `src/email-normalise.ts`, TC-1439). The alias rules below are confirmed by Sam on 2026-10-19\n * (card tc-1439-email-aliases); a change to them changes only here.\n *\n * - trim and lowercase;\n * - fold a `+tag` suffix of the local part, for every domain;\n * - remove dots of the local part for gmail.com and googlemail.com only (googlemail.com maps to\n * gmail.com, as in billing's implementation).\n *\n * Unicode/IDNA handling is being defined. Until then, inputs containing a UTF-16 code unit above\n * 0x7F are refused, checked before trim and lowercase. Billing applies the same rule.\n *\n * Returns null for empty or malformed input: callers must fail closed.\n */\nexport function normaliseEmail(email: string): string | null {\n if (typeof email === \"string\" && /[^\\x00-\\x7F]/.test(email)) return null;\n const trimmed = typeof email === \"string\" ? email.trim().toLowerCase() : \"\";\n if (trimmed === \"\" || /\\s/.test(trimmed)) return null;\n const parts = trimmed.split(\"@\");\n if (parts.length !== 2) return null;\n let [local, domain] = parts as [string, string];\n if (local === \"\" || domain === \"\") return null;\n if (domain === \"googlemail.com\") domain = \"gmail.com\";\n if (!domain.includes(\".\") || domain.startsWith(\".\") || domain.endsWith(\".\") || domain.includes(\"..\")) return null;\n const plus = local.indexOf(\"+\");\n if (plus !== -1) local = local.slice(0, plus);\n if (domain === \"gmail.com\") local = local.replace(/\\./g, \"\");\n if (local === \"\") return null;\n return `${local}@${domain}`;\n}\n","import { secp256k1 } from \"@noble/curves/secp256k1\";\nimport { recoverEip191Address } from \"./crypto\";\nimport { HELD_LIMITS, sdHash } from \"./credential\";\nimport { parsePkhDid, pkhPrincipalsEqual, toChecksumAddress } from \"./did\";\nimport { hexToBytes, isAscii, isObject } from \"./encoding\";\nimport { refuse } from \"./errors\";\nimport { PRESENTATION_STATEMENT, SD_HASH_RESOURCE_PREFIX, assertValidAudience, parseRfc3339, parseStrictSiwe } from \"./siwe\";\nimport type { HeldPresentation } from \"./verify\";\n\n/** Signs an EIP-191 `personal_sign` message. Returns `0x` + 130 hex (r, s, v) with low-S. Called once per presentation. */\nexport type HeldPresentationSigner = (message: string) => Promise<string>;\n\nexport interface HeldPresentationSiwe {\n message: string;\n signature: string;\n}\n\nexport interface BuildHeldPresentationSiweInput {\n /** The verifier's audience: a bare host[:port] authority. It becomes the SIWE domain and the https origin. */\n audience: string;\n /** Nonce minted by the verifier for `audience`, bound to `account`. At least 22 base62 characters. */\n nonce: string;\n /** Base64url SHA-256 of the presented SD-JWT: `sdHash(selectDisclosures(...))`. */\n sdHash: string;\n /** The holder's `did:pkh:eip155:1:<address>`. */\n account: string;\n /** Whole seconds only, as RFC 3339 `Z`. Sub-second precision is refused, not truncated. */\n issuedAt: Date;\n /** Whole seconds only, as RFC 3339 `Z`. Must be after `issuedAt`. */\n expirationTime?: Date;\n /** Produces the signature. Each call builds a fresh message; nothing is cached. */\n sign: HeldPresentationSigner;\n}\n\nexport interface CheckReturnedPresentationOptions {\n /** Explicit clock for the SIWE expiry and not-before windows. */\n now: Date;\n /** The verifier's audience. The SIWE domain and https origin must match it. */\n audience: string;\n /** The holder's `did:pkh:eip155:1:<address>`. The SIWE address must be principal-equal to it. */\n account: string;\n}\n\nconst NONCE_FORMAT = /^[0-9A-Za-z]{22,}$/;\nconst SD_HASH_FORMAT = /^[A-Za-z0-9_-]{43}$/;\nconst SIGNATURE_FORMAT = /^0x[0-9a-fA-F]{130}$/;\nconst SECP256K1_HALF_ORDER = secp256k1.CURVE.n / 2n;\n\nfunction isValidDate(value: unknown): value is Date {\n return value instanceof Date && !Number.isNaN(value.getTime());\n}\n\n/** The node accepts only low-S signatures (`recover_from_prehash`). A high-S twin is refused, not normalised. */\nfunction hasLowS(signature: string): boolean {\n return BigInt(`0x${signature.slice(66, 130)}`) <= SECP256K1_HALF_ORDER;\n}\n\n/** RFC 3339 UTC with whole seconds, the form the node fixtures use. Sub-second precision is refused, not dropped. */\nfunction wholeSecondRfc3339(value: Date, field: string): string {\n if (value.getUTCMilliseconds() !== 0) refuse(\"bad-config\", `${field} must be a whole second`);\n return value.toISOString().replace(\".000Z\", \"Z\");\n}\n\nfunction rfc3339Millis(value: string): number {\n const millis = parseRfc3339(value);\n if (millis === null) refuse(\"bad-siwe\", \"SIWE timestamp is not RFC 3339\");\n return millis;\n}\n\n/**\n * Builds the EIP-4361 message for a held presentation in the layout `parseStrictSiwe` accepts, and\n * signs it with `sign`. The signature must recover to `account`. The verifier, not this builder,\n * decides whether the presentation is accepted.\n */\nexport async function buildHeldPresentationSiwe(input: BuildHeldPresentationSiweInput): Promise<HeldPresentationSiwe> {\n const { audience, nonce, sdHash: hash, account, issuedAt, expirationTime, sign } = input;\n if (typeof audience !== \"string\") refuse(\"bad-config\", \"audience must be a string\");\n assertValidAudience(audience);\n const subject = parsePkhDid(account);\n if (subject === null) refuse(\"bad-config\", \"account must be a did:pkh:eip155 account\");\n if (subject.chainId !== \"1\") refuse(\"bad-config\", \"held presentations are on chain 1\");\n if (typeof nonce !== \"string\" || !NONCE_FORMAT.test(nonce)) refuse(\"bad-config\", \"nonce must be at least 22 base62 characters\");\n if (typeof hash !== \"string\" || !SD_HASH_FORMAT.test(hash)) refuse(\"bad-config\", \"sdHash must be a base64url SHA-256\");\n if (!isValidDate(issuedAt)) refuse(\"bad-config\", \"issuedAt must be a valid Date\");\n if (expirationTime !== undefined && !isValidDate(expirationTime)) refuse(\"bad-config\", \"expirationTime must be a valid Date\");\n if (expirationTime !== undefined && expirationTime.getTime() <= issuedAt.getTime()) refuse(\"bad-config\", \"expirationTime must be after issuedAt\");\n if (typeof sign !== \"function\") refuse(\"bad-config\", \"sign is required\");\n\n const message = [\n `${audience} wants you to sign in with your Ethereum account:`,\n toChecksumAddress(subject.address),\n \"\",\n PRESENTATION_STATEMENT,\n \"\",\n `URI: https://${audience}`,\n \"Version: 1\",\n \"Chain ID: 1\",\n `Nonce: ${nonce}`,\n `Issued At: ${wholeSecondRfc3339(issuedAt, \"issuedAt\")}`,\n ...(expirationTime === undefined ? [] : [`Expiration Time: ${wholeSecondRfc3339(expirationTime, \"expirationTime\")}`]),\n \"Resources:\",\n `- ${SD_HASH_RESOURCE_PREFIX}${hash}`,\n ].join(\"\\n\");\n parseStrictSiwe(message);\n if (message.length > HELD_LIMITS.siweMessageChars) refuse(\"bad-config\", \"SIWE message exceeds the node limit\");\n\n const signature = await sign(message);\n if (typeof signature !== \"string\" || !SIGNATURE_FORMAT.test(signature)) refuse(\"bad-config\", \"signer must return 0x-prefixed 65-byte hex\");\n if (!hasLowS(signature)) refuse(\"bad-siwe-signature\", \"signer must return a low-S signature\");\n const recovered = recoverEip191Address(message, hexToBytes(signature.slice(2), \"bad-siwe-signature\"));\n if (recovered !== subject.address) refuse(\"bad-siwe-signature\", \"signer does not control the account\");\n return { message, signature };\n}\n\n/**\n * Client-side check of a presentation before it is sent on. It is not a verifier: it does not look\n * up issuers, spend a nonce, or check the credential signature. `verifyHeldPresentation` decides acceptance.\n * It refuses a high-S signature, as `buildHeldPresentationSiwe` does. That is stricter than the Rust verifier,\n * which accepts high-S signatures: a presentation this check refuses can still be one the verifier would accept.\n */\nexport function checkReturnedPresentation(presentation: unknown, options: CheckReturnedPresentationOptions): HeldPresentation {\n const { now, audience, account } = options;\n if (!isValidDate(now)) refuse(\"bad-config\", \"now must be a valid Date\");\n assertValidAudience(audience);\n if (parsePkhDid(account) === null) refuse(\"bad-config\", \"account must be a did:pkh:eip155 account\");\n if (!isObject(presentation) || Object.keys(presentation).sort().join() !== \"sdJwt,siwe\") refuse(\"bad-presentation\", \"unexpected shape\");\n const { sdJwt, siwe } = presentation as { sdJwt: unknown; siwe: unknown };\n if (typeof sdJwt !== \"string\" || !isAscii(sdJwt) || sdJwt.length > HELD_LIMITS.sdJwtChars) refuse(\"bad-presentation\", \"sdJwt must be a bounded ASCII string\");\n if (!isObject(siwe) || Object.keys(siwe).sort().join() !== \"message,signature\" || typeof siwe.message !== \"string\" || typeof siwe.signature !== \"string\") {\n refuse(\"bad-presentation\", \"siwe must carry message and signature\");\n }\n const siweMessage = siwe.message as string;\n const siweSignature = siwe.signature as string;\n if (siweMessage.length > HELD_LIMITS.siweMessageChars) refuse(\"bad-presentation\", \"siwe message is too long\");\n if (!SIGNATURE_FORMAT.test(siweSignature) || !hasLowS(siweSignature)) refuse(\"bad-siwe-signature\", \"signature must be 0x-prefixed 65-byte low-S hex\");\n\n const parsed = parseStrictSiwe(siweMessage);\n if (parsed.domain !== audience) refuse(\"bad-siwe\", \"domain is not the audience\");\n let origin: string | undefined;\n try {\n const url = new URL(parsed.uri);\n origin = url.protocol === \"https:\" && !url.username && !url.password ? url.origin : undefined;\n } catch {\n origin = undefined;\n }\n if (origin !== `https://${audience}`) refuse(\"bad-siwe\", \"uri must be an https origin matching the audience\");\n if (parsed.version !== \"1\") refuse(\"bad-siwe\", \"version must be 1\");\n if (parsed.statement !== PRESENTATION_STATEMENT) refuse(\"bad-siwe\", \"unexpected statement\");\n if (parsed.chainId !== \"1\") refuse(\"bad-siwe\", \"chain id must be 1\");\n if (!NONCE_FORMAT.test(parsed.nonce)) refuse(\"bad-nonce\", \"nonce must be at least 22 base62 characters\");\n if (parsed.resources.length !== 1 || parsed.resources[0] !== `${SD_HASH_RESOURCE_PREFIX}${sdHash(sdJwt)}`) {\n refuse(\"bad-siwe\", \"resources must be exactly the sd_hash of this presentation\");\n }\n const nowMs = now.getTime();\n if (parsed.expirationTime !== undefined && nowMs >= rfc3339Millis(parsed.expirationTime)) refuse(\"bad-siwe\", \"SIWE has expired\");\n if (parsed.notBefore !== undefined && nowMs < rfc3339Millis(parsed.notBefore)) refuse(\"bad-siwe\", \"SIWE is not yet valid\");\n if (!pkhPrincipalsEqual(`did:pkh:eip155:${parsed.chainId}:${parsed.address}`, account)) refuse(\"bad-siwe\", \"SIWE address is not the account\");\n const recovered = recoverEip191Address(siweMessage, hexToBytes(siweSignature.slice(2), \"bad-siwe-signature\"));\n if (recovered === null || recovered !== parsed.address.toLowerCase()) refuse(\"bad-siwe-signature\", \"signature does not recover the SIWE address\");\n\n return { sdJwt, siwe: { message: siweMessage, signature: siweSignature } };\n}\n"],"mappings":"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","names":["secp256k1","secp256k1"]}
package/dist/index.cjs CHANGED
@@ -39,6 +39,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
39
39
  // src/index.ts
40
40
  var index_exports = {};
41
41
  __export(index_exports, {
42
+ ACCOUNT_HOLDER_BINDING_DOMAIN: () => ACCOUNT_HOLDER_BINDING_DOMAIN,
42
43
  ACCOUNT_INDEX_SCHEMA: () => import_bootstrap4.ACCOUNT_INDEX_SCHEMA,
43
44
  ACCOUNT_MANIFEST_PERMISSIONS: () => import_bootstrap4.ACCOUNT_MANIFEST_PERMISSIONS,
44
45
  ACCOUNT_REGISTRY_PATH: () => ACCOUNT_REGISTRY_PATH,
@@ -864,6 +865,7 @@ function credentialError(value, fallbackCode = "VERIFICATION_FAILED") {
864
865
  // src/credentials/types.ts
865
866
  var CREDENTIAL_ACQUISITION_PROTOCOL = "tinycloud.credentials/acquisition/v1";
866
867
  var HOLDER_BINDING_DOMAIN = "tinycloud.credentials/holder-binding/v1";
868
+ var ACCOUNT_HOLDER_BINDING_DOMAIN = "tinycloud.credentials/account-binding/v1";
867
869
  var CREDENTIAL_CONTRACT_VERSION = 1;
868
870
  var CREDENTIAL_STEP_REGISTRY_VERSION = 1;
869
871
  var CREDENTIAL_FORMAT = "vc+sd-jwt";
@@ -871,18 +873,22 @@ var CREDENTIAL_FORMAT = "vc+sd-jwt";
871
873
  // src/credentials/binding.ts
872
874
  var OPAQUE = /^[A-Za-z0-9_-]{16,128}$/;
873
875
  var DIGEST = /^[A-Za-z0-9_-]{43}$/;
876
+ var JTI_V4 = /^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
877
+ var ACCOUNT_CREDENTIAL_LIFETIME_SECONDS = 31536e3;
874
878
  function createHolderBinding(input) {
875
879
  return validateCredentialHolderBinding({ type: HOLDER_BINDING_DOMAIN, protocol: CREDENTIAL_ACQUISITION_PROTOCOL, ...input });
876
880
  }
877
881
  function validateCredentialHolderBinding(value) {
878
882
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new CredentialError("REQUEST_SUBSTITUTED", "Holder binding is invalid");
879
883
  const raw = value;
880
- const fields = ["type", "protocol", "requestId", "profile", "profileVersion", "descriptorDigest", "requirementDigest", "issuer", "issuerKid", "holderDid", "normalizedClaimsDigest", "challengeNonce", "audience", "openerOrigin", "completionOrigin", "completionContext", "jti", "issuedAt", "expiresAt"].sort();
884
+ const account = raw.type === ACCOUNT_HOLDER_BINDING_DOMAIN;
885
+ const fields = ["type", "protocol", "requestId", "profile", "profileVersion", "descriptorDigest", "requirementDigest", "issuer", "issuerKid", "holderDid", "normalizedClaimsDigest", "challengeNonce", "audience", "openerOrigin", "completionOrigin", "completionContext", "jti", "issuedAt", "expiresAt", ...account ? ["credentialLifetimeSeconds"] : []].sort();
881
886
  const actual = Object.keys(raw).sort();
882
- if (actual.length !== fields.length || actual.some((key, index) => key !== fields[index]) || raw.type !== HOLDER_BINDING_DOMAIN || raw.protocol !== CREDENTIAL_ACQUISITION_PROTOCOL || raw.profileVersion !== 1) throw new CredentialError("REQUEST_SUBSTITUTED", "Holder binding version is unsupported");
887
+ if (actual.length !== fields.length || actual.some((key, index) => key !== fields[index]) || !account && raw.type !== HOLDER_BINDING_DOMAIN || account && raw.credentialLifetimeSeconds !== ACCOUNT_CREDENTIAL_LIFETIME_SECONDS || raw.protocol !== CREDENTIAL_ACQUISITION_PROTOCOL || raw.profileVersion !== 1) throw new CredentialError("REQUEST_SUBSTITUTED", "Holder binding version is unsupported");
883
888
  for (const name2 of ["requestId", "profile", "descriptorDigest", "requirementDigest", "issuer", "issuerKid", "holderDid", "normalizedClaimsDigest", "challengeNonce", "audience", "openerOrigin", "completionOrigin", "completionContext", "jti", "issuedAt", "expiresAt"]) if (typeof raw[name2] !== "string" || raw[name2].length === 0) throw new CredentialError("REQUEST_SUBSTITUTED", "Holder binding field is invalid");
884
889
  for (const name2 of ["descriptorDigest", "requirementDigest", "normalizedClaimsDigest"]) if (!DIGEST.test(raw[name2])) throw new CredentialError("REQUEST_SUBSTITUTED", "Holder binding digest is invalid");
885
- for (const name2 of ["requestId", "challengeNonce", "jti"]) if (!OPAQUE.test(raw[name2])) throw new CredentialError("REQUEST_SUBSTITUTED", "Holder binding nonce is invalid");
890
+ for (const name2 of ["requestId", "challengeNonce"]) if (!OPAQUE.test(raw[name2])) throw new CredentialError("REQUEST_SUBSTITUTED", "Holder binding nonce is invalid");
891
+ if (!(account ? JTI_V4 : OPAQUE).test(raw.jti)) throw new CredentialError("REQUEST_SUBSTITUTED", "Holder binding nonce is invalid");
886
892
  for (const name2 of ["openerOrigin", "completionOrigin"]) {
887
893
  let url;
888
894
  try {
@@ -901,7 +907,8 @@ function holderBindingCanonicalBytes(binding) {
901
907
  return new TextEncoder().encode(jcsCanonicalize(validateCredentialHolderBinding(binding)));
902
908
  }
903
909
  function holderBindingSigningBytes(binding) {
904
- return new TextEncoder().encode(`${HOLDER_BINDING_DOMAIN}\0${jcsCanonicalize(validateCredentialHolderBinding(binding))}`);
910
+ const valid = validateCredentialHolderBinding(binding);
911
+ return new TextEncoder().encode(`${valid.type}\0${jcsCanonicalize(valid)}`);
905
912
  }
906
913
 
907
914
  // src/credentials/descriptor.ts
@@ -991,7 +998,7 @@ function validateCredentialFlowDescriptor(value) {
991
998
  if (claimNames.has(name2) || claim.matching !== "normalized_exact" || typeof claim.selectiveDisclosure !== "boolean") fail("claim is invalid");
992
999
  claimNames.add(name2);
993
1000
  }
994
- if (raw.subjectRelationship !== "holder_is_subject") unsupported("Subject relationship is unsupported");
1001
+ if (raw.subjectRelationship !== "holder_is_subject" && raw.subjectRelationship !== "account_is_subject") unsupported("Subject relationship is unsupported");
995
1002
  if (!Array.isArray(raw.inputs) || raw.inputs.length === 0 || raw.inputs.length > 64) fail("inputs are invalid");
996
1003
  const inputIds = /* @__PURE__ */ new Set();
997
1004
  for (const value2 of raw.inputs) {
@@ -1025,7 +1032,8 @@ function validateCredentialFlowDescriptor(value) {
1025
1032
  if (!raw.steps.some((step) => step.type === "holder_signature")) fail("holder_signature is required");
1026
1033
  const binding = object(raw.holderBinding, "holderBinding");
1027
1034
  exact(binding, ["required", "alg", "domain", "version"], "holderBinding");
1028
- if (binding.required !== true || binding.alg !== "EdDSA" || binding.domain !== HOLDER_BINDING_DOMAIN || binding.version !== 1) unsupported("Holder binding is unsupported");
1035
+ const held = raw.subjectRelationship === "account_is_subject";
1036
+ if (binding.required !== true || binding.alg !== (held ? "EIP191" : "EdDSA") || binding.domain !== (held ? ACCOUNT_HOLDER_BINDING_DOMAIN : HOLDER_BINDING_DOMAIN) || binding.version !== 1) unsupported("Holder binding is unsupported");
1029
1037
  const endpoints = object(raw.endpoints, "endpoints");
1030
1038
  exact(endpoints, Object.keys(ENDPOINTS), "endpoints");
1031
1039
  for (const [name2, id] of Object.entries(ENDPOINTS)) if (endpoints[name2] !== id) fail("endpoint identifiers must use the registered vocabulary");