@sphereon/ssi-sdk.sd-jwt 0.34.1-fix.79 → 0.34.1-next.278

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/dist/index.js CHANGED
@@ -3,9 +3,8 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
3
3
 
4
4
  // src/action-handler.ts
5
5
  import { SDJwt } from "@sd-jwt/core";
6
- import { SDJwtVcInstance } from "@sd-jwt/sd-jwt-vc";
6
+ import { SDJwtVcInstance as SDJwtVcInstance2 } from "@sd-jwt/sd-jwt-vc";
7
7
  import { calculateJwkThumbprint, signatureAlgorithmFromKey } from "@sphereon/ssi-sdk-ext.key-utils";
8
- import { decodeBase64url } from "@veramo/utils";
9
8
  import Debug from "debug";
10
9
 
11
10
  // src/defaultCallbacks.ts
@@ -81,8 +80,189 @@ function assertValidTypeMetadata(metadata, vct) {
81
80
  }
82
81
  }
83
82
  __name(assertValidTypeMetadata, "assertValidTypeMetadata");
83
+ function isVcdm2SdJwtPayload(payload) {
84
+ return "type" in payload && Array.isArray(payload.type) && payload.type.includes("VerifiableCredential") && "@context" in payload && (typeof payload["@context"] === "string" && payload["@context"].length > 0 || Array.isArray(payload["@context"]) && payload["@context"].length > 0 && payload["@context"].includes("https://www.w3.org/ns/credentials/v2"));
85
+ }
86
+ __name(isVcdm2SdJwtPayload, "isVcdm2SdJwtPayload");
87
+ function isSdjwtVcPayload(payload) {
88
+ return !isVcdm2SdJwtPayload(payload) && "vct" in payload && typeof payload.vct === "string";
89
+ }
90
+ __name(isSdjwtVcPayload, "isSdjwtVcPayload");
91
+ function getIssuerFromSdJwt(payload) {
92
+ let issuer;
93
+ if (isSdjwtVcPayload(payload) || "iss" in payload) {
94
+ issuer = payload.iss;
95
+ } else if (isVcdm2SdJwtPayload(payload) || "issuer" in payload && payload.issuer) {
96
+ issuer = typeof payload.issuer === "string" ? payload.issuer : payload.issuer?.id;
97
+ }
98
+ if (!issuer) {
99
+ throw new Error("No issuer (iss or VCDM 2 issuer) found in SD-JWT or no VCDM2 SD-JWT or SD-JWT VC");
100
+ }
101
+ return issuer;
102
+ }
103
+ __name(getIssuerFromSdJwt, "getIssuerFromSdJwt");
104
+
105
+ // src/sdJwtVcdm2Instance.ts
106
+ import { SDJwtInstance } from "@sd-jwt/core";
107
+ import { SDJWTException } from "@sd-jwt/utils";
108
+ import { SDJwtVcInstance } from "@sd-jwt/sd-jwt-vc";
109
+
110
+ // src/types.ts
111
+ import { contextHasPlugin } from "@sphereon/ssi-sdk.agent-config";
112
+ var sdJwtPluginContextMethods = [
113
+ "createSdJwtVc",
114
+ "createSdJwtPresentation",
115
+ "verifySdJwtVc",
116
+ "verifySdJwtPresentation"
117
+ ];
118
+ function contextHasSDJwtPlugin(context) {
119
+ return contextHasPlugin(context, "verifySdJwtVc");
120
+ }
121
+ __name(contextHasSDJwtPlugin, "contextHasSDJwtPlugin");
122
+ function isVcdm2SdJwt(type) {
123
+ return type === "vc+sd-jwt" || type === "vp+sd-jwt";
124
+ }
125
+ __name(isVcdm2SdJwt, "isVcdm2SdJwt");
126
+
127
+ // src/sdJwtVcdm2Instance.ts
128
+ var SDJwtVcdmInstanceFactory = class {
129
+ static {
130
+ __name(this, "SDJwtVcdmInstanceFactory");
131
+ }
132
+ static create(type, config) {
133
+ if (isVcdm2SdJwt(type)) {
134
+ return new SDJwtVcdm2Instance(config);
135
+ }
136
+ return new SDJwtVcInstance(config);
137
+ }
138
+ };
139
+ var SDJwtVcdm2Instance = class extends SDJwtInstance {
140
+ static {
141
+ __name(this, "SDJwtVcdm2Instance");
142
+ }
143
+ /**
144
+ * The type of the SD-JWT VCDM2 set in the header.typ field.
145
+ */
146
+ static type = "vc+sd-jwt";
147
+ userConfig = {};
148
+ constructor(userConfig) {
149
+ super(userConfig);
150
+ if (userConfig) {
151
+ this.userConfig = userConfig;
152
+ }
153
+ }
154
+ /**
155
+ * Validates if the disclosureFrame contains any reserved fields. If so it will throw an error.
156
+ * @param disclosureFrame
157
+ */
158
+ validateReservedFields(disclosureFrame) {
159
+ if (disclosureFrame?._sd && Array.isArray(disclosureFrame._sd) && disclosureFrame._sd.length > 0) {
160
+ const reservedNames = [
161
+ "iss",
162
+ "nbf",
163
+ "exp",
164
+ "cnf",
165
+ "@context",
166
+ "type",
167
+ "credentialStatus",
168
+ "credentialSchema",
169
+ "relatedResource"
170
+ ];
171
+ const reservedNamesInDisclosureFrame = disclosureFrame._sd.filter((key) => reservedNames.includes(key));
172
+ if (reservedNamesInDisclosureFrame.length > 0) {
173
+ throw new SDJWTException(`Cannot disclose protected field(s): ${reservedNamesInDisclosureFrame.join(", ")}`);
174
+ }
175
+ }
176
+ }
177
+ /**
178
+ * Verifies the SD-JWT-VC. It will validate the signature, the keybindings when required, the status, and the VCT.
179
+ * @param encodedSDJwt
180
+ * @param options
181
+ */
182
+ async verify(encodedSDJwt, options) {
183
+ const result = await super.verify(encodedSDJwt, options).then((res) => {
184
+ return {
185
+ payload: res.payload,
186
+ header: res.header,
187
+ kb: res.kb
188
+ };
189
+ });
190
+ return result;
191
+ }
192
+ /**
193
+ * Validates the integrity of the response if the integrity is passed. If the integrity does not match, an error is thrown.
194
+ * @param integrity
195
+ * @param response
196
+ */
197
+ async validateIntegrity(response, url, integrity) {
198
+ if (integrity) {
199
+ const arrayBuffer = await response.arrayBuffer();
200
+ const alg = integrity.split("-")[0];
201
+ const hashBuffer = await this.userConfig.hasher(arrayBuffer, alg);
202
+ const integrityHash = integrity.split("-")[1];
203
+ const hash = Array.from(new Uint8Array(hashBuffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
204
+ if (hash !== integrityHash) {
205
+ throw new Error(`Integrity check for ${url} failed: is ${hash}, but expected ${integrityHash}`);
206
+ }
207
+ }
208
+ }
209
+ /**
210
+ * Fetches the content from the url with a timeout of 10 seconds.
211
+ * @param url
212
+ * @param integrity
213
+ * @returns
214
+ */
215
+ async fetch(url, integrity) {
216
+ try {
217
+ const response = await fetch(url, {
218
+ signal: AbortSignal.timeout(this.userConfig.timeout ?? 1e4)
219
+ });
220
+ if (!response.ok) {
221
+ const errorText = await response.text();
222
+ return Promise.reject(new Error(`Error fetching ${url}: ${response.status} ${response.statusText} - ${errorText}`));
223
+ }
224
+ await this.validateIntegrity(response.clone(), url, integrity);
225
+ return response.json();
226
+ } catch (error) {
227
+ if (error.name === "TimeoutError") {
228
+ throw new Error(`Request to ${url} timed out`);
229
+ }
230
+ throw error;
231
+ }
232
+ }
233
+ async issue(payload, disclosureFrame, options) {
234
+ if (payload.iss && !payload.issuer) {
235
+ payload.issuer = {
236
+ id: payload.iss
237
+ };
238
+ delete payload.iss;
239
+ }
240
+ if (payload.nbf && !payload.validFrom) {
241
+ payload.validFrom = toVcdm2Date(payload.nbf);
242
+ delete payload.nbf;
243
+ }
244
+ if (payload.exp && !payload.validUntil) {
245
+ payload.validUntil = toVcdm2Date(payload.exp);
246
+ delete payload.exp;
247
+ }
248
+ if (payload.sub && !Array.isArray(payload.credentialSubject) && !payload.credentialSubject.id) {
249
+ payload.credentialSubject.id = payload.sub;
250
+ delete payload.sub;
251
+ }
252
+ return super.issue(payload, disclosureFrame, options);
253
+ }
254
+ };
255
+ function toVcdm2Date(value) {
256
+ const num = typeof value === "string" ? Number(value) : value;
257
+ if (!Number.isFinite(num)) {
258
+ throw new SDJWTException(`Invalid numeric date: ${value}`);
259
+ }
260
+ return new Date(num * 1e3).toISOString();
261
+ }
262
+ __name(toVcdm2Date, "toVcdm2Date");
84
263
 
85
264
  // src/action-handler.ts
265
+ import * as u8a from "uint8arrays";
86
266
  var debug = Debug("@sphereon/ssi-sdk.sd-jwt");
87
267
  var SDJwtPlugin = class {
88
268
  static {
@@ -152,7 +332,11 @@ var SDJwtPlugin = class {
152
332
  * @returns A signed SD-JWT credential.
153
333
  */
154
334
  async createSdJwtVc(args, context) {
155
- const issuer = args.credentialPayload.iss;
335
+ const payload = args.credentialPayload;
336
+ const isVcdm2 = isVcdm2SdJwtPayload(payload);
337
+ const isSdJwtVc = isSdjwtVcPayload(payload);
338
+ const type = args.type ?? (isVcdm2 ? "vc+sd-jwt" : "dc+sd-jwt");
339
+ const issuer = getIssuerFromSdJwt(args.credentialPayload);
156
340
  if (!issuer) {
157
341
  throw new Error("credential.issuer must not be empty");
158
342
  }
@@ -160,24 +344,46 @@ var SDJwtPlugin = class {
160
344
  identifier: issuer,
161
345
  resolution: args.resolution
162
346
  }, context);
163
- const sdjwt = new SDJwtVcInstance({
347
+ const signAlg = alg ?? signingKey?.alg ?? "ES256";
348
+ const hashAlg = /(\d{3})$/.test(signAlg) ? `sha-${signAlg.slice(-3)}` : "sha-256";
349
+ const sdjwt = SDJwtVcdmInstanceFactory.create(type, {
350
+ omitTyp: true,
164
351
  signer,
165
352
  hasher: this.registeredImplementations.hasher,
166
353
  saltGenerator: this.registeredImplementations.saltGenerator,
167
- signAlg: alg ?? "ES256",
168
- hashAlg: "sha-256"
354
+ signAlg,
355
+ hashAlg
169
356
  });
170
- const credential = await sdjwt.issue(args.credentialPayload, args.disclosureFrame, {
171
- header: {
172
- ...signingKey?.key.kid !== void 0 && {
173
- kid: signingKey.key.kid
174
- },
175
- ...signingKey?.key.x5c !== void 0 && {
176
- x5c: signingKey.key.x5c
177
- }
357
+ const header = {
358
+ ...signingKey?.key.kid !== void 0 && {
359
+ kid: signingKey.key.kid
360
+ },
361
+ ...signingKey?.key.x5c !== void 0 && {
362
+ x5c: signingKey.key.x5c
363
+ },
364
+ ...type && {
365
+ typ: type
178
366
  }
179
- });
367
+ };
368
+ let credential;
369
+ if (isVcdm2) {
370
+ credential = await sdjwt.issue(
371
+ payload,
372
+ // @ts-ignore
373
+ args.disclosureFrame,
374
+ {
375
+ header
376
+ }
377
+ );
378
+ } else if (isSdJwtVc) {
379
+ credential = await sdjwt.issue(payload, args.disclosureFrame, {
380
+ header
381
+ });
382
+ } else {
383
+ return Promise.reject(new Error(`invalid_argument: credential '${type}' type is not supported`));
384
+ }
180
385
  return {
386
+ type,
181
387
  credential
182
388
  };
183
389
  }
@@ -303,6 +509,7 @@ var SDJwtPlugin = class {
303
509
  * @returns A signed SD-JWT presentation.
304
510
  */
305
511
  async createSdJwtPresentation(args, context) {
512
+ const type = args.type ?? "dc+sd-jwt";
306
513
  const cred = await SDJwt.fromEncode(args.presentation, this.registeredImplementations.hasher);
307
514
  const claims = await cred.getClaims(this.registeredImplementations.hasher);
308
515
  let holder;
@@ -323,8 +530,9 @@ var SDJwtPlugin = class {
323
530
  const { alg, signer } = await this.getSignerForIdentifier({
324
531
  identifier: holder
325
532
  }, context);
326
- const sdjwt = new SDJwtVcInstance({
327
- hasher: this.registeredImplementations.hasher ?? defaultGenerateDigest,
533
+ const sdjwt = SDJwtVcdmInstanceFactory.create(type, {
534
+ omitTyp: true,
535
+ hasher: this.registeredImplementations.hasher,
328
536
  saltGenerator: this.registeredImplementations.saltGenerator,
329
537
  kbSigner: signer,
330
538
  kbSignAlg: alg ?? "ES256"
@@ -333,6 +541,7 @@ var SDJwtPlugin = class {
333
541
  kb: args.kb
334
542
  });
335
543
  return {
544
+ type,
336
545
  presentation
337
546
  };
338
547
  }
@@ -343,13 +552,18 @@ var SDJwtPlugin = class {
343
552
  * @returns
344
553
  */
345
554
  async verifySdJwtVc(args, context) {
346
- const verifier = /* @__PURE__ */ __name(async (data, signature) => this.verify(sdjwt, context, data, signature), "verifier");
347
- const sdjwt = new SDJwtVcInstance({
555
+ const verifier = /* @__PURE__ */ __name(async (data, signature) => this.verifyCallbackImpl(sdjwt, context, data, signature), "verifier");
556
+ const cred = await SDJwt.fromEncode(args.credential, this.registeredImplementations.hasher);
557
+ const type = isVcdm2SdJwtPayload(cred.jwt?.payload) ? "vc+sd-jwt" : "dc+sd-jwt";
558
+ const sdjwt = SDJwtVcdmInstanceFactory.create(type, {
348
559
  verifier,
349
560
  hasher: this.registeredImplementations.hasher ?? defaultGenerateDigest
350
561
  });
351
- const { header = {}, payload, kb } = await sdjwt.verify(args.credential);
562
+ const { header = {}, payload, kb } = await sdjwt.verify(args.credential, {
563
+ skewSeconds: 60 * 60 * 24 * 5
564
+ });
352
565
  return {
566
+ type,
353
567
  header,
354
568
  payload,
355
569
  kb
@@ -364,7 +578,7 @@ var SDJwtPlugin = class {
364
578
  * @param payload - The payload of the SD-JWT
365
579
  * @returns
366
580
  */
367
- verifyKb(sdjwt, context, data, signature, payload) {
581
+ verifyKb(context, data, signature, payload) {
368
582
  if (!payload.cnf) {
369
583
  throw Error("other method than cnf is not supported yet");
370
584
  }
@@ -378,9 +592,10 @@ var SDJwtPlugin = class {
378
592
  * @param signature - The signature
379
593
  * @returns
380
594
  */
381
- async verify(sdjwt, context, data, signature, opts) {
595
+ async verifyCallbackImpl(sdjwt, context, data, signature, opts) {
382
596
  const decodedVC = await sdjwt.decode(`${data}.${signature}`);
383
- const issuer = decodedVC.jwt.payload.iss;
597
+ const payload = decodedVC.jwt.payload;
598
+ const issuer = getIssuerFromSdJwt(payload);
384
599
  const header = decodedVC.jwt.header;
385
600
  const x5c = header?.x5c;
386
601
  let jwk = header.jwk;
@@ -446,14 +661,18 @@ var SDJwtPlugin = class {
446
661
  */
447
662
  async verifySdJwtPresentation(args, context) {
448
663
  let sdjwt;
449
- const verifier = /* @__PURE__ */ __name(async (data, signature) => this.verify(sdjwt, context, data, signature), "verifier");
450
- const verifierKb = /* @__PURE__ */ __name(async (data, signature, payload) => this.verifyKb(sdjwt, context, data, signature, payload), "verifierKb");
451
- sdjwt = new SDJwtVcInstance({
664
+ const verifier = /* @__PURE__ */ __name(async (data, signature) => this.verifyCallbackImpl(sdjwt, context, data, signature), "verifier");
665
+ const verifierKb = /* @__PURE__ */ __name(async (data, signature, payload) => this.verifyKb(context, data, signature, payload), "verifierKb");
666
+ sdjwt = new SDJwtVcInstance2({
452
667
  verifier,
453
668
  hasher: this.registeredImplementations.hasher,
454
669
  kbVerifier: verifierKb
455
670
  });
456
- return sdjwt.verify(args.presentation, args.requiredClaimKeys, args.kb);
671
+ const verifierOpts = {
672
+ requiredClaimKeys: args.requiredClaimKeys,
673
+ keyBindingNonce: args.keyBindingNonce
674
+ };
675
+ return sdjwt.verify(args.presentation, verifierOpts);
457
676
  }
458
677
  /**
459
678
  * Fetch and validate Type Metadata.
@@ -524,7 +743,7 @@ var SDJwtPlugin = class {
524
743
  return payload.cnf.jwk;
525
744
  } else if (payload.cnf !== void 0 && "kid" in payload.cnf && typeof payload.cnf.kid === "string" && payload.cnf.kid.startsWith("did:jwk:")) {
526
745
  const encoded = this.extractBase64FromDIDJwk(payload.cnf.kid);
527
- const decoded = decodeBase64url(encoded);
746
+ const decoded = u8a.toString(u8a.fromString(encoded, "base64url"), "utf-8");
528
747
  const jwt = JSON.parse(decoded);
529
748
  return jwt;
530
749
  }
@@ -538,26 +757,18 @@ var SDJwtPlugin = class {
538
757
  return parts[2].split("#")[0];
539
758
  }
540
759
  };
541
-
542
- // src/types.ts
543
- import { contextHasPlugin } from "@sphereon/ssi-sdk.agent-config";
544
- var sdJwtPluginContextMethods = [
545
- "createSdJwtVc",
546
- "createSdJwtPresentation",
547
- "verifySdJwtVc",
548
- "verifySdJwtPresentation"
549
- ];
550
- function contextHasSDJwtPlugin(context) {
551
- return contextHasPlugin(context, "verifySdJwtVc");
552
- }
553
- __name(contextHasSDJwtPlugin, "contextHasSDJwtPlugin");
554
760
  export {
555
761
  SDJwtPlugin,
556
762
  assertValidTypeMetadata,
557
763
  contextHasSDJwtPlugin,
558
764
  createIntegrity,
765
+ defaultGenerateDigest,
559
766
  extractHashFromIntegrity,
560
767
  fetchUrlWithErrorHandling,
768
+ getIssuerFromSdJwt,
769
+ isSdjwtVcPayload,
770
+ isVcdm2SdJwt,
771
+ isVcdm2SdJwtPayload,
561
772
  sdJwtPluginContextMethods,
562
773
  validateIntegrity
563
774
  };