@ixo/credentials-kit 0.0.0-stage → 0.1.0
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.
- package/README.md +469 -2
- package/dist/chunk-2SF56NEJ.js +1053 -0
- package/dist/chunk-2SF56NEJ.js.map +1 -0
- package/dist/chunk-46VRPQPD.js +176 -0
- package/dist/chunk-46VRPQPD.js.map +1 -0
- package/dist/chunk-AQ3IINHC.js +138 -0
- package/dist/chunk-AQ3IINHC.js.map +1 -0
- package/dist/chunk-FSKCDHZ7.js +613 -0
- package/dist/chunk-FSKCDHZ7.js.map +1 -0
- package/dist/chunk-I6GBYFIJ.js +89 -0
- package/dist/chunk-I6GBYFIJ.js.map +1 -0
- package/dist/chunk-JBUX6V2Q.js +210 -0
- package/dist/chunk-JBUX6V2Q.js.map +1 -0
- package/dist/chunk-Q3MZNMCK.js +183 -0
- package/dist/chunk-Q3MZNMCK.js.map +1 -0
- package/dist/chunk-RKKRO4ZK.js +133 -0
- package/dist/chunk-RKKRO4ZK.js.map +1 -0
- package/dist/chunk-STMMFNVW.js +391 -0
- package/dist/chunk-STMMFNVW.js.map +1 -0
- package/dist/chunk-VXKONZK4.js +189 -0
- package/dist/chunk-VXKONZK4.js.map +1 -0
- package/dist/chunk-WIEGZFUO.js +337 -0
- package/dist/chunk-WIEGZFUO.js.map +1 -0
- package/dist/chunk-WND2LZ4O.js +66 -0
- package/dist/chunk-WND2LZ4O.js.map +1 -0
- package/dist/chunk-XREZY53L.js +260 -0
- package/dist/chunk-XREZY53L.js.map +1 -0
- package/dist/chunk-ZZY4WGQ6.js +13 -0
- package/dist/chunk-ZZY4WGQ6.js.map +1 -0
- package/dist/did.d.ts +182 -0
- package/dist/did.js +6 -0
- package/dist/did.js.map +1 -0
- package/dist/display.d.ts +117 -0
- package/dist/display.js +5 -0
- package/dist/display.js.map +1 -0
- package/dist/errors-BKQmNpXX.d.ts +11 -0
- package/dist/holder.d.ts +85 -0
- package/dist/holder.js +8 -0
- package/dist/holder.js.map +1 -0
- package/dist/http-BhuJ_-RZ.d.ts +25 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/jose.d.ts +134 -0
- package/dist/jose.js +4 -0
- package/dist/jose.js.map +1 -0
- package/dist/oid4vci.d.ts +428 -0
- package/dist/oid4vci.js +11 -0
- package/dist/oid4vci.js.map +1 -0
- package/dist/oid4vp.d.ts +416 -0
- package/dist/oid4vp.js +11 -0
- package/dist/oid4vp.js.map +1 -0
- package/dist/profile.d.ts +218 -0
- package/dist/profile.js +4 -0
- package/dist/profile.js.map +1 -0
- package/dist/sdjwt.d.ts +133 -0
- package/dist/sdjwt.js +5 -0
- package/dist/sdjwt.js.map +1 -0
- package/dist/sender.d.ts +8 -0
- package/dist/sender.js +7 -0
- package/dist/sender.js.map +1 -0
- package/dist/status.d.ts +108 -0
- package/dist/status.js +7 -0
- package/dist/status.js.map +1 -0
- package/dist/util-DOTEZgTt.d.ts +3 -0
- package/dist/verify.d.ts +144 -0
- package/dist/verify.js +9 -0
- package/dist/verify.js.map +1 -0
- package/dist/webhook.d.ts +62 -0
- package/dist/webhook.js +7 -0
- package/dist/webhook.js.map +1 -0
- package/package.json +168 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/errors.ts","../src/internal/util.ts","../src/jose.ts"],"names":["nobleEd25519"],"mappings":"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","file":"chunk-VXKONZK4.js","sourcesContent":["/**\n * The base class of every error the kit throws. `code` is a constant snake_case string from a\n * documented set per module; `message` is a fixed sentence for that code. Neither ever carries\n * credential contents, keys, tokens or other secrets, so both are safe to log.\n */\nexport class CredentialsKitError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n this.name = new.target.name;\n }\n}\n","export type JsonObject = Record<string, unknown>;\n\nexport function isRecord(v: unknown): v is JsonObject {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nexport function isNonEmptyString(v: unknown): v is string {\n return typeof v === 'string' && v.length > 0;\n}\n\n/** Whole seconds since the epoch for a millisecond clock reading. */\nexport function toSeconds(ms: number): number {\n return Math.floor(ms / 1000);\n}\n\n/** Allowed clock skew between parties, in seconds (2 minutes, as SPEC §3). */\nexport const CLOCK_SKEW_S = 120;\n\n/**\n * The clock reading in ms: `now` when given, else `Date.now()`. Every `now` option of the kit is\n * MILLISECONDS; a value below 10^12 (before September 2001) is almost certainly unix seconds\n * passed by mistake, which would move the clock to 1970 and make every `exp` pass. It throws a\n * `TypeError` instead.\n */\nexport function clockMs(now: number | undefined): number {\n if (now === undefined) return Date.now();\n if (typeof now !== 'number' || !Number.isFinite(now) || now < 1e12) {\n throw new TypeError(\n '`now` is milliseconds since the epoch (Date.now()), not seconds.'\n );\n }\n return now;\n}\n","/**\n * `@ixo/credentials-kit/jose` — compact JWS with EdDSA (Ed25519) only, Ed25519 key helpers and\n * the `Signer` abstraction every signing function in the kit takes.\n *\n * Ed25519 only. The kit SIGNS with `alg: \"EdDSA\"` (v1 profile, SPEC §3) and ACCEPTS both\n * `\"EdDSA\"` and the fully-specified `\"Ed25519\"` (RFC 9864) when verifying (SPEC §13.3). A header\n * with `crit` is refused (no JWS extension is understood, RFC 7515 §4.1.11). Verification is\n * strict RFC 8032 (no ZIP-215 relaxations), matching WebCrypto's Ed25519.\n */\nimport { ed25519 as nobleEd25519 } from '@noble/curves/ed25519.js';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport {\n base58btcDecode,\n base58btcEncode,\n base64urlDecode,\n base64urlEncode,\n base64urlJson,\n fromUtf8,\n utf8,\n} from './internal/bytes';\nimport { CredentialsKitError } from './internal/errors';\nimport { isRecord, type JsonObject } from './internal/util';\n\nexport { CredentialsKitError } from './internal/errors';\nexport type { JsonObject } from './internal/util';\n\n/** Signs bytes with one Ed25519 key. `kid` is the key's DID URL (`<did>#<fragment>`). */\nexport interface Signer {\n kid: string;\n sign(bytes: Uint8Array): Promise<Uint8Array>;\n}\n\n/** A `Signer` built from a local seed, which also knows its public key. */\nexport interface Ed25519Signer extends Signer {\n publicKey: Uint8Array;\n publicJwk: Ed25519PublicJwk;\n}\n\n/** An Ed25519 public key as an OKP JWK (RFC 8037). */\nexport interface Ed25519PublicJwk {\n kty: 'OKP';\n crv: 'Ed25519';\n x: string;\n}\n\n/** The JWS `alg` values the kit accepts for an Ed25519 signature (RFC 8037, RFC 9864). */\nexport const ED25519_JWS_ALGS = ['EdDSA', 'Ed25519'] as const;\nexport type Ed25519JwsAlg = (typeof ED25519_JWS_ALGS)[number];\n\n/** True for `\"EdDSA\"` and `\"Ed25519\"`. */\nexport function isEd25519JwsAlg(alg: unknown): alg is Ed25519JwsAlg {\n return alg === 'EdDSA' || alg === 'Ed25519';\n}\n\nexport interface JwsHeader {\n /** The kit's own artefacts use `EdDSA`; `Ed25519` is allowed for callers that need it. */\n alg: Ed25519JwsAlg;\n typ?: string;\n kid?: string;\n [name: string]: unknown;\n}\n\nexport interface DecodedJws {\n header: JsonObject;\n payload: JsonObject;\n signature: Uint8Array;\n /** `<base64url header>.<base64url payload>`, the bytes the signature covers. */\n signingInput: string;\n}\n\nexport type JoseErrorCode =\n 'malformed' | 'unsupported_alg' | 'bad_signature' | 'bad_key';\n\nconst JOSE_MESSAGES: Record<JoseErrorCode, string> = {\n malformed:\n 'The JWS is not a well-formed compact JWS with JSON header and payload.',\n unsupported_alg: 'The JWS does not use an Ed25519 algorithm.',\n bad_signature: 'The JWS signature does not verify.',\n bad_key: 'The key is not a valid Ed25519 public key.',\n};\n\nexport class JoseError extends CredentialsKitError {\n declare readonly code: JoseErrorCode;\n constructor(code: JoseErrorCode) {\n super(code, JOSE_MESSAGES[code]);\n }\n}\n\n/** Largest compact JWS `decodeJws` accepts (1 MiB). */\nexport const MAX_JWS_LENGTH = 1024 * 1024;\n\n/**\n * Sign `payload` under exactly `header` (`alg` `\"EdDSA\"` or `\"Ed25519\"`). The caller sets `typ`\n * and `kid`; nothing is added implicitly.\n */\nexport async function signJws(\n header: JwsHeader,\n payload: JsonObject,\n signer: Pick<Signer, 'sign'>\n): Promise<string> {\n if (!isEd25519JwsAlg(header.alg)) throw new JoseError('unsupported_alg');\n const signingInput = `${base64urlJson(header)}.${base64urlJson(payload)}`;\n const signature = await signer.sign(utf8(signingInput));\n return `${signingInput}.${base64urlEncode(signature)}`;\n}\n\n/**\n * Split and decode a compact JWS without verifying it. Throws `JoseError('malformed')`, also for\n * a header carrying `crit` (no extension is understood).\n */\nexport function decodeJws(compact: string): DecodedJws {\n if (typeof compact !== 'string' || compact.length > MAX_JWS_LENGTH) {\n throw new JoseError('malformed');\n }\n const parts = compact.split('.');\n if (parts.length !== 3) throw new JoseError('malformed');\n const [h, p, s] = parts as [string, string, string];\n let header: unknown;\n let payload: unknown;\n let signature: Uint8Array;\n try {\n header = JSON.parse(fromUtf8(base64urlDecode(h)));\n payload = JSON.parse(fromUtf8(base64urlDecode(p)));\n signature = base64urlDecode(s);\n } catch {\n throw new JoseError('malformed');\n }\n if (!isRecord(header) || !isRecord(payload)) throw new JoseError('malformed');\n if (header.crit !== undefined) throw new JoseError('malformed');\n return { header, payload, signature, signingInput: `${h}.${p}` };\n}\n\n/** Ed25519 signature check (strict RFC 8032). False for any malformed input. */\nexport function verifyEd25519(\n signature: Uint8Array,\n message: Uint8Array,\n publicKey: Uint8Array\n): boolean {\n if (signature.length !== 64 || publicKey.length !== 32) return false;\n try {\n return nobleEd25519.verify(signature, message, publicKey, {\n zip215: false,\n });\n } catch {\n return false;\n }\n}\n\n/**\n * Decode and verify a compact JWS with an Ed25519 public key. Requires `alg` `\"EdDSA\"` or\n * `\"Ed25519\"`.\n * Returns the decoded JWS; throws `JoseError` (`malformed`, `unsupported_alg`,\n * `bad_signature`). Checks nothing in the payload: callers check `typ`, `exp`, `aud`, ….\n */\nexport function verifyJws(\n compact: string,\n publicKey: Uint8Array\n): Promise<DecodedJws> {\n try {\n const jws = decodeJws(compact);\n if (!isEd25519JwsAlg(jws.header.alg))\n throw new JoseError('unsupported_alg');\n if (!verifyEd25519(jws.signature, utf8(jws.signingInput), publicKey)) {\n throw new JoseError('bad_signature');\n }\n return Promise.resolve(jws);\n } catch (e) {\n return Promise.reject(e instanceof Error ? e : new JoseError('malformed'));\n }\n}\n\n/** The OKP JWK of a raw 32-byte Ed25519 public key. */\nexport function publicJwkFromRaw(raw: Uint8Array): Ed25519PublicJwk {\n if (raw.length !== 32) throw new JoseError('bad_key');\n return { kty: 'OKP', crv: 'Ed25519', x: base64urlEncode(raw) };\n}\n\n/** The raw 32-byte key of an Ed25519 OKP public JWK. Refuses a private JWK (one with `d`). */\nexport function rawFromPublicJwk(jwk: unknown): Uint8Array {\n if (\n !isRecord(jwk) ||\n jwk.kty !== 'OKP' ||\n jwk.crv !== 'Ed25519' ||\n typeof jwk.x !== 'string' ||\n jwk.x.length > MAX_KEY_STRING_LENGTHS.jwkX ||\n 'd' in jwk\n ) {\n throw new JoseError('bad_key');\n }\n let raw: Uint8Array;\n try {\n raw = base64urlDecode(jwk.x);\n } catch {\n throw new JoseError('bad_key');\n }\n if (raw.length !== 32) throw new JoseError('bad_key');\n return raw;\n}\n\n/**\n * Longest key strings decoded (SPEC §13.3: cap before decoding; base58 decoding is quadratic).\n * A 34-byte multicodec key is at most 47 base58 characters plus `z`; a bare 32-byte key at most\n * 44; hex is exactly 64; a JWK `x` exactly 43.\n */\nexport const MAX_KEY_STRING_LENGTHS = {\n multibase: 60,\n base58: 50,\n hex: 64,\n jwkX: 43,\n} as const;\n\n/** The ed25519-pub multicodec header (varint 0xed). */\nconst ED25519_PUB_MULTICODEC = [0xed, 0x01] as const;\n\n/**\n * A multibase (`z` + base58btc) Ed25519 public key to raw bytes. Accepts the multicodec-prefixed\n * form (`z6Mk…`, as did:key and Ed25519VerificationKey2020) and a bare 32-byte key.\n */\nexport function multibaseToRaw(multibase: string): Uint8Array {\n if (\n typeof multibase !== 'string' ||\n !multibase.startsWith('z') ||\n multibase.length > MAX_KEY_STRING_LENGTHS.multibase\n ) {\n throw new JoseError('bad_key');\n }\n let bytes: Uint8Array;\n try {\n bytes = base58btcDecode(multibase.slice(1));\n } catch {\n throw new JoseError('bad_key');\n }\n if (\n bytes.length === 34 &&\n bytes[0] === ED25519_PUB_MULTICODEC[0] &&\n bytes[1] === ED25519_PUB_MULTICODEC[1]\n ) {\n return bytes.slice(2);\n }\n if (bytes.length === 32) return bytes;\n throw new JoseError('bad_key');\n}\n\n/** A raw Ed25519 public key as multibase with the ed25519-pub multicodec (`z6Mk…`). */\nexport function rawToMultibase(raw: Uint8Array): string {\n if (raw.length !== 32) throw new JoseError('bad_key');\n const prefixed = new Uint8Array(34);\n prefixed.set(ED25519_PUB_MULTICODEC);\n prefixed.set(raw, 2);\n return `z${base58btcEncode(prefixed)}`;\n}\n\nexport const base58btc = {\n encode: base58btcEncode,\n decode: base58btcDecode,\n} as const;\n\nexport const base64url = {\n encode: base64urlEncode,\n decode: base64urlDecode,\n} as const;\n\n/** Ed25519 helpers (SPEC §4 names), grouped. Each is also exported on its own. */\nexport const ed25519 = {\n publicJwkFromRaw,\n rawFromPublicJwk,\n base58btc,\n multibaseToRaw,\n rawToMultibase,\n verify: verifyEd25519,\n /** The raw 32-byte public key of a 32-byte seed. */\n publicKeyFromSeed(seed: Uint8Array): Uint8Array {\n if (seed.length !== 32) throw new JoseError('bad_key');\n return nobleEd25519.getPublicKey(seed);\n },\n} as const;\n\n/**\n * A `Signer` for a 32-byte Ed25519 seed (RFC 8032 secret key). The seed stays in the closure;\n * `kid` must be the DID URL under which the matching public key is published.\n */\nexport function signerFromEd25519Seed(\n seed: Uint8Array,\n kid: string\n): Ed25519Signer {\n if (!(seed instanceof Uint8Array) || seed.length !== 32) {\n throw new JoseError('bad_key');\n }\n const secret = Uint8Array.from(seed);\n const publicKey = nobleEd25519.getPublicKey(secret);\n return {\n kid,\n publicKey,\n publicJwk: publicJwkFromRaw(publicKey),\n sign(bytes: Uint8Array) {\n return Promise.resolve(nobleEd25519.sign(bytes, secret));\n },\n };\n}\n\n/**\n * The IXO service-key convention (VFS, kyc-server, UCAN store, Self): seed =\n * SHA-256(UTF-8(mnemonic.trim())). Use with `signerFromEd25519Seed`. This is NOT a BIP-39\n * derivation; wallets that derive keys from a mnemonic differently must use their own.\n */\nexport function ed25519SeedFromMnemonic(mnemonic: string): Uint8Array {\n return sha256(utf8(mnemonic.trim()));\n}\n"]}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { request, DEFAULT_TIMEOUT_MS, isLoopbackHost, isPrivateHost } from './chunk-Q3MZNMCK.js';
|
|
2
|
+
import { CredentialsKitError, isRecord, multibaseToRaw, MAX_KEY_STRING_LENGTHS, rawFromPublicJwk } from './chunk-VXKONZK4.js';
|
|
3
|
+
import { base58btcDecode, hexToBytes } from './chunk-AQ3IINHC.js';
|
|
4
|
+
|
|
5
|
+
// src/internal/cache.ts
|
|
6
|
+
function createMemoryCache(opts = {}) {
|
|
7
|
+
const maxEntries = opts.maxEntries ?? 1e3;
|
|
8
|
+
const now = opts.now ?? (() => Date.now());
|
|
9
|
+
const entries = /* @__PURE__ */ new Map();
|
|
10
|
+
return {
|
|
11
|
+
get(key) {
|
|
12
|
+
const hit = entries.get(key);
|
|
13
|
+
if (!hit) return Promise.resolve(null);
|
|
14
|
+
if (hit.until <= now()) {
|
|
15
|
+
entries.delete(key);
|
|
16
|
+
return Promise.resolve(null);
|
|
17
|
+
}
|
|
18
|
+
return Promise.resolve(hit.value);
|
|
19
|
+
},
|
|
20
|
+
put(key, value, options) {
|
|
21
|
+
const ttl = options?.expirationTtl ?? 60;
|
|
22
|
+
entries.delete(key);
|
|
23
|
+
if (entries.size >= maxEntries) {
|
|
24
|
+
const oldest = entries.keys().next();
|
|
25
|
+
if (!oldest.done) entries.delete(oldest.value);
|
|
26
|
+
}
|
|
27
|
+
entries.set(key, { value, until: now() + ttl * 1e3 });
|
|
28
|
+
return Promise.resolve();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function cacheGet(cache, key) {
|
|
33
|
+
if (!cache) return null;
|
|
34
|
+
try {
|
|
35
|
+
return await cache.get(key);
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function cachePut(cache, key, value, ttlSeconds) {
|
|
41
|
+
if (!cache) return;
|
|
42
|
+
try {
|
|
43
|
+
await cache.put(key, value, {
|
|
44
|
+
expirationTtl: Math.max(60, Math.floor(ttlSeconds))
|
|
45
|
+
});
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/did.ts
|
|
51
|
+
var ResolverUnavailable = class extends CredentialsKitError {
|
|
52
|
+
source;
|
|
53
|
+
constructor(source) {
|
|
54
|
+
super(
|
|
55
|
+
"resolver_unavailable",
|
|
56
|
+
source === "blocksync" ? "Blocksync could not be read to resolve the DID." : "The did:web document could not be read."
|
|
57
|
+
);
|
|
58
|
+
this.source = source;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var IXO_PERSON_DID = /^did:ixo:ixo1[02-9ac-hj-np-z]{38}$/;
|
|
62
|
+
var IXO_ENTITY_DID = /^did:ixo:entity:[0-9a-f]{32}$/;
|
|
63
|
+
function didFromKid(kid) {
|
|
64
|
+
const hash = kid.indexOf("#");
|
|
65
|
+
return hash < 0 ? kid : kid.slice(0, hash);
|
|
66
|
+
}
|
|
67
|
+
var DEFAULT_DID_TTL_S = 600;
|
|
68
|
+
var MAX_DID_DOCUMENT_BYTES = 256 * 1024;
|
|
69
|
+
var MAX_BLOCKSYNC_BYTES = 1024 * 1024;
|
|
70
|
+
function absolute(did, id) {
|
|
71
|
+
return id.startsWith("#") ? `${did}${id}` : id;
|
|
72
|
+
}
|
|
73
|
+
function normalizeMethod(did, v) {
|
|
74
|
+
if (!isRecord(v) || typeof v.id !== "string" || typeof v.type !== "string") {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const controller = typeof v.controller === "string" ? absolute(did, v.controller) : "";
|
|
78
|
+
return { ...v, id: absolute(did, v.id), type: v.type, controller };
|
|
79
|
+
}
|
|
80
|
+
function normalizeRelationship(did, v) {
|
|
81
|
+
if (!Array.isArray(v)) return [];
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const entry of v) {
|
|
84
|
+
if (typeof entry === "string") out.push(absolute(did, entry));
|
|
85
|
+
else {
|
|
86
|
+
const vm = normalizeMethod(did, entry);
|
|
87
|
+
if (vm) out.push(vm);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
function normalizeDidDocument(did, v) {
|
|
93
|
+
if (!isRecord(v) || v.id !== did) return null;
|
|
94
|
+
const controllers = typeof v.controller === "string" ? [v.controller] : Array.isArray(v.controller) ? v.controller.filter((c) => typeof c === "string") : [];
|
|
95
|
+
const methods = Array.isArray(v.verificationMethod) ? v.verificationMethod.map((m) => normalizeMethod(did, m)).filter((m) => m !== null) : [];
|
|
96
|
+
return {
|
|
97
|
+
...v,
|
|
98
|
+
id: did,
|
|
99
|
+
controller: controllers,
|
|
100
|
+
verificationMethod: methods,
|
|
101
|
+
authentication: normalizeRelationship(did, v.authentication),
|
|
102
|
+
assertionMethod: normalizeRelationship(did, v.assertionMethod),
|
|
103
|
+
capabilityInvocation: normalizeRelationship(did, v.capabilityInvocation),
|
|
104
|
+
capabilityDelegation: normalizeRelationship(did, v.capabilityDelegation),
|
|
105
|
+
keyAgreement: normalizeRelationship(did, v.keyAgreement)
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function isEd25519Method(vm) {
|
|
109
|
+
return vm.type.includes("Ed25519") || vm.type === "JsonWebKey2020";
|
|
110
|
+
}
|
|
111
|
+
function ed25519KeyOfMethod(vm) {
|
|
112
|
+
if (!isEd25519Method(vm)) return null;
|
|
113
|
+
try {
|
|
114
|
+
if (typeof vm.publicKeyMultibase === "string") {
|
|
115
|
+
return multibaseToRaw(vm.publicKeyMultibase);
|
|
116
|
+
}
|
|
117
|
+
if (typeof vm.publicKeyBase58 === "string") {
|
|
118
|
+
if (vm.publicKeyBase58.length > MAX_KEY_STRING_LENGTHS.base58)
|
|
119
|
+
return null;
|
|
120
|
+
const key = base58btcDecode(vm.publicKeyBase58);
|
|
121
|
+
return key.length === 32 ? key : null;
|
|
122
|
+
}
|
|
123
|
+
if (typeof vm.publicKeyHex === "string") {
|
|
124
|
+
if (vm.publicKeyHex.length !== MAX_KEY_STRING_LENGTHS.hex) return null;
|
|
125
|
+
const key = hexToBytes(vm.publicKeyHex);
|
|
126
|
+
return key.length === 32 ? key : null;
|
|
127
|
+
}
|
|
128
|
+
if (vm.publicKeyJwk !== void 0) return rawFromPublicJwk(vm.publicKeyJwk);
|
|
129
|
+
} catch {
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
var MAX_METHODS_PER_RELATIONSHIP = 32;
|
|
134
|
+
function methodOf(doc, entry) {
|
|
135
|
+
if (typeof entry !== "string") return entry;
|
|
136
|
+
return (doc.verificationMethod ?? []).find((vm) => vm.id === entry);
|
|
137
|
+
}
|
|
138
|
+
function usableKey(doc, vm) {
|
|
139
|
+
if (vm.controller !== doc.id || !vm.id.startsWith(`${doc.id}#`)) return null;
|
|
140
|
+
const publicKey = ed25519KeyOfMethod(vm);
|
|
141
|
+
return publicKey ? { id: vm.id, publicKey } : null;
|
|
142
|
+
}
|
|
143
|
+
function keysFor(doc, relationship) {
|
|
144
|
+
const out = [];
|
|
145
|
+
const seen = /* @__PURE__ */ new Set();
|
|
146
|
+
for (const entry of doc[relationship] ?? []) {
|
|
147
|
+
if (seen.size >= MAX_METHODS_PER_RELATIONSHIP) break;
|
|
148
|
+
const vm = methodOf(doc, entry);
|
|
149
|
+
if (!vm || seen.has(vm.id)) continue;
|
|
150
|
+
seen.add(vm.id);
|
|
151
|
+
const key = usableKey(doc, vm);
|
|
152
|
+
if (key) out.push(key);
|
|
153
|
+
}
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
156
|
+
function findKey(doc, kid, relationship) {
|
|
157
|
+
if (typeof kid !== "string" || kid.length === 0) return null;
|
|
158
|
+
const id = absolute(doc.id, kid);
|
|
159
|
+
for (const entry of doc[relationship] ?? []) {
|
|
160
|
+
const entryId = typeof entry === "string" ? entry : entry.id;
|
|
161
|
+
if (entryId !== id) continue;
|
|
162
|
+
const vm = methodOf(doc, entry);
|
|
163
|
+
return vm ? usableKey(doc, vm) : null;
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
function composeResolvers(...resolvers) {
|
|
168
|
+
return async (did) => {
|
|
169
|
+
for (const resolve of resolvers) {
|
|
170
|
+
const doc = await resolve(did);
|
|
171
|
+
if (doc) return doc;
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function createStaticResolver(docs) {
|
|
177
|
+
const byId = new Map(docs.map((d) => [d.id, normalizeDidDocument(d.id, d)]));
|
|
178
|
+
return (did) => Promise.resolve(byId.get(did) ?? null);
|
|
179
|
+
}
|
|
180
|
+
async function cachedDocument(cache, key, did) {
|
|
181
|
+
const hit = await cacheGet(cache, key);
|
|
182
|
+
if (hit === null) return null;
|
|
183
|
+
try {
|
|
184
|
+
return normalizeDidDocument(did, JSON.parse(hit));
|
|
185
|
+
} catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
var IID_QUERY = "query CredentialsKitIid($id: String!) { iids(filter: { id: { equalTo: $id } }) { nodes { id controller verificationMethod authentication assertionMethod capabilityInvocation capabilityDelegation keyAgreement service } } }";
|
|
190
|
+
function createIxoResolver(opts) {
|
|
191
|
+
const cache = opts.cache ?? createMemoryCache({ maxEntries: 5e3 });
|
|
192
|
+
const ttl = opts.cacheTtlSeconds ?? DEFAULT_DID_TTL_S;
|
|
193
|
+
return async (did) => {
|
|
194
|
+
if (!IXO_PERSON_DID.test(did) && !IXO_ENTITY_DID.test(did)) return null;
|
|
195
|
+
const key = `ixo-credentials-kit:did:v1:${did}`;
|
|
196
|
+
const hit = await cachedDocument(cache, key, did);
|
|
197
|
+
if (hit) return hit;
|
|
198
|
+
let body;
|
|
199
|
+
try {
|
|
200
|
+
const res = await request(opts.fetch, opts.blocksyncUrl, {
|
|
201
|
+
method: "POST",
|
|
202
|
+
headers: {
|
|
203
|
+
"content-type": "application/json",
|
|
204
|
+
accept: "application/json"
|
|
205
|
+
},
|
|
206
|
+
body: JSON.stringify({ query: IID_QUERY, variables: { id: did } }),
|
|
207
|
+
maxBytes: MAX_BLOCKSYNC_BYTES,
|
|
208
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
209
|
+
});
|
|
210
|
+
if (res.status !== 200) throw new ResolverUnavailable("blocksync");
|
|
211
|
+
body = JSON.parse(res.text);
|
|
212
|
+
} catch (e) {
|
|
213
|
+
if (e instanceof ResolverUnavailable) throw e;
|
|
214
|
+
throw new ResolverUnavailable("blocksync");
|
|
215
|
+
}
|
|
216
|
+
if (!isRecord(body) || body.errors !== void 0) {
|
|
217
|
+
throw new ResolverUnavailable("blocksync");
|
|
218
|
+
}
|
|
219
|
+
const data = isRecord(body.data) ? body.data : null;
|
|
220
|
+
const iids = data && isRecord(data.iids) ? data.iids : null;
|
|
221
|
+
if (!iids || !Array.isArray(iids.nodes)) {
|
|
222
|
+
throw new ResolverUnavailable("blocksync");
|
|
223
|
+
}
|
|
224
|
+
const node = iids.nodes.find((n) => isRecord(n) && n.id === did);
|
|
225
|
+
if (node === void 0) return null;
|
|
226
|
+
const doc = normalizeDidDocument(did, node);
|
|
227
|
+
if (!doc) return null;
|
|
228
|
+
await cachePut(cache, key, JSON.stringify(doc), ttl);
|
|
229
|
+
return doc;
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
var MAX_DID_WEB_LENGTH = 2048;
|
|
233
|
+
var MAX_HOST_LENGTH = 253;
|
|
234
|
+
var DNS_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
235
|
+
var DID_WEB_DEFAULT_PORT = 443;
|
|
236
|
+
var UNRESERVED = /^[A-Za-z0-9._~-]$/;
|
|
237
|
+
function isCanonicalHost(host) {
|
|
238
|
+
if (host.length === 0 || host.length > MAX_HOST_LENGTH) return false;
|
|
239
|
+
if (!host.split(".").every((label) => DNS_LABEL.test(label))) return false;
|
|
240
|
+
try {
|
|
241
|
+
return new URL(`https://${host}/`).hostname === host;
|
|
242
|
+
} catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function parseDidWeb(did) {
|
|
247
|
+
if (typeof did !== "string" || did.length > MAX_DID_WEB_LENGTH) return null;
|
|
248
|
+
if (!did.startsWith("did:web:")) return null;
|
|
249
|
+
const parts = did.slice("did:web:".length).split(":");
|
|
250
|
+
const authority = parts[0] ?? "";
|
|
251
|
+
const m = /^([^%]+)(?:%3A([1-9][0-9]{0,4}))?$/.exec(authority);
|
|
252
|
+
const host = m?.[1];
|
|
253
|
+
if (!m || host === void 0 || !isCanonicalHost(host)) return null;
|
|
254
|
+
if (/^[0-9]+$/.test(host.slice(host.lastIndexOf(".") + 1))) return null;
|
|
255
|
+
let port;
|
|
256
|
+
if (m[2] !== void 0) {
|
|
257
|
+
port = Number(m[2]);
|
|
258
|
+
if (port > 65535 || port === DID_WEB_DEFAULT_PORT) return null;
|
|
259
|
+
}
|
|
260
|
+
const path = [];
|
|
261
|
+
for (const seg of parts.slice(1)) {
|
|
262
|
+
if (!/^[A-Za-z0-9._%-]+$/.test(seg) || seg === "." || seg === ".." || // `.well-known` would map a path DID onto a bare host's document URL (PD-06).
|
|
263
|
+
seg.toLowerCase() === ".well-known" || /%(?:2e|2f|5c)/i.test(seg) || // One spelling per segment (PD-06, RFC 3986 §6.2.2): every `%` starts an upper-case hex
|
|
264
|
+
// pair, and never one that encodes an unreserved character (that is the same URL as the
|
|
265
|
+
// character itself).
|
|
266
|
+
/%(?![0-9A-F]{2})/.test(seg) || [...seg.matchAll(/%([0-9A-F]{2})/g)].some(
|
|
267
|
+
(m2) => UNRESERVED.test(String.fromCharCode(parseInt(m2[1] ?? "", 16)))
|
|
268
|
+
)) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
let decoded;
|
|
272
|
+
try {
|
|
273
|
+
decoded = decodeURIComponent(seg);
|
|
274
|
+
} catch {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
if (decoded === "." || decoded === ".." || /[/\\?#]/.test(decoded)) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
path.push(seg);
|
|
281
|
+
}
|
|
282
|
+
return port === void 0 ? { host, authority, path } : { host, port, authority, path };
|
|
283
|
+
}
|
|
284
|
+
function didWebUrl(did, policy = {}) {
|
|
285
|
+
const { allowHttp = false, allowLocal = false } = typeof policy === "boolean" ? { allowHttp: policy, allowLocal: policy } : policy;
|
|
286
|
+
const web = parseDidWeb(did);
|
|
287
|
+
if (!web) return null;
|
|
288
|
+
const scheme = allowHttp && isLoopbackHost(web.host) ? "http" : "https";
|
|
289
|
+
const path = web.path.length > 0 ? `/${web.path.join("/")}/did.json` : "/.well-known/did.json";
|
|
290
|
+
const origin = web.port === void 0 ? web.host : `${web.host}:${String(web.port)}`;
|
|
291
|
+
const url = `${scheme}://${origin}${path}`;
|
|
292
|
+
let parsed;
|
|
293
|
+
try {
|
|
294
|
+
parsed = new URL(url);
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
if (parsed.hostname !== web.host || parsed.pathname !== path) return null;
|
|
299
|
+
if (!allowLocal && isPrivateHost(parsed.hostname)) return null;
|
|
300
|
+
return url;
|
|
301
|
+
}
|
|
302
|
+
function createWebResolver(opts) {
|
|
303
|
+
const cache = opts.cache ?? createMemoryCache({ maxEntries: 1e3 });
|
|
304
|
+
const ttl = opts.cacheTtlSeconds ?? DEFAULT_DID_TTL_S;
|
|
305
|
+
return async (did) => {
|
|
306
|
+
const url = didWebUrl(did, {
|
|
307
|
+
allowHttp: opts.allowHttp ?? false,
|
|
308
|
+
allowLocal: opts.allowLocal ?? false
|
|
309
|
+
});
|
|
310
|
+
if (!url) return null;
|
|
311
|
+
const key = `ixo-credentials-kit:did:v1:${did}`;
|
|
312
|
+
const hit = await cachedDocument(cache, key, did);
|
|
313
|
+
if (hit) return hit;
|
|
314
|
+
let parsed;
|
|
315
|
+
try {
|
|
316
|
+
const res = await request(opts.fetch, url, {
|
|
317
|
+
headers: { accept: "application/did+json, application/json" },
|
|
318
|
+
maxBytes: MAX_DID_DOCUMENT_BYTES,
|
|
319
|
+
timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
320
|
+
});
|
|
321
|
+
if (res.status === 404 || res.status === 410) return null;
|
|
322
|
+
if (res.status !== 200) throw new ResolverUnavailable("did_web");
|
|
323
|
+
parsed = JSON.parse(res.text);
|
|
324
|
+
} catch (e) {
|
|
325
|
+
if (e instanceof ResolverUnavailable) throw e;
|
|
326
|
+
throw new ResolverUnavailable("did_web");
|
|
327
|
+
}
|
|
328
|
+
const doc = normalizeDidDocument(did, parsed);
|
|
329
|
+
if (!doc) return null;
|
|
330
|
+
await cachePut(cache, key, JSON.stringify(doc), ttl);
|
|
331
|
+
return doc;
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export { IXO_ENTITY_DID, IXO_PERSON_DID, MAX_DID_WEB_LENGTH, MAX_METHODS_PER_RELATIONSHIP, ResolverUnavailable, cacheGet, cachePut, composeResolvers, createIxoResolver, createMemoryCache, createStaticResolver, createWebResolver, didFromKid, didWebUrl, ed25519KeyOfMethod, findKey, isCanonicalHost, keysFor, normalizeDidDocument, parseDidWeb };
|
|
336
|
+
//# sourceMappingURL=chunk-WIEGZFUO.js.map
|
|
337
|
+
//# sourceMappingURL=chunk-WIEGZFUO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/cache.ts","../src/did.ts"],"names":["m"],"mappings":"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","file":"chunk-WIEGZFUO.js","sourcesContent":["/**\n * The cache the kit's network functions accept. The shape is a subset of Cloudflare's\n * `KVNamespace`, so a KV binding can be passed as is; `createMemoryCache()` is an in-isolate\n * implementation for Node, browsers and tests. A cache is never a source of truth: a read or\n * write failure is treated as a miss and ignored.\n */\nexport interface KeyValueCache {\n get(key: string): Promise<string | null>;\n put(\n key: string,\n value: string,\n options?: { expirationTtl?: number }\n ): Promise<void>;\n}\n\n/** An in-memory cache with per-entry TTL and a size bound (oldest entry evicted first). */\nexport function createMemoryCache(\n opts: { maxEntries?: number; now?: () => number } = {}\n): KeyValueCache {\n const maxEntries = opts.maxEntries ?? 1000;\n const now = opts.now ?? (() => Date.now());\n const entries = new Map<string, { value: string; until: number }>();\n return {\n get(key) {\n const hit = entries.get(key);\n if (!hit) return Promise.resolve(null);\n if (hit.until <= now()) {\n entries.delete(key);\n return Promise.resolve(null);\n }\n return Promise.resolve(hit.value);\n },\n put(key, value, options) {\n const ttl = options?.expirationTtl ?? 60;\n entries.delete(key);\n if (entries.size >= maxEntries) {\n const oldest = entries.keys().next();\n if (!oldest.done) entries.delete(oldest.value);\n }\n entries.set(key, { value, until: now() + ttl * 1000 });\n return Promise.resolve();\n },\n };\n}\n\nexport async function cacheGet(\n cache: KeyValueCache | undefined,\n key: string\n): Promise<string | null> {\n if (!cache) return null;\n try {\n return await cache.get(key);\n } catch {\n return null;\n }\n}\n\n/** Cloudflare KV refuses a TTL under 60 s; clamp so a KV binding works as is. */\nexport async function cachePut(\n cache: KeyValueCache | undefined,\n key: string,\n value: string,\n ttlSeconds: number\n): Promise<void> {\n if (!cache) return;\n try {\n await cache.put(key, value, {\n expirationTtl: Math.max(60, Math.floor(ttlSeconds)),\n });\n } catch {\n // a cache write failure is not an error\n }\n}\n","/**\n * `@ixo/credentials-kit/did` — DID documents, resolvers and key selection.\n *\n * - `did:ixo` people (`did:ixo:ixo1…`) and entities (`did:ixo:entity:…`) are read from the IID\n * document on Blocksync GraphQL (`createIxoResolver`).\n * - `did:web` services are read from `https://<host>/.well-known/did.json` (or\n * `/<path>/did.json`) (`createWebResolver`); plain http only for a loopback host and only when\n * the caller allows it (local development). A did:web whose host is loopback, private,\n * link-local, unique-local or a local name (`localhost`, `*.local`, `*.internal`) is treated as\n * not existing unless the caller passes `allowLocal` (local development).\n *\n * A resolver returns `null` when the DID does not exist and throws `ResolverUnavailable` when its\n * source cannot be read (callers map that to 503 and fail closed). Only positive results are\n * cached.\n *\n * Key selection is strict (SPEC §3): `keysFor` / `findKey` return only Ed25519 verification\n * methods that are controlled by the document's own DID and listed under the asked relationship\n * (`assertionMethod` for issuers, `authentication` for holders). There is no \"try every key\".\n * Key strings are length-checked before they are decoded, and `findKey` decodes only the method\n * the `kid` names (a hostile document cannot make a caller decode megabytes of base58).\n */\nimport { hexToBytes, base58btcDecode } from './internal/bytes';\nimport {\n cacheGet,\n cachePut,\n createMemoryCache,\n type KeyValueCache,\n} from './internal/cache';\nimport { CredentialsKitError } from './internal/errors';\nimport {\n DEFAULT_TIMEOUT_MS,\n isLoopbackHost,\n isPrivateHost,\n request,\n type FetchLike,\n type HostPolicy,\n} from './internal/http';\nimport { isRecord, type JsonObject } from './internal/util';\nimport {\n MAX_KEY_STRING_LENGTHS,\n multibaseToRaw,\n rawFromPublicJwk,\n} from './jose';\n\nexport { createMemoryCache, type KeyValueCache } from './internal/cache';\nexport type { FetchLike, HostPolicy } from './internal/http';\nexport { isFetchableUrl, isPrivateHost } from './internal/http';\n\nexport interface VerificationMethod {\n id: string;\n type: string;\n controller: string;\n publicKeyMultibase?: string;\n publicKeyBase58?: string;\n publicKeyHex?: string;\n publicKeyJwk?: JsonObject;\n [name: string]: unknown;\n}\n\nexport type VerificationRelationshipEntry = string | VerificationMethod;\n\nexport interface DidDocument {\n id: string;\n controller?: string[];\n verificationMethod?: VerificationMethod[];\n authentication?: VerificationRelationshipEntry[];\n assertionMethod?: VerificationRelationshipEntry[];\n capabilityInvocation?: VerificationRelationshipEntry[];\n capabilityDelegation?: VerificationRelationshipEntry[];\n keyAgreement?: VerificationRelationshipEntry[];\n service?: unknown[];\n [name: string]: unknown;\n}\n\n/** `null`: the DID does not exist. Throws `ResolverUnavailable` when the source cannot be read. */\nexport type DidResolver = (did: string) => Promise<DidDocument | null>;\n\n/** The relationships a credentials key may be selected under. */\nexport type KeyRelationship = 'assertionMethod' | 'authentication';\n\nexport interface ResolvedKey {\n /** The verification method's absolute id (a DID URL). */\n id: string;\n /** Raw 32-byte Ed25519 public key. */\n publicKey: Uint8Array;\n}\n\nexport type ResolverSource = 'blocksync' | 'did_web';\n\n/** The DID's source could not be read (down, timed out, errored, malformed answer). */\nexport class ResolverUnavailable extends CredentialsKitError {\n declare readonly code: 'resolver_unavailable';\n readonly source: ResolverSource;\n constructor(source: ResolverSource) {\n super(\n 'resolver_unavailable',\n source === 'blocksync'\n ? 'Blocksync could not be read to resolve the DID.'\n : 'The did:web document could not be read.'\n );\n this.source = source;\n }\n}\n\n/** `did:ixo` of a person: an ixo1 account address (20-byte bech32). */\nexport const IXO_PERSON_DID = /^did:ixo:ixo1[02-9ac-hj-np-z]{38}$/;\n/** `did:ixo` of an entity. */\nexport const IXO_ENTITY_DID = /^did:ixo:entity:[0-9a-f]{32}$/;\n\n/** The DID part of a DID URL (`did:x:y#frag` → `did:x:y`). */\nexport function didFromKid(kid: string): string {\n const hash = kid.indexOf('#');\n return hash < 0 ? kid : kid.slice(0, hash);\n}\n\nconst DEFAULT_DID_TTL_S = 600;\nconst MAX_DID_DOCUMENT_BYTES = 256 * 1024;\nconst MAX_BLOCKSYNC_BYTES = 1024 * 1024;\n\nfunction absolute(did: string, id: string): string {\n return id.startsWith('#') ? `${did}${id}` : id;\n}\n\nfunction normalizeMethod(did: string, v: unknown): VerificationMethod | null {\n if (!isRecord(v) || typeof v.id !== 'string' || typeof v.type !== 'string') {\n return null;\n }\n const controller =\n typeof v.controller === 'string' ? absolute(did, v.controller) : '';\n return { ...v, id: absolute(did, v.id), type: v.type, controller };\n}\n\nfunction normalizeRelationship(\n did: string,\n v: unknown\n): VerificationRelationshipEntry[] {\n if (!Array.isArray(v)) return [];\n const out: VerificationRelationshipEntry[] = [];\n for (const entry of v) {\n if (typeof entry === 'string') out.push(absolute(did, entry));\n else {\n const vm = normalizeMethod(did, entry);\n if (vm) out.push(vm);\n }\n }\n return out;\n}\n\n/**\n * A DID document in the kit's normal form: every id absolute, relationships as arrays,\n * `controller` as an array. Unknown members are kept. Returns null when `v` is not a document\n * for `did`.\n */\nexport function normalizeDidDocument(\n did: string,\n v: unknown\n): DidDocument | null {\n if (!isRecord(v) || v.id !== did) return null;\n const controllers =\n typeof v.controller === 'string'\n ? [v.controller]\n : Array.isArray(v.controller)\n ? v.controller.filter((c): c is string => typeof c === 'string')\n : [];\n const methods = Array.isArray(v.verificationMethod)\n ? v.verificationMethod\n .map(m => normalizeMethod(did, m))\n .filter((m): m is VerificationMethod => m !== null)\n : [];\n return {\n ...v,\n id: did,\n controller: controllers,\n verificationMethod: methods,\n authentication: normalizeRelationship(did, v.authentication),\n assertionMethod: normalizeRelationship(did, v.assertionMethod),\n capabilityInvocation: normalizeRelationship(did, v.capabilityInvocation),\n capabilityDelegation: normalizeRelationship(did, v.capabilityDelegation),\n keyAgreement: normalizeRelationship(did, v.keyAgreement),\n };\n}\n\nfunction isEd25519Method(vm: VerificationMethod): boolean {\n return vm.type.includes('Ed25519') || vm.type === 'JsonWebKey2020';\n}\n\n/**\n * The raw Ed25519 key of a verification method, or null when it has none. Key strings longer\n * than an Ed25519 key can be (`MAX_KEY_STRING_LENGTHS`) are refused before decoding.\n */\nexport function ed25519KeyOfMethod(vm: VerificationMethod): Uint8Array | null {\n if (!isEd25519Method(vm)) return null;\n try {\n if (typeof vm.publicKeyMultibase === 'string') {\n return multibaseToRaw(vm.publicKeyMultibase);\n }\n if (typeof vm.publicKeyBase58 === 'string') {\n if (vm.publicKeyBase58.length > MAX_KEY_STRING_LENGTHS.base58)\n return null;\n const key = base58btcDecode(vm.publicKeyBase58);\n return key.length === 32 ? key : null;\n }\n if (typeof vm.publicKeyHex === 'string') {\n if (vm.publicKeyHex.length !== MAX_KEY_STRING_LENGTHS.hex) return null;\n const key = hexToBytes(vm.publicKeyHex);\n return key.length === 32 ? key : null;\n }\n if (vm.publicKeyJwk !== undefined) return rawFromPublicJwk(vm.publicKeyJwk);\n } catch {\n // malformed key material: no key\n }\n return null;\n}\n\n/** Most verification methods `keysFor` considers under one relationship. */\nexport const MAX_METHODS_PER_RELATIONSHIP = 32;\n\n/** The method a relationship entry names (an id resolved against `verificationMethod`). */\nfunction methodOf(\n doc: DidDocument,\n entry: VerificationRelationshipEntry\n): VerificationMethod | undefined {\n if (typeof entry !== 'string') return entry;\n return (doc.verificationMethod ?? []).find(vm => vm.id === entry);\n}\n\nfunction usableKey(\n doc: DidDocument,\n vm: VerificationMethod\n): ResolvedKey | null {\n if (vm.controller !== doc.id || !vm.id.startsWith(`${doc.id}#`)) return null;\n const publicKey = ed25519KeyOfMethod(vm);\n return publicKey ? { id: vm.id, publicKey } : null;\n}\n\n/**\n * The Ed25519 keys listed under `relationship` whose verification method is controlled by the\n * document's own DID and whose id is a DID URL of that DID. Only the first\n * `MAX_METHODS_PER_RELATIONSHIP` distinct entries are considered.\n */\nexport function keysFor(\n doc: DidDocument,\n relationship: KeyRelationship\n): ResolvedKey[] {\n const out: ResolvedKey[] = [];\n const seen = new Set<string>();\n for (const entry of doc[relationship] ?? []) {\n if (seen.size >= MAX_METHODS_PER_RELATIONSHIP) break;\n const vm = methodOf(doc, entry);\n if (!vm || seen.has(vm.id)) continue;\n seen.add(vm.id);\n const key = usableKey(doc, vm);\n if (key) out.push(key);\n }\n return out;\n}\n\n/**\n * The ONE key `kid` names, if it is an Ed25519 key of this document under `relationship`\n * (see `keysFor`). A `#fragment` kid is taken relative to the document. Null otherwise. Only\n * that method's key material is decoded.\n */\nexport function findKey(\n doc: DidDocument,\n kid: string,\n relationship: KeyRelationship\n): ResolvedKey | null {\n if (typeof kid !== 'string' || kid.length === 0) return null;\n const id = absolute(doc.id, kid);\n for (const entry of doc[relationship] ?? []) {\n const entryId = typeof entry === 'string' ? entry : entry.id;\n if (entryId !== id) continue;\n const vm = methodOf(doc, entry);\n return vm ? usableKey(doc, vm) : null;\n }\n return null;\n}\n\n/** First resolver that knows the DID wins; `ResolverUnavailable` from any of them propagates. */\nexport function composeResolvers(...resolvers: DidResolver[]): DidResolver {\n return async did => {\n for (const resolve of resolvers) {\n const doc = await resolve(did);\n if (doc) return doc;\n }\n return null;\n };\n}\n\n/** A resolver over fixed documents (tests, local development, the service's own DID). */\nexport function createStaticResolver(docs: DidDocument[]): DidResolver {\n const byId = new Map(docs.map(d => [d.id, normalizeDidDocument(d.id, d)]));\n return did => Promise.resolve(byId.get(did) ?? null);\n}\n\nasync function cachedDocument(\n cache: KeyValueCache,\n key: string,\n did: string\n): Promise<DidDocument | null> {\n const hit = await cacheGet(cache, key);\n if (hit === null) return null;\n try {\n return normalizeDidDocument(did, JSON.parse(hit));\n } catch {\n return null;\n }\n}\n\nexport interface IxoResolverOptions {\n /** Blocksync GraphQL endpoint, e.g. `https://blocksync.ixo.earth/graphql`. */\n blocksyncUrl: string;\n fetch: FetchLike;\n /** Positive results only. Default: a per-resolver in-memory cache. */\n cache?: KeyValueCache;\n /** Default 600 s. A key removed from the IID keeps working for up to this long. */\n cacheTtlSeconds?: number;\n timeoutMs?: number;\n}\n\nconst IID_QUERY =\n 'query CredentialsKitIid($id: String!) { iids(filter: { id: { equalTo: $id } }) { nodes { id controller verificationMethod authentication assertionMethod capabilityInvocation capabilityDelegation keyAgreement service } } }';\n\n/**\n * did:ixo resolver over Blocksync GraphQL. Resolves people (`did:ixo:ixo1…`) and entities\n * (`did:ixo:entity:<32 hex>`); returns null for any other DID without a network call.\n */\nexport function createIxoResolver(opts: IxoResolverOptions): DidResolver {\n const cache = opts.cache ?? createMemoryCache({ maxEntries: 5000 });\n const ttl = opts.cacheTtlSeconds ?? DEFAULT_DID_TTL_S;\n return async did => {\n if (!IXO_PERSON_DID.test(did) && !IXO_ENTITY_DID.test(did)) return null;\n const key = `ixo-credentials-kit:did:v1:${did}`;\n const hit = await cachedDocument(cache, key, did);\n if (hit) return hit;\n let body: unknown;\n try {\n const res = await request(opts.fetch, opts.blocksyncUrl, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n accept: 'application/json',\n },\n body: JSON.stringify({ query: IID_QUERY, variables: { id: did } }),\n maxBytes: MAX_BLOCKSYNC_BYTES,\n timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n });\n if (res.status !== 200) throw new ResolverUnavailable('blocksync');\n body = JSON.parse(res.text);\n } catch (e) {\n if (e instanceof ResolverUnavailable) throw e;\n throw new ResolverUnavailable('blocksync');\n }\n // A GraphQL error (a statement timeout arrives as one, HTTP 200) is Blocksync's trouble.\n if (!isRecord(body) || body.errors !== undefined) {\n throw new ResolverUnavailable('blocksync');\n }\n const data = isRecord(body.data) ? body.data : null;\n const iids = data && isRecord(data.iids) ? data.iids : null;\n if (!iids || !Array.isArray(iids.nodes)) {\n throw new ResolverUnavailable('blocksync');\n }\n const node: unknown = iids.nodes.find(n => isRecord(n) && n.id === did);\n if (node === undefined) return null;\n const doc = normalizeDidDocument(did, node);\n if (!doc) return null;\n await cachePut(cache, key, JSON.stringify(doc), ttl);\n return doc;\n };\n}\n\nexport interface WebResolverOptions {\n fetch: FetchLike;\n /**\n * Resolve a did:web whose host is localhost / 127.0.0.1 / [::1] over plain http (local\n * development only). Every other host is always https. Needs `allowLocal` as well.\n */\n allowHttp?: boolean;\n /**\n * Resolve a did:web whose host is loopback, private, link-local, unique-local or a local name.\n * Default false (such a DID resolves to null). Local development only.\n */\n allowLocal?: boolean;\n cache?: KeyValueCache;\n cacheTtlSeconds?: number;\n timeoutMs?: number;\n}\n\n/** Longest did:web accepted. */\nexport const MAX_DID_WEB_LENGTH = 2048;\n\n/** Longest host name (RFC 1035). */\nconst MAX_HOST_LENGTH = 253;\n/** One DNS label: a-z, 0-9 and inner hyphens, 1–63 characters (punycode `xn--` labels included). */\nconst DNS_LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;\n/** The port did:web implies (https); naming it (`%3A443`) is a second spelling, so it is refused. */\nconst DID_WEB_DEFAULT_PORT = 443;\n/** RFC 3986 unreserved characters: percent-encoding one is a second spelling of the same URL. */\nconst UNRESERVED = /^[A-Za-z0-9._~-]$/;\n\n/** A did:web in canonical form, split into its parts (`parseDidWeb`). */\nexport interface DidWeb {\n /** The host: lower case, ASCII (an IDN in punycode), no trailing dot, no port. */\n host: string;\n /** The port, only when the DID names one (never 443). */\n port?: number;\n /** The host part exactly as the DID writes it (`issuer.example`, `localhost%3A34672`). */\n authority: string;\n /** The path segments after the host, as written (empty for a bare-host did:web). */\n path: string[];\n}\n\n/**\n * Is `host` a host name in canonical form: lower-case LDH labels, no empty label (so no trailing\n * dot), at most 253 characters, and one the WHATWG URL parser leaves exactly as it is (that\n * refuses punycode that does not decode, and numeric forms such as `0x7f.1` that it would rewrite\n * to another address)?\n */\nexport function isCanonicalHost(host: string): boolean {\n if (host.length === 0 || host.length > MAX_HOST_LENGTH) return false;\n if (!host.split('.').every(label => DNS_LABEL.test(label))) return false;\n try {\n return new URL(`https://${host}/`).hostname === host;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse a did:web (did:web method spec, https://w3c-ccg.github.io/did-method-web/:\n * `did:web:<host>[%3A<port>][:<path>…]`), accepting ONLY its canonical spelling, so that one DID\n * has one spelling (SPEC §17.4, R3-W-03). Any host (a subdomain is fine), any port (written\n * `%3A<port>`) and any path the spec allows are accepted. Refused (null): upper case in the host,\n * a trailing dot, any percent-encoding in the host other than the `%3A` before a port, `%3a`, a\n * port of 443 (the URL `https://` already implies; naming it is a second spelling) or with a\n * leading zero, a host that is not ASCII (write an IDN in punycode), an IP address (the spec: the\n * identifier \"MUST NOT include IP addresses\"), an empty path segment, a segment with a character\n * DID syntax does not allow (`idchar`: letters, digits, `.`, `-`, `_` and percent-encoding), and a\n * segment that is or percent-encodes `.`, `..`, `/`, `\\`, `?` or `#`. In a path segment (SPEC\n * §17.5, PD-06) also: lower-case hex in a percent-encoding (`%7e`; write `%7E`), a `%` not followed\n * by two hex digits, a percent-encoded unreserved character (`%61lice` is `alice`; RFC 3986\n * §6.2.2.2: `A-Z a-z 0-9 - . _ ~`, so a `~` cannot be written at all: DID syntax has no bare `~`),\n * and a `.well-known` segment in any letter case (`did:web:example.com:.well-known` would read the\n * bare host's document). This says nothing about whether the host may be fetched (see\n * `didWebUrl`).\n */\nexport function parseDidWeb(did: string): DidWeb | null {\n if (typeof did !== 'string' || did.length > MAX_DID_WEB_LENGTH) return null;\n if (!did.startsWith('did:web:')) return null;\n const parts = did.slice('did:web:'.length).split(':');\n const authority = parts[0] ?? '';\n const m = /^([^%]+)(?:%3A([1-9][0-9]{0,4}))?$/.exec(authority);\n const host = m?.[1];\n if (!m || host === undefined || !isCanonicalHost(host)) return null;\n // An IPv4 address (a last label of digits only: no domain ends that way, and the URL parser\n // reads such a host as an address). IPv6 literals never get here (`[`, `]` are not LDH).\n if (/^[0-9]+$/.test(host.slice(host.lastIndexOf('.') + 1))) return null;\n let port: number | undefined;\n if (m[2] !== undefined) {\n port = Number(m[2]);\n if (port > 65_535 || port === DID_WEB_DEFAULT_PORT) return null;\n }\n const path: string[] = [];\n for (const seg of parts.slice(1)) {\n if (\n !/^[A-Za-z0-9._%-]+$/.test(seg) ||\n seg === '.' ||\n seg === '..' ||\n // `.well-known` would map a path DID onto a bare host's document URL (PD-06).\n seg.toLowerCase() === '.well-known' ||\n /%(?:2e|2f|5c)/i.test(seg) ||\n // One spelling per segment (PD-06, RFC 3986 §6.2.2): every `%` starts an upper-case hex\n // pair, and never one that encodes an unreserved character (that is the same URL as the\n // character itself).\n /%(?![0-9A-F]{2})/.test(seg) ||\n [...seg.matchAll(/%([0-9A-F]{2})/g)].some(m =>\n UNRESERVED.test(String.fromCharCode(parseInt(m[1] ?? '', 16)))\n )\n ) {\n return null;\n }\n let decoded: string;\n try {\n decoded = decodeURIComponent(seg);\n } catch {\n return null;\n }\n if (decoded === '.' || decoded === '..' || /[/\\\\?#]/.test(decoded)) {\n return null;\n }\n path.push(seg);\n }\n return port === undefined\n ? { host, authority, path }\n : { host, port, authority, path };\n}\n\n/**\n * The URL of a did:web document (did:web method spec §3.2): `did:web:<host>[%3A<port>][:<path>…]`\n * → `https://<host>[:<port>]/.well-known/did.json` or `https://<host>[:<port>]/<path…>/did.json`.\n * Null when the DID is not a did:web in canonical form (`parseDidWeb`: one spelling per server, so\n * a server cannot appear under several DIDs), or when the host is local/private and `allowLocal`\n * is not set. `allowHttp` switches a loopback host to http. A boolean `policy` (the earlier\n * signature) means `{ allowHttp: b, allowLocal: b }`.\n */\nexport function didWebUrl(\n did: string,\n policy: boolean | HostPolicy = {}\n): string | null {\n const { allowHttp = false, allowLocal = false } =\n typeof policy === 'boolean'\n ? { allowHttp: policy, allowLocal: policy }\n : policy;\n const web = parseDidWeb(did);\n if (!web) return null;\n const scheme = allowHttp && isLoopbackHost(web.host) ? 'http' : 'https';\n const path =\n web.path.length > 0\n ? `/${web.path.join('/')}/did.json`\n : '/.well-known/did.json';\n const origin =\n web.port === undefined ? web.host : `${web.host}:${String(web.port)}`;\n const url = `${scheme}://${origin}${path}`;\n let parsed: URL;\n try {\n parsed = new URL(url);\n } catch {\n return null;\n }\n // The URL parser must not have rewritten the host or the path (dot segments, backslashes).\n if (parsed.hostname !== web.host || parsed.pathname !== path) return null;\n if (!allowLocal && isPrivateHost(parsed.hostname)) return null;\n return url;\n}\n\n/** did:web resolver. 404/410 → null; any other failure → `ResolverUnavailable`. */\nexport function createWebResolver(opts: WebResolverOptions): DidResolver {\n const cache = opts.cache ?? createMemoryCache({ maxEntries: 1000 });\n const ttl = opts.cacheTtlSeconds ?? DEFAULT_DID_TTL_S;\n return async did => {\n const url = didWebUrl(did, {\n allowHttp: opts.allowHttp ?? false,\n allowLocal: opts.allowLocal ?? false,\n });\n if (!url) return null;\n const key = `ixo-credentials-kit:did:v1:${did}`;\n const hit = await cachedDocument(cache, key, did);\n if (hit) return hit;\n let parsed: unknown;\n try {\n const res = await request(opts.fetch, url, {\n headers: { accept: 'application/did+json, application/json' },\n maxBytes: MAX_DID_DOCUMENT_BYTES,\n timeoutMs: opts.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n });\n if (res.status === 404 || res.status === 410) return null;\n if (res.status !== 200) throw new ResolverUnavailable('did_web');\n parsed = JSON.parse(res.text);\n } catch (e) {\n if (e instanceof ResolverUnavailable) throw e;\n throw new ResolverUnavailable('did_web');\n }\n const doc = normalizeDidDocument(did, parsed);\n if (!doc) return null;\n await cachePut(cache, key, JSON.stringify(doc), ttl);\n return doc;\n };\n}\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { didFromKid, findKey } from './chunk-WIEGZFUO.js';
|
|
2
|
+
import { toSeconds, clockMs, verifyEd25519 } from './chunk-VXKONZK4.js';
|
|
3
|
+
import { utf8, base64urlEncode, base64urlDecode } from './chunk-AQ3IINHC.js';
|
|
4
|
+
|
|
5
|
+
// src/webhook.ts
|
|
6
|
+
async function signWebhook(input) {
|
|
7
|
+
const t = toSeconds(clockMs(input.now));
|
|
8
|
+
if (/[,=\s]/.test(input.signer.kid)) {
|
|
9
|
+
throw new Error("The signer kid cannot be carried in a webhook header.");
|
|
10
|
+
}
|
|
11
|
+
const sig = await input.signer.sign(utf8(`${t}.${input.body}`));
|
|
12
|
+
return `t=${t},kid=${input.signer.kid},sig=${base64urlEncode(sig)}`;
|
|
13
|
+
}
|
|
14
|
+
var MAX_HEADER_LENGTH = 1024;
|
|
15
|
+
var WEBHOOK_MAX_FUTURE_SECONDS = 60;
|
|
16
|
+
var WEBHOOK_MAX_AGE_SECONDS = 300;
|
|
17
|
+
function parseHeader(header) {
|
|
18
|
+
if (header.length > MAX_HEADER_LENGTH) return null;
|
|
19
|
+
const parts = header.split(",");
|
|
20
|
+
if (parts.length !== 3) return null;
|
|
21
|
+
const fields = /* @__PURE__ */ new Map();
|
|
22
|
+
for (const part of parts) {
|
|
23
|
+
const eq = part.indexOf("=");
|
|
24
|
+
if (eq <= 0) return null;
|
|
25
|
+
const name = part.slice(0, eq);
|
|
26
|
+
if (fields.has(name)) return null;
|
|
27
|
+
fields.set(name, part.slice(eq + 1));
|
|
28
|
+
}
|
|
29
|
+
const t = fields.get("t");
|
|
30
|
+
const kid = fields.get("kid");
|
|
31
|
+
const sig = fields.get("sig");
|
|
32
|
+
if (!t || !kid || !sig || !/^\d{1,12}$/.test(t)) return null;
|
|
33
|
+
try {
|
|
34
|
+
const bytes = base64urlDecode(sig);
|
|
35
|
+
return bytes.length === 64 ? { t: Number(t), kid, sig: bytes } : null;
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function verifyWebhook(input) {
|
|
41
|
+
const tolerance = input.toleranceSeconds ?? WEBHOOK_MAX_AGE_SECONDS;
|
|
42
|
+
if (!Number.isInteger(tolerance) || tolerance < 1 || tolerance > WEBHOOK_MAX_AGE_SECONDS)
|
|
43
|
+
throw new RangeError("toleranceSeconds must be an integer from 1 to 300");
|
|
44
|
+
const nowS = toSeconds(clockMs(input.now));
|
|
45
|
+
if (typeof input.header !== "string") return { ok: false, code: "malformed" };
|
|
46
|
+
const parsed = parseHeader(input.header);
|
|
47
|
+
if (!parsed) return { ok: false, code: "malformed" };
|
|
48
|
+
if (nowS - parsed.t > tolerance || parsed.t - nowS > WEBHOOK_MAX_FUTURE_SECONDS)
|
|
49
|
+
return { ok: false, code: "stale" };
|
|
50
|
+
if (didFromKid(parsed.kid) !== input.expectedDid) {
|
|
51
|
+
return { ok: false, code: "wrong_did" };
|
|
52
|
+
}
|
|
53
|
+
const doc = await input.resolver(input.expectedDid);
|
|
54
|
+
const key = doc ? findKey(doc, parsed.kid, "assertionMethod") : null;
|
|
55
|
+
if (!key) return { ok: false, code: "unknown_key" };
|
|
56
|
+
const ok = verifyEd25519(
|
|
57
|
+
parsed.sig,
|
|
58
|
+
utf8(`${parsed.t}.${input.body}`),
|
|
59
|
+
key.publicKey
|
|
60
|
+
);
|
|
61
|
+
return ok ? { ok: true, kid: key.id, timestamp: parsed.t } : { ok: false, code: "bad_signature" };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { WEBHOOK_MAX_AGE_SECONDS, WEBHOOK_MAX_FUTURE_SECONDS, signWebhook, verifyWebhook };
|
|
65
|
+
//# sourceMappingURL=chunk-WND2LZ4O.js.map
|
|
66
|
+
//# sourceMappingURL=chunk-WND2LZ4O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/webhook.ts"],"names":[],"mappings":"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","file":"chunk-WND2LZ4O.js","sourcesContent":["/**\n * `@ixo/credentials-kit/webhook` — signed webhooks (Credentials worker → Hub, SPEC §5.4).\n *\n * Header value (`x-ixo-signature`): `t=<unix seconds>,kid=<DID URL>,sig=<base64url>` where `sig`\n * is Ed25519 over the UTF-8 bytes of `\"<t>.<body>\"` and `body` is the exact request body string.\n * The timestamp is inside the signed bytes. The verifier resolves `expectedDid`, takes the key\n * `kid` names under `assertionMethod`, and refuses a timestamp older than `toleranceSeconds`\n * (default and maximum 300: SPEC §13.3) or more than 60 seconds in the future. A signed webhook\n * still verifies again within that window: receivers MUST de-duplicate on the event `id`\n * (`evt_…`) for at least the window (the Hub keeps every id it took). Senders sign at every\n * delivery attempt (a fresh `t`).\n */\nimport { findKey, didFromKid, type DidResolver } from './did';\nimport { base64urlDecode, base64urlEncode, utf8 } from './internal/bytes';\nimport { clockMs, toSeconds } from './internal/util';\nimport { verifyEd25519, type Signer } from './jose';\n\nexport interface SignWebhookInput {\n /** The exact request body that will be sent. */\n body: string;\n signer: Signer;\n /** ms since the epoch; default `Date.now()`. */\n now?: number;\n}\n\n/** The `x-ixo-signature` header value for `body`. */\nexport async function signWebhook(input: SignWebhookInput): Promise<string> {\n const t = toSeconds(clockMs(input.now));\n if (/[,=\\s]/.test(input.signer.kid)) {\n throw new Error('The signer kid cannot be carried in a webhook header.');\n }\n const sig = await input.signer.sign(utf8(`${t}.${input.body}`));\n return `t=${t},kid=${input.signer.kid},sig=${base64urlEncode(sig)}`;\n}\n\nexport type WebhookFailure =\n 'malformed' | 'stale' | 'wrong_did' | 'unknown_key' | 'bad_signature';\n\nexport type WebhookVerification =\n | { ok: true; kid: string; timestamp: number }\n | { ok: false; code: WebhookFailure };\n\nexport interface VerifyWebhookInput {\n body: string;\n /** The `x-ixo-signature` header value (null/undefined when absent). */\n header: string | null | undefined;\n resolver: DidResolver;\n /** The only DID allowed to sign (e.g. the Credentials worker's did:web). */\n expectedDid: string;\n /** How old a signature may be, in seconds: default and maximum 300 (RangeError above). */\n toleranceSeconds?: number;\n /** ms since the epoch; default `Date.now()`. */\n now?: number;\n}\n\nconst MAX_HEADER_LENGTH = 1024;\n/** Most the signed timestamp may be ahead of the receiver's clock. */\nexport const WEBHOOK_MAX_FUTURE_SECONDS = 60;\n/** Largest (and default) `toleranceSeconds`. */\nexport const WEBHOOK_MAX_AGE_SECONDS = 300;\n\nfunction parseHeader(\n header: string\n): { t: number; kid: string; sig: Uint8Array } | null {\n if (header.length > MAX_HEADER_LENGTH) return null;\n const parts = header.split(',');\n if (parts.length !== 3) return null;\n const fields = new Map<string, string>();\n for (const part of parts) {\n const eq = part.indexOf('=');\n if (eq <= 0) return null;\n const name = part.slice(0, eq);\n if (fields.has(name)) return null;\n fields.set(name, part.slice(eq + 1));\n }\n const t = fields.get('t');\n const kid = fields.get('kid');\n const sig = fields.get('sig');\n if (!t || !kid || !sig || !/^\\d{1,12}$/.test(t)) return null;\n try {\n const bytes = base64urlDecode(sig);\n return bytes.length === 64 ? { t: Number(t), kid, sig: bytes } : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Verify a webhook. Returns `{ ok: false, code }` for every refusal; throws only\n * `ResolverUnavailable` (the signer's DID document could not be read — answer 503, the\n * sender retries).\n */\nexport async function verifyWebhook(\n input: VerifyWebhookInput\n): Promise<WebhookVerification> {\n const tolerance = input.toleranceSeconds ?? WEBHOOK_MAX_AGE_SECONDS;\n if (\n !Number.isInteger(tolerance) ||\n tolerance < 1 ||\n tolerance > WEBHOOK_MAX_AGE_SECONDS\n )\n throw new RangeError('toleranceSeconds must be an integer from 1 to 300');\n const nowS = toSeconds(clockMs(input.now));\n if (typeof input.header !== 'string') return { ok: false, code: 'malformed' };\n const parsed = parseHeader(input.header);\n if (!parsed) return { ok: false, code: 'malformed' };\n if (\n nowS - parsed.t > tolerance ||\n parsed.t - nowS > WEBHOOK_MAX_FUTURE_SECONDS\n )\n return { ok: false, code: 'stale' };\n if (didFromKid(parsed.kid) !== input.expectedDid) {\n return { ok: false, code: 'wrong_did' };\n }\n const doc = await input.resolver(input.expectedDid);\n const key = doc ? findKey(doc, parsed.kid, 'assertionMethod') : null;\n if (!key) return { ok: false, code: 'unknown_key' };\n const ok = verifyEd25519(\n parsed.sig,\n utf8(`${parsed.t}.${input.body}`),\n key.publicKey\n );\n return ok\n ? { ok: true, kid: key.id, timestamp: parsed.t }\n : { ok: false, code: 'bad_signature' };\n}\n"]}
|