@sphereon/ssi-sdk.sd-jwt 0.34.1-next.6 → 0.34.1-next.85

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