@sd-jwt/core 0.20.1-next.5 → 0.20.1-next.6

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
@@ -69,6 +69,7 @@ var __async = (__this, __arguments, generator) => {
69
69
  // src/index.ts
70
70
  var index_exports = {};
71
71
  __export(index_exports, {
72
+ DEFAULT_SECURE_HASH_ALGORITHMS: () => DEFAULT_SECURE_HASH_ALGORITHMS,
72
73
  Disclosure: () => Disclosure,
73
74
  FlattenJSON: () => FlattenJSON,
74
75
  GeneralJSON: () => GeneralJSON,
@@ -94,6 +95,8 @@ __export(index_exports, {
94
95
  decodeJwt: () => decodeJwt,
95
96
  decodeSdJwt: () => decodeSdJwt,
96
97
  decodeSdJwtSync: () => decodeSdJwtSync,
98
+ encodePath: () => encodePath,
99
+ encodePathSegment: () => encodePathSegment,
97
100
  ensureError: () => ensureError,
98
101
  getClaims: () => getClaims,
99
102
  getClaimsSync: () => getClaimsSync,
@@ -110,7 +113,8 @@ __export(index_exports, {
110
113
  uint8ArrayToBase64Url: () => import_identity_common.uint8ArrayToBase64Url,
111
114
  unpack: () => unpack,
112
115
  unpackObj: () => unpackObj,
113
- unpackSync: () => unpackSync
116
+ unpackSync: () => unpackSync,
117
+ validateJwtPayload: () => validateJwtPayload
114
118
  });
115
119
  module.exports = __toCommonJS(index_exports);
116
120
 
@@ -120,6 +124,8 @@ var SD_LIST_KEY = "...";
120
124
  var SD_DIGEST = "_sd";
121
125
  var SD_DECOY = "_sd_decoy";
122
126
  var KB_JWT_TYP = "kb+jwt";
127
+ var encodePathSegment = (segment) => segment.replace(/~/g, "~0").replace(/\./g, "~1");
128
+ var encodePath = (segments) => segments.map(encodePathSegment).join(".");
123
129
  var IANA_HASH_ALGORITHMS = [
124
130
  "sha-256",
125
131
  "sha-256-128",
@@ -139,6 +145,19 @@ var IANA_HASH_ALGORITHMS = [
139
145
  "k12-256",
140
146
  "k12-512"
141
147
  ];
148
+ var DEFAULT_SECURE_HASH_ALGORITHMS = [
149
+ "sha-256",
150
+ "sha-384",
151
+ "sha-512",
152
+ "sha3-256",
153
+ "sha3-384",
154
+ "sha3-512",
155
+ "blake2s-256",
156
+ "blake2b-256",
157
+ "blake2b-512",
158
+ "k12-256",
159
+ "k12-512"
160
+ ];
142
161
 
143
162
  // src/utils/base64url.ts
144
163
  var import_identity_common = require("@owf/identity-common");
@@ -176,6 +195,12 @@ var decodeBase64urlJsonStrict = (encoded, errorMessage) => {
176
195
  // src/utils/disclosure.ts
177
196
  var Disclosure = class _Disclosure {
178
197
  constructor(data, _meta) {
198
+ if (!Array.isArray(data) || data.length !== 2 && data.length !== 3) {
199
+ throw new SDJWTException("Invalid disclosure data");
200
+ }
201
+ if (typeof data[0] !== "string") {
202
+ throw new SDJWTException("Invalid disclosure salt");
203
+ }
179
204
  this._digest = _meta == null ? void 0 : _meta.digest;
180
205
  this._encoded = _meta == null ? void 0 : _meta.encoded;
181
206
  if (data.length === 2) {
@@ -184,12 +209,19 @@ var Disclosure = class _Disclosure {
184
209
  return;
185
210
  }
186
211
  if (data.length === 3) {
212
+ if (typeof data[1] !== "string") {
213
+ throw new SDJWTException("Invalid disclosure claim name");
214
+ }
215
+ if (data[1] === SD_DIGEST || data[1] === SD_LIST_KEY) {
216
+ throw new SDJWTException(
217
+ `Reserved disclosure claim name "${data[1]}" is not allowed`
218
+ );
219
+ }
187
220
  this.salt = data[0];
188
221
  this.key = data[1];
189
222
  this.value = data[2];
190
223
  return;
191
224
  }
192
- throw new SDJWTException("Invalid disclosure data");
193
225
  }
194
226
  // We need to digest of the original encoded data.
195
227
  // After decode process, we use JSON.stringify to encode the data.
@@ -263,10 +295,7 @@ var decodeJwt = (jwt) => {
263
295
  var splitSdJwt = (sdjwt) => {
264
296
  const [encodedJwt, ...encodedDisclosures] = sdjwt.split(SD_SEPARATOR);
265
297
  if (encodedDisclosures.length === 0) {
266
- return {
267
- jwt: encodedJwt,
268
- disclosures: []
269
- };
298
+ throw new SDJWTException("Invalid SD-JWT: missing SD-JWT separator");
270
299
  }
271
300
  const encodedKeyBindingJwt = encodedDisclosures.pop();
272
301
  return {
@@ -279,10 +308,7 @@ var decodeSdJwt = (sdjwt, hasher) => __async(null, null, function* () {
279
308
  const [encodedJwt, ...encodedDisclosures] = sdjwt.split(SD_SEPARATOR);
280
309
  const jwt = decodeJwt(encodedJwt);
281
310
  if (encodedDisclosures.length === 0) {
282
- return {
283
- jwt,
284
- disclosures: []
285
- };
311
+ throw new SDJWTException("Invalid SD-JWT: missing SD-JWT separator");
286
312
  }
287
313
  const encodedKeyBindingJwt = encodedDisclosures.pop();
288
314
  const kbJwt = encodedKeyBindingJwt ? decodeJwt(encodedKeyBindingJwt) : void 0;
@@ -302,10 +328,7 @@ var decodeSdJwtSync = (sdjwt, hasher) => {
302
328
  const [encodedJwt, ...encodedDisclosures] = sdjwt.split(SD_SEPARATOR);
303
329
  const jwt = decodeJwt(encodedJwt);
304
330
  if (encodedDisclosures.length === 0) {
305
- return {
306
- jwt,
307
- disclosures: []
308
- };
331
+ throw new SDJWTException("Invalid SD-JWT: missing SD-JWT separator");
309
332
  }
310
333
  const encodedKeyBindingJwt = encodedDisclosures.pop();
311
334
  const kbJwt = encodedKeyBindingJwt ? decodeJwt(encodedKeyBindingJwt) : void 0;
@@ -334,7 +357,10 @@ var unpackArray = (arr, map, prefix = "", seenDigests) => {
334
357
  arr.forEach((item, idx) => {
335
358
  if (isRecord(item)) {
336
359
  const hash = item[SD_LIST_KEY];
337
- if (typeof hash === "string") {
360
+ if (SD_LIST_KEY in item) {
361
+ if (Object.keys(item).length !== 1 || typeof hash !== "string") {
362
+ throw new SDJWTException("Invalid array disclosure placeholder");
363
+ }
338
364
  if (seenDigests) {
339
365
  if (seenDigests.has(hash)) {
340
366
  throw new SDJWTException(
@@ -345,6 +371,11 @@ var unpackArray = (arr, map, prefix = "", seenDigests) => {
345
371
  }
346
372
  const disclosed = map[hash];
347
373
  if (disclosed) {
374
+ if (typeof disclosed.key === "string") {
375
+ throw new SDJWTException(
376
+ "Object-property disclosure cannot be used as an array element"
377
+ );
378
+ }
348
379
  const presentKey = prefix ? `${prefix}.${idx}` : `${idx}`;
349
380
  keys[presentKey] = hash;
350
381
  const { unpackedObj, disclosureKeymap: disclosureKeys } = unpackObjInternal(disclosed.value, map, presentKey, seenDigests);
@@ -387,8 +418,12 @@ var unpackObjInternal = (obj, map, prefix = "", seenDigests) => {
387
418
  }
388
419
  const record = obj;
389
420
  for (const key in record) {
421
+ if (prefix && key === "_sd_alg") {
422
+ throw new SDJWTException("Nested _sd_alg is not allowed");
423
+ }
390
424
  if (key !== SD_DIGEST && key !== SD_LIST_KEY && typeof record[key] === "object") {
391
- const newKey = prefix ? `${prefix}.${key}` : key;
425
+ const escapedKey = encodePathSegment(key);
426
+ const newKey = prefix ? `${prefix}.${escapedKey}` : escapedKey;
392
427
  const { unpackedObj: unpackedObj2, disclosureKeymap: disclosureKeys } = unpackObjInternal(record[key], map, newKey, seenDigests);
393
428
  record[key] = unpackedObj2;
394
429
  Object.assign(keys, disclosureKeys);
@@ -396,7 +431,12 @@ var unpackObjInternal = (obj, map, prefix = "", seenDigests) => {
396
431
  }
397
432
  const _a = record, { _sd } = _a, payload = __objRest(_a, ["_sd"]);
398
433
  const claims = {};
399
- if (_sd) {
434
+ if (_sd !== void 0) {
435
+ if (!Array.isArray(_sd) || !_sd.every((hash) => typeof hash === "string")) {
436
+ throw new SDJWTException(
437
+ "Invalid _sd claim: expected array of strings"
438
+ );
439
+ }
400
440
  for (const hash of _sd) {
401
441
  if (seenDigests) {
402
442
  if (seenDigests.has(hash)) {
@@ -407,13 +447,24 @@ var unpackObjInternal = (obj, map, prefix = "", seenDigests) => {
407
447
  seenDigests.add(hash);
408
448
  }
409
449
  const disclosed = map[hash];
410
- if (disclosed == null ? void 0 : disclosed.key) {
450
+ if (disclosed) {
451
+ if (typeof disclosed.key !== "string") {
452
+ throw new SDJWTException(
453
+ "Array disclosure cannot be used as an object property"
454
+ );
455
+ }
411
456
  if (disclosed.key in payload) {
412
457
  throw new SDJWTException(
413
458
  `Disclosed claim name "${disclosed.key}" conflicts with existing payload key`
414
459
  );
415
460
  }
416
- const presentKey = prefix ? `${prefix}.${disclosed.key}` : disclosed.key;
461
+ if (disclosed.key in claims) {
462
+ throw new SDJWTException(
463
+ `Disclosed claim name "${disclosed.key}" conflicts with another disclosure`
464
+ );
465
+ }
466
+ const escapedKey = encodePathSegment(disclosed.key);
467
+ const presentKey = prefix ? `${prefix}.${escapedKey}` : escapedKey;
417
468
  keys[presentKey] = hash;
418
469
  const { unpackedObj: unpackedObj2, disclosureKeymap: disclosureKeys } = unpackObjInternal(disclosed.value, map, presentKey, seenDigests);
419
470
  claims[disclosed.key] = unpackedObj2;
@@ -431,6 +482,9 @@ var createHashMapping = (disclosures, hash) => __async(null, null, function* ()
431
482
  for (let i = 0; i < disclosures.length; i++) {
432
483
  const disclosure = disclosures[i];
433
484
  const digest = yield disclosure.digest(hash);
485
+ if (digest in map) {
486
+ throw new SDJWTException("Duplicate disclosure digest detected");
487
+ }
434
488
  map[digest] = disclosure;
435
489
  }
436
490
  return map;
@@ -440,20 +494,29 @@ var createHashMappingSync = (disclosures, hash) => {
440
494
  for (let i = 0; i < disclosures.length; i++) {
441
495
  const disclosure = disclosures[i];
442
496
  const digest = disclosure.digestSync(hash);
497
+ if (digest in map) {
498
+ throw new SDJWTException("Duplicate disclosure digest detected");
499
+ }
443
500
  map[digest] = disclosure;
444
501
  }
445
502
  return map;
446
503
  };
447
- var getSDAlgAndPayload = (SdJwtPayload) => {
504
+ var getSDAlgAndPayload = (SdJwtPayload, allowedAlgorithms = DEFAULT_SECURE_HASH_ALGORITHMS) => {
448
505
  const _a = SdJwtPayload, { _sd_alg } = _a, payload = __objRest(_a, ["_sd_alg"]);
449
- if (typeof _sd_alg !== "string") {
506
+ if (_sd_alg === void 0) {
450
507
  return { _sd_alg: "sha-256", payload };
451
508
  }
509
+ if (typeof _sd_alg !== "string") {
510
+ throw new SDJWTException("Invalid _sd_alg: expected string");
511
+ }
452
512
  if (!IANA_HASH_ALGORITHMS.includes(
453
513
  _sd_alg
454
514
  )) {
455
515
  throw new SDJWTException(`Invalid _sd_alg: ${_sd_alg}`);
456
516
  }
517
+ if (!allowedAlgorithms.includes(_sd_alg)) {
518
+ throw new SDJWTException(`Disallowed _sd_alg: ${_sd_alg}`);
519
+ }
457
520
  return { _sd_alg, payload };
458
521
  };
459
522
  var unpack = (SdJwtPayload, disclosures, hasher) => __async(null, null, function* () {
@@ -562,6 +625,14 @@ var GeneralJSON = class _GeneralJSON {
562
625
  if (!json.signatures[0]) {
563
626
  throw new SDJWTException("Invalid JSON");
564
627
  }
628
+ for (let index = 1; index < json.signatures.length; index++) {
629
+ const header = json.signatures[index].header;
630
+ if (header && ("disclosures" in header || "kb_jwt" in header)) {
631
+ throw new SDJWTException(
632
+ "disclosures and kb_jwt MUST only appear in the first unprotected header"
633
+ );
634
+ }
635
+ }
565
636
  const disclosures = (_b = (_a = json.signatures[0].header) == null ? void 0 : _a.disclosures) != null ? _b : [];
566
637
  const kb_jwt = (_c = json.signatures[0].header) == null ? void 0 : _c.kb_jwt;
567
638
  return new _GeneralJSON({
@@ -634,6 +705,49 @@ var GeneralJSON = class _GeneralJSON {
634
705
  };
635
706
 
636
707
  // src/jwt.ts
708
+ var isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
709
+ var validateNumericDate = (payload, claim) => {
710
+ const value = payload[claim];
711
+ if (value === void 0) return void 0;
712
+ if (typeof value !== "number" || !Number.isFinite(value)) {
713
+ throw new SDJWTException(`Verify Error: JWT ${claim} must be a number`);
714
+ }
715
+ return value;
716
+ };
717
+ var getAudiences = (audience) => {
718
+ if (typeof audience === "string") return [audience];
719
+ if (isStringArray(audience)) return audience;
720
+ return void 0;
721
+ };
722
+ var validateAudience = (payload, expectedAudience) => {
723
+ if (expectedAudience === void 0) return;
724
+ const expectedAudiences = Array.isArray(expectedAudience) ? expectedAudience : [expectedAudience];
725
+ const audiences = getAudiences(payload.aud);
726
+ if (!audiences || !expectedAudiences.some((expected) => audiences.includes(expected))) {
727
+ throw new SDJWTException("Verify Error: Invalid audience");
728
+ }
729
+ };
730
+ var validateJwtPayload = (payload, options) => {
731
+ var _a;
732
+ if (!payload) {
733
+ throw new SDJWTException("Verify Error: JWT payload is missing");
734
+ }
735
+ const skew = (options == null ? void 0 : options.skewSeconds) ? options.skewSeconds : 0;
736
+ const currentDate = (_a = options == null ? void 0 : options.currentDate) != null ? _a : Math.floor(Date.now() / 1e3);
737
+ const iat = validateNumericDate(payload, "iat");
738
+ const nbf = validateNumericDate(payload, "nbf");
739
+ const exp = validateNumericDate(payload, "exp");
740
+ if (iat !== void 0 && iat - skew > currentDate) {
741
+ throw new SDJWTException("Verify Error: JWT is not yet valid");
742
+ }
743
+ if (nbf !== void 0 && nbf - skew > currentDate) {
744
+ throw new SDJWTException("Verify Error: JWT is not yet valid");
745
+ }
746
+ if (exp !== void 0 && exp + skew <= currentDate) {
747
+ throw new SDJWTException("Verify Error: JWT is expired");
748
+ }
749
+ validateAudience(payload, options == null ? void 0 : options.expectedAudience);
750
+ };
637
751
  var Jwt = class _Jwt {
638
752
  constructor(data) {
639
753
  this.header = data == null ? void 0 : data.header;
@@ -684,6 +798,9 @@ var Jwt = class _Jwt {
684
798
  }
685
799
  sign(signer) {
686
800
  return __async(this, null, function* () {
801
+ if (!this.header || this.header.alg === "none") {
802
+ throw new SDJWTException('Sign Error: alg "none" is not allowed');
803
+ }
687
804
  const data = this.getUnsignedToken();
688
805
  this.signature = yield signer(data);
689
806
  return this.encodeJwt();
@@ -712,20 +829,16 @@ var Jwt = class _Jwt {
712
829
  */
713
830
  verify(verifier, options) {
714
831
  return __async(this, null, function* () {
715
- var _a, _b, _c, _d;
716
- const skew = (options == null ? void 0 : options.skewSeconds) ? options.skewSeconds : 0;
717
- const currentDate = (_a = options == null ? void 0 : options.currentDate) != null ? _a : Math.floor(Date.now() / 1e3);
718
- const iat = (_b = this.payload) == null ? void 0 : _b.iat;
719
- const nbf = (_c = this.payload) == null ? void 0 : _c.nbf;
720
- const exp = (_d = this.payload) == null ? void 0 : _d.exp;
721
- if (typeof iat === "number" && iat - skew > currentDate) {
722
- throw new SDJWTException("Verify Error: JWT is not yet valid");
832
+ var _a;
833
+ const alg = (_a = this.header) == null ? void 0 : _a.alg;
834
+ if (typeof alg !== "string" || alg === "none") {
835
+ throw new SDJWTException('Verify Error: alg "none" is not allowed');
723
836
  }
724
- if (typeof nbf === "number" && nbf - skew > currentDate) {
725
- throw new SDJWTException("Verify Error: JWT is not yet valid");
837
+ if ((options == null ? void 0 : options.allowedIssuerAlgorithms) && !options.allowedIssuerAlgorithms.includes(alg)) {
838
+ throw new SDJWTException(`Verify Error: Disallowed alg ${alg}`);
726
839
  }
727
- if (typeof exp === "number" && exp + skew < currentDate) {
728
- throw new SDJWTException("Verify Error: JWT is expired");
840
+ if (!(options == null ? void 0 : options.skipJwtClaimValidation)) {
841
+ validateJwtPayload(this.payload, options);
729
842
  }
730
843
  if (!this.signature) {
731
844
  throw new SDJWTException("Verify Error: no signature in JWT");
@@ -746,16 +859,31 @@ var KBJwt = class _KBJwt extends Jwt {
746
859
  // the type unknown is not good, but we don't know at this point how to get the public key of the signer, this is defined in the kbVerifier
747
860
  verifyKB(values) {
748
861
  return __async(this, null, function* () {
862
+ var _a, _b, _c, _d;
749
863
  if (!this.header || !this.payload || !this.signature) {
750
864
  throw new SDJWTException("Verify Error: Invalid JWT");
751
865
  }
752
- if (!this.header.alg || this.header.alg === "none" || !this.header.typ || this.header.typ !== KB_JWT_TYP || !this.payload.iat || !this.payload.aud || !this.payload.nonce || // this is for backward compatibility with version 06
753
- !(this.payload.sd_hash || "_sd_hash" in this.payload && this.payload._sd_hash)) {
866
+ if (typeof this.header.alg !== "string" || this.header.alg === "none" || typeof this.header.typ !== "string" || this.header.typ !== KB_JWT_TYP || typeof this.payload.iat !== "number" || typeof this.payload.aud !== "string" || typeof this.payload.nonce !== "string" || typeof this.payload.sd_hash !== "string" || this.payload.sd_hash.length === 0) {
754
867
  throw new SDJWTException("Invalid Key Binding Jwt");
755
868
  }
756
869
  if (this.payload.nonce !== values.nonce) {
757
870
  throw new SDJWTException("Verify Error: Invalid Nonce");
758
871
  }
872
+ if (((_a = values.options) == null ? void 0 : _a.expectedKeyBindingAudience) !== void 0) {
873
+ const expectedAudiences = Array.isArray(
874
+ values.options.expectedKeyBindingAudience
875
+ ) ? values.options.expectedKeyBindingAudience : [values.options.expectedKeyBindingAudience];
876
+ if (!expectedAudiences.includes(this.payload.aud)) {
877
+ throw new SDJWTException("Verify Error: Invalid Key Binding audience");
878
+ }
879
+ }
880
+ if (((_b = values.options) == null ? void 0 : _b.keyBindingMaxAgeSeconds) !== void 0) {
881
+ const currentDate = (_c = values.options.currentDate) != null ? _c : Math.floor(Date.now() / 1e3);
882
+ const skew = (_d = values.options.skewSeconds) != null ? _d : 0;
883
+ if (this.payload.iat + values.options.keyBindingMaxAgeSeconds + skew < currentDate) {
884
+ throw new SDJWTException("Verify Error: Key Binding JWT is too old");
885
+ }
886
+ }
759
887
  yield this.verify(
760
888
  (data, sig) => values.verifier(data, sig, values.payload),
761
889
  values.options
@@ -831,16 +959,17 @@ var presentSync = (sdJwt, presentFrame, hasher) => {
831
959
  kbJwt != null ? kbJwt : ""
832
960
  ].join(SD_SEPARATOR);
833
961
  };
834
- var transformPresentationFrame = (obj, prefix = "") => {
962
+ var transformPresentationFrame = (obj, prefix = []) => {
835
963
  return Object.entries(obj).reduce((acc, [key, value]) => {
836
- const newPrefix = prefix ? `${prefix}.${key}` : key;
964
+ const newPrefix = [...prefix, key];
965
+ const encodedPrefix = encodePath(newPrefix);
837
966
  if (typeof value === "boolean") {
838
967
  if (value) {
839
- acc.push(newPrefix);
968
+ acc.push(encodedPrefix);
840
969
  }
841
970
  } else if (typeof value === "object" && value !== null) {
842
971
  acc.push(
843
- newPrefix,
972
+ encodedPrefix,
844
973
  ...transformPresentationFrame(
845
974
  value,
846
975
  newPrefix
@@ -891,6 +1020,23 @@ var selectDisclosures = (payload, disclosures, presentationFrame) => {
891
1020
  };
892
1021
 
893
1022
  // src/sdjwt.ts
1023
+ var createDisclosureSalt = (saltGenerator, seenSalts) => __async(null, null, function* () {
1024
+ const salt = yield saltGenerator(16);
1025
+ if (typeof salt !== "string") {
1026
+ throw new SDJWTException("SaltGenerator must return a string");
1027
+ }
1028
+ if (seenSalts.has(salt)) {
1029
+ throw new SDJWTException("Duplicate disclosure salt detected");
1030
+ }
1031
+ seenSalts.add(salt);
1032
+ return salt;
1033
+ });
1034
+ var addDisclosureDigest = (digest, seenDigests) => {
1035
+ if (seenDigests.has(digest)) {
1036
+ throw new SDJWTException("Duplicate disclosure digest detected");
1037
+ }
1038
+ seenDigests.add(digest);
1039
+ };
894
1040
  var SDJwt = class _SDJwt {
895
1041
  constructor(data) {
896
1042
  this.jwt = data == null ? void 0 : data.jwt;
@@ -900,16 +1046,13 @@ var SDJwt = class _SDJwt {
900
1046
  static decodeSDJwt(sdjwt, hasher) {
901
1047
  return __async(this, null, function* () {
902
1048
  const [encodedJwt, ...encodedDisclosures] = sdjwt.split(SD_SEPARATOR);
1049
+ if (encodedDisclosures.length === 0) {
1050
+ throw new SDJWTException("Invalid SD-JWT: missing SD-JWT separator");
1051
+ }
903
1052
  const jwt = Jwt.fromEncode(encodedJwt);
904
1053
  if (!jwt.payload) {
905
1054
  throw new Error("Payload is undefined on the JWT. Invalid state reached");
906
1055
  }
907
- if (encodedDisclosures.length === 0) {
908
- return {
909
- jwt,
910
- disclosures: []
911
- };
912
- }
913
1056
  const encodedKeyBindingJwt = encodedDisclosures.pop();
914
1057
  const kbJwt = encodedKeyBindingJwt ? KBJwt.fromKBEncode(encodedKeyBindingJwt) : void 0;
915
1058
  const { _sd_alg } = getSDAlgAndPayload(jwt.payload);
@@ -1023,7 +1166,8 @@ var listKeys = (obj, prefix = "") => {
1023
1166
  const keys = [];
1024
1167
  for (const key in obj) {
1025
1168
  if (obj[key] === void 0) continue;
1026
- const newKey = prefix ? `${prefix}.${key}` : key;
1169
+ const escapedKey = encodePathSegment(key);
1170
+ const newKey = prefix ? `${prefix}.${escapedKey}` : escapedKey;
1027
1171
  keys.push(newKey);
1028
1172
  const value = obj[key];
1029
1173
  if (value && typeof value === "object") {
@@ -1032,7 +1176,7 @@ var listKeys = (obj, prefix = "") => {
1032
1176
  }
1033
1177
  return keys;
1034
1178
  };
1035
- var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(null, null, function* () {
1179
+ var pack = (_0, _1, _2, _3, ..._4) => __async(null, [_0, _1, _2, _3, ..._4], function* (claims, disclosureFrame, hash, saltGenerator, seenSalts = /* @__PURE__ */ new Set(), seenDigests = /* @__PURE__ */ new Set()) {
1036
1180
  var _a, _b;
1037
1181
  if (!disclosureFrame) {
1038
1182
  return {
@@ -1053,7 +1197,9 @@ var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(null, null,
1053
1197
  claims[idx],
1054
1198
  disclosureFrame[idx],
1055
1199
  hash,
1056
- saltGenerator
1200
+ saltGenerator,
1201
+ seenSalts,
1202
+ seenDigests
1057
1203
  );
1058
1204
  recursivePackedClaims2[idx] = packed.packedClaims;
1059
1205
  disclosures2.push(...packed.disclosures);
@@ -1062,9 +1208,10 @@ var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(null, null,
1062
1208
  for (let i = 0; i < claims.length; i++) {
1063
1209
  const claim = recursivePackedClaims2[i] ? recursivePackedClaims2[i] : claims[i];
1064
1210
  if (sd.includes(i)) {
1065
- const salt = yield saltGenerator(16);
1211
+ const salt = yield createDisclosureSalt(saltGenerator, seenSalts);
1066
1212
  const disclosure = new Disclosure([salt, claim]);
1067
1213
  const digest = yield disclosure.digest(hash);
1214
+ addDisclosureDigest(digest, seenDigests);
1068
1215
  packedClaims2.push({ [SD_LIST_KEY]: digest });
1069
1216
  disclosures2.push(disclosure);
1070
1217
  } else {
@@ -1073,6 +1220,7 @@ var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(null, null,
1073
1220
  }
1074
1221
  for (let j = 0; j < decoyCount; j++) {
1075
1222
  const decoyDigest = yield createDecoy(hash, saltGenerator);
1223
+ addDisclosureDigest(decoyDigest, seenDigests);
1076
1224
  packedClaims2.push({ [SD_LIST_KEY]: decoyDigest });
1077
1225
  }
1078
1226
  return { packedClaims: packedClaims2, disclosures: disclosures2 };
@@ -1087,7 +1235,9 @@ var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(null, null,
1087
1235
  claims[key],
1088
1236
  disclosureFrame[key],
1089
1237
  hash,
1090
- saltGenerator
1238
+ saltGenerator,
1239
+ seenSalts,
1240
+ seenDigests
1091
1241
  );
1092
1242
  recursivePackedClaims[key] = packed.packedClaims;
1093
1243
  disclosures.push(...packed.disclosures);
@@ -1097,9 +1247,10 @@ var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(null, null,
1097
1247
  for (const key in claims) {
1098
1248
  const claim = recursivePackedClaims[key] ? recursivePackedClaims[key] : claims[key];
1099
1249
  if (sd.includes(key)) {
1100
- const salt = yield saltGenerator(16);
1250
+ const salt = yield createDisclosureSalt(saltGenerator, seenSalts);
1101
1251
  const disclosure = new Disclosure([salt, key, claim]);
1102
1252
  const digest = yield disclosure.digest(hash);
1253
+ addDisclosureDigest(digest, seenDigests);
1103
1254
  _sd.push(digest);
1104
1255
  disclosures.push(disclosure);
1105
1256
  } else {
@@ -1108,6 +1259,7 @@ var pack = (claims, disclosureFrame, hash, saltGenerator) => __async(null, null,
1108
1259
  }
1109
1260
  for (let j = 0; j < decoyCount; j++) {
1110
1261
  const decoyDigest = yield createDecoy(hash, saltGenerator);
1262
+ addDisclosureDigest(decoyDigest, seenDigests);
1111
1263
  _sd.push(decoyDigest);
1112
1264
  }
1113
1265
  if (_sd.length > 0) {
@@ -1137,12 +1289,19 @@ function validateReservedFieldsInternal(payload) {
1137
1289
  var _SDJwtInstance = class _SDJwtInstance {
1138
1290
  constructor(userConfig) {
1139
1291
  this.userConfig = {};
1292
+ var _a;
1140
1293
  if (userConfig) {
1141
1294
  if (userConfig.hashAlg && !IANA_HASH_ALGORITHMS.includes(userConfig.hashAlg)) {
1142
1295
  throw new SDJWTException(
1143
1296
  `Invalid hash algorithm: ${userConfig.hashAlg}`
1144
1297
  );
1145
1298
  }
1299
+ const allowedDisclosureHashAlgorithms = (_a = userConfig.allowedDisclosureHashAlgorithms) != null ? _a : DEFAULT_SECURE_HASH_ALGORITHMS;
1300
+ if (userConfig.hashAlg && !allowedDisclosureHashAlgorithms.includes(userConfig.hashAlg)) {
1301
+ throw new SDJWTException(
1302
+ `Disallowed hash algorithm: ${userConfig.hashAlg}`
1303
+ );
1304
+ }
1146
1305
  this.userConfig = userConfig;
1147
1306
  }
1148
1307
  }
@@ -1185,7 +1344,7 @@ var _SDJwtInstance = class _SDJwtInstance {
1185
1344
  }
1186
1345
  issue(payload, disclosureFrame, options) {
1187
1346
  return __async(this, null, function* () {
1188
- var _a, _b;
1347
+ var _a, _b, _c;
1189
1348
  if (!this.userConfig.hasher) {
1190
1349
  throw new SDJWTException("Hasher not found");
1191
1350
  }
@@ -1195,10 +1354,17 @@ var _SDJwtInstance = class _SDJwtInstance {
1195
1354
  if (!this.userConfig.signAlg) {
1196
1355
  throw new SDJWTException("sign alogrithm not specified");
1197
1356
  }
1357
+ if (this.userConfig.signAlg === "none") {
1358
+ throw new SDJWTException('sign algorithm "none" is not allowed');
1359
+ }
1198
1360
  this.validateReservedFields(payload);
1199
1361
  this.validateDisclosureFrame(disclosureFrame);
1200
1362
  const hasher = this.userConfig.hasher;
1201
1363
  const hashAlg = (_a = this.userConfig.hashAlg) != null ? _a : _SDJwtInstance.DEFAULT_hashAlg;
1364
+ const allowedDisclosureHashAlgorithms = (_b = this.userConfig.allowedDisclosureHashAlgorithms) != null ? _b : DEFAULT_SECURE_HASH_ALGORITHMS;
1365
+ if (!allowedDisclosureHashAlgorithms.includes(hashAlg)) {
1366
+ throw new SDJWTException(`Disallowed hash algorithm: ${hashAlg}`);
1367
+ }
1202
1368
  const { packedClaims, disclosures } = yield pack(
1203
1369
  payload,
1204
1370
  disclosureFrame,
@@ -1206,7 +1372,7 @@ var _SDJwtInstance = class _SDJwtInstance {
1206
1372
  this.userConfig.saltGenerator
1207
1373
  );
1208
1374
  const alg = this.userConfig.signAlg;
1209
- const OptionHeader = (_b = options == null ? void 0 : options.header) != null ? _b : {};
1375
+ const OptionHeader = (_c = options == null ? void 0 : options.header) != null ? _c : {};
1210
1376
  const CustomHeader = this.userConfig.omitTyp ? OptionHeader : __spreadValues({ typ: this.type }, OptionHeader);
1211
1377
  const header = __spreadProps(__spreadValues({}, CustomHeader), { alg });
1212
1378
  const jwt = new Jwt({
@@ -1242,6 +1408,9 @@ var _SDJwtInstance = class _SDJwtInstance {
1242
1408
  }
1243
1409
  const hasher = this.userConfig.hasher;
1244
1410
  const sdjwt = yield SDJwt.fromEncode(encodedSDJwt, hasher);
1411
+ if (sdjwt.kbJwt) {
1412
+ throw new SDJWTException("Holder cannot present an SD-JWT with KB-JWT");
1413
+ }
1245
1414
  if (!((_a = sdjwt.jwt) == null ? void 0 : _a.payload)) throw new SDJWTException("Payload not found");
1246
1415
  const presentSdJwtWithoutKb = yield sdjwt.present(
1247
1416
  presentationFrame,
@@ -1384,10 +1553,13 @@ var _SDJwtInstance = class _SDJwtInstance {
1384
1553
  }
1385
1554
  if (sdjwt == null ? void 0 : sdjwt.jwt) {
1386
1555
  try {
1387
- const result = yield this.VerifyJwt(sdjwt.jwt, options);
1556
+ const result = yield this.VerifyJwt(sdjwt.jwt, __spreadProps(__spreadValues({}, options), {
1557
+ skipJwtClaimValidation: true
1558
+ }));
1388
1559
  header = result.header;
1389
1560
  const claims = yield sdjwt.getClaims(hasher);
1390
1561
  payload = claims;
1562
+ validateJwtPayload(payload, options);
1391
1563
  } catch (e) {
1392
1564
  const error = ensureError(e);
1393
1565
  const code = exceptionToCode(error);
@@ -1513,9 +1685,12 @@ var _SDJwtInstance = class _SDJwtInstance {
1513
1685
  if (!sdjwt.jwt) {
1514
1686
  throw new SDJWTException("Invalid SD JWT");
1515
1687
  }
1516
- const verifiedPayloads = yield this.VerifyJwt(sdjwt.jwt, options);
1688
+ const verifiedPayloads = yield this.VerifyJwt(sdjwt.jwt, __spreadProps(__spreadValues({}, options), {
1689
+ skipJwtClaimValidation: true
1690
+ }));
1517
1691
  const claims = yield sdjwt.getClaims(hasher);
1518
1692
  validateReservedFieldsInternal(claims);
1693
+ validateJwtPayload(claims, options);
1519
1694
  return { payload: claims, header: verifiedPayloads.header };
1520
1695
  });
1521
1696
  }
@@ -1570,12 +1745,19 @@ var SDJwtInstance = _SDJwtInstance;
1570
1745
  var SDJwtGeneralJSONInstance = class {
1571
1746
  constructor(userConfig) {
1572
1747
  this.userConfig = {};
1748
+ var _a;
1573
1749
  if (userConfig) {
1574
1750
  if (userConfig.hashAlg && !IANA_HASH_ALGORITHMS.includes(userConfig.hashAlg)) {
1575
1751
  throw new SDJWTException(
1576
1752
  `Invalid hash algorithm: ${userConfig.hashAlg}`
1577
1753
  );
1578
1754
  }
1755
+ const allowedDisclosureHashAlgorithms = (_a = userConfig.allowedDisclosureHashAlgorithms) != null ? _a : DEFAULT_SECURE_HASH_ALGORITHMS;
1756
+ if (userConfig.hashAlg && !allowedDisclosureHashAlgorithms.includes(userConfig.hashAlg)) {
1757
+ throw new SDJWTException(
1758
+ `Disallowed hash algorithm: ${userConfig.hashAlg}`
1759
+ );
1760
+ }
1579
1761
  this.userConfig = userConfig;
1580
1762
  }
1581
1763
  }
@@ -1669,6 +1851,9 @@ var SDJwtGeneralJSONInstance = class {
1669
1851
  const hasher = this.userConfig.hasher;
1670
1852
  const encodedSDJwt = generalJSON.toEncoded(0);
1671
1853
  const sdjwt = yield SDJwt.fromEncode(encodedSDJwt, hasher);
1854
+ if (sdjwt.kbJwt) {
1855
+ throw new SDJWTException("Holder cannot present an SD-JWT with KB-JWT");
1856
+ }
1672
1857
  if (!((_a = sdjwt.jwt) == null ? void 0 : _a.payload)) throw new SDJWTException("Payload not found");
1673
1858
  const disclosures = yield sdjwt.getPresentDisclosures(
1674
1859
  presentationFrame,
@@ -1708,7 +1893,7 @@ var SDJwtGeneralJSONInstance = class {
1708
1893
  throw new SDJWTException("Hasher not found");
1709
1894
  }
1710
1895
  const hasher = this.userConfig.hasher;
1711
- const { payload, headers } = yield this.validate(generalJSON);
1896
+ const { payload, headers } = yield this.validate(generalJSON, options);
1712
1897
  const encodedSDJwt = generalJSON.toEncoded(0);
1713
1898
  const sdjwt = yield SDJwt.fromEncode(encodedSDJwt, hasher);
1714
1899
  if (!((_a = sdjwt.jwt) == null ? void 0 : _a.payload)) {
@@ -1774,7 +1959,7 @@ var SDJwtGeneralJSONInstance = class {
1774
1959
  }
1775
1960
  // This function is for validating the SD JWT
1776
1961
  // Just checking signature and return its the claims
1777
- validate(generalJSON) {
1962
+ validate(generalJSON, options) {
1778
1963
  return __async(this, null, function* () {
1779
1964
  if (!this.userConfig.hasher) {
1780
1965
  throw new SDJWTException("Hasher not found");
@@ -1790,9 +1975,21 @@ var SDJwtGeneralJSONInstance = class {
1790
1975
  const { protected: encodedHeader, signature } = s;
1791
1976
  const verified2 = yield verifier(
1792
1977
  `${encodedHeader}.${payload}`,
1793
- signature
1978
+ signature,
1979
+ __spreadProps(__spreadValues({}, options), { skipJwtClaimValidation: true })
1980
+ );
1981
+ const header = decodeBase64urlJsonStrict(
1982
+ encodedHeader,
1983
+ "Invalid JWT"
1794
1984
  );
1795
- const header = decodeBase64urlJsonStrict(encodedHeader, "Invalid JWT");
1985
+ if (typeof header.alg !== "string" || header.alg === "none") {
1986
+ throw new SDJWTException('Verify Error: alg "none" is not allowed');
1987
+ }
1988
+ if ((options == null ? void 0 : options.allowedIssuerAlgorithms) && !options.allowedIssuerAlgorithms.includes(header.alg)) {
1989
+ throw new SDJWTException(
1990
+ `Verify Error: Disallowed alg ${header.alg}`
1991
+ );
1992
+ }
1796
1993
  return { verified: verified2, header };
1797
1994
  }))
1798
1995
  );
@@ -1807,6 +2004,7 @@ var SDJwtGeneralJSONInstance = class {
1807
2004
  }
1808
2005
  const claims = yield sdjwt.getClaims(hasher);
1809
2006
  validateReservedFieldsInternal(claims);
2007
+ validateJwtPayload(claims, options);
1810
2008
  return { payload: claims, headers: results.map((r) => r.header) };
1811
2009
  });
1812
2010
  }
@@ -1853,6 +2051,7 @@ var SDJwtGeneralJSONInstance = class {
1853
2051
  SDJwtGeneralJSONInstance.DEFAULT_hashAlg = "sha-256";
1854
2052
  // Annotate the CommonJS export names for ESM import in node:
1855
2053
  0 && (module.exports = {
2054
+ DEFAULT_SECURE_HASH_ALGORITHMS,
1856
2055
  Disclosure,
1857
2056
  FlattenJSON,
1858
2057
  GeneralJSON,
@@ -1878,6 +2077,8 @@ SDJwtGeneralJSONInstance.DEFAULT_hashAlg = "sha-256";
1878
2077
  decodeJwt,
1879
2078
  decodeSdJwt,
1880
2079
  decodeSdJwtSync,
2080
+ encodePath,
2081
+ encodePathSegment,
1881
2082
  ensureError,
1882
2083
  getClaims,
1883
2084
  getClaimsSync,
@@ -1894,5 +2095,6 @@ SDJwtGeneralJSONInstance.DEFAULT_hashAlg = "sha-256";
1894
2095
  uint8ArrayToBase64Url,
1895
2096
  unpack,
1896
2097
  unpackObj,
1897
- unpackSync
2098
+ unpackSync,
2099
+ validateJwtPayload
1898
2100
  });