@ixo/credentials-kit 0.0.0-stage → 0.1.1
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,391 @@
|
|
|
1
|
+
import { parseSdJwt, SdJwtError, processDisclosures, sdHash } from './chunk-XREZY53L.js';
|
|
2
|
+
import { MAX_STATUS_LIST_TOKEN_LENGTH, STATUS_LIST_MEDIA_TYPE, statusAt, verifyStatusListToken, StatusListError } from './chunk-JBUX6V2Q.js';
|
|
3
|
+
import { findKey, cacheGet, cachePut, ResolverUnavailable } from './chunk-WIEGZFUO.js';
|
|
4
|
+
import { isFetchableUrl, request, HttpError } from './chunk-Q3MZNMCK.js';
|
|
5
|
+
import { CredentialsKitError, clockMs, toSeconds, rawFromPublicJwk, JoseError, isEd25519JwsAlg, decodeJws, verifyEd25519, isRecord, publicJwkFromRaw, CLOCK_SKEW_S } from './chunk-VXKONZK4.js';
|
|
6
|
+
import { timingSafeEqual, utf8, bytesToHex } from './chunk-AQ3IINHC.js';
|
|
7
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
8
|
+
|
|
9
|
+
var VERIFY_FAILURE_DETAILS = {
|
|
10
|
+
malformed: "The presentation is not a well-formed SD-JWT VC.",
|
|
11
|
+
untrusted_issuer: "The credential issuer is not accepted.",
|
|
12
|
+
wrong_type: "The credential type is not accepted.",
|
|
13
|
+
issuer_key_unpublished: "The credential's signing key is not an assertion key on the issuer's DID document.",
|
|
14
|
+
bad_signature: "The credential signature does not verify.",
|
|
15
|
+
expired: "The credential has expired.",
|
|
16
|
+
not_yet_valid: "The credential is not valid yet.",
|
|
17
|
+
bad_disclosure: "A disclosure does not belong to the credential.",
|
|
18
|
+
wrong_subject: "The credential was issued to someone else.",
|
|
19
|
+
key_binding_required: "The presentation has no key binding.",
|
|
20
|
+
bad_key_binding: "The key binding does not verify for this request.",
|
|
21
|
+
revoked: "The credential has been revoked.",
|
|
22
|
+
no_status: "The credential has no usable status list entry."
|
|
23
|
+
};
|
|
24
|
+
var VerificationUnavailable = class extends CredentialsKitError {
|
|
25
|
+
source;
|
|
26
|
+
/** A constant cause code (`unreachable`, `http_status`, `too_large`, a `StatusListError` code, …). */
|
|
27
|
+
reason;
|
|
28
|
+
constructor(source, reason) {
|
|
29
|
+
super(
|
|
30
|
+
"verification_unavailable",
|
|
31
|
+
source === "resolver" ? "The issuer's DID document could not be read." : "The credential status list could not be read or verified."
|
|
32
|
+
);
|
|
33
|
+
this.source = source;
|
|
34
|
+
this.reason = reason;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var KB_MAX_AGE_S = 300;
|
|
38
|
+
var DEFAULT_STATUS_TTL_S = 300;
|
|
39
|
+
var MAX_STATUS_TTL_S = 86400;
|
|
40
|
+
var SD_JWT_TYPS = /* @__PURE__ */ new Set(["dc+sd-jwt", "vc+sd-jwt"]);
|
|
41
|
+
var NEVER_DISCLOSED = /* @__PURE__ */ new Set([
|
|
42
|
+
"iss",
|
|
43
|
+
"nbf",
|
|
44
|
+
"exp",
|
|
45
|
+
"cnf",
|
|
46
|
+
"vct",
|
|
47
|
+
"vct#integrity",
|
|
48
|
+
"aka_vcts",
|
|
49
|
+
"status"
|
|
50
|
+
]);
|
|
51
|
+
var NOT_CLAIMS = /* @__PURE__ */ new Set([
|
|
52
|
+
"iss",
|
|
53
|
+
"sub",
|
|
54
|
+
"iat",
|
|
55
|
+
"nbf",
|
|
56
|
+
"exp",
|
|
57
|
+
"vct",
|
|
58
|
+
"vct#integrity",
|
|
59
|
+
"aka_vcts",
|
|
60
|
+
"cnf",
|
|
61
|
+
"status"
|
|
62
|
+
]);
|
|
63
|
+
var UNBOUND = { keyBindingValid: false, holderAuthenticated: false };
|
|
64
|
+
function fail(code, binding = UNBOUND) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
code,
|
|
68
|
+
detail: VERIFY_FAILURE_DETAILS[code],
|
|
69
|
+
holderAuthenticated: binding.holderAuthenticated,
|
|
70
|
+
keyBindingValid: binding.keyBindingValid
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function issuerAccepted(issuers, did) {
|
|
74
|
+
return typeof issuers === "function" ? issuers(did) : issuers.includes(did);
|
|
75
|
+
}
|
|
76
|
+
async function resolveIssuer(resolver, did) {
|
|
77
|
+
try {
|
|
78
|
+
return await resolver(did);
|
|
79
|
+
} catch (e) {
|
|
80
|
+
if (e instanceof ResolverUnavailable) {
|
|
81
|
+
throw new VerificationUnavailable("resolver", e.source);
|
|
82
|
+
}
|
|
83
|
+
throw e;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function parse(presentation) {
|
|
87
|
+
try {
|
|
88
|
+
return parseSdJwt(
|
|
89
|
+
typeof presentation === "string" ? presentation.trim() : ""
|
|
90
|
+
);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
if (e instanceof SdJwtError || e instanceof JoseError) return null;
|
|
93
|
+
throw e;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function checkIssuerSigned(parsed, issuers, resolver) {
|
|
97
|
+
const { header, payload } = parsed;
|
|
98
|
+
if (!isEd25519JwsAlg(header.alg) || !SD_JWT_TYPS.has(header.typ))
|
|
99
|
+
return fail("malformed");
|
|
100
|
+
if (payload._sd_alg !== void 0 && payload._sd_alg !== "sha-256")
|
|
101
|
+
return fail("malformed");
|
|
102
|
+
const { iss, vct, exp, nbf, iat } = payload;
|
|
103
|
+
if (typeof iss !== "string" || typeof vct !== "string")
|
|
104
|
+
return fail("malformed");
|
|
105
|
+
if (typeof exp !== "number" || !Number.isFinite(exp))
|
|
106
|
+
return fail("malformed");
|
|
107
|
+
if (nbf !== void 0 && (typeof nbf !== "number" || !Number.isFinite(nbf)))
|
|
108
|
+
return fail("malformed");
|
|
109
|
+
if (iat !== void 0 && (typeof iat !== "number" || !Number.isFinite(iat)))
|
|
110
|
+
return fail("malformed");
|
|
111
|
+
if (!issuerAccepted(issuers, iss)) return fail("untrusted_issuer");
|
|
112
|
+
const issuerDoc = await resolveIssuer(resolver, iss);
|
|
113
|
+
const kid = typeof header.kid === "string" ? header.kid : "";
|
|
114
|
+
const issuerKey = issuerDoc?.id === iss ? findKey(issuerDoc, kid, "assertionMethod") : null;
|
|
115
|
+
if (!issuerDoc || !issuerKey) return fail("issuer_key_unpublished");
|
|
116
|
+
const jws = decodeJws(parsed.jwt);
|
|
117
|
+
if (!verifyEd25519(jws.signature, utf8(jws.signingInput), issuerKey.publicKey)) {
|
|
118
|
+
return fail("bad_signature");
|
|
119
|
+
}
|
|
120
|
+
let processed;
|
|
121
|
+
try {
|
|
122
|
+
processed = processDisclosures(payload, parsed.disclosures);
|
|
123
|
+
} catch (e) {
|
|
124
|
+
if (e instanceof SdJwtError)
|
|
125
|
+
return fail(e.code === "malformed" ? "malformed" : "bad_disclosure");
|
|
126
|
+
throw e;
|
|
127
|
+
}
|
|
128
|
+
if (processed.disclosed.some((name) => NEVER_DISCLOSED.has(name)))
|
|
129
|
+
return fail("bad_disclosure");
|
|
130
|
+
const { claims } = processed;
|
|
131
|
+
const sub = claims.sub;
|
|
132
|
+
if (typeof sub !== "string") return fail("malformed");
|
|
133
|
+
if (claims.iat !== void 0 && (typeof claims.iat !== "number" || !Number.isFinite(claims.iat)))
|
|
134
|
+
return fail("malformed");
|
|
135
|
+
let holderRaw = null;
|
|
136
|
+
let holderKey = null;
|
|
137
|
+
const cnf = payload.cnf;
|
|
138
|
+
if (cnf !== void 0) {
|
|
139
|
+
try {
|
|
140
|
+
holderRaw = rawFromPublicJwk(isRecord(cnf) ? cnf.jwk : void 0);
|
|
141
|
+
holderKey = publicJwkFromRaw(holderRaw);
|
|
142
|
+
} catch {
|
|
143
|
+
holderRaw = null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
iss,
|
|
148
|
+
sub,
|
|
149
|
+
vct,
|
|
150
|
+
exp,
|
|
151
|
+
nbf: typeof nbf === "number" ? nbf : void 0,
|
|
152
|
+
claims: Object.fromEntries(
|
|
153
|
+
Object.entries(claims).filter(([name]) => !NOT_CLAIMS.has(name))
|
|
154
|
+
),
|
|
155
|
+
disclosed: processed.disclosed,
|
|
156
|
+
issuerDoc,
|
|
157
|
+
holderRaw,
|
|
158
|
+
holderKey
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function validity(c, nowS, binding) {
|
|
162
|
+
if (nowS > c.exp + CLOCK_SKEW_S) return fail("expired", binding);
|
|
163
|
+
if (c.nbf !== void 0 && nowS + CLOCK_SKEW_S < c.nbf)
|
|
164
|
+
return fail("not_yet_valid", binding);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
async function nonceAccepted(check, nonce) {
|
|
168
|
+
return typeof check === "string" ? nonce === check : await check(nonce);
|
|
169
|
+
}
|
|
170
|
+
async function keyBindingHolds(kbJwt, presentedSdJwt, holderKey, audience, nonce, nowS) {
|
|
171
|
+
let kb;
|
|
172
|
+
try {
|
|
173
|
+
kb = decodeJws(kbJwt);
|
|
174
|
+
} catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
if (kb.header.typ !== "kb+jwt" || !isEd25519JwsAlg(kb.header.alg))
|
|
178
|
+
return false;
|
|
179
|
+
if (!verifyEd25519(kb.signature, utf8(kb.signingInput), holderKey))
|
|
180
|
+
return false;
|
|
181
|
+
const { payload } = kb;
|
|
182
|
+
if (payload.aud !== audience) return false;
|
|
183
|
+
if (typeof payload.iat !== "number" || payload.iat > nowS + CLOCK_SKEW_S || payload.iat < nowS - KB_MAX_AGE_S - CLOCK_SKEW_S) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
if (payload.exp !== void 0 && (typeof payload.exp !== "number" || nowS > payload.exp + CLOCK_SKEW_S))
|
|
187
|
+
return false;
|
|
188
|
+
if (payload.nbf !== void 0 && (typeof payload.nbf !== "number" || nowS + CLOCK_SKEW_S < payload.nbf))
|
|
189
|
+
return false;
|
|
190
|
+
if (payload.sd_hash !== sdHash(presentedSdJwt)) return false;
|
|
191
|
+
if (typeof payload.nonce !== "string" || payload.nonce.length === 0)
|
|
192
|
+
return false;
|
|
193
|
+
return nonceAccepted(nonce, payload.nonce);
|
|
194
|
+
}
|
|
195
|
+
function statusRef(status, deps) {
|
|
196
|
+
const list = isRecord(status) ? status.status_list : void 0;
|
|
197
|
+
if (!isRecord(list)) return null;
|
|
198
|
+
const { uri, idx } = list;
|
|
199
|
+
if (typeof uri !== "string" || !isFetchableUrl(uri, {
|
|
200
|
+
allowHttp: deps.allowHttp ?? false,
|
|
201
|
+
allowLocal: deps.allowLocal ?? false
|
|
202
|
+
}))
|
|
203
|
+
return null;
|
|
204
|
+
if (typeof idx !== "number" || !Number.isSafeInteger(idx) || idx < 0)
|
|
205
|
+
return null;
|
|
206
|
+
return { uri, idx };
|
|
207
|
+
}
|
|
208
|
+
function readCachedStatus(raw) {
|
|
209
|
+
if (raw === null) return null;
|
|
210
|
+
try {
|
|
211
|
+
const v = JSON.parse(raw);
|
|
212
|
+
if (isRecord(v) && typeof v.token === "string" && typeof v.iat === "number" && typeof v.freshUntil === "number") {
|
|
213
|
+
return { token: v.token, iat: v.iat, freshUntil: v.freshUntil };
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
async function statusOf(deps, issuerDoc, ref, nowMs) {
|
|
220
|
+
const cacheKey = `ixo-credentials-kit:status:v1:${bytesToHex(
|
|
221
|
+
sha256(utf8(`${issuerDoc.id}|${ref.uri}`))
|
|
222
|
+
)}`;
|
|
223
|
+
const cached = readCachedStatus(await cacheGet(deps.statusCache, cacheKey));
|
|
224
|
+
const unavailable = (reason) => new VerificationUnavailable("status_list", reason);
|
|
225
|
+
const check = async (token2, minIat) => {
|
|
226
|
+
try {
|
|
227
|
+
return await verifyStatusListToken(token2, {
|
|
228
|
+
uri: ref.uri,
|
|
229
|
+
issuerDoc,
|
|
230
|
+
now: nowMs,
|
|
231
|
+
...minIat !== void 0 ? { minIat } : {}
|
|
232
|
+
});
|
|
233
|
+
} catch (e) {
|
|
234
|
+
if (e instanceof StatusListError) throw unavailable(e.code);
|
|
235
|
+
throw e;
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
const valueAt = (list2) => {
|
|
239
|
+
try {
|
|
240
|
+
return statusAt(list2.bits, ref.idx, list2.bitsPerEntry);
|
|
241
|
+
} catch {
|
|
242
|
+
return "out_of_range";
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
if (cached && cached.freshUntil > nowMs) {
|
|
246
|
+
try {
|
|
247
|
+
return valueAt(await check(cached.token));
|
|
248
|
+
} catch (e) {
|
|
249
|
+
if (!(e instanceof VerificationUnavailable)) throw e;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
let token;
|
|
253
|
+
try {
|
|
254
|
+
const res = await request(deps.fetch, ref.uri, {
|
|
255
|
+
headers: { accept: STATUS_LIST_MEDIA_TYPE },
|
|
256
|
+
maxBytes: MAX_STATUS_LIST_TOKEN_LENGTH
|
|
257
|
+
});
|
|
258
|
+
if (res.status !== 200) throw unavailable("http_status");
|
|
259
|
+
token = res.text.trim();
|
|
260
|
+
} catch (e) {
|
|
261
|
+
if (e instanceof VerificationUnavailable) throw e;
|
|
262
|
+
if (e instanceof HttpError) {
|
|
263
|
+
throw unavailable(
|
|
264
|
+
e.kind === "too_large" ? "too_large" : e.kind === "status" ? "http_status" : "unreachable"
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
throw unavailable("unreachable");
|
|
268
|
+
}
|
|
269
|
+
const list = await check(token, cached?.iat);
|
|
270
|
+
const ttl = Math.min(list.ttl ?? DEFAULT_STATUS_TTL_S, MAX_STATUS_TTL_S);
|
|
271
|
+
const entry = {
|
|
272
|
+
token,
|
|
273
|
+
iat: list.iat,
|
|
274
|
+
freshUntil: nowMs + ttl * 1e3
|
|
275
|
+
};
|
|
276
|
+
await cachePut(
|
|
277
|
+
deps.statusCache,
|
|
278
|
+
cacheKey,
|
|
279
|
+
JSON.stringify(entry),
|
|
280
|
+
ttl + MAX_STATUS_TTL_S
|
|
281
|
+
);
|
|
282
|
+
return valueAt(list);
|
|
283
|
+
}
|
|
284
|
+
function verified(c, jwt) {
|
|
285
|
+
return {
|
|
286
|
+
ok: true,
|
|
287
|
+
issuer: c.iss,
|
|
288
|
+
subject: c.sub,
|
|
289
|
+
vct: c.vct,
|
|
290
|
+
claims: c.claims,
|
|
291
|
+
disclosed: c.disclosed,
|
|
292
|
+
expiresAt: c.exp,
|
|
293
|
+
credentialSha256: bytesToHex(sha256(utf8(jwt))),
|
|
294
|
+
holderKey: c.holderKey
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
async function verifyPresentation(presentation, policy, deps) {
|
|
298
|
+
const nowMs = clockMs(policy.now);
|
|
299
|
+
const nowS = toSeconds(nowMs);
|
|
300
|
+
const requireKeyBinding = policy.requireKeyBinding ?? true;
|
|
301
|
+
const requireStatus = policy.requireStatus ?? true;
|
|
302
|
+
if (requireKeyBinding && (typeof policy.audience !== "string" || policy.audience.length === 0 || policy.nonce === void 0 || typeof policy.nonce === "string" && policy.nonce.length === 0)) {
|
|
303
|
+
throw new TypeError(
|
|
304
|
+
"policy.audience and policy.nonce are required when key binding is required"
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
const expectedSubject = policy.expectedSubject;
|
|
308
|
+
if (expectedSubject !== void 0 && (typeof expectedSubject !== "string" || expectedSubject.length === 0))
|
|
309
|
+
throw new TypeError("policy.expectedSubject must be a non-empty DID");
|
|
310
|
+
const parsed = parse(presentation);
|
|
311
|
+
if (!parsed) return fail("malformed");
|
|
312
|
+
const c = await checkIssuerSigned(parsed, policy.issuers, deps.resolver);
|
|
313
|
+
if ("ok" in c) return c;
|
|
314
|
+
if (policy.expectedSubject !== void 0 && c.sub !== policy.expectedSubject)
|
|
315
|
+
return fail("wrong_subject");
|
|
316
|
+
let binding = UNBOUND;
|
|
317
|
+
if (parsed.kbJwt === void 0) {
|
|
318
|
+
if (requireKeyBinding) return fail("key_binding_required");
|
|
319
|
+
} else {
|
|
320
|
+
if (!c.holderRaw || policy.audience === void 0 || policy.nonce === void 0)
|
|
321
|
+
return fail("bad_key_binding");
|
|
322
|
+
const presented = parsed.jwt + "~" + parsed.disclosures.map((d) => `${d.raw}~`).join("");
|
|
323
|
+
const holds = await keyBindingHolds(
|
|
324
|
+
parsed.kbJwt,
|
|
325
|
+
presented,
|
|
326
|
+
c.holderRaw,
|
|
327
|
+
policy.audience,
|
|
328
|
+
policy.nonce,
|
|
329
|
+
nowS
|
|
330
|
+
);
|
|
331
|
+
if (!holds) return fail("bad_key_binding");
|
|
332
|
+
binding = {
|
|
333
|
+
keyBindingValid: true,
|
|
334
|
+
holderAuthenticated: policy.expectedSubject !== void 0
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
if (!policy.vcts.includes(c.vct)) return fail("wrong_type", binding);
|
|
338
|
+
const invalid = validity(c, nowS, binding);
|
|
339
|
+
if (invalid) return invalid;
|
|
340
|
+
const status = parsed.payload.status;
|
|
341
|
+
if (status === void 0) {
|
|
342
|
+
if (requireStatus) return fail("no_status", binding);
|
|
343
|
+
} else {
|
|
344
|
+
const ref = statusRef(status, deps);
|
|
345
|
+
if (!ref) return fail("malformed", binding);
|
|
346
|
+
const value = await statusOf(deps, c.issuerDoc, ref, nowMs);
|
|
347
|
+
if (value === "out_of_range") return fail("no_status", binding);
|
|
348
|
+
if (value !== 0) return fail("revoked", binding);
|
|
349
|
+
}
|
|
350
|
+
return verified(c, parsed.jwt);
|
|
351
|
+
}
|
|
352
|
+
async function verifyIssuedCredential(credential, opts) {
|
|
353
|
+
const nowS = toSeconds(clockMs(opts.now));
|
|
354
|
+
let expectedHolder;
|
|
355
|
+
if (opts.holderKey instanceof Uint8Array) {
|
|
356
|
+
expectedHolder = opts.holderKey;
|
|
357
|
+
} else {
|
|
358
|
+
try {
|
|
359
|
+
expectedHolder = rawFromPublicJwk(opts.holderKey);
|
|
360
|
+
} catch {
|
|
361
|
+
expectedHolder = new Uint8Array(0);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (expectedHolder.length !== 32)
|
|
365
|
+
throw new TypeError("holderKey is not an Ed25519 public key");
|
|
366
|
+
const expectedIssuer = opts.issuer;
|
|
367
|
+
if (expectedIssuer !== false && (typeof expectedIssuer !== "string" || expectedIssuer.length === 0))
|
|
368
|
+
throw new TypeError(
|
|
369
|
+
"issuer is required: the expected issuer DID, or false to accept any issuer"
|
|
370
|
+
);
|
|
371
|
+
const parsed = parse(credential);
|
|
372
|
+
if (!parsed || parsed.kbJwt !== void 0) return fail("malformed");
|
|
373
|
+
const c = await checkIssuerSigned(
|
|
374
|
+
parsed,
|
|
375
|
+
typeof expectedIssuer === "string" ? [expectedIssuer] : () => true,
|
|
376
|
+
opts.resolver
|
|
377
|
+
);
|
|
378
|
+
if ("ok" in c) return c;
|
|
379
|
+
if (c.sub !== opts.subject) return fail("wrong_subject");
|
|
380
|
+
if (!c.holderRaw || !timingSafeEqual(c.holderRaw, expectedHolder))
|
|
381
|
+
return fail("bad_key_binding");
|
|
382
|
+
const vcts = typeof opts.vct === "string" ? [opts.vct] : opts.vct;
|
|
383
|
+
if (!vcts.includes(c.vct)) return fail("wrong_type");
|
|
384
|
+
const invalid = validity(c, nowS, UNBOUND);
|
|
385
|
+
if (invalid) return invalid;
|
|
386
|
+
return verified(c, parsed.jwt);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export { VERIFY_FAILURE_DETAILS, VerificationUnavailable, verifyIssuedCredential, verifyPresentation };
|
|
390
|
+
//# sourceMappingURL=chunk-STMMFNVW.js.map
|
|
391
|
+
//# sourceMappingURL=chunk-STMMFNVW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/verify.ts"],"names":["token","list"],"mappings":"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","file":"chunk-STMMFNVW.js","sourcesContent":["/**\n * `@ixo/credentials-kit/verify` — verify an SD-JWT VC under a verifier's policy\n * (`verifyPresentation`) or as a wallet receiving it from an issuer (`verifyIssuedCredential`).\n * A generalisation of ixo-yo-rewards `src/eligibility/credential.ts` (itself a port of\n * pay-convert-oracle `src/convert/kyc.ts`) that returns the disclosed claims instead of reducing\n * them to booleans.\n *\n * `verifyPresentation` rules, in order:\n *\n * 1. well formed (`malformed`): an SD-JWT of at most 32 KiB, header `alg` `EdDSA` or `Ed25519`\n * and `typ: dc+sd-jwt` (`vc+sd-jwt`, the pre-November-2024 value, is still accepted),\n * `_sd_alg` absent or `sha-256`, string `iss` and `vct`, numeric `exp` (a credential without\n * an expiry is refused), numeric `nbf`/`iat` when present;\n * 2. `iss` accepted by `policy.issuers` (`untrusted_issuer`);\n * 3. signed by THE key the header `kid` names: an Ed25519 `assertionMethod` key of the issuer's\n * DID document controlled by the issuer (`issuer_key_unpublished`, `bad_signature`); no\n * \"try every key\";\n * 4. disclosures, RFC 9901 §7.1 (`bad_disclosure`): processed recursively (nested objects and\n * array elements); every disclosure must match exactly one digest, no digest may appear twice,\n * a disclosure never overwrites a claim at its level, and `iss`, `nbf`, `exp`, `cnf`, `vct`,\n * `vct#integrity`, `aka_vcts` and `status` are never accepted from a disclosure (SD-JWT VC\n * §2.2.2.3). `sub` and `iat` may be disclosed. `_sd`, `_sd_alg` and `...` never reach\n * `claims`; claims named `__proto__`, `constructor`, `prototype` are refused (`malformed`).\n * `sub` must then be a string (plaintext or disclosed);\n * 5. `sub` = `policy.expectedSubject` when given (`wrong_subject`);\n * 6. key binding (default required): a KB-JWT `typ: kb+jwt`, `alg` `EdDSA`/`Ed25519`, signed by\n * the credential's `cnf.jwk` (Ed25519), `aud` = `policy.audience`, `nonce` accepted by\n * `policy.nonce` (the exact string, or a callback for single-use nonce stores), `iat` at most\n * 5 minutes old (2 minutes of skew), `exp`/`nbf` honoured when present, `sd_hash` over the\n * presented `<jwt>~<disclosures>~` (`key_binding_required`, `bad_key_binding`). Whenever key\n * binding is required, `policy.audience` and `policy.nonce` MUST be given (TypeError\n * otherwise: there is no \"accept any nonce\"). A KB-JWT that is present is always checked; when\n * key binding is not required and no audience/nonce is given, a present KB-JWT fails\n * (`bad_key_binding`) rather than being accepted unchecked;\n * — up to here, a failure says nothing about who sent the presentation. From here on the\n * key binding holds (`keyBindingValid: true` on a failure): the issuer is trusted, its\n * signature holds and the KB-JWT proved possession of the credential's own `cnf` for this\n * nonce and audience. Whoever holds ANY credential from an accepted issuer gets that far,\n * so it is `holderAuthenticated: true` only when `policy.expectedSubject` was given (and\n * matched): then the answer is bound to the person the verifier expected. —\n * 7. `vct` ∈ `policy.vcts` (`wrong_type`);\n * 8. `exp` / `nbf` hold, 2 minutes of skew (`expired`, `not_yet_valid`);\n * 9. status (default required): `status.status_list.{uri, idx}` names a Token Status List that\n * verifies (see `status`) and whose entry is 0 (`no_status`, `revoked`). An index outside the\n * list is a hard failure (`no_status`, TSL §8.3 step 6).\n *\n * Infrastructure failures (DID source down, status list unreachable or not verifying) THROW\n * `VerificationUnavailable`: \"down is not no\", and never \"yes\".\n */\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport {\n ResolverUnavailable,\n findKey,\n type DidDocument,\n type DidResolver,\n} from './did';\nimport { bytesToHex, timingSafeEqual, utf8 } from './internal/bytes';\nimport { cacheGet, cachePut, type KeyValueCache } from './internal/cache';\nimport { CredentialsKitError } from './internal/errors';\nimport {\n HttpError,\n isFetchableUrl,\n request,\n type FetchLike,\n} from './internal/http';\nimport {\n CLOCK_SKEW_S,\n clockMs,\n isRecord,\n toSeconds,\n type JsonObject,\n} from './internal/util';\nimport {\n JoseError,\n decodeJws,\n isEd25519JwsAlg,\n publicJwkFromRaw,\n rawFromPublicJwk,\n verifyEd25519,\n type Ed25519PublicJwk,\n} from './jose';\nimport {\n SdJwtError,\n parseSdJwt,\n processDisclosures,\n sdHash,\n type ParsedSdJwt,\n} from './sdjwt';\nimport {\n MAX_STATUS_LIST_TOKEN_LENGTH,\n STATUS_LIST_MEDIA_TYPE,\n StatusListError,\n statusAt,\n verifyStatusListToken,\n} from './status';\n\nexport type VerifyFailureCode =\n | 'malformed'\n | 'untrusted_issuer'\n | 'wrong_type'\n | 'issuer_key_unpublished'\n | 'bad_signature'\n | 'expired'\n | 'not_yet_valid'\n | 'bad_disclosure'\n | 'wrong_subject'\n | 'key_binding_required'\n | 'bad_key_binding'\n | 'revoked'\n | 'no_status';\n\nexport const VERIFY_FAILURE_DETAILS: Record<VerifyFailureCode, string> = {\n malformed: 'The presentation is not a well-formed SD-JWT VC.',\n untrusted_issuer: 'The credential issuer is not accepted.',\n wrong_type: 'The credential type is not accepted.',\n issuer_key_unpublished:\n \"The credential's signing key is not an assertion key on the issuer's DID document.\",\n bad_signature: 'The credential signature does not verify.',\n expired: 'The credential has expired.',\n not_yet_valid: 'The credential is not valid yet.',\n bad_disclosure: 'A disclosure does not belong to the credential.',\n wrong_subject: 'The credential was issued to someone else.',\n key_binding_required: 'The presentation has no key binding.',\n bad_key_binding: 'The key binding does not verify for this request.',\n revoked: 'The credential has been revoked.',\n no_status: 'The credential has no usable status list entry.',\n};\n\n/** Checks a KB-JWT nonce: the exact value, or a callback (e.g. a single-use nonce store). */\nexport type NonceCheck =\n string | ((nonce: string) => boolean | Promise<boolean>);\n\nexport interface VerifyPolicy {\n /** Accepted issuer DIDs, or a predicate. */\n issuers: readonly string[] | ((did: string) => boolean);\n /** Accepted `vct` values. */\n vcts: readonly string[];\n /**\n * The KB-JWT `aud`: the verifier's full OpenID4VP client_id (or the audience your own protocol\n * fixed). REQUIRED when key binding is required.\n */\n audience?: string;\n /**\n * The nonce the KB-JWT must carry: the one you issued for THIS check, or a callback that\n * accepts each issued nonce once. REQUIRED when key binding is required.\n */\n nonce?: NonceCheck;\n /** Default true. */\n requireKeyBinding?: boolean;\n /** Default true. */\n requireStatus?: boolean;\n /**\n * The credential's `sub` must equal this (`wrong_subject`). Also what makes a failure\n * `holderAuthenticated`: without it no answer is bound to a person you expected, so\n * `holderAuthenticated` is always false (see `keyBindingValid`).\n */\n expectedSubject?: string;\n /** ms since the epoch; default `Date.now()`. */\n now?: number;\n}\n\nexport interface VerifyDeps {\n resolver: DidResolver;\n fetch: FetchLike;\n /**\n * Verified status list tokens (pass a KV binding or `createMemoryCache()`). Without one every\n * verification fetches the list.\n */\n statusCache?: KeyValueCache;\n /** Fetch a status list on a loopback host over http (local development only; needs `allowLocal`). */\n allowHttp?: boolean;\n /** Fetch a status list on a loopback / private / local-name host (local development only). */\n allowLocal?: boolean;\n}\n\nexport interface VerifiedPresentation {\n ok: true;\n issuer: string;\n subject: string;\n vct: string;\n /**\n * The processed claims: plaintext claims (registered claims removed) plus the disclosed ones,\n * with nested disclosures in place. Never contains `_sd`, `_sd_alg` or `...`.\n */\n claims: JsonObject;\n /** Names of the top-level claims that came from a disclosure. */\n disclosed: string[];\n /** The credential's `exp` (unix seconds). */\n expiresAt: number | null;\n /** Lower-case hex SHA-256 of the issuer-signed JWT: one value per credential, whatever is disclosed. */\n credentialSha256: string;\n /** The credential's `cnf.jwk`, when it has one. */\n holderKey: Ed25519PublicJwk | null;\n}\n\nexport interface FailedPresentation {\n ok: false;\n code: VerifyFailureCode;\n /** A fixed sentence for the code (no credential content). */\n detail: string;\n /**\n * True only when the failure came AFTER the answer was bound to the person you expected: a\n * trusted issuer's valid signature, the key binding for this nonce and audience, AND\n * `policy.expectedSubject` given and equal to `sub`. The person really answered, and their\n * answer does not meet the policy (wrong type, expired, revoked, …). False for everything a\n * third party could have sent, always false without key binding, and always false without\n * `expectedSubject` (a stranger's own credential would pass every other step).\n */\n holderAuthenticated: boolean;\n /**\n * True when the failure came after the KB-JWT verified for this nonce and audience with the\n * presented credential's OWN `cnf` key (trusted issuer, valid signature). It proves that the\n * holder of THAT credential answered, not that it is the person you meant: never close a\n * check on it alone. Always false without key binding.\n */\n keyBindingValid: boolean;\n}\n\nexport type VerifyResult = VerifiedPresentation | FailedPresentation;\n\nexport type UnavailableSource = 'resolver' | 'status_list';\n\n/** A dependency could not answer; nothing can be concluded about the credential (answer 503). */\nexport class VerificationUnavailable extends CredentialsKitError {\n declare readonly code: 'verification_unavailable';\n readonly source: UnavailableSource;\n /** A constant cause code (`unreachable`, `http_status`, `too_large`, a `StatusListError` code, …). */\n readonly reason: string;\n constructor(source: UnavailableSource, reason: string) {\n super(\n 'verification_unavailable',\n source === 'resolver'\n ? \"The issuer's DID document could not be read.\"\n : 'The credential status list could not be read or verified.'\n );\n this.source = source;\n this.reason = reason;\n }\n}\n\nconst KB_MAX_AGE_S = 300;\nconst DEFAULT_STATUS_TTL_S = 300;\nconst MAX_STATUS_TTL_S = 86_400;\nconst SD_JWT_TYPS = new Set(['dc+sd-jwt', 'vc+sd-jwt']);\n/**\n * Top-level claims never accepted from a disclosure (SD-JWT VC §2.2.2.3 \"MUST NOT be selectively\n * disclosed\"). Taking them only from the issuer-signed plaintext means a holder can never\n * withhold `status`, `exp` or `cnf`.\n */\nconst NEVER_DISCLOSED = new Set([\n 'iss',\n 'nbf',\n 'exp',\n 'cnf',\n 'vct',\n 'vct#integrity',\n 'aka_vcts',\n 'status',\n]);\n/** Registered / structural members left out of `claims`. */\nconst NOT_CLAIMS = new Set([\n 'iss',\n 'sub',\n 'iat',\n 'nbf',\n 'exp',\n 'vct',\n 'vct#integrity',\n 'aka_vcts',\n 'cnf',\n 'status',\n]);\n\n/** How far a failed presentation got: see `FailedPresentation`. */\ninterface Binding {\n keyBindingValid: boolean;\n holderAuthenticated: boolean;\n}\n\nconst UNBOUND: Binding = { keyBindingValid: false, holderAuthenticated: false };\n\nfunction fail(\n code: VerifyFailureCode,\n binding: Binding = UNBOUND\n): FailedPresentation {\n return {\n ok: false,\n code,\n detail: VERIFY_FAILURE_DETAILS[code],\n holderAuthenticated: binding.holderAuthenticated,\n keyBindingValid: binding.keyBindingValid,\n };\n}\n\nfunction issuerAccepted(\n issuers: VerifyPolicy['issuers'],\n did: string\n): boolean {\n return typeof issuers === 'function' ? issuers(did) : issuers.includes(did);\n}\n\nasync function resolveIssuer(\n resolver: DidResolver,\n did: string\n): Promise<DidDocument | null> {\n try {\n return await resolver(did);\n } catch (e) {\n if (e instanceof ResolverUnavailable) {\n throw new VerificationUnavailable('resolver', e.source);\n }\n throw e;\n }\n}\n\nfunction parse(presentation: unknown): ParsedSdJwt | null {\n try {\n return parseSdJwt(\n typeof presentation === 'string' ? presentation.trim() : ''\n );\n } catch (e) {\n if (e instanceof SdJwtError || e instanceof JoseError) return null;\n throw e;\n }\n}\n\ninterface CheckedCredential {\n iss: string;\n sub: string;\n vct: string;\n exp: number;\n nbf: number | undefined;\n claims: JsonObject;\n disclosed: string[];\n issuerDoc: DidDocument;\n /** Raw cnf key, when `cnf.jwk` is an Ed25519 OKP key. */\n holderRaw: Uint8Array | null;\n holderKey: Ed25519PublicJwk | null;\n}\n\n/**\n * Steps 1–4 (shape, trust, issuer signature, disclosures), shared by `verifyPresentation` and\n * `verifyIssuedCredential`. Returns the checked credential or the failure.\n */\nasync function checkIssuerSigned(\n parsed: ParsedSdJwt,\n issuers: VerifyPolicy['issuers'],\n resolver: DidResolver\n): Promise<CheckedCredential | FailedPresentation> {\n const { header, payload } = parsed;\n if (!isEd25519JwsAlg(header.alg) || !SD_JWT_TYPS.has(header.typ as string))\n return fail('malformed');\n if (payload._sd_alg !== undefined && payload._sd_alg !== 'sha-256')\n return fail('malformed');\n const { iss, vct, exp, nbf, iat } = payload;\n if (typeof iss !== 'string' || typeof vct !== 'string')\n return fail('malformed');\n if (typeof exp !== 'number' || !Number.isFinite(exp))\n return fail('malformed');\n if (nbf !== undefined && (typeof nbf !== 'number' || !Number.isFinite(nbf)))\n return fail('malformed');\n if (iat !== undefined && (typeof iat !== 'number' || !Number.isFinite(iat)))\n return fail('malformed');\n\n // 2. Trust.\n if (!issuerAccepted(issuers, iss)) return fail('untrusted_issuer');\n\n // 3. The issuer's key, by kid, under assertionMethod.\n const issuerDoc = await resolveIssuer(resolver, iss);\n const kid = typeof header.kid === 'string' ? header.kid : '';\n // findKey only returns a key whose id is a DID URL of the document (= iss).\n const issuerKey =\n issuerDoc?.id === iss ? findKey(issuerDoc, kid, 'assertionMethod') : null;\n if (!issuerDoc || !issuerKey) return fail('issuer_key_unpublished');\n const jws = decodeJws(parsed.jwt);\n if (\n !verifyEd25519(jws.signature, utf8(jws.signingInput), issuerKey.publicKey)\n ) {\n return fail('bad_signature');\n }\n\n // 4. Disclosures (RFC 9901 §7.1, recursive).\n let processed;\n try {\n processed = processDisclosures(payload, parsed.disclosures);\n } catch (e) {\n if (e instanceof SdJwtError)\n return fail(e.code === 'malformed' ? 'malformed' : 'bad_disclosure');\n throw e;\n }\n if (processed.disclosed.some(name => NEVER_DISCLOSED.has(name)))\n return fail('bad_disclosure');\n const { claims } = processed;\n const sub = claims.sub;\n if (typeof sub !== 'string') return fail('malformed');\n if (\n claims.iat !== undefined &&\n (typeof claims.iat !== 'number' || !Number.isFinite(claims.iat))\n )\n return fail('malformed');\n\n let holderRaw: Uint8Array | null = null;\n let holderKey: Ed25519PublicJwk | null = null;\n const cnf = payload.cnf;\n if (cnf !== undefined) {\n try {\n holderRaw = rawFromPublicJwk(isRecord(cnf) ? cnf.jwk : undefined);\n holderKey = publicJwkFromRaw(holderRaw);\n } catch {\n holderRaw = null;\n }\n }\n\n return {\n iss,\n sub,\n vct,\n exp,\n nbf: typeof nbf === 'number' ? nbf : undefined,\n claims: Object.fromEntries(\n Object.entries(claims).filter(([name]) => !NOT_CLAIMS.has(name))\n ),\n disclosed: processed.disclosed,\n issuerDoc,\n holderRaw,\n holderKey,\n };\n}\n\nfunction validity(\n c: CheckedCredential,\n nowS: number,\n binding: Binding\n): FailedPresentation | null {\n if (nowS > c.exp + CLOCK_SKEW_S) return fail('expired', binding);\n if (c.nbf !== undefined && nowS + CLOCK_SKEW_S < c.nbf)\n return fail('not_yet_valid', binding);\n return null;\n}\n\nasync function nonceAccepted(check: NonceCheck, nonce: string) {\n return typeof check === 'string' ? nonce === check : await check(nonce);\n}\n\nasync function keyBindingHolds(\n kbJwt: string,\n presentedSdJwt: string,\n holderKey: Uint8Array,\n audience: string,\n nonce: NonceCheck,\n nowS: number\n): Promise<boolean> {\n let kb;\n try {\n kb = decodeJws(kbJwt);\n } catch {\n return false;\n }\n if (kb.header.typ !== 'kb+jwt' || !isEd25519JwsAlg(kb.header.alg))\n return false;\n if (!verifyEd25519(kb.signature, utf8(kb.signingInput), holderKey))\n return false;\n const { payload } = kb;\n if (payload.aud !== audience) return false;\n if (\n typeof payload.iat !== 'number' ||\n payload.iat > nowS + CLOCK_SKEW_S ||\n payload.iat < nowS - KB_MAX_AGE_S - CLOCK_SKEW_S\n ) {\n return false;\n }\n if (\n payload.exp !== undefined &&\n (typeof payload.exp !== 'number' || nowS > payload.exp + CLOCK_SKEW_S)\n )\n return false;\n if (\n payload.nbf !== undefined &&\n (typeof payload.nbf !== 'number' || nowS + CLOCK_SKEW_S < payload.nbf)\n )\n return false;\n if (payload.sd_hash !== sdHash(presentedSdJwt)) return false;\n // Last: a callback may consume a single-use nonce, so only a binding that holds reaches it.\n if (typeof payload.nonce !== 'string' || payload.nonce.length === 0)\n return false;\n return nonceAccepted(nonce, payload.nonce);\n}\n\ninterface StatusRef {\n uri: string;\n idx: number;\n}\n\nfunction statusRef(status: unknown, deps: VerifyDeps): StatusRef | null {\n const list = isRecord(status) ? status.status_list : undefined;\n if (!isRecord(list)) return null;\n const { uri, idx } = list;\n if (\n typeof uri !== 'string' ||\n !isFetchableUrl(uri, {\n allowHttp: deps.allowHttp ?? false,\n allowLocal: deps.allowLocal ?? false,\n })\n )\n return null;\n if (typeof idx !== 'number' || !Number.isSafeInteger(idx) || idx < 0)\n return null;\n return { uri, idx };\n}\n\ninterface CachedStatus {\n token: string;\n iat: number;\n /** ms since the epoch until which the token may be used without a fetch. */\n freshUntil: number;\n}\n\nfunction readCachedStatus(raw: string | null): CachedStatus | null {\n if (raw === null) return null;\n try {\n const v: unknown = JSON.parse(raw);\n if (\n isRecord(v) &&\n typeof v.token === 'string' &&\n typeof v.iat === 'number' &&\n typeof v.freshUntil === 'number'\n ) {\n return { token: v.token, iat: v.iat, freshUntil: v.freshUntil };\n }\n } catch {\n // a bad cache entry is a miss\n }\n return null;\n}\n\n/**\n * The status value at `ref.idx` of the issuer's list, or `'out_of_range'` when the index is\n * past the list. A cached token is used until its `ttl` (capped at a day; 5 minutes when\n * absent) runs out, and re-verified each time; a fresh token must not be older than the cached\n * one (rollback protection).\n */\nasync function statusOf(\n deps: VerifyDeps,\n issuerDoc: DidDocument,\n ref: StatusRef,\n nowMs: number\n): Promise<number | 'out_of_range'> {\n const cacheKey = `ixo-credentials-kit:status:v1:${bytesToHex(\n sha256(utf8(`${issuerDoc.id}|${ref.uri}`))\n )}`;\n const cached = readCachedStatus(await cacheGet(deps.statusCache, cacheKey));\n const unavailable = (reason: string) =>\n new VerificationUnavailable('status_list', reason);\n const check = async (token: string, minIat?: number) => {\n try {\n return await verifyStatusListToken(token, {\n uri: ref.uri,\n issuerDoc,\n now: nowMs,\n ...(minIat !== undefined ? { minIat } : {}),\n });\n } catch (e) {\n if (e instanceof StatusListError) throw unavailable(e.code);\n throw e;\n }\n };\n const valueAt = (\n list: Awaited<ReturnType<typeof check>>\n ): number | 'out_of_range' => {\n try {\n return statusAt(list.bits, ref.idx, list.bitsPerEntry);\n } catch {\n return 'out_of_range';\n }\n };\n\n if (cached && cached.freshUntil > nowMs) {\n try {\n return valueAt(await check(cached.token));\n } catch (e) {\n // A cached token that no longer verifies (expired, key rotated): fetch a fresh one.\n if (!(e instanceof VerificationUnavailable)) throw e;\n }\n }\n\n let token: string;\n try {\n const res = await request(deps.fetch, ref.uri, {\n headers: { accept: STATUS_LIST_MEDIA_TYPE },\n maxBytes: MAX_STATUS_LIST_TOKEN_LENGTH,\n });\n if (res.status !== 200) throw unavailable('http_status');\n token = res.text.trim();\n } catch (e) {\n if (e instanceof VerificationUnavailable) throw e;\n if (e instanceof HttpError) {\n throw unavailable(\n e.kind === 'too_large'\n ? 'too_large'\n : e.kind === 'status'\n ? 'http_status'\n : 'unreachable'\n );\n }\n throw unavailable('unreachable');\n }\n const list = await check(token, cached?.iat);\n const ttl = Math.min(list.ttl ?? DEFAULT_STATUS_TTL_S, MAX_STATUS_TTL_S);\n const entry: CachedStatus = {\n token,\n iat: list.iat,\n freshUntil: nowMs + ttl * 1000,\n };\n // Kept for a day past freshness so the rollback check has the last iat.\n await cachePut(\n deps.statusCache,\n cacheKey,\n JSON.stringify(entry),\n ttl + MAX_STATUS_TTL_S\n );\n return valueAt(list);\n}\n\nfunction verified(c: CheckedCredential, jwt: string): VerifiedPresentation {\n return {\n ok: true,\n issuer: c.iss,\n subject: c.sub,\n vct: c.vct,\n claims: c.claims,\n disclosed: c.disclosed,\n expiresAt: c.exp,\n credentialSha256: bytesToHex(sha256(utf8(jwt))),\n holderKey: c.holderKey,\n };\n}\n\n/**\n * Verify an SD-JWT VC presentation (`<jwt>~<disclosures>~<kb-jwt>`). Returns\n * `{ ok: true, … }` or `{ ok: false, code, detail, holderAuthenticated, keyBindingValid }`\n * (`holderAuthenticated` needs `policy.expectedSubject`); throws\n * `VerificationUnavailable` when a dependency cannot answer, and `TypeError` when key binding is\n * required but `policy.audience` or `policy.nonce` is missing.\n */\nexport async function verifyPresentation(\n presentation: string,\n policy: VerifyPolicy,\n deps: VerifyDeps\n): Promise<VerifyResult> {\n const nowMs = clockMs(policy.now);\n const nowS = toSeconds(nowMs);\n const requireKeyBinding = policy.requireKeyBinding ?? true;\n const requireStatus = policy.requireStatus ?? true;\n if (\n requireKeyBinding &&\n (typeof policy.audience !== 'string' ||\n policy.audience.length === 0 ||\n policy.nonce === undefined ||\n (typeof policy.nonce === 'string' && policy.nonce.length === 0))\n ) {\n throw new TypeError(\n 'policy.audience and policy.nonce are required when key binding is required'\n );\n }\n const expectedSubject: unknown = policy.expectedSubject;\n if (\n expectedSubject !== undefined &&\n (typeof expectedSubject !== 'string' || expectedSubject.length === 0)\n )\n throw new TypeError('policy.expectedSubject must be a non-empty DID');\n\n // 1. Shape.\n const parsed = parse(presentation);\n if (!parsed) return fail('malformed');\n\n // 1–4. Shape, trust, issuer signature, disclosures.\n const c = await checkIssuerSigned(parsed, policy.issuers, deps.resolver);\n if ('ok' in c) return c;\n\n // 5. Subject.\n if (policy.expectedSubject !== undefined && c.sub !== policy.expectedSubject)\n return fail('wrong_subject');\n\n // 6. Key binding.\n let binding: Binding = UNBOUND;\n if (parsed.kbJwt === undefined) {\n if (requireKeyBinding) return fail('key_binding_required');\n } else {\n if (\n !c.holderRaw ||\n policy.audience === undefined ||\n policy.nonce === undefined\n )\n return fail('bad_key_binding');\n const presented =\n parsed.jwt + '~' + parsed.disclosures.map(d => `${d.raw}~`).join('');\n const holds = await keyBindingHolds(\n parsed.kbJwt,\n presented,\n c.holderRaw,\n policy.audience,\n policy.nonce,\n nowS\n );\n if (!holds) return fail('bad_key_binding');\n // Bound to the person only when the verifier said who it expected (step 5 matched).\n binding = {\n keyBindingValid: true,\n holderAuthenticated: policy.expectedSubject !== undefined,\n };\n }\n\n // 7. Type.\n if (!policy.vcts.includes(c.vct)) return fail('wrong_type', binding);\n\n // 8. Validity period.\n const invalid = validity(c, nowS, binding);\n if (invalid) return invalid;\n\n // 9. Status (last: it reaches the network).\n const status = parsed.payload.status;\n if (status === undefined) {\n if (requireStatus) return fail('no_status', binding);\n } else {\n const ref = statusRef(status, deps);\n if (!ref) return fail('malformed', binding);\n const value = await statusOf(deps, c.issuerDoc, ref, nowMs);\n if (value === 'out_of_range') return fail('no_status', binding);\n if (value !== 0) return fail('revoked', binding);\n }\n\n return verified(c, parsed.jwt);\n}\n\nexport interface VerifyIssuedCredentialOptions {\n /**\n * The issuer DID the credential must name in `iss` (for example the DID that deposited the\n * offer). REQUIRED. `false` accepts any issuer whose own DID document holds the signing key:\n * UNSAFE, since the credential is then authentic but anyone may have issued it (a foreign\n * issuer can mint your types); judge `result.issuer` yourself before storing or showing it.\n */\n issuer: string | false;\n /** The holder DID: the credential's `sub` must equal it. */\n subject: string;\n /** The key the wallet proved possession of: the credential's `cnf.jwk` must be exactly it. */\n holderKey: Ed25519PublicJwk | Uint8Array;\n /** The `vct` of the offered credential configuration (or the accepted list). */\n vct: string | readonly string[];\n resolver: DidResolver;\n /** ms since the epoch; default `Date.now()`. */\n now?: number;\n}\n\n/**\n * Check a credential a wallet just received from an issuer, before storing it (RFC 9901 §7.2,\n * SPEC §13.3): no KB-JWT, header and shape as `verifyPresentation`, `iss` = `opts.issuer` when\n * given (`untrusted_issuer`), the issuer's signature by its `assertionMethod` key named by `kid`,\n * every disclosure matching exactly one digest (recursive, `bad_disclosure`), `sub` =\n * `opts.subject` (`wrong_subject`), `cnf.jwk` = `opts.holderKey` (`bad_key_binding`), `vct` as\n * offered (`wrong_type`), `exp`/`nbf` (`expired`, `not_yet_valid`). The status list is not read\n * (a just-issued credential); verifiers check it at presentation. Returns the same result shape\n * as `verifyPresentation` (`holderAuthenticated` and `keyBindingValid` are always false); throws\n * `VerificationUnavailable` when the issuer's DID document cannot be read and `TypeError` when\n * `issuer` is neither a DID nor `false`.\n */\nexport async function verifyIssuedCredential(\n credential: string,\n opts: VerifyIssuedCredentialOptions\n): Promise<VerifyResult> {\n const nowS = toSeconds(clockMs(opts.now));\n let expectedHolder: Uint8Array;\n if (opts.holderKey instanceof Uint8Array) {\n expectedHolder = opts.holderKey;\n } else {\n try {\n expectedHolder = rawFromPublicJwk(opts.holderKey);\n } catch {\n expectedHolder = new Uint8Array(0);\n }\n }\n if (expectedHolder.length !== 32)\n throw new TypeError('holderKey is not an Ed25519 public key');\n const expectedIssuer: unknown = opts.issuer;\n if (\n expectedIssuer !== false &&\n (typeof expectedIssuer !== 'string' || expectedIssuer.length === 0)\n )\n throw new TypeError(\n 'issuer is required: the expected issuer DID, or false to accept any issuer'\n );\n const parsed = parse(credential);\n if (!parsed || parsed.kbJwt !== undefined) return fail('malformed');\n const c = await checkIssuerSigned(\n parsed,\n typeof expectedIssuer === 'string' ? [expectedIssuer] : () => true,\n opts.resolver\n );\n if ('ok' in c) return c;\n if (c.sub !== opts.subject) return fail('wrong_subject');\n if (!c.holderRaw || !timingSafeEqual(c.holderRaw, expectedHolder))\n return fail('bad_key_binding');\n const vcts = typeof opts.vct === 'string' ? [opts.vct] : opts.vct;\n if (!vcts.includes(c.vct)) return fail('wrong_type');\n const invalid = validity(c, nowS, UNBOUND);\n if (invalid) return invalid;\n return verified(c, parsed.jwt);\n}\n"]}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { base58btcDecode, base58btcEncode, base64urlDecode, base64urlEncode, base64urlJson, utf8, fromUtf8 } from './chunk-AQ3IINHC.js';
|
|
2
|
+
import { ed25519 as ed25519$1 } from '@noble/curves/ed25519.js';
|
|
3
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
4
|
+
|
|
5
|
+
// src/internal/errors.ts
|
|
6
|
+
var CredentialsKitError = class extends Error {
|
|
7
|
+
code;
|
|
8
|
+
constructor(code, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.name = new.target.name;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/internal/util.ts
|
|
16
|
+
function isRecord(v) {
|
|
17
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
18
|
+
}
|
|
19
|
+
function toSeconds(ms) {
|
|
20
|
+
return Math.floor(ms / 1e3);
|
|
21
|
+
}
|
|
22
|
+
var CLOCK_SKEW_S = 120;
|
|
23
|
+
function clockMs(now) {
|
|
24
|
+
if (now === void 0) return Date.now();
|
|
25
|
+
if (typeof now !== "number" || !Number.isFinite(now) || now < 1e12) {
|
|
26
|
+
throw new TypeError(
|
|
27
|
+
"`now` is milliseconds since the epoch (Date.now()), not seconds."
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return now;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/jose.ts
|
|
34
|
+
var ED25519_JWS_ALGS = ["EdDSA", "Ed25519"];
|
|
35
|
+
function isEd25519JwsAlg(alg) {
|
|
36
|
+
return alg === "EdDSA" || alg === "Ed25519";
|
|
37
|
+
}
|
|
38
|
+
var JOSE_MESSAGES = {
|
|
39
|
+
malformed: "The JWS is not a well-formed compact JWS with JSON header and payload.",
|
|
40
|
+
unsupported_alg: "The JWS does not use an Ed25519 algorithm.",
|
|
41
|
+
bad_signature: "The JWS signature does not verify.",
|
|
42
|
+
bad_key: "The key is not a valid Ed25519 public key."
|
|
43
|
+
};
|
|
44
|
+
var JoseError = class extends CredentialsKitError {
|
|
45
|
+
constructor(code) {
|
|
46
|
+
super(code, JOSE_MESSAGES[code]);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var MAX_JWS_LENGTH = 1024 * 1024;
|
|
50
|
+
async function signJws(header, payload, signer) {
|
|
51
|
+
if (!isEd25519JwsAlg(header.alg)) throw new JoseError("unsupported_alg");
|
|
52
|
+
const signingInput = `${base64urlJson(header)}.${base64urlJson(payload)}`;
|
|
53
|
+
const signature = await signer.sign(utf8(signingInput));
|
|
54
|
+
return `${signingInput}.${base64urlEncode(signature)}`;
|
|
55
|
+
}
|
|
56
|
+
function decodeJws(compact) {
|
|
57
|
+
if (typeof compact !== "string" || compact.length > MAX_JWS_LENGTH) {
|
|
58
|
+
throw new JoseError("malformed");
|
|
59
|
+
}
|
|
60
|
+
const parts = compact.split(".");
|
|
61
|
+
if (parts.length !== 3) throw new JoseError("malformed");
|
|
62
|
+
const [h, p, s] = parts;
|
|
63
|
+
let header;
|
|
64
|
+
let payload;
|
|
65
|
+
let signature;
|
|
66
|
+
try {
|
|
67
|
+
header = JSON.parse(fromUtf8(base64urlDecode(h)));
|
|
68
|
+
payload = JSON.parse(fromUtf8(base64urlDecode(p)));
|
|
69
|
+
signature = base64urlDecode(s);
|
|
70
|
+
} catch {
|
|
71
|
+
throw new JoseError("malformed");
|
|
72
|
+
}
|
|
73
|
+
if (!isRecord(header) || !isRecord(payload)) throw new JoseError("malformed");
|
|
74
|
+
if (header.crit !== void 0) throw new JoseError("malformed");
|
|
75
|
+
return { header, payload, signature, signingInput: `${h}.${p}` };
|
|
76
|
+
}
|
|
77
|
+
function verifyEd25519(signature, message, publicKey) {
|
|
78
|
+
if (signature.length !== 64 || publicKey.length !== 32) return false;
|
|
79
|
+
try {
|
|
80
|
+
return ed25519$1.verify(signature, message, publicKey, {
|
|
81
|
+
zip215: false
|
|
82
|
+
});
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function verifyJws(compact, publicKey) {
|
|
88
|
+
try {
|
|
89
|
+
const jws = decodeJws(compact);
|
|
90
|
+
if (!isEd25519JwsAlg(jws.header.alg))
|
|
91
|
+
throw new JoseError("unsupported_alg");
|
|
92
|
+
if (!verifyEd25519(jws.signature, utf8(jws.signingInput), publicKey)) {
|
|
93
|
+
throw new JoseError("bad_signature");
|
|
94
|
+
}
|
|
95
|
+
return Promise.resolve(jws);
|
|
96
|
+
} catch (e) {
|
|
97
|
+
return Promise.reject(e instanceof Error ? e : new JoseError("malformed"));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function publicJwkFromRaw(raw) {
|
|
101
|
+
if (raw.length !== 32) throw new JoseError("bad_key");
|
|
102
|
+
return { kty: "OKP", crv: "Ed25519", x: base64urlEncode(raw) };
|
|
103
|
+
}
|
|
104
|
+
function rawFromPublicJwk(jwk) {
|
|
105
|
+
if (!isRecord(jwk) || jwk.kty !== "OKP" || jwk.crv !== "Ed25519" || typeof jwk.x !== "string" || jwk.x.length > MAX_KEY_STRING_LENGTHS.jwkX || "d" in jwk) {
|
|
106
|
+
throw new JoseError("bad_key");
|
|
107
|
+
}
|
|
108
|
+
let raw;
|
|
109
|
+
try {
|
|
110
|
+
raw = base64urlDecode(jwk.x);
|
|
111
|
+
} catch {
|
|
112
|
+
throw new JoseError("bad_key");
|
|
113
|
+
}
|
|
114
|
+
if (raw.length !== 32) throw new JoseError("bad_key");
|
|
115
|
+
return raw;
|
|
116
|
+
}
|
|
117
|
+
var MAX_KEY_STRING_LENGTHS = {
|
|
118
|
+
multibase: 60,
|
|
119
|
+
base58: 50,
|
|
120
|
+
hex: 64,
|
|
121
|
+
jwkX: 43
|
|
122
|
+
};
|
|
123
|
+
var ED25519_PUB_MULTICODEC = [237, 1];
|
|
124
|
+
function multibaseToRaw(multibase) {
|
|
125
|
+
if (typeof multibase !== "string" || !multibase.startsWith("z") || multibase.length > MAX_KEY_STRING_LENGTHS.multibase) {
|
|
126
|
+
throw new JoseError("bad_key");
|
|
127
|
+
}
|
|
128
|
+
let bytes;
|
|
129
|
+
try {
|
|
130
|
+
bytes = base58btcDecode(multibase.slice(1));
|
|
131
|
+
} catch {
|
|
132
|
+
throw new JoseError("bad_key");
|
|
133
|
+
}
|
|
134
|
+
if (bytes.length === 34 && bytes[0] === ED25519_PUB_MULTICODEC[0] && bytes[1] === ED25519_PUB_MULTICODEC[1]) {
|
|
135
|
+
return bytes.slice(2);
|
|
136
|
+
}
|
|
137
|
+
if (bytes.length === 32) return bytes;
|
|
138
|
+
throw new JoseError("bad_key");
|
|
139
|
+
}
|
|
140
|
+
function rawToMultibase(raw) {
|
|
141
|
+
if (raw.length !== 32) throw new JoseError("bad_key");
|
|
142
|
+
const prefixed = new Uint8Array(34);
|
|
143
|
+
prefixed.set(ED25519_PUB_MULTICODEC);
|
|
144
|
+
prefixed.set(raw, 2);
|
|
145
|
+
return `z${base58btcEncode(prefixed)}`;
|
|
146
|
+
}
|
|
147
|
+
var base58btc = {
|
|
148
|
+
encode: base58btcEncode,
|
|
149
|
+
decode: base58btcDecode
|
|
150
|
+
};
|
|
151
|
+
var base64url = {
|
|
152
|
+
encode: base64urlEncode,
|
|
153
|
+
decode: base64urlDecode
|
|
154
|
+
};
|
|
155
|
+
var ed25519 = {
|
|
156
|
+
publicJwkFromRaw,
|
|
157
|
+
rawFromPublicJwk,
|
|
158
|
+
base58btc,
|
|
159
|
+
multibaseToRaw,
|
|
160
|
+
rawToMultibase,
|
|
161
|
+
verify: verifyEd25519,
|
|
162
|
+
/** The raw 32-byte public key of a 32-byte seed. */
|
|
163
|
+
publicKeyFromSeed(seed) {
|
|
164
|
+
if (seed.length !== 32) throw new JoseError("bad_key");
|
|
165
|
+
return ed25519$1.getPublicKey(seed);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
function signerFromEd25519Seed(seed, kid) {
|
|
169
|
+
if (!(seed instanceof Uint8Array) || seed.length !== 32) {
|
|
170
|
+
throw new JoseError("bad_key");
|
|
171
|
+
}
|
|
172
|
+
const secret = Uint8Array.from(seed);
|
|
173
|
+
const publicKey = ed25519$1.getPublicKey(secret);
|
|
174
|
+
return {
|
|
175
|
+
kid,
|
|
176
|
+
publicKey,
|
|
177
|
+
publicJwk: publicJwkFromRaw(publicKey),
|
|
178
|
+
sign(bytes) {
|
|
179
|
+
return Promise.resolve(ed25519$1.sign(bytes, secret));
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function ed25519SeedFromMnemonic(mnemonic) {
|
|
184
|
+
return sha256(utf8(mnemonic.trim()));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export { CLOCK_SKEW_S, CredentialsKitError, ED25519_JWS_ALGS, JoseError, MAX_JWS_LENGTH, MAX_KEY_STRING_LENGTHS, base58btc, base64url, clockMs, decodeJws, ed25519, ed25519SeedFromMnemonic, isEd25519JwsAlg, isRecord, multibaseToRaw, publicJwkFromRaw, rawFromPublicJwk, rawToMultibase, signJws, signerFromEd25519Seed, toSeconds, verifyEd25519, verifyJws };
|
|
188
|
+
//# sourceMappingURL=chunk-VXKONZK4.js.map
|
|
189
|
+
//# sourceMappingURL=chunk-VXKONZK4.js.map
|