@learncard/sss-key-manager 0.2.1 → 0.2.3

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.
Files changed (46) hide show
  1. package/dist/escrow-attestation.d.ts +2 -9
  2. package/dist/escrow-attestation.d.ts.map +1 -1
  3. package/dist/escrow-authority.d.ts +138 -0
  4. package/dist/escrow-authority.d.ts.map +1 -0
  5. package/dist/escrow-authority.types.d.ts +51 -0
  6. package/dist/escrow-authority.types.d.ts.map +1 -0
  7. package/dist/escrow-crypto.d.ts +9 -1
  8. package/dist/escrow-crypto.d.ts.map +1 -1
  9. package/dist/escrow-enrollment-intent.d.ts +13 -0
  10. package/dist/escrow-enrollment-intent.d.ts.map +1 -0
  11. package/dist/escrow-enrollment-intent.types.d.ts +28 -0
  12. package/dist/escrow-enrollment-intent.types.d.ts.map +1 -0
  13. package/dist/escrow-nitro-attestation.d.ts +2 -0
  14. package/dist/escrow-nitro-attestation.d.ts.map +1 -1
  15. package/dist/index.d.ts +8 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/sss-key-manager.cjs.development.js +1652 -214
  18. package/dist/sss-key-manager.cjs.development.js.map +4 -4
  19. package/dist/sss-key-manager.cjs.production.min.js +28 -23
  20. package/dist/sss-key-manager.cjs.production.min.js.map +4 -4
  21. package/dist/sss-key-manager.esm.js +1167 -164
  22. package/dist/sss-key-manager.esm.js.map +4 -4
  23. package/dist/sss-strategy.d.ts +2 -1
  24. package/dist/sss-strategy.d.ts.map +1 -1
  25. package/dist/types.d.ts +21 -1
  26. package/dist/types.d.ts.map +1 -1
  27. package/package.json +2 -2
  28. package/src/__fixtures__/escrow-authority-v1.json +96 -0
  29. package/src/__fixtures__/escrow-carry-contact-contract-v1.json +33 -0
  30. package/src/__fixtures__/escrow-enrollment-intent-v1.json +26 -0
  31. package/src/__fixtures__/nitro-attestation-real/staging-nsm-document.json +16 -0
  32. package/src/escrow-attestation.ts +10 -9
  33. package/src/escrow-authority.test.ts +757 -0
  34. package/src/escrow-authority.ts +671 -0
  35. package/src/escrow-authority.types.ts +56 -0
  36. package/src/escrow-crypto.ts +39 -1
  37. package/src/escrow-enrollment-intent.test.ts +224 -0
  38. package/src/escrow-enrollment-intent.ts +236 -0
  39. package/src/escrow-enrollment-intent.types.ts +28 -0
  40. package/src/escrow-nitro-attestation.test.ts +72 -0
  41. package/src/escrow-nitro-attestation.ts +34 -3
  42. package/src/index.ts +29 -0
  43. package/src/sss-authority.test.ts +889 -0
  44. package/src/sss-strategy.test.ts +32 -0
  45. package/src/sss-strategy.ts +503 -39
  46. package/src/types.ts +27 -3
@@ -199,6 +199,7 @@ var require_types_cjs_development = __commonJS({
199
199
  FlatAuthGrantValidator: /* @__PURE__ */ __name(() => FlatAuthGrantValidator, "FlatAuthGrantValidator"),
200
200
  FrameworkWithSkillsValidator: /* @__PURE__ */ __name(() => FrameworkWithSkillsValidator, "FrameworkWithSkillsValidator"),
201
201
  FullClaimHookValidator: /* @__PURE__ */ __name(() => FullClaimHookValidator, "FullClaimHookValidator"),
202
+ GenerateSkillIconsInputValidator: /* @__PURE__ */ __name(() => GenerateSkillIconsInputValidator, "GenerateSkillIconsInputValidator"),
202
203
  GeoCoordinatesValidator: /* @__PURE__ */ __name(() => GeoCoordinatesValidator, "GeoCoordinatesValidator"),
203
204
  GetCounterEventValidator: /* @__PURE__ */ __name(() => GetCounterEventValidator, "GetCounterEventValidator"),
204
205
  GetCountersEventValidator: /* @__PURE__ */ __name(() => GetCountersEventValidator, "GetCountersEventValidator"),
@@ -359,6 +360,8 @@ var require_types_cjs_development = __commonJS({
359
360
  SHARE_RECOVERY_PROTOCOL: /* @__PURE__ */ __name(() => SHARE_RECOVERY_PROTOCOL, "SHARE_RECOVERY_PROTOCOL"),
360
361
  SHARE_TAG_BYTES: /* @__PURE__ */ __name(() => SHARE_TAG_BYTES, "SHARE_TAG_BYTES"),
361
362
  SHARE_VIEW_RECEIPT_TTL_SECONDS: /* @__PURE__ */ __name(() => SHARE_VIEW_RECEIPT_TTL_SECONDS, "SHARE_VIEW_RECEIPT_TTL_SECONDS"),
363
+ SKILL_ICON_MAX_CHARACTERS_PER_REQUEST: /* @__PURE__ */ __name(() => SKILL_ICON_MAX_CHARACTERS_PER_REQUEST, "SKILL_ICON_MAX_CHARACTERS_PER_REQUEST"),
364
+ SKILL_ICON_MAX_NAMES_PER_REQUEST: /* @__PURE__ */ __name(() => SKILL_ICON_MAX_NAMES_PER_REQUEST, "SKILL_ICON_MAX_NAMES_PER_REQUEST"),
362
365
  SendAiSessionCredentialEventValidator: /* @__PURE__ */ __name(() => SendAiSessionCredentialEventValidator, "SendAiSessionCredentialEventValidator"),
363
366
  SendBoostInputValidator: /* @__PURE__ */ __name(() => SendBoostInputValidator, "SendBoostInputValidator"),
364
367
  SendBoostResponseValidator: /* @__PURE__ */ __name(() => SendBoostResponseValidator, "SendBoostResponseValidator"),
@@ -411,6 +414,7 @@ var require_types_cjs_development = __commonJS({
411
414
  SkillFrameworkQueryValidator: /* @__PURE__ */ __name(() => SkillFrameworkQueryValidator, "SkillFrameworkQueryValidator"),
412
415
  SkillFrameworkStatusEnum: /* @__PURE__ */ __name(() => SkillFrameworkStatusEnum, "SkillFrameworkStatusEnum"),
413
416
  SkillFrameworkValidator: /* @__PURE__ */ __name(() => SkillFrameworkValidator, "SkillFrameworkValidator"),
417
+ SkillIconNamesValidator: /* @__PURE__ */ __name(() => SkillIconNamesValidator, "SkillIconNamesValidator"),
414
418
  SkillQueryValidator: /* @__PURE__ */ __name(() => SkillQueryValidator, "SkillQueryValidator"),
415
419
  SkillSemanticSearchInputValidator: /* @__PURE__ */ __name(() => SkillSemanticSearchInputValidator, "SkillSemanticSearchInputValidator"),
416
420
  SkillSemanticSearchResultItemValidator: /* @__PURE__ */ __name(() => SkillSemanticSearchResultItemValidator, "SkillSemanticSearchResultItemValidator"),
@@ -568,7 +572,7 @@ var require_types_cjs_development = __commonJS({
568
572
  _function: /* @__PURE__ */ __name(() => _function, "_function"),
569
573
  any: /* @__PURE__ */ __name(() => any, "any"),
570
574
  array: /* @__PURE__ */ __name(() => array, "array"),
571
- base64: /* @__PURE__ */ __name(() => base642, "base64"),
575
+ base64: /* @__PURE__ */ __name(() => base6422, "base64"),
572
576
  base64url: /* @__PURE__ */ __name(() => base64url2, "base64url"),
573
577
  bigint: /* @__PURE__ */ __name(() => bigint2, "bigint"),
574
578
  boolean: /* @__PURE__ */ __name(() => boolean2, "boolean"),
@@ -609,7 +613,7 @@ var require_types_cjs_development = __commonJS({
609
613
  gt: /* @__PURE__ */ __name(() => _gt, "gt"),
610
614
  gte: /* @__PURE__ */ __name(() => _gte, "gte"),
611
615
  guid: /* @__PURE__ */ __name(() => guid2, "guid"),
612
- hash: /* @__PURE__ */ __name(() => hash, "hash"),
616
+ hash: /* @__PURE__ */ __name(() => hash2, "hash"),
613
617
  hex: /* @__PURE__ */ __name(() => hex22, "hex"),
614
618
  hostname: /* @__PURE__ */ __name(() => hostname2, "hostname"),
615
619
  httpUrl: /* @__PURE__ */ __name(() => httpUrl, "httpUrl"),
@@ -658,7 +662,7 @@ var require_types_cjs_development = __commonJS({
658
662
  nullable: /* @__PURE__ */ __name(() => nullable, "nullable"),
659
663
  nullish: /* @__PURE__ */ __name(() => nullish2, "nullish"),
660
664
  number: /* @__PURE__ */ __name(() => number2, "number"),
661
- object: /* @__PURE__ */ __name(() => object, "object"),
665
+ object: /* @__PURE__ */ __name(() => object2, "object"),
662
666
  optional: /* @__PURE__ */ __name(() => optional, "optional"),
663
667
  overwrite: /* @__PURE__ */ __name(() => _overwrite, "overwrite"),
664
668
  parse: /* @__PURE__ */ __name(() => parse2, "parse"),
@@ -1020,9 +1024,9 @@ var require_types_cjs_development = __commonJS({
1020
1024
  inst._zod.traits.add(name);
1021
1025
  initializer3(inst, def);
1022
1026
  const proto = _.prototype;
1023
- const keys = Object.keys(proto);
1024
- for (let i = 0; i < keys.length; i++) {
1025
- const k = keys[i];
1027
+ const keys2 = Object.keys(proto);
1028
+ for (let i = 0; i < keys2.length; i++) {
1029
+ const k = keys2[i];
1026
1030
  if (!(k in inst)) {
1027
1031
  inst[k] = proto[k].bind(inst);
1028
1032
  }
@@ -1244,9 +1248,9 @@ var require_types_cjs_development = __commonJS({
1244
1248
  __name(floatSafeRemainder, "floatSafeRemainder");
1245
1249
  __name2(floatSafeRemainder, "floatSafeRemainder");
1246
1250
  var EVALUATING = /* @__PURE__ */ Symbol("evaluating");
1247
- function defineLazy(object2, key, getter) {
1251
+ function defineLazy(object22, key, getter) {
1248
1252
  let value = void 0;
1249
- Object.defineProperty(object2, key, {
1253
+ Object.defineProperty(object22, key, {
1250
1254
  get() {
1251
1255
  if (value === EVALUATING) {
1252
1256
  return void 0;
@@ -1258,7 +1262,7 @@ var require_types_cjs_development = __commonJS({
1258
1262
  return value;
1259
1263
  },
1260
1264
  set(v) {
1261
- Object.defineProperty(object2, key, {
1265
+ Object.defineProperty(object22, key, {
1262
1266
  value: v
1263
1267
  // configurable: true,
1264
1268
  });
@@ -1306,12 +1310,12 @@ var require_types_cjs_development = __commonJS({
1306
1310
  __name(getElementAtPath, "getElementAtPath");
1307
1311
  __name2(getElementAtPath, "getElementAtPath");
1308
1312
  function promiseAllObject(promisesObj) {
1309
- const keys = Object.keys(promisesObj);
1310
- const promises = keys.map((key) => promisesObj[key]);
1313
+ const keys2 = Object.keys(promisesObj);
1314
+ const promises = keys2.map((key) => promisesObj[key]);
1311
1315
  return Promise.all(promises).then((results) => {
1312
1316
  const resolvedObj = {};
1313
- for (let i = 0; i < keys.length; i++) {
1314
- resolvedObj[keys[i]] = results[i];
1317
+ for (let i = 0; i < keys2.length; i++) {
1318
+ resolvedObj[keys2[i]] = results[i];
1315
1319
  }
1316
1320
  return resolvedObj;
1317
1321
  });
@@ -1851,18 +1855,18 @@ var require_types_cjs_development = __commonJS({
1851
1855
  __name2(cleanEnum, "cleanEnum");
1852
1856
  function base64ToUint8Array(base643) {
1853
1857
  const binaryString = atob(base643);
1854
- const bytes2 = new Uint8Array(binaryString.length);
1858
+ const bytes3 = new Uint8Array(binaryString.length);
1855
1859
  for (let i = 0; i < binaryString.length; i++) {
1856
- bytes2[i] = binaryString.charCodeAt(i);
1860
+ bytes3[i] = binaryString.charCodeAt(i);
1857
1861
  }
1858
- return bytes2;
1862
+ return bytes3;
1859
1863
  }
1860
1864
  __name(base64ToUint8Array, "base64ToUint8Array");
1861
1865
  __name2(base64ToUint8Array, "base64ToUint8Array");
1862
- function uint8ArrayToBase64(bytes2) {
1866
+ function uint8ArrayToBase64(bytes3) {
1863
1867
  let binaryString = "";
1864
- for (let i = 0; i < bytes2.length; i++) {
1865
- binaryString += String.fromCharCode(bytes2[i]);
1868
+ for (let i = 0; i < bytes3.length; i++) {
1869
+ binaryString += String.fromCharCode(bytes3[i]);
1866
1870
  }
1867
1871
  return btoa(binaryString);
1868
1872
  }
@@ -1875,8 +1879,8 @@ var require_types_cjs_development = __commonJS({
1875
1879
  }
1876
1880
  __name(base64urlToUint8Array, "base64urlToUint8Array");
1877
1881
  __name2(base64urlToUint8Array, "base64urlToUint8Array");
1878
- function uint8ArrayToBase64url(bytes2) {
1879
- return uint8ArrayToBase64(bytes2).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
1882
+ function uint8ArrayToBase64url(bytes3) {
1883
+ return uint8ArrayToBase64(bytes3).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
1880
1884
  }
1881
1885
  __name(uint8ArrayToBase64url, "uint8ArrayToBase64url");
1882
1886
  __name2(uint8ArrayToBase64url, "uint8ArrayToBase64url");
@@ -1885,16 +1889,16 @@ var require_types_cjs_development = __commonJS({
1885
1889
  if (cleanHex.length % 2 !== 0) {
1886
1890
  throw new Error("Invalid hex string length");
1887
1891
  }
1888
- const bytes2 = new Uint8Array(cleanHex.length / 2);
1892
+ const bytes3 = new Uint8Array(cleanHex.length / 2);
1889
1893
  for (let i = 0; i < cleanHex.length; i += 2) {
1890
- bytes2[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
1894
+ bytes3[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
1891
1895
  }
1892
- return bytes2;
1896
+ return bytes3;
1893
1897
  }
1894
1898
  __name(hexToUint8Array, "hexToUint8Array");
1895
1899
  __name2(hexToUint8Array, "hexToUint8Array");
1896
- function uint8ArrayToHex(bytes2) {
1897
- return Array.from(bytes2).map((b) => b.toString(16).padStart(2, "0")).join("");
1900
+ function uint8ArrayToHex(bytes3) {
1901
+ return Array.from(bytes3).map((b) => b.toString(16).padStart(2, "0")).join("");
1898
1902
  }
1899
1903
  __name(uint8ArrayToHex, "uint8ArrayToHex");
1900
1904
  __name2(uint8ArrayToHex, "uint8ArrayToHex");
@@ -2143,7 +2147,7 @@ var require_types_cjs_development = __commonJS({
2143
2147
  var safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync($ZodRealError);
2144
2148
  var regexes_exports = {};
2145
2149
  __export(regexes_exports, {
2146
- base64: /* @__PURE__ */ __name(() => base64, "base64"),
2150
+ base64: /* @__PURE__ */ __name(() => base642, "base64"),
2147
2151
  base64url: /* @__PURE__ */ __name(() => base64url, "base64url"),
2148
2152
  bigint: /* @__PURE__ */ __name(() => bigint, "bigint"),
2149
2153
  boolean: /* @__PURE__ */ __name(() => boolean, "boolean"),
@@ -2166,7 +2170,7 @@ var require_types_cjs_development = __commonJS({
2166
2170
  html5Email: /* @__PURE__ */ __name(() => html5Email, "html5Email"),
2167
2171
  httpProtocol: /* @__PURE__ */ __name(() => httpProtocol, "httpProtocol"),
2168
2172
  idnEmail: /* @__PURE__ */ __name(() => idnEmail, "idnEmail"),
2169
- integer: /* @__PURE__ */ __name(() => integer, "integer"),
2173
+ integer: /* @__PURE__ */ __name(() => integer3, "integer"),
2170
2174
  ipv4: /* @__PURE__ */ __name(() => ipv4, "ipv4"),
2171
2175
  ipv6: /* @__PURE__ */ __name(() => ipv6, "ipv6"),
2172
2176
  ksuid: /* @__PURE__ */ __name(() => ksuid, "ksuid"),
@@ -2240,7 +2244,7 @@ var require_types_cjs_development = __commonJS({
2240
2244
  }, "mac");
2241
2245
  var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
2242
2246
  var cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
2243
- var base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
2247
+ var base642 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
2244
2248
  var base64url = /^[A-Za-z0-9_-]*$/;
2245
2249
  var hostname = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
2246
2250
  var domain = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
@@ -2277,7 +2281,7 @@ var require_types_cjs_development = __commonJS({
2277
2281
  return new RegExp(`^${regex}$`);
2278
2282
  }, "string");
2279
2283
  var bigint = /^-?\d+n?$/;
2280
- var integer = /^-?\d+$/;
2284
+ var integer3 = /^-?\d+$/;
2281
2285
  var number = /^-?\d+(?:\.\d+)?$/;
2282
2286
  var boolean = /^(?:true|false)$/i;
2283
2287
  var _null = /^null$/i;
@@ -2411,7 +2415,7 @@ var require_types_cjs_development = __commonJS({
2411
2415
  bag.minimum = minimum;
2412
2416
  bag.maximum = maximum;
2413
2417
  if (isInt)
2414
- bag.pattern = integer;
2418
+ bag.pattern = integer3;
2415
2419
  });
2416
2420
  inst._zod.check = (payload) => {
2417
2421
  const input = payload.value;
@@ -3251,7 +3255,7 @@ var require_types_cjs_development = __commonJS({
3251
3255
  __name(isValidBase64, "isValidBase64");
3252
3256
  __name2(isValidBase64, "isValidBase64");
3253
3257
  var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => {
3254
- def.pattern ?? (def.pattern = base64);
3258
+ def.pattern ?? (def.pattern = base642);
3255
3259
  $ZodStringFormat.init(inst, def);
3256
3260
  inst._zod.bag.contentEncoding = "base64";
3257
3261
  inst._zod.check = (payload) => {
@@ -3597,8 +3601,8 @@ var require_types_cjs_development = __commonJS({
3597
3601
  __name(handlePropertyResult, "handlePropertyResult");
3598
3602
  __name2(handlePropertyResult, "handlePropertyResult");
3599
3603
  function normalizeDef(def) {
3600
- const keys = Object.keys(def.shape);
3601
- for (const k of keys) {
3604
+ const keys2 = Object.keys(def.shape);
3605
+ for (const k of keys2) {
3602
3606
  if (!def.shape?.[k]?._zod?.traits?.has("$ZodType")) {
3603
3607
  throw new Error(`Invalid element at key "${k}": expected a Zod schema`);
3604
3608
  }
@@ -3606,9 +3610,9 @@ var require_types_cjs_development = __commonJS({
3606
3610
  const okeys = optionalKeys(def.shape);
3607
3611
  return {
3608
3612
  ...def,
3609
- keys,
3610
- keySet: new Set(keys),
3611
- numKeys: keys.length,
3613
+ keys: keys2,
3614
+ keySet: new Set(keys2),
3615
+ numKeys: keys2.length,
3612
3616
  optionalKeys: new Set(okeys)
3613
3617
  };
3614
3618
  }
@@ -8370,8 +8374,8 @@ var require_types_cjs_development = __commonJS({
8370
8374
  }
8371
8375
  __name(ko_default, "ko_default");
8372
8376
  __name2(ko_default, "default");
8373
- var capitalizeFirstCharacter = /* @__PURE__ */ __name2((text) => {
8374
- return text.charAt(0).toUpperCase() + text.slice(1);
8377
+ var capitalizeFirstCharacter = /* @__PURE__ */ __name2((text2) => {
8378
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
8375
8379
  }, "capitalizeFirstCharacter");
8376
8380
  function getUnitTypeFromNumber(number4) {
8377
8381
  const abs = Math.abs(number4);
@@ -13491,7 +13495,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13491
13495
  _function: /* @__PURE__ */ __name(() => _function, "_function"),
13492
13496
  any: /* @__PURE__ */ __name(() => any, "any"),
13493
13497
  array: /* @__PURE__ */ __name(() => array, "array"),
13494
- base64: /* @__PURE__ */ __name(() => base642, "base64"),
13498
+ base64: /* @__PURE__ */ __name(() => base6422, "base64"),
13495
13499
  base64url: /* @__PURE__ */ __name(() => base64url2, "base64url"),
13496
13500
  bigint: /* @__PURE__ */ __name(() => bigint2, "bigint"),
13497
13501
  boolean: /* @__PURE__ */ __name(() => boolean2, "boolean"),
@@ -13516,7 +13520,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13516
13520
  float64: /* @__PURE__ */ __name(() => float64, "float64"),
13517
13521
  function: /* @__PURE__ */ __name(() => _function, "function"),
13518
13522
  guid: /* @__PURE__ */ __name(() => guid2, "guid"),
13519
- hash: /* @__PURE__ */ __name(() => hash, "hash"),
13523
+ hash: /* @__PURE__ */ __name(() => hash2, "hash"),
13520
13524
  hex: /* @__PURE__ */ __name(() => hex22, "hex"),
13521
13525
  hostname: /* @__PURE__ */ __name(() => hostname2, "hostname"),
13522
13526
  httpUrl: /* @__PURE__ */ __name(() => httpUrl, "httpUrl"),
@@ -13548,7 +13552,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13548
13552
  nullable: /* @__PURE__ */ __name(() => nullable, "nullable"),
13549
13553
  nullish: /* @__PURE__ */ __name(() => nullish2, "nullish"),
13550
13554
  number: /* @__PURE__ */ __name(() => number2, "number"),
13551
- object: /* @__PURE__ */ __name(() => object, "object"),
13555
+ object: /* @__PURE__ */ __name(() => object2, "object"),
13552
13556
  optional: /* @__PURE__ */ __name(() => optional, "optional"),
13553
13557
  partialRecord: /* @__PURE__ */ __name(() => partialRecord, "partialRecord"),
13554
13558
  pipe: /* @__PURE__ */ __name(() => pipe, "pipe"),
@@ -14149,11 +14153,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14149
14153
  $ZodBase64.init(inst, def);
14150
14154
  ZodStringFormat.init(inst, def);
14151
14155
  });
14152
- function base642(params) {
14156
+ function base6422(params) {
14153
14157
  return /* @__PURE__ */ _base64(ZodBase64, params);
14154
14158
  }
14155
- __name(base642, "base642");
14156
- __name2(base642, "base64");
14159
+ __name(base6422, "base642");
14160
+ __name2(base6422, "base64");
14157
14161
  var ZodBase64URL = /* @__PURE__ */ $constructor("ZodBase64URL", (inst, def) => {
14158
14162
  $ZodBase64URL.init(inst, def);
14159
14163
  ZodStringFormat.init(inst, def);
@@ -14200,7 +14204,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14200
14204
  }
14201
14205
  __name(hex22, "hex2");
14202
14206
  __name2(hex22, "hex");
14203
- function hash(alg, params) {
14207
+ function hash2(alg, params) {
14204
14208
  const enc = params?.enc ?? "hex";
14205
14209
  const format = `${alg}_${enc}`;
14206
14210
  const regex = regexes_exports[format];
@@ -14208,8 +14212,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14208
14212
  throw new Error(`Unrecognized hash format: ${format}`);
14209
14213
  return /* @__PURE__ */ _stringFormat(ZodCustomStringFormat, format, regex, params);
14210
14214
  }
14211
- __name(hash, "hash");
14212
- __name2(hash, "hash");
14215
+ __name(hash2, "hash");
14216
+ __name2(hash2, "hash");
14213
14217
  var ZodNumber = /* @__PURE__ */ $constructor("ZodNumber", (inst, def) => {
14214
14218
  $ZodNumber.init(inst, def);
14215
14219
  ZodType.init(inst, def);
@@ -14521,7 +14525,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14521
14525
  }
14522
14526
  });
14523
14527
  });
14524
- function object(shape, params) {
14528
+ function object2(shape, params) {
14525
14529
  const def = {
14526
14530
  type: "object",
14527
14531
  shape: shape ?? {},
@@ -14529,8 +14533,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14529
14533
  };
14530
14534
  return new ZodObject(def);
14531
14535
  }
14532
- __name(object, "object");
14533
- __name2(object, "object");
14536
+ __name(object2, "object");
14537
+ __name2(object2, "object");
14534
14538
  function strictObject(shape, params) {
14535
14539
  return new ZodObject({
14536
14540
  type: "object",
@@ -14725,11 +14729,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14725
14729
  inst._zod.processJSONSchema = (ctx, json2, params) => enumProcessor(inst, ctx, json2, params);
14726
14730
  inst.enum = def.entries;
14727
14731
  inst.options = Object.values(def.entries);
14728
- const keys = new Set(Object.keys(def.entries));
14732
+ const keys2 = new Set(Object.keys(def.entries));
14729
14733
  inst.extract = (values, params) => {
14730
14734
  const newEntries = {};
14731
14735
  for (const value of values) {
14732
- if (keys.has(value)) {
14736
+ if (keys2.has(value)) {
14733
14737
  newEntries[value] = def.entries[value];
14734
14738
  } else
14735
14739
  throw new Error(`Key ${value} not found in enum`);
@@ -14744,7 +14748,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14744
14748
  inst.exclude = (values, params) => {
14745
14749
  const newEntries = { ...def.entries };
14746
14750
  for (const value of values) {
14747
- if (keys.has(value)) {
14751
+ if (keys2.has(value)) {
14748
14752
  delete newEntries[value];
14749
14753
  } else
14750
14754
  throw new Error(`Key ${value} not found in enum`);
@@ -15880,7 +15884,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15880
15884
  verificationMethod: external_exports.string(),
15881
15885
  jws: external_exports.string().optional()
15882
15886
  }).catchall(external_exports.any()).superRefine((proof, ctx) => {
15883
- if (proof.created === void 0 && (proof.type !== "DataIntegrityProof" || proof.cryptosuite !== "ecdsa-rdfc-2019")) {
15887
+ if (proof.created === void 0 && // These standardized Data Integrity suites make created optional.
15888
+ // Keep the prior requirement for legacy and unknown proof suites.
15889
+ // https://www.w3.org/TR/vc-di-eddsa/#proof-configuration-eddsa-jcs-2022
15890
+ // https://www.w3.org/TR/vc-di-bbs/#proof-configuration
15891
+ (proof.type !== "DataIntegrityProof" || ![
15892
+ "ecdsa-rdfc-2019",
15893
+ "ecdsa-jcs-2019",
15894
+ "ecdsa-sd-2023",
15895
+ "eddsa-rdfc-2022",
15896
+ "eddsa-jcs-2022",
15897
+ "bbs-2023"
15898
+ ].includes(proof.cryptosuite))) {
15884
15899
  ctx.addIssue({
15885
15900
  code: "custom",
15886
15901
  path: ["created"],
@@ -16137,6 +16152,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16137
16152
  term: external_exports.string().optional()
16138
16153
  }).catchall(external_exports.any());
16139
16154
  var UnsignedAchievementCredentialValidator = UnsignedVCValidator.extend({
16155
+ awardedDate: external_exports.string().optional(),
16140
16156
  name: external_exports.string().optional(),
16141
16157
  description: external_exports.string().optional(),
16142
16158
  image: ImageValidator.optional(),
@@ -18519,12 +18535,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18519
18535
  }
18520
18536
  return table;
18521
18537
  })();
18522
- var encodeBase64Url = /* @__PURE__ */ __name2((bytes2) => {
18538
+ var encodeBase64Url = /* @__PURE__ */ __name2((bytes3) => {
18523
18539
  let output = "";
18524
- for (let index = 0; index < bytes2.length; index += 3) {
18525
- const first = bytes2[index];
18526
- const second = bytes2[index + 1];
18527
- const third = bytes2[index + 2];
18540
+ for (let index = 0; index < bytes3.length; index += 3) {
18541
+ const first = bytes3[index];
18542
+ const second = bytes3[index + 1];
18543
+ const third = bytes3[index + 2];
18528
18544
  output += BASE64URL_ALPHABET[first >> 2];
18529
18545
  output += BASE64URL_ALPHABET[(first & 3) << 4 | (second ?? 0) >> 4];
18530
18546
  if (second === void 0) break;
@@ -18565,8 +18581,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18565
18581
  }, "isCanonicalBase64Url");
18566
18582
  var decodedBase64UrlByteLength = /* @__PURE__ */ __name2((value) => decodeBase64Url(value)?.length ?? null, "decodedBase64UrlByteLength");
18567
18583
  var utf8ByteLength = /* @__PURE__ */ __name2((value) => new TextEncoder().encode(value).length, "utf8ByteLength");
18568
- var canonicalBase64Url = /* @__PURE__ */ __name2((bytes2) => external_exports.string().refine((value) => isCanonicalBase64Url(value, bytes2), {
18569
- message: `must be canonical base64url for exactly ${bytes2} bytes`
18584
+ var canonicalBase64Url = /* @__PURE__ */ __name2((bytes3) => external_exports.string().refine((value) => isCanonicalBase64Url(value, bytes3), {
18585
+ message: `must be canonical base64url for exactly ${bytes3} bytes`
18570
18586
  }), "canonicalBase64Url");
18571
18587
  var ShareLinkIdValidator = canonicalBase64Url(SHARE_LINK_ID_BYTES);
18572
18588
  var ShareContentKeyValidator = canonicalBase64Url(SHARE_CONTENT_KEY_BYTES);
@@ -18993,6 +19009,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18993
19009
  contentUrl: external_exports.string(),
18994
19010
  receipt: external_exports.string().min(22).max(128).regex(/^[A-Za-z0-9_-]+$/)
18995
19011
  }).strict();
19012
+ var SKILL_ICON_MAX_NAMES_PER_REQUEST = 100;
19013
+ var SKILL_ICON_MAX_CHARACTERS_PER_REQUEST = 2500;
19014
+ var SkillIconNamesValidator = external_exports.array(external_exports.string().nonempty()).min(1).max(SKILL_ICON_MAX_NAMES_PER_REQUEST, {
19015
+ message: `A maximum of ${SKILL_ICON_MAX_NAMES_PER_REQUEST} skill names can be sent per request`
19016
+ }).refine(
19017
+ (names) => names.reduce((total, name) => total + name.length, 0) <= SKILL_ICON_MAX_CHARACTERS_PER_REQUEST,
19018
+ {
19019
+ message: `Skill names may total at most ${SKILL_ICON_MAX_CHARACTERS_PER_REQUEST} characters per request`
19020
+ }
19021
+ );
19022
+ var GenerateSkillIconsInputValidator = external_exports.object({ names: SkillIconNamesValidator });
18996
19023
  }
18997
19024
  });
18998
19025
 
@@ -19165,37 +19192,37 @@ var DEFAULT_KDF_PARAMS = {
19165
19192
  parallelism: ARGON2_PARALLELISM
19166
19193
  };
19167
19194
  function bufferToBase64(buf) {
19168
- const bytes2 = new Uint8Array(buf);
19195
+ const bytes3 = new Uint8Array(buf);
19169
19196
  let binary = "";
19170
- for (let i = 0; i < bytes2.byteLength; i++) {
19171
- binary += String.fromCharCode(bytes2[i]);
19197
+ for (let i = 0; i < bytes3.byteLength; i++) {
19198
+ binary += String.fromCharCode(bytes3[i]);
19172
19199
  }
19173
19200
  return btoa(binary);
19174
19201
  }
19175
19202
  __name(bufferToBase64, "bufferToBase64");
19176
19203
  function base64ToBuffer(b64) {
19177
19204
  const binary = atob(b64);
19178
- const bytes2 = new Uint8Array(binary.length);
19205
+ const bytes3 = new Uint8Array(binary.length);
19179
19206
  for (let i = 0; i < binary.length; i++) {
19180
- bytes2[i] = binary.charCodeAt(i);
19207
+ bytes3[i] = binary.charCodeAt(i);
19181
19208
  }
19182
- return bytes2;
19209
+ return bytes3;
19183
19210
  }
19184
19211
  __name(base64ToBuffer, "base64ToBuffer");
19185
19212
  function hexToBytes(hex2) {
19186
- const bytes2 = new Uint8Array(hex2.length / 2);
19213
+ const bytes3 = new Uint8Array(hex2.length / 2);
19187
19214
  for (let i = 0; i < hex2.length; i += 2) {
19188
- bytes2[i / 2] = parseInt(hex2.slice(i, i + 2), 16);
19215
+ bytes3[i / 2] = parseInt(hex2.slice(i, i + 2), 16);
19189
19216
  }
19190
- return bytes2;
19217
+ return bytes3;
19191
19218
  }
19192
19219
  __name(hexToBytes, "hexToBytes");
19193
- function bytesToHex(bytes2) {
19194
- return Array.from(bytes2).map((b) => b.toString(16).padStart(2, "0")).join("");
19220
+ function bytesToHex(bytes3) {
19221
+ return Array.from(bytes3).map((b) => b.toString(16).padStart(2, "0")).join("");
19195
19222
  }
19196
19223
  __name(bytesToHex, "bytesToHex");
19197
19224
  async function deriveKeyFromPassword(password, salt, params = DEFAULT_KDF_PARAMS) {
19198
- const hash = await argon2id({
19225
+ const hash2 = await argon2id({
19199
19226
  password,
19200
19227
  salt,
19201
19228
  iterations: params.timeCost,
@@ -19204,7 +19231,7 @@ async function deriveKeyFromPassword(password, salt, params = DEFAULT_KDF_PARAMS
19204
19231
  hashLength: ARGON2_HASH_LENGTH,
19205
19232
  outputType: "binary"
19206
19233
  });
19207
- return Uint8Array.from(hash);
19234
+ return Uint8Array.from(hash2);
19208
19235
  }
19209
19236
  __name(deriveKeyFromPassword, "deriveKeyFromPassword");
19210
19237
  async function encryptWithPassword(plaintext, password) {
@@ -19358,21 +19385,21 @@ async function getOrCreateMasterKey() {
19358
19385
  }
19359
19386
  __name(getOrCreateMasterKey, "getOrCreateMasterKey");
19360
19387
  function bufferToBase642(buf) {
19361
- const bytes2 = new Uint8Array(buf);
19388
+ const bytes3 = new Uint8Array(buf);
19362
19389
  let binary = "";
19363
- for (let i = 0; i < bytes2.byteLength; i++) {
19364
- binary += String.fromCharCode(bytes2[i]);
19390
+ for (let i = 0; i < bytes3.byteLength; i++) {
19391
+ binary += String.fromCharCode(bytes3[i]);
19365
19392
  }
19366
19393
  return btoa(binary);
19367
19394
  }
19368
19395
  __name(bufferToBase642, "bufferToBase64");
19369
19396
  function base64ToBuffer2(b64) {
19370
19397
  const binary = atob(b64);
19371
- const bytes2 = new Uint8Array(binary.length);
19398
+ const bytes3 = new Uint8Array(binary.length);
19372
19399
  for (let i = 0; i < binary.length; i++) {
19373
- bytes2[i] = binary.charCodeAt(i);
19400
+ bytes3[i] = binary.charCodeAt(i);
19374
19401
  }
19375
- return bytes2.buffer;
19402
+ return bytes3.buffer;
19376
19403
  }
19377
19404
  __name(base64ToBuffer2, "base64ToBuffer");
19378
19405
  async function encryptShare(share, id) {
@@ -19780,21 +19807,21 @@ async function getWordlist() {
19780
19807
  return wordlistPromise;
19781
19808
  }
19782
19809
  __name(getWordlist, "getWordlist");
19783
- function bytesToBits(bytes2) {
19784
- return Array.from(bytes2).map((b) => b.toString(2).padStart(8, "0")).join("");
19810
+ function bytesToBits(bytes3) {
19811
+ return Array.from(bytes3).map((b) => b.toString(2).padStart(8, "0")).join("");
19785
19812
  }
19786
19813
  __name(bytesToBits, "bytesToBits");
19787
19814
  function bitsToBytes(bits) {
19788
- const bytes2 = new Uint8Array(Math.ceil(bits.length / 8));
19789
- for (let i = 0; i < bytes2.length; i++) {
19790
- bytes2[i] = parseInt(bits.slice(i * 8, (i + 1) * 8).padEnd(8, "0"), 2);
19815
+ const bytes3 = new Uint8Array(Math.ceil(bits.length / 8));
19816
+ for (let i = 0; i < bytes3.length; i++) {
19817
+ bytes3[i] = parseInt(bits.slice(i * 8, (i + 1) * 8).padEnd(8, "0"), 2);
19791
19818
  }
19792
- return bytes2;
19819
+ return bytes3;
19793
19820
  }
19794
19821
  __name(bitsToBytes, "bitsToBytes");
19795
19822
  async function computeChecksum(data) {
19796
- const hash = await crypto.subtle.digest("SHA-256", data);
19797
- const hashBits = bytesToBits(new Uint8Array(hash));
19823
+ const hash2 = await crypto.subtle.digest("SHA-256", data);
19824
+ const hashBits = bytesToBits(new Uint8Array(hash2));
19798
19825
  const checksumLength = Math.floor(data.length / 4);
19799
19826
  return hashBits.slice(0, checksumLength);
19800
19827
  }
@@ -19827,12 +19854,12 @@ async function recoveryPhraseToShare(phrase) {
19827
19854
  bits += index.toString(2).padStart(11, "0");
19828
19855
  }
19829
19856
  let dataByteCount = 0;
19830
- for (let bytes2 = 1; bytes2 <= 64; bytes2++) {
19831
- const checksumBits2 = Math.floor(bytes2 / 4);
19832
- const totalBits = bytes2 * 8 + checksumBits2;
19857
+ for (let bytes3 = 1; bytes3 <= 64; bytes3++) {
19858
+ const checksumBits2 = Math.floor(bytes3 / 4);
19859
+ const totalBits = bytes3 * 8 + checksumBits2;
19833
19860
  const wordsNeeded = Math.ceil(totalBits / 11);
19834
19861
  if (wordsNeeded === words.length) {
19835
- dataByteCount = bytes2;
19862
+ dataByteCount = bytes3;
19836
19863
  } else if (wordsNeeded > words.length) {
19837
19864
  break;
19838
19865
  }
@@ -20126,9 +20153,9 @@ __name(createSSSKeyManager, "createSSSKeyManager");
20126
20153
  var EMAIL_RELAY_ENVELOPE_VERSION = 1;
20127
20154
  var EMAIL_RELAY_ALGORITHM = "P-256-HKDF-SHA256-AES-256-GCM";
20128
20155
  var encodeUtf8 = /* @__PURE__ */ __name((value) => {
20129
- const bytes2 = new TextEncoder().encode(value);
20130
- const copy = new Uint8Array(new ArrayBuffer(bytes2.byteLength));
20131
- copy.set(bytes2);
20156
+ const bytes3 = new TextEncoder().encode(value);
20157
+ const copy = new Uint8Array(new ArrayBuffer(bytes3.byteLength));
20158
+ copy.set(bytes3);
20132
20159
  return copy;
20133
20160
  }, "encodeUtf8");
20134
20161
  var EMAIL_RELAY_INFO = encodeUtf8("learncard-email-relay-v1");
@@ -20147,7 +20174,7 @@ var parseBranding = /* @__PURE__ */ __name((value) => {
20147
20174
  if (value === void 0) return void 0;
20148
20175
  if (!isRecord(value)) throw new Error("Invalid email relay branding");
20149
20176
  const branding = {};
20150
- const keys = [
20177
+ const keys2 = [
20151
20178
  "brandName",
20152
20179
  "logoUrl",
20153
20180
  "logoAlt",
@@ -20159,7 +20186,7 @@ var parseBranding = /* @__PURE__ */ __name((value) => {
20159
20186
  "fromDomain",
20160
20187
  "copyrightHolder"
20161
20188
  ];
20162
- for (const key of keys) {
20189
+ for (const key of keys2) {
20163
20190
  const field = value[key];
20164
20191
  if (field === void 0) continue;
20165
20192
  if (typeof field !== "string" || field.length > 2048) {
@@ -20212,7 +20239,7 @@ var parseEmailRelayPlaintext = /* @__PURE__ */ __name((value) => {
20212
20239
  branding: parseBranding(value.branding)
20213
20240
  };
20214
20241
  }, "parseEmailRelayPlaintext");
20215
- var getAad = /* @__PURE__ */ __name((envelope) => encodeUtf8(`${envelope.version}|${envelope.algorithm}|${envelope.keyId}`), "getAad");
20242
+ var getAad = /* @__PURE__ */ __name((envelope2) => encodeUtf8(`${envelope2.version}|${envelope2.algorithm}|${envelope2.keyId}`), "getAad");
20216
20243
  var deriveAesKey = /* @__PURE__ */ __name(async (sharedSecret, salt, usage) => {
20217
20244
  const keyMaterial = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, [
20218
20245
  "deriveKey"
@@ -20284,7 +20311,7 @@ var encryptEmailRelayPayload = /* @__PURE__ */ __name(async (input, publicKeyBas
20284
20311
  };
20285
20312
  }, "encryptEmailRelayPayload");
20286
20313
  var decryptEmailRelayPayload = /* @__PURE__ */ __name(async (value, privateKeyBase64) => {
20287
- const envelope = parseEmailRelayEnvelope(value);
20314
+ const envelope2 = parseEmailRelayEnvelope(value);
20288
20315
  const privateKey = await crypto.subtle.importKey(
20289
20316
  "pkcs8",
20290
20317
  base64ToBuffer(privateKeyBase64.trim()).buffer,
@@ -20294,7 +20321,7 @@ var decryptEmailRelayPayload = /* @__PURE__ */ __name(async (value, privateKeyBa
20294
20321
  );
20295
20322
  const ephemeralPublicKey = await crypto.subtle.importKey(
20296
20323
  "raw",
20297
- base64ToBuffer(envelope.ephemeralPublicKey).buffer,
20324
+ base64ToBuffer(envelope2.ephemeralPublicKey).buffer,
20298
20325
  { name: "ECDH", namedCurve: "P-256" },
20299
20326
  false,
20300
20327
  []
@@ -20304,13 +20331,13 @@ var decryptEmailRelayPayload = /* @__PURE__ */ __name(async (value, privateKeyBa
20304
20331
  privateKey,
20305
20332
  256
20306
20333
  );
20307
- const salt = base64ToBuffer(envelope.salt);
20308
- const iv = base64ToBuffer(envelope.iv);
20334
+ const salt = base64ToBuffer(envelope2.salt);
20335
+ const iv = base64ToBuffer(envelope2.iv);
20309
20336
  const aesKey = await deriveAesKey(sharedSecret, salt, "decrypt");
20310
20337
  const decrypted = await crypto.subtle.decrypt(
20311
- { name: "AES-GCM", iv, additionalData: getAad(envelope) },
20338
+ { name: "AES-GCM", iv, additionalData: getAad(envelope2) },
20312
20339
  aesKey,
20313
- base64ToBuffer(envelope.ciphertext)
20340
+ base64ToBuffer(envelope2.ciphertext)
20314
20341
  );
20315
20342
  const parsed = JSON.parse(new TextDecoder().decode(decrypted));
20316
20343
  return parseEmailRelayPlaintext(parsed);
@@ -20322,10 +20349,11 @@ var ESCROW_ALGORITHM = "P-256-HKDF-SHA256-AES-256-GCM";
20322
20349
  var ESCROW_BLOB_INFO = "learncard-escrow-blob-v1";
20323
20350
  var ESCROW_RELEASE_INFO = "learncard-escrow-release-v1";
20324
20351
  var ESCROW_CLIENT_KEY_ID = "client";
20352
+ var RECOVERY_LANGUAGES = ["en", "es", "fr", "ar"];
20325
20353
  var encodeUtf82 = /* @__PURE__ */ __name((value) => {
20326
- const bytes2 = new TextEncoder().encode(value);
20327
- const copy = new Uint8Array(new ArrayBuffer(bytes2.byteLength));
20328
- copy.set(bytes2);
20354
+ const bytes3 = new TextEncoder().encode(value);
20355
+ const copy = new Uint8Array(new ArrayBuffer(bytes3.byteLength));
20356
+ copy.set(bytes3);
20329
20357
  return copy;
20330
20358
  }, "encodeUtf8");
20331
20359
  var isRecord2 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
@@ -20361,6 +20389,22 @@ var parseEscrowBlobPlaintext = /* @__PURE__ */ __name((value) => {
20361
20389
  if (typeof value.shareVersion !== "number" || !Number.isInteger(value.shareVersion) || value.shareVersion <= 0) {
20362
20390
  throw new Error("Invalid escrow shareVersion");
20363
20391
  }
20392
+ if (value.recoveryEmail !== void 0 !== (value.recoveryLanguage !== void 0)) {
20393
+ throw new Error("Invalid escrow recoveryEmail");
20394
+ }
20395
+ let recoveryContact = {};
20396
+ if (value.recoveryEmail !== void 0) {
20397
+ const email = requireString2(value, "recoveryEmail", { min: 3, max: 254 }).trim().toLowerCase();
20398
+ const at = email.split("@");
20399
+ if (!/^[\x21-\x7e]+$/.test(email) || at.length !== 2 || at[0].length === 0 || at[1].length === 0 || !at[1].includes(".") || at[1].startsWith(".") || at[1].endsWith(".")) {
20400
+ throw new Error("Invalid escrow recoveryEmail");
20401
+ }
20402
+ const language = requireString2(value, "recoveryLanguage", { min: 2, max: 2 });
20403
+ if (!RECOVERY_LANGUAGES.includes(language)) {
20404
+ throw new Error("Invalid escrow recoveryLanguage");
20405
+ }
20406
+ recoveryContact = { recoveryEmail: email, recoveryLanguage: language };
20407
+ }
20364
20408
  return {
20365
20409
  version: ESCROW_ENVELOPE_VERSION,
20366
20410
  recoveryShare: requireString2(value, "recoveryShare", {
@@ -20376,7 +20420,8 @@ var parseEscrowBlobPlaintext = /* @__PURE__ */ __name((value) => {
20376
20420
  max: 64,
20377
20421
  pattern: /^[0-9a-f]{64}$/i
20378
20422
  }).toLowerCase()
20379
- } : {}
20423
+ } : {},
20424
+ ...recoveryContact
20380
20425
  };
20381
20426
  }, "parseEscrowBlobPlaintext");
20382
20427
  var parseEscrowReleasePlaintext = /* @__PURE__ */ __name((value) => {
@@ -20387,7 +20432,7 @@ var parseEscrowReleasePlaintext = /* @__PURE__ */ __name((value) => {
20387
20432
  holdId: requireString2(value, "holdId", { max: 128, pattern: /^[A-Za-z0-9._-]+$/ })
20388
20433
  };
20389
20434
  }, "parseEscrowReleasePlaintext");
20390
- var getAad2 = /* @__PURE__ */ __name((envelope) => encodeUtf82(`${envelope.version}|${envelope.algorithm}|${envelope.keyId}`), "getAad");
20435
+ var getAad2 = /* @__PURE__ */ __name((envelope2) => encodeUtf82(`${envelope2.version}|${envelope2.algorithm}|${envelope2.keyId}`), "getAad");
20391
20436
  var deriveAesKey2 = /* @__PURE__ */ __name(async (sharedSecret, salt, usage, info) => {
20392
20437
  const keyMaterial = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, [
20393
20438
  "deriveKey"
@@ -20458,7 +20503,7 @@ var encryptToRecipient = /* @__PURE__ */ __name(async (plaintextJson, spki, keyI
20458
20503
  };
20459
20504
  }, "encryptToRecipient");
20460
20505
  var decryptFromEnvelope = /* @__PURE__ */ __name(async (value, pkcs8, info) => {
20461
- const envelope = parseEscrowEnvelope(value);
20506
+ const envelope2 = parseEscrowEnvelope(value);
20462
20507
  const privateKey = await crypto.subtle.importKey(
20463
20508
  "pkcs8",
20464
20509
  base64ToBuffer(pkcs8.trim()),
@@ -20468,7 +20513,7 @@ var decryptFromEnvelope = /* @__PURE__ */ __name(async (value, pkcs8, info) => {
20468
20513
  );
20469
20514
  const ephemeralPublicKey = await crypto.subtle.importKey(
20470
20515
  "raw",
20471
- base64ToBuffer(envelope.ephemeralPublicKey),
20516
+ base64ToBuffer(envelope2.ephemeralPublicKey),
20472
20517
  { name: "ECDH", namedCurve: "P-256" },
20473
20518
  false,
20474
20519
  []
@@ -20478,13 +20523,13 @@ var decryptFromEnvelope = /* @__PURE__ */ __name(async (value, pkcs8, info) => {
20478
20523
  privateKey,
20479
20524
  256
20480
20525
  );
20481
- const salt = base64ToBuffer(envelope.salt);
20482
- const iv = base64ToBuffer(envelope.iv);
20526
+ const salt = base64ToBuffer(envelope2.salt);
20527
+ const iv = base64ToBuffer(envelope2.iv);
20483
20528
  const aesKey = await deriveAesKey2(sharedSecret, salt, "decrypt", info);
20484
20529
  const decrypted = await crypto.subtle.decrypt(
20485
- { name: "AES-GCM", iv, additionalData: getAad2(envelope) },
20530
+ { name: "AES-GCM", iv, additionalData: getAad2(envelope2) },
20486
20531
  aesKey,
20487
- base64ToBuffer(envelope.ciphertext)
20532
+ base64ToBuffer(envelope2.ciphertext)
20488
20533
  );
20489
20534
  const parsed = JSON.parse(new TextDecoder().decode(decrypted));
20490
20535
  return parsed;
@@ -20498,8 +20543,8 @@ var encryptEscrowBlob = /* @__PURE__ */ __name(async (input, enclavePublicKeySpk
20498
20543
  ESCROW_BLOB_INFO
20499
20544
  );
20500
20545
  }, "encryptEscrowBlob");
20501
- var decryptEscrowBlob = /* @__PURE__ */ __name(async (envelope, enclavePrivateKeyPkcs8B64) => parseEscrowBlobPlaintext(
20502
- await decryptFromEnvelope(envelope, enclavePrivateKeyPkcs8B64, ESCROW_BLOB_INFO)
20546
+ var decryptEscrowBlob = /* @__PURE__ */ __name(async (envelope2, enclavePrivateKeyPkcs8B64) => parseEscrowBlobPlaintext(
20547
+ await decryptFromEnvelope(envelope2, enclavePrivateKeyPkcs8B64, ESCROW_BLOB_INFO)
20503
20548
  ), "decryptEscrowBlob");
20504
20549
  var sealEscrowRelease = /* @__PURE__ */ __name(async (input, clientEphemeralPublicKeySpkiB64) => {
20505
20550
  const plaintext = parseEscrowReleasePlaintext({ ...input, version: ESCROW_ENVELOPE_VERSION });
@@ -20510,10 +20555,196 @@ var sealEscrowRelease = /* @__PURE__ */ __name(async (input, clientEphemeralPubl
20510
20555
  ESCROW_RELEASE_INFO
20511
20556
  );
20512
20557
  }, "sealEscrowRelease");
20513
- var openEscrowRelease = /* @__PURE__ */ __name(async (envelope, clientEphemeralPrivateKeyPkcs8B64) => parseEscrowReleasePlaintext(
20514
- await decryptFromEnvelope(envelope, clientEphemeralPrivateKeyPkcs8B64, ESCROW_RELEASE_INFO)
20558
+ var openEscrowRelease = /* @__PURE__ */ __name(async (envelope2, clientEphemeralPrivateKeyPkcs8B64) => parseEscrowReleasePlaintext(
20559
+ await decryptFromEnvelope(envelope2, clientEphemeralPrivateKeyPkcs8B64, ESCROW_RELEASE_INFO)
20515
20560
  ), "openEscrowRelease");
20516
20561
 
20562
+ // src/escrow-enrollment-intent.ts
20563
+ import { ExtendedPoint, getPublicKeyAsync, signAsync } from "@noble/ed25519";
20564
+ var ESCROW_ENROLLMENT_INTENT_DOMAIN = "learncard-escrow-enrollment-intent-v2\0";
20565
+ var BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
20566
+ var FIELDS = [
20567
+ "version",
20568
+ "tenant",
20569
+ "did",
20570
+ "authorityGeneration",
20571
+ "expectedEnrollmentVersion",
20572
+ "action",
20573
+ "sourceBlobIdentity",
20574
+ "targetBlobIdentity",
20575
+ "pinAction",
20576
+ "shareVersion",
20577
+ "enrollmentEpoch",
20578
+ "requestId",
20579
+ "allowRewrap",
20580
+ "recoveryEmail",
20581
+ "recoveryLanguage"
20582
+ ];
20583
+ var ACTIONS = ["enroll", "replace", "disable", "carry"];
20584
+ var PIN_ACTIONS = ["set", "change", "clear", "keep"];
20585
+ var RECOVERY_LANGUAGES2 = ["en", "es", "fr", "ar"];
20586
+ var requireIntent = /* @__PURE__ */ __name((condition) => {
20587
+ if (!condition) throw new Error("Invalid escrow enrollment intent");
20588
+ }, "requireIntent");
20589
+ var matches = /* @__PURE__ */ __name((value, pattern) => typeof value === "string" && pattern.exec(value)?.[0] === value, "matches");
20590
+ var utf8 = /* @__PURE__ */ __name((value) => new TextEncoder().encode(value), "utf8");
20591
+ var integer = /* @__PURE__ */ __name((value, width) => {
20592
+ requireIntent(
20593
+ Number.isSafeInteger(value) && value >= 0 && (width === 8 || value <= 4294967295)
20594
+ );
20595
+ const bytes3 = new Uint8Array(width);
20596
+ const view = new DataView(bytes3.buffer);
20597
+ if (width === 4) view.setUint32(0, value, false);
20598
+ else view.setBigUint64(0, BigInt(value), false);
20599
+ return bytes3;
20600
+ }, "integer");
20601
+ var join = /* @__PURE__ */ __name((fields) => {
20602
+ const result = new Uint8Array(fields.reduce((length, field) => length + field.length, 0));
20603
+ let offset = 0;
20604
+ for (const field of fields) {
20605
+ result.set(field, offset);
20606
+ offset += field.length;
20607
+ }
20608
+ return result;
20609
+ }, "join");
20610
+ var hashBytes = /* @__PURE__ */ __name((value) => {
20611
+ if (value === null) return new Uint8Array();
20612
+ requireIntent(matches(value, /^[0-9a-f]{64}$/));
20613
+ return new Uint8Array(hexToBytes(value));
20614
+ }, "hashBytes");
20615
+ var didPublicKey = /* @__PURE__ */ __name((did) => {
20616
+ requireIntent(typeof did === "string" && did.startsWith("did:key:z") && did.length === 56);
20617
+ let value = 0n;
20618
+ for (const char of did.slice(9)) {
20619
+ const digit = BASE58.indexOf(char);
20620
+ requireIntent(digit >= 0);
20621
+ value = value * 58n + BigInt(digit);
20622
+ }
20623
+ const bytes3 = new Uint8Array(34);
20624
+ for (let i = 33; i >= 0; i--) {
20625
+ bytes3[i] = Number(value & 255n);
20626
+ value >>= 8n;
20627
+ }
20628
+ requireIntent(value === 0n && bytes3[0] === 237 && bytes3[1] === 1);
20629
+ const key = bytes3.slice(2);
20630
+ try {
20631
+ requireIntent(!ExtendedPoint.fromHex(key, false).isSmallOrder());
20632
+ } catch {
20633
+ throw new Error("Invalid escrow enrollment intent");
20634
+ }
20635
+ return key;
20636
+ }, "didPublicKey");
20637
+ var encodeEscrowEnrollmentIntent = /* @__PURE__ */ __name((intent) => {
20638
+ requireIntent(typeof intent === "object" && intent !== null && !Array.isArray(intent));
20639
+ requireIntent(
20640
+ Object.keys(intent).length === FIELDS.length && FIELDS.every((field) => Object.prototype.hasOwnProperty.call(intent, field))
20641
+ );
20642
+ requireIntent(intent.version === 2 && typeof intent.allowRewrap === "boolean");
20643
+ requireIntent(matches(intent.tenant, /^[A-Za-z0-9_-]{1,128}$/));
20644
+ requireIntent(matches(intent.authorityGeneration, /^[A-Za-z0-9_-]{1,128}$/));
20645
+ requireIntent(matches(intent.requestId, /^[A-Za-z0-9._-]{1,112}$/));
20646
+ didPublicKey(intent.did);
20647
+ const action = ACTIONS.indexOf(intent.action);
20648
+ const pin = PIN_ACTIONS.indexOf(intent.pinAction);
20649
+ requireIntent(action >= 0 && pin >= 0 && intent.shareVersion > 0 && intent.enrollmentEpoch > 0);
20650
+ const source = hashBytes(intent.sourceBlobIdentity);
20651
+ const target = hashBytes(intent.targetBlobIdentity);
20652
+ if (intent.action === "enroll") requireIntent(source.length === 0 && target.length === 32);
20653
+ else if (intent.action === "disable")
20654
+ requireIntent(
20655
+ source.length === 32 && target.length === 0 && intent.pinAction === "keep" && !intent.allowRewrap
20656
+ );
20657
+ else
20658
+ requireIntent(
20659
+ source.length === 32 && target.length === 32 && intent.sourceBlobIdentity !== intent.targetBlobIdentity
20660
+ );
20661
+ if (intent.action === "carry") requireIntent(intent.pinAction === "keep");
20662
+ if (intent.recoveryEmail === null) {
20663
+ requireIntent(
20664
+ intent.recoveryLanguage === null && (intent.action === "disable" || intent.action === "carry")
20665
+ );
20666
+ } else {
20667
+ requireIntent(intent.action === "enroll" || intent.action === "replace");
20668
+ requireIntent(
20669
+ matches(intent.recoveryEmail, /^[\x21-\x7e]{3,254}$/) && intent.recoveryEmail === intent.recoveryEmail.trim().toLowerCase()
20670
+ );
20671
+ const at = intent.recoveryEmail.split("@");
20672
+ requireIntent(
20673
+ at.length === 2 && at[0].length > 0 && at[1].length > 0 && at[1].includes(".") && !at[1].startsWith(".") && !at[1].endsWith(".")
20674
+ );
20675
+ requireIntent(
20676
+ intent.recoveryLanguage !== null && RECOVERY_LANGUAGES2.includes(intent.recoveryLanguage)
20677
+ );
20678
+ }
20679
+ const fields = [
20680
+ integer(intent.version, 4),
20681
+ utf8(intent.tenant),
20682
+ utf8(intent.did),
20683
+ utf8(intent.authorityGeneration),
20684
+ integer(intent.expectedEnrollmentVersion, 8),
20685
+ Uint8Array.of(action),
20686
+ source,
20687
+ target,
20688
+ Uint8Array.of(pin),
20689
+ integer(intent.shareVersion, 4),
20690
+ integer(intent.enrollmentEpoch, 8),
20691
+ utf8(intent.requestId),
20692
+ Uint8Array.of(Number(intent.allowRewrap)),
20693
+ intent.recoveryEmail === null ? new Uint8Array() : utf8(intent.recoveryEmail),
20694
+ intent.recoveryLanguage === null ? new Uint8Array() : utf8(intent.recoveryLanguage)
20695
+ ];
20696
+ return join([
20697
+ utf8(ESCROW_ENROLLMENT_INTENT_DOMAIN),
20698
+ ...fields.flatMap((field) => [integer(field.length, 4), field])
20699
+ ]);
20700
+ }, "encodeEscrowEnrollmentIntent");
20701
+ var signEscrowEnrollmentIntent = /* @__PURE__ */ __name(async (intent, privateKey) => {
20702
+ encodeEscrowEnrollmentIntent(intent);
20703
+ const snapshot = { ...intent };
20704
+ const message = encodeEscrowEnrollmentIntent(snapshot);
20705
+ requireIntent(matches(privateKey, /^[0-9a-f]{64}$/i));
20706
+ const seed = hexToBytes(privateKey);
20707
+ try {
20708
+ const publicKey = await getPublicKeyAsync(seed);
20709
+ const expected = didPublicKey(snapshot.did);
20710
+ requireIntent(publicKey.every((byte, index) => byte === expected[index]));
20711
+ const signature = await signAsync(message, seed);
20712
+ return { intent: snapshot, signature: bufferToBase64(new Uint8Array(signature).buffer) };
20713
+ } finally {
20714
+ seed.fill(0);
20715
+ }
20716
+ }, "signEscrowEnrollmentIntent");
20717
+ var getEscrowBlobIdentity = /* @__PURE__ */ __name(async (input) => {
20718
+ const envelope2 = parseEscrowEnvelope(input);
20719
+ const decode2 = /* @__PURE__ */ __name((value) => {
20720
+ const bytes3 = new Uint8Array(base64ToBuffer(value));
20721
+ requireIntent(bufferToBase64(bytes3.buffer) === value);
20722
+ return bytes3;
20723
+ }, "decode");
20724
+ const point = decode2(envelope2.ephemeralPublicKey);
20725
+ const salt = decode2(envelope2.salt);
20726
+ const iv = decode2(envelope2.iv);
20727
+ const ciphertext = decode2(envelope2.ciphertext);
20728
+ requireIntent(
20729
+ point.length === 65 && point[0] === 4 && salt.length === 32 && iv.length === 12 && ciphertext.length >= 16
20730
+ );
20731
+ await crypto.subtle.importKey("raw", point, { name: "ECDH", namedCurve: "P-256" }, false, []);
20732
+ const fields = [
20733
+ Uint8Array.of(1),
20734
+ utf8(envelope2.algorithm),
20735
+ utf8(envelope2.keyId),
20736
+ point.slice(1, 33),
20737
+ salt,
20738
+ iv,
20739
+ ciphertext
20740
+ ];
20741
+ const message = join([
20742
+ utf8("learncard-escrow-blob-id-v1\0"),
20743
+ ...fields.flatMap((field) => [integer(field.length, 8), field])
20744
+ ]);
20745
+ return bytesToHex(new Uint8Array(await crypto.subtle.digest("SHA-256", message)));
20746
+ }, "getEscrowBlobIdentity");
20747
+
20517
20748
  // src/escrow-nitro-attestation.ts
20518
20749
  import "reflect-metadata";
20519
20750
  import { decode, encode } from "cborg";
@@ -20537,15 +20768,15 @@ var AWS_ROOT_SHA256 = "641a0321a3e244efe456463195d606317ed7cdcc3c1756e09893f3c68
20537
20768
  var fail = /* @__PURE__ */ __name((reason) => {
20538
20769
  throw new NitroAttestationError(reason);
20539
20770
  }, "fail");
20540
- var hex = /* @__PURE__ */ __name((bytes2) => Array.from(bytes2, (byte) => byte.toString(16).padStart(2, "0")).join(""), "hex");
20771
+ var hex = /* @__PURE__ */ __name((bytes3) => Array.from(bytes3, (byte) => byte.toString(16).padStart(2, "0")).join(""), "hex");
20541
20772
  var bytes = /* @__PURE__ */ __name((value, reason) => value instanceof Uint8Array && value.length > 0 ? value : fail(reason), "bytes");
20542
20773
  var map = /* @__PURE__ */ __name((value, reason) => value instanceof Map ? value : fail(reason), "map");
20543
- var decodeCbor = /* @__PURE__ */ __name((value) => {
20774
+ var decodeCbor = /* @__PURE__ */ __name((value, { allowIndefinite = false } = {}) => {
20544
20775
  try {
20545
20776
  return decode(value, {
20546
20777
  useMaps: true,
20547
20778
  rejectDuplicateMapKeys: true,
20548
- allowIndefinite: false,
20779
+ allowIndefinite,
20549
20780
  allowUndefined: false,
20550
20781
  allowNaN: false,
20551
20782
  allowInfinity: false,
@@ -20603,7 +20834,7 @@ var verifyNitroAttestationDocument = /* @__PURE__ */ __name(async (documentB64,
20603
20834
  const payloadBytes = bytes(payloadValue, "cose");
20604
20835
  const signature = bytes(signatureValue, "signature");
20605
20836
  if (signature.length !== 96) fail("signature");
20606
- const payload = map(decodeCbor(payloadBytes), "cbor");
20837
+ const payload = map(decodeCbor(payloadBytes, { allowIndefinite: true }), "cbor");
20607
20838
  const moduleId = payload.get("module_id");
20608
20839
  const timestamp = payload.get("timestamp");
20609
20840
  if (typeof moduleId !== "string" || !moduleId || payload.get("digest") !== "SHA384")
@@ -20693,13 +20924,36 @@ var verifyNitroAttestationDocument = /* @__PURE__ */ __name(async (documentB64,
20693
20924
  } catch {
20694
20925
  return fail("user-data");
20695
20926
  }
20927
+ const rawLedgerKey = payload.get("public_key");
20928
+ let ledgerPublicKey;
20929
+ let measurementHash;
20930
+ if (rawLedgerKey !== void 0 && rawLedgerKey !== null) {
20931
+ const ledger = new Uint8Array(bytes(rawLedgerKey, "user-data"));
20932
+ if (ledger.length !== 65 || ledger[0] !== 4) fail("user-data");
20933
+ try {
20934
+ await crypto.subtle.importKey(
20935
+ "raw",
20936
+ ledger,
20937
+ { name: "ECDSA", namedCurve: "P-256" },
20938
+ false,
20939
+ ["verify"]
20940
+ );
20941
+ } catch {
20942
+ return fail("user-data");
20943
+ }
20944
+ const tuple = new Uint8Array(144);
20945
+ for (let i = 0; i < 3; i++) tuple.set(bytes(rawPcrs.get(i), "pcr"), i * 48);
20946
+ measurementHash = hex(new Uint8Array(await crypto.subtle.digest("SHA-256", tuple)));
20947
+ ledgerPublicKey = bufferToBase64(ledger.buffer);
20948
+ }
20696
20949
  return {
20697
20950
  escrowPublicKeySpkiB64: bufferToBase64(new Uint8Array(spki).buffer),
20698
20951
  keyId: newFormat?.keyId,
20699
20952
  previousKeyIds: newFormat?.previousKeyIds ?? [],
20700
20953
  pcrs,
20701
20954
  timestamp,
20702
- moduleId
20955
+ moduleId,
20956
+ ...ledgerPublicKey ? { ledgerPublicKey, measurementHash } : {}
20703
20957
  };
20704
20958
  }, "verifyNitroAttestationDocument");
20705
20959
 
@@ -20753,7 +21007,13 @@ var verifyEnclaveAttestation = /* @__PURE__ */ __name(async (attestation, policy
20753
21007
  publicKey: verified.escrowPublicKeySpkiB64,
20754
21008
  keyId: attestation.keyId,
20755
21009
  previousKeyIds: verified.previousKeyIds,
20756
- mode: "nitro"
21010
+ mode: "nitro",
21011
+ ...verified.keyId && verified.ledgerPublicKey && verified.measurementHash ? {
21012
+ authority: {
21013
+ ledgerPublicKey: verified.ledgerPublicKey,
21014
+ measurement: verified.measurementHash
21015
+ }
21016
+ } : {}
20757
21017
  };
20758
21018
  }
20759
21019
  const publicKey = normalizePublicKey(attestation.publicKey);
@@ -20775,6 +21035,424 @@ var verifyEnclaveAttestation = /* @__PURE__ */ __name(async (attestation, policy
20775
21035
  };
20776
21036
  }, "verifyEnclaveAttestation");
20777
21037
 
21038
+ // src/escrow-authority.types.ts
21039
+ var EscrowEvidenceError = class extends Error {
21040
+ static {
21041
+ __name(this, "EscrowEvidenceError");
21042
+ }
21043
+ constructor() {
21044
+ super("Automatic recovery evidence could not be verified.");
21045
+ this.name = "EscrowEvidenceError";
21046
+ }
21047
+ };
21048
+
21049
+ // src/escrow-authority.ts
21050
+ var fail2 = /* @__PURE__ */ __name(() => {
21051
+ throw new EscrowEvidenceError();
21052
+ }, "fail");
21053
+ var verifyEscrowHoldState = /* @__PURE__ */ __name(async (response, nonce, expected, identity) => {
21054
+ requireEvidence(/^[0-9a-f]{64}$/.test(nonce));
21055
+ const { payload } = await verifyRecord(response.proof, "snapshot", identity);
21056
+ requireEvidence(Array.isArray(payload) && payload.length === 12);
21057
+ const p = payload;
21058
+ requireEvidence(
21059
+ p[0] === "learncard-authority-hold-state-v3" && p[1] === nonce && p[2] === expected.tenant && p[3] === expected.authorityGeneration && p[4] === expected.did && p[5] === expected.holdId && p[6] === expected.holdDigest
21060
+ );
21061
+ const status = p[7];
21062
+ if (status !== "awaitingConfirmation" && status !== "pending" && status !== "released" && status !== "cancelled")
21063
+ return fail2();
21064
+ const confirmedHiMs = p[8] === null ? null : integer2(p[8]);
21065
+ const releaseWarningSentHiMs = p[9] === null ? null : integer2(p[9]);
21066
+ const attemptsRemaining = integer2(p[10], 0, 5);
21067
+ const codeSendStatus = p[11];
21068
+ if (codeSendStatus !== "unknown" && codeSendStatus !== "notRequired") return fail2();
21069
+ requireEvidence(
21070
+ status !== "awaitingConfirmation" || confirmedHiMs === null && releaseWarningSentHiMs === null
21071
+ );
21072
+ requireEvidence(status !== "pending" && status !== "released" || confirmedHiMs !== null);
21073
+ return { status, confirmedHiMs, releaseWarningSentHiMs, attemptsRemaining, codeSendStatus };
21074
+ }, "verifyEscrowHoldState");
21075
+ var verifyEscrowCodeDelivery = /* @__PURE__ */ __name(async (delivery, expected, identity) => {
21076
+ const { payload } = await verifyRecord(delivery.proof, "snapshot", identity);
21077
+ requireEvidence(Array.isArray(payload) && payload.length === 5);
21078
+ const values = payload;
21079
+ requireEvidence(
21080
+ values[0] === "learncard-authority-code-delivery-v1" && values[1] === expected.tenant && values[2] === expected.authorityGeneration && values[3] === expected.holdDigest && typeof values[4] === "boolean" && values[4] === delivery.codeSent
21081
+ );
21082
+ return delivery.codeSent;
21083
+ }, "verifyEscrowCodeDelivery");
21084
+ var requireEvidence = /* @__PURE__ */ __name((condition) => {
21085
+ if (!condition) fail2();
21086
+ }, "requireEvidence");
21087
+ var object = /* @__PURE__ */ __name((value) => {
21088
+ if (!value || typeof value !== "object" || Array.isArray(value)) return fail2();
21089
+ return value;
21090
+ }, "object");
21091
+ var keys = /* @__PURE__ */ __name((value, names) => {
21092
+ requireEvidence(
21093
+ Object.keys(value).length === names.length && names.every((name) => Object.prototype.hasOwnProperty.call(value, name))
21094
+ );
21095
+ }, "keys");
21096
+ var integer2 = /* @__PURE__ */ __name((value, minimum = 0, maximum = Number.MAX_SAFE_INTEGER) => {
21097
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum || value > maximum)
21098
+ return fail2();
21099
+ return value;
21100
+ }, "integer");
21101
+ var text = /* @__PURE__ */ __name((value, maximum) => {
21102
+ if (typeof value !== "string" || !value.length || value.length > maximum) return fail2();
21103
+ return value;
21104
+ }, "text");
21105
+ var bytes2 = /* @__PURE__ */ __name((value, length) => {
21106
+ if (!Array.isArray(value) || value.length !== length || !value.every(
21107
+ (byte) => typeof byte === "number" && Number.isInteger(byte) && byte >= 0 && byte <= 255
21108
+ ))
21109
+ return fail2();
21110
+ return Uint8Array.from(value);
21111
+ }, "bytes");
21112
+ var base64 = /* @__PURE__ */ __name((value, maximum) => {
21113
+ const encoded = text(value, maximum);
21114
+ try {
21115
+ const decoded = new Uint8Array(base64ToBuffer(encoded));
21116
+ requireEvidence(bufferToBase64(decoded.buffer) === encoded);
21117
+ return decoded;
21118
+ } catch {
21119
+ return fail2();
21120
+ }
21121
+ }, "base64");
21122
+ var frame = /* @__PURE__ */ __name((value) => {
21123
+ const result = new Uint8Array(4 + value.length);
21124
+ new DataView(result.buffer).setUint32(0, value.length, false);
21125
+ result.set(value, 4);
21126
+ return result;
21127
+ }, "frame");
21128
+ var join2 = /* @__PURE__ */ __name((...values) => {
21129
+ const result = new Uint8Array(values.reduce((n, v) => n + v.length, 0));
21130
+ let offset = 0;
21131
+ for (const value of values) {
21132
+ result.set(value, offset);
21133
+ offset += value.length;
21134
+ }
21135
+ return result;
21136
+ }, "join");
21137
+ var hash = /* @__PURE__ */ __name(async (value) => bytesToHex(new Uint8Array(await crypto.subtle.digest("SHA-256", new Uint8Array(value)))), "hash");
21138
+ var utf82 = /* @__PURE__ */ __name((value) => new TextEncoder().encode(value), "utf8");
21139
+ var escrowAuthorityMeasurements = /* @__PURE__ */ __name(async (policy) => {
21140
+ if (policy.mode !== "nitro") return [];
21141
+ const hashes = [];
21142
+ for (const pin of policy.pinnedMeasurements) {
21143
+ const tuple = [pin.pcr0, pin.pcr1, pin.pcr2];
21144
+ if (!tuple.every((pcr) => typeof pcr === "string" && /^[0-9a-f]{96}$/i.test(pcr))) continue;
21145
+ if (pin.pcr0 === "00".repeat(48)) continue;
21146
+ const encoded = tuple.join("");
21147
+ const decoded = Uint8Array.from(
21148
+ { length: 144 },
21149
+ (_, i) => parseInt(encoded.slice(i * 2, i * 2 + 2), 16)
21150
+ );
21151
+ hashes.push(await hash(decoded));
21152
+ }
21153
+ return hashes;
21154
+ }, "escrowAuthorityMeasurements");
21155
+ var P256_ORDER = BigInt("0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
21156
+ var verifyRecord = /* @__PURE__ */ __name(async (input, kind, identity, historical) => {
21157
+ requireEvidence(identity.mode === "nitro" && !!identity.authority);
21158
+ const raw = object(input);
21159
+ keys(raw, ["version", "kind", "keyId", "measurement", "payload", "signature"]);
21160
+ requireEvidence(raw.version === 1 && raw.kind === kind);
21161
+ const keyId = text(raw.keyId, 128);
21162
+ requireEvidence(
21163
+ historical ? [identity.keyId, ...identity.previousKeyIds].includes(keyId) : keyId === identity.keyId
21164
+ );
21165
+ const measurement = bytes2(raw.measurement, 32);
21166
+ requireEvidence(
21167
+ historical ? historical.allowedMeasurements.includes(bytesToHex(measurement)) : bytesToHex(measurement) === identity.authority?.measurement
21168
+ );
21169
+ const payloadB64 = text(raw.payload, kind === "snapshot" ? 49152 : 8192);
21170
+ const signatureB64 = text(raw.signature, 88);
21171
+ const payload = base64(payloadB64, kind === "snapshot" ? 49152 : 8192);
21172
+ const signature = base64(signatureB64, 88);
21173
+ const ledgerKey = base64(identity.authority?.ledgerPublicKey, 88);
21174
+ requireEvidence(ledgerKey.length === 65 && ledgerKey[0] === 4);
21175
+ requireEvidence(signature.length === 64);
21176
+ const s = BigInt(`0x${bytesToHex(signature.slice(32))}`);
21177
+ requireEvidence(s > 0n && s <= P256_ORDER / 2n);
21178
+ const message = join2(
21179
+ utf82("learncard-authority-signed-record-v1\0"),
21180
+ Uint8Array.of(kind === "snapshot" ? 2 : 1),
21181
+ frame(utf82(keyId)),
21182
+ measurement,
21183
+ frame(payload)
21184
+ );
21185
+ try {
21186
+ const key = await crypto.subtle.importKey(
21187
+ "raw",
21188
+ ledgerKey,
21189
+ { name: "ECDSA", namedCurve: "P-256" },
21190
+ false,
21191
+ ["verify"]
21192
+ );
21193
+ requireEvidence(
21194
+ await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, key, signature, message)
21195
+ );
21196
+ const decoded = JSON.parse(
21197
+ new TextDecoder("utf-8", { fatal: true }).decode(payload)
21198
+ );
21199
+ const record = {
21200
+ version: 1,
21201
+ kind,
21202
+ keyId,
21203
+ measurement: Array.from(measurement),
21204
+ payload: payloadB64,
21205
+ signature: signatureB64
21206
+ };
21207
+ return { record, payload: decoded };
21208
+ } catch {
21209
+ return fail2();
21210
+ }
21211
+ }, "verifyRecord");
21212
+ var envelope = /* @__PURE__ */ __name((value) => {
21213
+ try {
21214
+ const e = parseEscrowEnvelope(value);
21215
+ return {
21216
+ version: e.version,
21217
+ algorithm: e.algorithm,
21218
+ keyId: e.keyId,
21219
+ ephemeralPublicKey: e.ephemeralPublicKey,
21220
+ salt: e.salt,
21221
+ iv: e.iv,
21222
+ ciphertext: e.ciphertext
21223
+ };
21224
+ } catch {
21225
+ return fail2();
21226
+ }
21227
+ }, "envelope");
21228
+ var state = /* @__PURE__ */ __name((value) => {
21229
+ const raw = object(value);
21230
+ keys(raw, [
21231
+ "tenant",
21232
+ "authorityGeneration",
21233
+ "did",
21234
+ "enrollmentVersion",
21235
+ "enrollmentEpoch",
21236
+ "status",
21237
+ "envelope",
21238
+ "shareVersion",
21239
+ "hasPin",
21240
+ "pinGeneration",
21241
+ "attemptsUsed"
21242
+ ]);
21243
+ if (raw.status !== "empty" && raw.status !== "active" && raw.status !== "disabled")
21244
+ return fail2();
21245
+ if (typeof raw.hasPin !== "boolean") return fail2();
21246
+ const result = {
21247
+ tenant: text(raw.tenant, 128),
21248
+ authorityGeneration: text(raw.authorityGeneration, 128),
21249
+ did: text(raw.did, 8192),
21250
+ enrollmentVersion: integer2(raw.enrollmentVersion),
21251
+ enrollmentEpoch: integer2(raw.enrollmentEpoch),
21252
+ status: raw.status,
21253
+ envelope: raw.envelope === null ? null : envelope(raw.envelope),
21254
+ shareVersion: raw.shareVersion === null ? null : integer2(raw.shareVersion, 1, 4294967295),
21255
+ hasPin: raw.hasPin,
21256
+ pinGeneration: integer2(raw.pinGeneration),
21257
+ attemptsUsed: integer2(raw.attemptsUsed, 0, 10)
21258
+ };
21259
+ requireEvidence(
21260
+ result.pinGeneration <= result.enrollmentVersion && result.enrollmentEpoch <= result.enrollmentVersion && (!result.hasPin || result.pinGeneration > 0) && (result.hasPin || result.attemptsUsed === 0)
21261
+ );
21262
+ if (result.status === "empty")
21263
+ requireEvidence(
21264
+ result.enrollmentVersion === 0 && result.enrollmentEpoch === 0 && !result.envelope && result.shareVersion === null && !result.hasPin
21265
+ );
21266
+ else requireEvidence(result.enrollmentVersion > 0 && result.enrollmentEpoch > 0);
21267
+ if (result.status === "active")
21268
+ requireEvidence(!!result.envelope && result.shareVersion !== null);
21269
+ else requireEvidence(result.envelope === null && result.shareVersion === null);
21270
+ return result;
21271
+ }, "state");
21272
+ var verifyEscrowAuthorityState = /* @__PURE__ */ __name(async (response, nonce, tenant, did, identity) => {
21273
+ requireEvidence(/^[0-9a-f]{64}$/.test(nonce) && nonce.length === 64);
21274
+ const raw = object(response);
21275
+ keys(raw, ["state", "proof"]);
21276
+ const { payload } = await verifyRecord(raw.proof, "snapshot", identity);
21277
+ if (!Array.isArray(payload) || payload.length !== 2 || payload[0] !== nonce) return fail2();
21278
+ const signed = state(payload[1]);
21279
+ requireEvidence(signed.tenant === tenant && signed.did === did);
21280
+ requireEvidence(JSON.stringify(signed) === JSON.stringify(state(raw.state)));
21281
+ if (signed.envelope) {
21282
+ requireEvidence(
21283
+ [identity.keyId, ...identity.previousKeyIds].includes(signed.envelope.keyId)
21284
+ );
21285
+ await getEscrowBlobIdentity(signed.envelope);
21286
+ }
21287
+ return signed;
21288
+ }, "verifyEscrowAuthorityState");
21289
+ var verifyEscrowEnrollmentCommit = /* @__PURE__ */ __name(async (response, intent, identity) => {
21290
+ const raw = object(response);
21291
+ const { record, payload } = await verifyRecord(raw.receipt, "commit", identity);
21292
+ const data = object(payload);
21293
+ keys(data, ["commit", "eventHash", "requestDigest", "resultBlobIdentity", "enrollmentEpoch"]);
21294
+ const commit = object(data.commit);
21295
+ keys(commit, ["schemaVersion", "binding", "executionId", "revisions", "transactionDigest"]);
21296
+ const binding = object(commit.binding);
21297
+ keys(binding, ["tenant", "generation", "didHash"]);
21298
+ const revisions = object(commit.revisions);
21299
+ keys(revisions, ["operation", "enrollment"]);
21300
+ requireEvidence(
21301
+ commit.schemaVersion === 1 && binding.tenant === intent.tenant && binding.generation === intent.authorityGeneration
21302
+ );
21303
+ requireEvidence(bytesToHex(bytes2(binding.didHash, 32)) === await hash(utf82(intent.did)));
21304
+ requireEvidence(
21305
+ integer2(revisions.enrollment, 1) === intent.expectedEnrollmentVersion + 1 && integer2(revisions.operation, 1) >= integer2(revisions.enrollment, 1)
21306
+ );
21307
+ bytes2(commit.executionId, 16);
21308
+ bytes2(commit.transactionDigest, 32);
21309
+ bytes2(data.eventHash, 32);
21310
+ requireEvidence(integer2(data.enrollmentEpoch, 1) === intent.enrollmentEpoch);
21311
+ requireEvidence(
21312
+ bytesToHex(bytes2(data.requestDigest, 32)) === await hash(encodeEscrowEnrollmentIntent(intent))
21313
+ );
21314
+ if (intent.action === "disable") {
21315
+ requireEvidence(
21316
+ (raw.envelope === null || raw.envelope === void 0) && data.resultBlobIdentity === null
21317
+ );
21318
+ return { envelope: null, receipt: record };
21319
+ }
21320
+ const output = envelope(raw.envelope);
21321
+ requireEvidence(
21322
+ output.keyId === identity.keyId && bytesToHex(bytes2(data.resultBlobIdentity, 32)) === await getEscrowBlobIdentity(output)
21323
+ );
21324
+ return { envelope: output, receipt: record };
21325
+ }, "verifyEscrowEnrollmentCommit");
21326
+ var holdDigest = /* @__PURE__ */ __name(async (tenant, record) => {
21327
+ const h = record.hold;
21328
+ const didHash = await hash(utf82(h.did));
21329
+ return hash(
21330
+ utf82(
21331
+ JSON.stringify([
21332
+ "learncard-hold-v1",
21333
+ tenant,
21334
+ h.holdId,
21335
+ didHash,
21336
+ h.shareVersion,
21337
+ h.blobHash,
21338
+ h.enrollmentEpoch,
21339
+ h.releasePolicy,
21340
+ h.clientEphemeralPublicKey,
21341
+ h.createdLo,
21342
+ h.createdHi,
21343
+ record.holdDurationMs,
21344
+ h.policyVersion,
21345
+ record.ledgerSeq
21346
+ ])
21347
+ )
21348
+ );
21349
+ }, "holdDigest");
21350
+ var verifyEscrowHoldRecord = /* @__PURE__ */ __name(async (record, expected, identity, historical) => {
21351
+ requireEvidence(
21352
+ record.hold.holdId === expected.holdId && record.hold.did === expected.did && record.hold.clientEphemeralPublicKey === expected.clientEphemeralPublicKey && record.hold.blobHash === expected.blobHash
21353
+ );
21354
+ let signed;
21355
+ try {
21356
+ signed = JSON.parse(
21357
+ new TextDecoder("utf-8", { fatal: true }).decode(base64(record.hold.signature, 8192))
21358
+ );
21359
+ } catch {
21360
+ return fail2();
21361
+ }
21362
+ const { record: receipt, payload } = await verifyRecord(signed, "commit", identity, historical);
21363
+ const data = object(payload);
21364
+ keys(data, ["commit", "eventHash", "requestDigest", "resultBlobIdentity", "enrollmentEpoch"]);
21365
+ const commit = object(data.commit);
21366
+ keys(commit, ["schemaVersion", "binding", "executionId", "revisions", "transactionDigest"]);
21367
+ const binding = object(commit.binding);
21368
+ keys(binding, ["tenant", "generation", "didHash"]);
21369
+ requireEvidence(commit.schemaVersion === 1 && binding.tenant === expected.tenant);
21370
+ const authorityGeneration = text(binding.generation, 128);
21371
+ if (historical) requireEvidence(authorityGeneration === historical.authorityGeneration);
21372
+ requireEvidence(bytesToHex(bytes2(binding.didHash, 32)) === await hash(utf82(expected.did)));
21373
+ requireEvidence(integer2(data.enrollmentEpoch, 1) === record.hold.enrollmentEpoch);
21374
+ requireEvidence(bytesToHex(bytes2(data.resultBlobIdentity, 32)) === record.hold.blobHash);
21375
+ const digest = await holdDigest(expected.tenant, record);
21376
+ requireEvidence(bytesToHex(bytes2(data.requestDigest, 32)) === digest);
21377
+ return { receipt, authorityGeneration, holdDigest: digest };
21378
+ }, "verifyEscrowHoldRecord");
21379
+ var verifyEscrowHoldConfirmation = /* @__PURE__ */ __name(async (receipt, record, expected, identity) => {
21380
+ const raw = object(receipt);
21381
+ const kind = raw.kind;
21382
+ if (kind !== "commit" && kind !== "snapshot") return fail2();
21383
+ const { record: verified, payload } = await verifyRecord(raw, kind, identity);
21384
+ if (kind === "snapshot") {
21385
+ if (!Array.isArray(payload) || payload.length !== 7) return fail2();
21386
+ const [tag, holdId, digestBytes] = payload;
21387
+ requireEvidence(
21388
+ tag === "learncard-authority-hold-snapshot-v2" && holdId === record.hold.holdId && payload[6] === expected.authorityGeneration
21389
+ );
21390
+ requireEvidence(
21391
+ bytesToHex(bytes2(digestBytes, 32)) === await holdDigest(expected.tenant, record)
21392
+ );
21393
+ requireEvidence(payload[3] === "pending");
21394
+ integer2(payload[4], 1);
21395
+ return verified;
21396
+ }
21397
+ const data = object(payload);
21398
+ keys(data, ["commit", "eventHash", "requestDigest", "resultBlobIdentity", "enrollmentEpoch"]);
21399
+ const commit = object(data.commit);
21400
+ keys(commit, ["schemaVersion", "binding", "executionId", "revisions", "transactionDigest"]);
21401
+ const binding = object(commit.binding);
21402
+ keys(binding, ["tenant", "generation", "didHash"]);
21403
+ requireEvidence(
21404
+ commit.schemaVersion === 1 && binding.tenant === expected.tenant && binding.generation === expected.authorityGeneration
21405
+ );
21406
+ requireEvidence(bytesToHex(bytes2(binding.didHash, 32)) === await hash(utf82(expected.did)));
21407
+ requireEvidence(integer2(data.enrollmentEpoch, 1) === record.hold.enrollmentEpoch);
21408
+ requireEvidence(bytesToHex(bytes2(data.resultBlobIdentity, 32)) === record.hold.blobHash);
21409
+ bytes2(data.requestDigest, 32);
21410
+ return verified;
21411
+ }, "verifyEscrowHoldConfirmation");
21412
+ var resolveEscrowEnrollmentAction = /* @__PURE__ */ __name((current, input) => {
21413
+ const disabling = input === "disable";
21414
+ const pinAction = disabling ? "keep" : input === "set" ? current.hasPin ? "change" : "set" : input === "clear" ? current.hasPin ? "clear" : "keep" : "keep";
21415
+ const action = disabling ? "disable" : current.status !== "active" ? "enroll" : current.hasPin && pinAction === "keep" ? "carry" : "replace";
21416
+ return { action, pinAction, bindsRecoveryContact: action === "enroll" || action === "replace" };
21417
+ }, "resolveEscrowEnrollmentAction");
21418
+ var violatesCarryContactRule = /* @__PURE__ */ __name((action, target) => action === "carry" && !!target && (!!target.recoveryEmail || !!target.recoveryLanguage), "violatesCarryContactRule");
21419
+ var createEscrowEnrollmentIntent = /* @__PURE__ */ __name(async (input) => {
21420
+ const current = state(input.state);
21421
+ const target = input.target === null ? null : envelope(input.target);
21422
+ const disabling = input.pinAction === "disable";
21423
+ requireEvidence(
21424
+ current.enrollmentVersion < Number.MAX_SAFE_INTEGER && (disabling || current.enrollmentEpoch < Number.MAX_SAFE_INTEGER)
21425
+ );
21426
+ if (disabling && current.status !== "active") return fail2();
21427
+ if (current.status === "disabled" && current.hasPin && input.pinAction === "automatic")
21428
+ return fail2();
21429
+ const { action, pinAction, bindsRecoveryContact } = resolveEscrowEnrollmentAction(
21430
+ current,
21431
+ input.pinAction
21432
+ );
21433
+ requireEvidence(!disabling || input.target === null);
21434
+ requireEvidence(disabling || input.target !== null);
21435
+ if (action === "carry") requireEvidence(input.shareVersion > (current.shareVersion ?? 0));
21436
+ const intent = {
21437
+ version: 2,
21438
+ tenant: current.tenant,
21439
+ did: current.did,
21440
+ authorityGeneration: current.authorityGeneration,
21441
+ expectedEnrollmentVersion: current.enrollmentVersion,
21442
+ action,
21443
+ sourceBlobIdentity: current.envelope ? await getEscrowBlobIdentity(current.envelope) : null,
21444
+ targetBlobIdentity: target ? await getEscrowBlobIdentity(target) : null,
21445
+ pinAction,
21446
+ shareVersion: disabling ? current.shareVersion : input.shareVersion,
21447
+ enrollmentEpoch: current.enrollmentEpoch + (disabling ? 0 : 1),
21448
+ requestId: crypto.randomUUID(),
21449
+ allowRewrap: !disabling,
21450
+ recoveryEmail: bindsRecoveryContact ? input.recoveryEmail : null,
21451
+ recoveryLanguage: bindsRecoveryContact ? input.recoveryLanguage : null
21452
+ };
21453
+ return signEscrowEnrollmentIntent(intent, input.privateKey);
21454
+ }, "createEscrowEnrollmentIntent");
21455
+
20778
21456
  // src/types.ts
20779
21457
  var import_types = __toESM(require_dist());
20780
21458
  var EscrowPinUnavailableError = class extends Error {
@@ -21071,9 +21749,10 @@ var SERVER_REQUEST_TIMEOUT_MS = 3e4;
21071
21749
  var requestTimeoutSignal = /* @__PURE__ */ __name((ms = SERVER_REQUEST_TIMEOUT_MS) => typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(ms) : void 0, "requestTimeoutSignal");
21072
21750
  var describeHttpStatus = /* @__PURE__ */ __name((response) => response.statusText ? `${response.status} ${response.statusText}` : `${response.status}`, "describeHttpStatus");
21073
21751
  var EscrowRequestError = class extends Error {
21074
- constructor(status) {
21752
+ constructor(status, reason) {
21075
21753
  super("Escrow request failed");
21076
21754
  this.status = status;
21755
+ this.reason = reason;
21077
21756
  this.name = "EscrowRequestError";
21078
21757
  }
21079
21758
  static {
@@ -21519,6 +22198,10 @@ function createSSSStrategy(config) {
21519
22198
  const localWrites = /* @__PURE__ */ new Set();
21520
22199
  const guardedStorage = /* @__PURE__ */ __name(() => {
21521
22200
  const generation = storageGeneration;
22201
+ const assertCurrent = /* @__PURE__ */ __name(() => {
22202
+ if (generation !== storageGeneration)
22203
+ throw new Error("This recovery request was cancelled.");
22204
+ }, "assertCurrent");
21522
22205
  const write = /* @__PURE__ */ __name((operation) => {
21523
22206
  if (generation !== storageGeneration) {
21524
22207
  return Promise.reject(new Error("This recovery request was cancelled."));
@@ -21529,6 +22212,7 @@ function createSSSStrategy(config) {
21529
22212
  }, "write");
21530
22213
  const guarded = {
21531
22214
  ...storage,
22215
+ assertCurrent,
21532
22216
  storeDeviceShare: /* @__PURE__ */ __name((share, id) => write(() => storage.storeDeviceShare(share, id)), "storeDeviceShare"),
21533
22217
  storeShareVersion: /* @__PURE__ */ __name((version, id) => write(() => storage.storeShareVersion(version, id)), "storeShareVersion"),
21534
22218
  clearAllShares: /* @__PURE__ */ __name((id) => write(() => storage.clearAllShares(id)), "clearAllShares"),
@@ -21546,6 +22230,7 @@ function createSSSStrategy(config) {
21546
22230
  let lastServerSnapshot = null;
21547
22231
  let escrowEnrollmentInFlight;
21548
22232
  let staleEscrowReenrollAttempted = false;
22233
+ let verifiedEscrowEnvelope;
21549
22234
  let unenrolledRotation;
21550
22235
  const escrowRequest = /* @__PURE__ */ __name(async (path, init, pinAttempt = false) => {
21551
22236
  const response = await fetch(`${serverUrl}/keys/escrow${path}`, {
@@ -21553,6 +22238,14 @@ function createSSSStrategy(config) {
21553
22238
  ...init
21554
22239
  });
21555
22240
  if (!response.ok) {
22241
+ if (path === "/confirm" && response.status === 403) {
22242
+ const body = await response.json().catch(() => void 0);
22243
+ const reason = body && typeof body === "object" && "message" in body && typeof body.message === "string" ? body.message : void 0;
22244
+ throw new EscrowRequestError(
22245
+ response.status,
22246
+ reason === "codeMismatch" || reason === "codeExpired" || reason === "codeLocked" ? reason : void 0
22247
+ );
22248
+ }
21556
22249
  if (!pinAttempt && path === "/recover" && response.status === 429) {
21557
22250
  const body = await response.json().catch(() => void 0);
21558
22251
  const message = body && typeof body === "object" && "message" in body && typeof body.message === "string" ? body.message : "";
@@ -21604,16 +22297,89 @@ function createSSSStrategy(config) {
21604
22297
  );
21605
22298
  return verifyEnclaveAttestation(attestation, config.escrow.attestation, nonce);
21606
22299
  }, "fetchVerifiedEnclaveKey");
21607
- const enrollEscrow = /* @__PURE__ */ __name(async (token, providerType, privateKey, primaryDid, shares, shareVersion, signDidAuthVp, verifiedKey, pinMaterial, clearPin = false) => {
22300
+ const fetchEscrowHoldStatus = /* @__PURE__ */ __name(async (params) => {
22301
+ const query = new URLSearchParams(
22302
+ "holdId" in params ? { holdId: params.holdId } : { providerType: params.providerType }
22303
+ );
22304
+ const secret = "holdId" in params ? params.resumeToken : params.token;
22305
+ const result = await escrowRequest(`/status?${query}`, {
22306
+ method: "GET",
22307
+ headers: {
22308
+ ...buildHeaders("", void 0, tenantId),
22309
+ "X-Auth-Token": secret
22310
+ }
22311
+ });
22312
+ return result.hold;
22313
+ }, "fetchEscrowHoldStatus");
22314
+ const recoveryExpectations = /* @__PURE__ */ __name((holdId, expected) => {
22315
+ if (!expected || expected.holdId !== holdId || expected.tenant !== (tenantId ?? "learncard") || !expected.did || !expected.clientEphemeralPublicKey || !expected.blobHash || !expected.authorityGeneration || !expected.holdDigest || !expected.ledgerPublicKey || !expected.keyId)
22316
+ throw new EscrowEvidenceError();
22317
+ return { ...expected };
22318
+ }, "recoveryExpectations");
22319
+ const verifyPendingHold = /* @__PURE__ */ __name(async (status, expected, identity) => {
22320
+ if (!config.escrow || identity.mode !== "nitro" || !identity.authority || identity.authority.ledgerPublicKey !== expected.ledgerPublicKey || ![identity.keyId, ...identity.previousKeyIds].includes(expected.keyId) || status.holdId !== expected.holdId || !status.holdRecord)
22321
+ throw new EscrowEvidenceError();
22322
+ const record = status.holdRecord;
22323
+ const verified = await verifyEscrowHoldRecord(record, expected, identity, {
22324
+ historical: true,
22325
+ allowedMeasurements: await escrowAuthorityMeasurements(config.escrow.attestation),
22326
+ authorityGeneration: expected.authorityGeneration
22327
+ });
22328
+ if (verified.holdDigest !== expected.holdDigest || verified.receipt.keyId !== expected.keyId)
22329
+ throw new EscrowEvidenceError();
22330
+ return record;
22331
+ }, "verifyPendingHold");
22332
+ const fetchAuthorityState = /* @__PURE__ */ __name(async (token, providerType, privateKey, did, signer, identity, assertCurrent) => {
22333
+ assertCurrent();
22334
+ if (identity.mode !== "nitro" || !identity.authority)
22335
+ throw new Error("The enclave does not support verified enrollment.");
22336
+ const nonce = Array.from(
22337
+ crypto.getRandomValues(new Uint8Array(32)),
22338
+ (byte) => byte.toString(16).padStart(2, "0")
22339
+ ).join("");
22340
+ const vp = await requestFreshDidAuthVp(serverUrl, privateKey, did, signer, tenantId);
22341
+ assertCurrent();
22342
+ const response = await escrowRequest("/authority-state", {
22343
+ method: "POST",
22344
+ headers: buildHeaders(token, vp, tenantId),
22345
+ body: JSON.stringify({ authToken: token, providerType, nonce })
22346
+ });
22347
+ const result = await verifyEscrowAuthorityState(
22348
+ response,
22349
+ nonce,
22350
+ tenantId ?? "learncard",
22351
+ did,
22352
+ identity
22353
+ );
22354
+ assertCurrent();
22355
+ return result;
22356
+ }, "fetchAuthorityState");
22357
+ const enrollEscrow = /* @__PURE__ */ __name(async (token, providerType, privateKey, primaryDid, shares, shareVersion, signDidAuthVp, verifiedKey, pinMaterial, clearPin = false, assertCurrent = guardedStorage().assertCurrent, recoveryEmail = "", recoveryLanguage = "") => {
21608
22358
  if (!config.escrow?.enabled) throw new Error("Escrow enrollment is disabled");
21609
22359
  if (!signDidAuthVp) throw new Error("DID proof signing is required for escrow enrollment");
21610
- const { publicKey, keyId } = verifiedKey ?? await fetchVerifiedEnclaveKey();
21611
- const envelope = await encryptEscrowBlob(
22360
+ const identity = verifiedKey ?? await fetchVerifiedEnclaveKey();
22361
+ assertCurrent();
22362
+ const { publicKey, keyId } = identity;
22363
+ const authority = config.escrow.attestation.mode === "nitro" ? await fetchAuthorityState(
22364
+ token,
22365
+ providerType,
22366
+ privateKey,
22367
+ primaryDid,
22368
+ signDidAuthVp,
22369
+ identity,
22370
+ assertCurrent
22371
+ ) : void 0;
22372
+ const bindsRecoveryContact = !authority || resolveEscrowEnrollmentAction(
22373
+ authority,
22374
+ pinMaterial ? "set" : clearPin ? "clear" : "automatic"
22375
+ ).bindsRecoveryContact;
22376
+ const envelope2 = await encryptEscrowBlob(
21612
22377
  {
21613
22378
  recoveryShare: shares.recoveryShare,
21614
22379
  did: primaryDid,
21615
22380
  shareVersion,
21616
- ...pinMaterial ? { pinVerifier: pinMaterial.pinVerifier } : {}
22381
+ ...pinMaterial ? { pinVerifier: pinMaterial.pinVerifier } : {},
22382
+ ...authority && recoveryEmail && bindsRecoveryContact ? { recoveryEmail, recoveryLanguage } : {}
21617
22383
  },
21618
22384
  publicKey,
21619
22385
  keyId
@@ -21625,19 +22391,40 @@ function createSSSStrategy(config) {
21625
22391
  signDidAuthVp,
21626
22392
  tenantId
21627
22393
  );
21628
- await escrowRequest("", {
22394
+ const signedIntent = authority ? await createEscrowEnrollmentIntent({
22395
+ state: authority,
22396
+ target: envelope2,
22397
+ shareVersion,
22398
+ privateKey,
22399
+ pinAction: pinMaterial ? "set" : clearPin ? "clear" : "automatic",
22400
+ recoveryEmail,
22401
+ recoveryLanguage
22402
+ }) : void 0;
22403
+ assertCurrent();
22404
+ verifiedEscrowEnvelope = void 0;
22405
+ const response = await escrowRequest("", {
21629
22406
  method: "POST",
21630
22407
  headers: buildHeaders(token, vp, tenantId),
21631
22408
  body: JSON.stringify({
21632
22409
  authToken: token,
21633
22410
  providerType,
21634
- envelope,
22411
+ envelope: envelope2,
21635
22412
  shareVersion,
21636
22413
  enclaveKeyId: keyId,
21637
22414
  ...pinMaterial ? { pinSalt: pinMaterial.pinSalt } : {},
21638
- ...clearPin ? { clearPin: true } : {}
22415
+ ...(signedIntent ? signedIntent.intent.pinAction === "clear" : clearPin) ? { clearPin: true } : {},
22416
+ ...signedIntent ? { signedIntent, sourceEnvelope: authority?.envelope ?? null } : {}
21639
22417
  })
21640
22418
  });
22419
+ if (signedIntent) {
22420
+ const committed = await verifyEscrowEnrollmentCommit(
22421
+ response,
22422
+ signedIntent.intent,
22423
+ identity
22424
+ );
22425
+ assertCurrent();
22426
+ verifiedEscrowEnvelope = committed.envelope ?? void 0;
22427
+ }
21641
22428
  }, "enrollEscrow");
21642
22429
  const tryEnrollEscrow = /* @__PURE__ */ __name(async (...args) => {
21643
22430
  if (!config.escrow?.enabled) return;
@@ -21652,9 +22439,12 @@ function createSSSStrategy(config) {
21652
22439
  }
21653
22440
  }
21654
22441
  }, "tryEnrollEscrow");
21655
- const ensureEscrowEnrollmentUnguarded = /* @__PURE__ */ __name(async (self, params, storage2, forceRotate = false) => {
22442
+ const ensureEscrowEnrollmentUnguarded = /* @__PURE__ */ __name(async (self, params, storage2, forceRotate = false, allowDisabledEnrollment = false) => {
22443
+ let clearPinForEnrollment = forceRotate;
21656
22444
  if (!config.escrow?.enabled) return { enrolled: false, reason: "disabled" };
21657
22445
  const pin = params.options?.pin;
22446
+ const recoveryEmail = params.options?.recoveryEmail ?? "";
22447
+ const recoveryLanguage = params.options?.recoveryLanguage ?? "";
21658
22448
  if (pin !== void 0) {
21659
22449
  const validation = validatePin(pin);
21660
22450
  if (!validation.ok)
@@ -21663,12 +22453,35 @@ function createSSSStrategy(config) {
21663
22453
  );
21664
22454
  }
21665
22455
  const status = await self.fetchServerKeyStatus(params.token, params.providerType);
22456
+ storage2.assertCurrent();
21666
22457
  if (status.escrowOptedOut) return { enrolled: false, reason: "opted-out" };
21667
- const currentlyEnrolled = status.shareVersion !== null && status.recoveryMethods.some(
22458
+ let currentlyEnrolled = status.shareVersion !== null && status.recoveryMethods.some(
21668
22459
  (method) => method.type === "escrow" && method.shareVersion === status.shareVersion && (Boolean(method.confirmedAt) || "confirmationStatus" in method && method.confirmationStatus === "confirmed")
21669
22460
  );
21670
- const repairingStaleBlob = pin === void 0 && !forceRotate && currentlyEnrolled && !!status.escrowStale && !staleEscrowReenrollAttempted;
21671
- if (!forceRotate && pin === void 0 && currentlyEnrolled && !repairingStaleBlob)
22461
+ let verifiedKey;
22462
+ if (config.escrow.attestation.mode === "nitro") {
22463
+ if (!status.primaryDid || !params.signDidAuthVp)
22464
+ throw new Error("Owner proof signing is required for escrow enrollment.");
22465
+ verifiedKey = await fetchVerifiedEnclaveKey();
22466
+ const current = await fetchAuthorityState(
22467
+ params.token,
22468
+ params.providerType,
22469
+ params.privateKey,
22470
+ status.primaryDid,
22471
+ params.signDidAuthVp,
22472
+ verifiedKey,
22473
+ storage2.assertCurrent
22474
+ );
22475
+ if (current.status === "disabled") {
22476
+ verifiedEscrowEnvelope = void 0;
22477
+ if (!allowDisabledEnrollment) return { enrolled: false, reason: "opted-out" };
22478
+ clearPinForEnrollment ||= current.hasPin && pin === void 0;
22479
+ }
22480
+ currentlyEnrolled = currentlyEnrolled && current.status === "active" && current.shareVersion === status.shareVersion && current.envelope?.keyId === verifiedKey.keyId;
22481
+ if (currentlyEnrolled) verifiedEscrowEnvelope = current.envelope ?? void 0;
22482
+ }
22483
+ const repairingStaleBlob = pin === void 0 && !clearPinForEnrollment && currentlyEnrolled && !!status.escrowStale && !staleEscrowReenrollAttempted;
22484
+ if (!clearPinForEnrollment && pin === void 0 && currentlyEnrolled && !repairingStaleBlob)
21672
22485
  return { enrolled: true, changed: false };
21673
22486
  if (repairingStaleBlob) staleEscrowReenrollAttempted = true;
21674
22487
  if (!status.primaryDid) throw new Error("Cannot enroll escrow without a primary DID");
@@ -21676,7 +22489,7 @@ function createSSSStrategy(config) {
21676
22489
  throw new Error("DID proof signing is required for escrow enrollment");
21677
22490
  }
21678
22491
  try {
21679
- if (!forceRotate && pin === void 0 && unenrolledRotation && unenrolledRotation.shareVersion === status.shareVersion && unenrolledRotation.primaryDid === status.primaryDid) {
22492
+ if (!clearPinForEnrollment && pin === void 0 && unenrolledRotation && unenrolledRotation.shareVersion === status.shareVersion && unenrolledRotation.primaryDid === status.primaryDid) {
21680
22493
  const retry = unenrolledRotation;
21681
22494
  await enrollEscrow(
21682
22495
  params.token,
@@ -21688,12 +22501,16 @@ function createSSSStrategy(config) {
21688
22501
  params.signDidAuthVp,
21689
22502
  void 0,
21690
22503
  void 0,
21691
- retry.clearPin
22504
+ retry.clearPin,
22505
+ storage2.assertCurrent,
22506
+ recoveryEmail,
22507
+ recoveryLanguage
21692
22508
  );
21693
22509
  unenrolledRotation = void 0;
21694
22510
  return { enrolled: true, changed: true, shareVersion: retry.shareVersion };
21695
22511
  }
21696
- const verifiedKey = await fetchVerifiedEnclaveKey();
22512
+ verifiedKey ??= await fetchVerifiedEnclaveKey();
22513
+ storage2.assertCurrent();
21697
22514
  const pinSalt = pin !== void 0 ? generatePinSalt() : void 0;
21698
22515
  const pinMaterial = pin !== void 0 && pinSalt !== void 0 ? { pinSalt, pinVerifier: await derivePinProof(pin, pinSalt) } : void 0;
21699
22516
  const storageId = activeStorageId;
@@ -21722,7 +22539,12 @@ function createSSSStrategy(config) {
21722
22539
  authShare: shares.authShare,
21723
22540
  primaryDid: status.primaryDid
21724
22541
  };
21725
- unenrolledRotation = pinMaterial ? void 0 : { shares, shareVersion, primaryDid: status.primaryDid, clearPin: forceRotate };
22542
+ unenrolledRotation = pinMaterial ? void 0 : {
22543
+ shares,
22544
+ shareVersion,
22545
+ primaryDid: status.primaryDid,
22546
+ clearPin: clearPinForEnrollment
22547
+ };
21726
22548
  await enrollEscrow(
21727
22549
  params.token,
21728
22550
  params.providerType,
@@ -21733,12 +22555,15 @@ function createSSSStrategy(config) {
21733
22555
  params.signDidAuthVp,
21734
22556
  verifiedKey,
21735
22557
  pinMaterial,
21736
- forceRotate
22558
+ clearPinForEnrollment,
22559
+ storage2.assertCurrent,
22560
+ recoveryEmail,
22561
+ recoveryLanguage
21737
22562
  );
21738
22563
  unenrolledRotation = void 0;
21739
22564
  return { enrolled: true, changed: true, shareVersion };
21740
22565
  } catch (err) {
21741
- if (!repairingStaleBlob) throw err;
22566
+ if (!repairingStaleBlob || config.escrow.attestation.mode === "nitro") throw err;
21742
22567
  try {
21743
22568
  if (config.onEscrowError) config.onEscrowError(err);
21744
22569
  else console.warn("SSS: escrow enrollment failed");
@@ -21779,6 +22604,7 @@ function createSSSStrategy(config) {
21779
22604
  },
21780
22605
  // --- User scoping ---
21781
22606
  setActiveUser(userId) {
22607
+ verifiedEscrowEnvelope = void 0;
21782
22608
  if (activeStorageId !== `sss-device-share:${userId}`) storageGeneration++;
21783
22609
  activeStorageId = `sss-device-share:${userId}`;
21784
22610
  lastServerSnapshot = null;
@@ -21823,6 +22649,7 @@ function createSSSStrategy(config) {
21823
22649
  );
21824
22650
  }
21825
22651
  storageGeneration++;
22652
+ verifiedEscrowEnvelope = void 0;
21826
22653
  await Promise.allSettled(localWrites);
21827
22654
  await clearPendingShareCandidates(storage, storageId);
21828
22655
  return storage.clearAllShares(storageId);
@@ -22121,8 +22948,58 @@ function createSSSStrategy(config) {
22121
22948
  return { state: enrolled ? "enrolled" : "not-enrolled", escrowPin: status.escrowPin };
22122
22949
  },
22123
22950
  async disableEscrowRecovery(params) {
22951
+ const assertCurrent = guardedStorage().assertCurrent;
22124
22952
  const status = await this.fetchServerKeyStatus(params.token, params.providerType);
22125
22953
  if (!status.primaryDid) throw new Error("Cannot disable escrow without a primary DID");
22954
+ if (config.escrow?.attestation.mode === "nitro") {
22955
+ const identity = await fetchVerifiedEnclaveKey();
22956
+ const current = await fetchAuthorityState(
22957
+ params.token,
22958
+ params.providerType,
22959
+ params.privateKey,
22960
+ status.primaryDid,
22961
+ params.signDidAuthVp,
22962
+ identity,
22963
+ assertCurrent
22964
+ );
22965
+ if (current.status === "disabled") {
22966
+ verifiedEscrowEnvelope = void 0;
22967
+ return;
22968
+ }
22969
+ const signedIntent = await createEscrowEnrollmentIntent({
22970
+ state: current,
22971
+ target: null,
22972
+ shareVersion: current.shareVersion ?? 0,
22973
+ privateKey: params.privateKey,
22974
+ pinAction: "disable",
22975
+ // Disable always nulls these server-side regardless of input.
22976
+ recoveryEmail: "",
22977
+ recoveryLanguage: ""
22978
+ });
22979
+ const vp2 = await requestFreshDidAuthVp(
22980
+ serverUrl,
22981
+ params.privateKey,
22982
+ status.primaryDid,
22983
+ params.signDidAuthVp,
22984
+ tenantId
22985
+ );
22986
+ assertCurrent();
22987
+ const response = await escrowRequest("/disable", {
22988
+ method: "POST",
22989
+ headers: buildHeaders(params.token, vp2, tenantId),
22990
+ body: JSON.stringify({
22991
+ authToken: params.token,
22992
+ providerType: params.providerType,
22993
+ optOut: true,
22994
+ signedIntent,
22995
+ sourceEnvelope: current.envelope
22996
+ })
22997
+ });
22998
+ await verifyEscrowEnrollmentCommit(response, signedIntent.intent, identity);
22999
+ assertCurrent();
23000
+ verifiedEscrowEnvelope = void 0;
23001
+ return;
23002
+ }
22126
23003
  const vp = await requestFreshDidAuthVp(
22127
23004
  serverUrl,
22128
23005
  params.privateKey,
@@ -22163,13 +23040,29 @@ function createSSSStrategy(config) {
22163
23040
  providerType: params.providerType
22164
23041
  })
22165
23042
  });
22166
- return ensureEscrowEnrollmentUnguarded(this, params, storage2);
23043
+ return ensureEscrowEnrollmentUnguarded(this, params, storage2, false, true);
22167
23044
  });
22168
23045
  },
22169
23046
  async setEscrowPin({ pin, ...params }) {
22170
23047
  const storage2 = guardedStorage();
22171
23048
  const result = await withRotationLock(
22172
- () => ensureEscrowEnrollmentUnguarded(this, { ...params, options: { pin } }, storage2)
23049
+ () => (
23050
+ // P9.1: preserve the caller's recoveryEmail/recoveryLanguage — do
23051
+ // not replace `params.options` wholesale, or nitro enroll/replace
23052
+ // loses its required recovery contact.
23053
+ ensureEscrowEnrollmentUnguarded(
23054
+ this,
23055
+ {
23056
+ ...params,
23057
+ options: {
23058
+ pin,
23059
+ recoveryEmail: params.options?.recoveryEmail,
23060
+ recoveryLanguage: params.options?.recoveryLanguage
23061
+ }
23062
+ },
23063
+ storage2
23064
+ )
23065
+ )
22173
23066
  );
22174
23067
  if (!result.enrolled || !result.changed) {
22175
23068
  throw new Error("Automatic recovery is not available for this account.");
@@ -22201,8 +23094,15 @@ function createSSSStrategy(config) {
22201
23094
  escrowEnrollmentInFlight = void 0;
22202
23095
  }
22203
23096
  },
23097
+ getVerifiedEscrowEnvelope() {
23098
+ return verifiedEscrowEnvelope ? { ...verifiedEscrowEnvelope } : null;
23099
+ },
22204
23100
  /** Start or explicitly restart a hold with fresh client-bound resume secrets. */
22205
23101
  async startEscrowRecovery(params) {
23102
+ const expectedDid = params.expectedDid;
23103
+ const nitro = config.escrow?.attestation.mode === "nitro";
23104
+ if (nitro && (!expectedDid || (params.tenantId ?? tenantId) !== tenantId))
23105
+ throw new EscrowEvidenceError();
22206
23106
  const sessionProof = params.recoverySessionToken !== void 0;
22207
23107
  if (sessionProof ? params.token !== void 0 || params.providerType !== void 0 : !params.token || !params.providerType) {
22208
23108
  throw new Error("Provide exactly one identity proof");
@@ -22222,25 +23122,102 @@ function createSSSStrategy(config) {
22222
23122
  },
22223
23123
  params.options?.releasePolicy === "pin"
22224
23124
  );
22225
- return { ...result, clientEphemeralPrivateKey: pair.privateKey };
23125
+ let expectations;
23126
+ if (nitro && result.resumeToken !== null) {
23127
+ const identity = await fetchVerifiedEnclaveKey();
23128
+ if (identity.mode !== "nitro" || !identity.authority || !result.holdRecord || !expectedDid)
23129
+ throw new EscrowEvidenceError();
23130
+ const verified = await verifyEscrowHoldRecord(
23131
+ result.holdRecord,
23132
+ {
23133
+ tenant: tenantId ?? "learncard",
23134
+ did: expectedDid,
23135
+ holdId: result.holdId,
23136
+ clientEphemeralPublicKey: pair.publicKey,
23137
+ blobHash: result.holdRecord.hold.blobHash
23138
+ },
23139
+ identity
23140
+ );
23141
+ expectations = {
23142
+ tenant: tenantId ?? "learncard",
23143
+ did: expectedDid,
23144
+ holdId: result.holdId,
23145
+ clientEphemeralPublicKey: pair.publicKey,
23146
+ blobHash: result.holdRecord.hold.blobHash,
23147
+ authorityGeneration: verified.authorityGeneration,
23148
+ holdDigest: verified.holdDigest,
23149
+ ledgerPublicKey: identity.authority.ledgerPublicKey,
23150
+ keyId: verified.receipt.keyId
23151
+ };
23152
+ result.codeSent = result.delivery ? await verifyEscrowCodeDelivery(result.delivery, expectations, identity) : void 0;
23153
+ }
23154
+ return { ...result, clientEphemeralPrivateKey: pair.privateKey, expectations };
22226
23155
  },
22227
23156
  /** Read hold status; secrets (auth or resume token) never travel in a URL. */
22228
23157
  async getEscrowRecoveryStatus(params) {
22229
- const query = new URLSearchParams(
22230
- "holdId" in params ? { holdId: params.holdId } : { providerType: params.providerType }
22231
- );
22232
- const secret = "holdId" in params ? params.resumeToken : params.token;
22233
- const result = await escrowRequest(
22234
- `/status?${query}`,
22235
- {
22236
- method: "GET",
22237
- headers: {
22238
- ...buildHeaders("", void 0, tenantId),
22239
- "X-Auth-Token": secret
22240
- }
23158
+ const expected = config.escrow?.attestation.mode === "nitro" && "holdId" in params ? recoveryExpectations(params.holdId, params.expectations) : void 0;
23159
+ const status = await fetchEscrowHoldStatus(params);
23160
+ if (expected && status)
23161
+ await verifyPendingHold(status, expected, await fetchVerifiedEnclaveKey());
23162
+ return status;
23163
+ },
23164
+ /** Submit the confirmation code emailed by the enclave for an authority-mode hold. */
23165
+ async confirmEscrowRecovery(params) {
23166
+ let pending;
23167
+ if (config.escrow?.attestation.mode === "nitro") {
23168
+ const expected = recoveryExpectations(params.holdId, params.expectations);
23169
+ if ((params.tenantId ?? tenantId) !== tenantId) throw new EscrowEvidenceError();
23170
+ const identity = await fetchVerifiedEnclaveKey();
23171
+ const status = await fetchEscrowHoldStatus({
23172
+ holdId: params.holdId,
23173
+ resumeToken: params.resumeToken
23174
+ });
23175
+ if (!status?.holdRecord) {
23176
+ throw new EscrowEvidenceError();
22241
23177
  }
22242
- );
22243
- return result.hold;
23178
+ const holdRecord = await verifyPendingHold(status, expected, identity);
23179
+ pending = { identity, holdRecord, expected };
23180
+ }
23181
+ const confirm = /* @__PURE__ */ __name(() => escrowRequest("/confirm", {
23182
+ method: "POST",
23183
+ headers: buildHeaders("", void 0, params.tenantId ?? tenantId),
23184
+ body: JSON.stringify({
23185
+ holdId: params.holdId,
23186
+ resumeToken: params.resumeToken,
23187
+ code: params.code
23188
+ })
23189
+ }), "confirm");
23190
+ const result = await confirm();
23191
+ if (pending) {
23192
+ if (!result.receipt) {
23193
+ throw new EscrowEvidenceError();
23194
+ }
23195
+ const receipt = await verifyEscrowHoldConfirmation(
23196
+ result.receipt,
23197
+ pending.holdRecord,
23198
+ pending.expected,
23199
+ pending.identity
23200
+ );
23201
+ if (receipt.kind === "commit") {
23202
+ const readback = await confirm();
23203
+ const snapshot = await verifyEscrowHoldConfirmation(
23204
+ readback.receipt,
23205
+ pending.holdRecord,
23206
+ pending.expected,
23207
+ pending.identity
23208
+ );
23209
+ if (snapshot.kind !== "snapshot") throw new EscrowEvidenceError();
23210
+ }
23211
+ }
23212
+ return { success: true };
23213
+ },
23214
+ /** Request a fresh confirmation code for a hold awaiting confirmation. */
23215
+ async resendEscrowRecoveryCode(params) {
23216
+ return escrowRequest("/resend", {
23217
+ method: "POST",
23218
+ headers: buildHeaders("", void 0, params.tenantId ?? tenantId),
23219
+ body: JSON.stringify({ holdId: params.holdId, resumeToken: params.resumeToken })
23220
+ });
22244
23221
  },
22245
23222
  /** Cancel a pending hold with a fresh DID-owner proof. */
22246
23223
  async cancelEscrowRecovery(params) {
@@ -22272,8 +23249,19 @@ function createSSSStrategy(config) {
22272
23249
  const start = input.method === "escrow-pin" ? await this.startEscrowRecovery({
22273
23250
  token,
22274
23251
  providerType,
23252
+ expectedDid: input.expectedDid,
22275
23253
  options: { releasePolicy: "pin" }
22276
23254
  }) : input;
23255
+ const expected = config.escrow?.attestation.mode === "nitro" ? recoveryExpectations(start.holdId, start.expectations) : void 0;
23256
+ if (expected) {
23257
+ if (!start.resumeToken) throw new EscrowEvidenceError();
23258
+ const status = await fetchEscrowHoldStatus({
23259
+ holdId: start.holdId,
23260
+ resumeToken: start.resumeToken
23261
+ });
23262
+ if (!status) throw new EscrowEvidenceError();
23263
+ await verifyPendingHold(status, expected, await fetchVerifiedEnclaveKey());
23264
+ }
22277
23265
  let pinProof;
22278
23266
  if (input.method === "escrow-pin") {
22279
23267
  if (!("pinSalt" in start) || !start.pinSalt || !start.resumeToken)
@@ -22297,6 +23285,8 @@ function createSSSStrategy(config) {
22297
23285
  material.sealedShare,
22298
23286
  start.clientEphemeralPrivateKey
22299
23287
  );
23288
+ if (expected && (plaintext.did !== expected.did || plaintext.holdId !== expected.holdId))
23289
+ throw new EscrowEvidenceError();
22300
23290
  if (plaintext.holdId !== start.holdId || plaintext.did !== material.primaryDid || plaintext.shareVersion !== material.shareVersion) {
22301
23291
  throw new Error("Escrow recovery material does not match the request");
22302
23292
  }
@@ -22710,10 +23700,10 @@ function createSSSStrategy(config) {
22710
23700
  const pending = pendingPhraseConfirmation;
22711
23701
  if (!pending) throw new Error("Generate a new recovery phrase to continue");
22712
23702
  const phraseWords = pending.phrase.trim().toLowerCase().split(/\s+/);
22713
- const matches = input.challengeWords.length === pending.challengeWordIndices.length && pending.challengeWordIndices.every(
23703
+ const matches2 = input.challengeWords.length === pending.challengeWordIndices.length && pending.challengeWordIndices.every(
22714
23704
  (wordIndex, answerIndex) => input.challengeWords[answerIndex]?.trim().toLowerCase() === phraseWords[wordIndex]
22715
23705
  );
22716
- if (!matches) {
23706
+ if (!matches2) {
22717
23707
  throw new Error("Those words do not match. Check your phrase and try again.");
22718
23708
  }
22719
23709
  } else if (input.method === "backup") {
@@ -23099,6 +24089,7 @@ function createSSSStrategy(config) {
23099
24089
  },
23100
24090
  async cleanup() {
23101
24091
  storageGeneration++;
24092
+ verifiedEscrowEnvelope = void 0;
23102
24093
  await Promise.allSettled(localWrites);
23103
24094
  }
23104
24095
  };
@@ -23329,9 +24320,11 @@ export {
23329
24320
  ESCROW_ALGORITHM,
23330
24321
  ESCROW_BLOB_INFO,
23331
24322
  ESCROW_CLIENT_KEY_ID,
24323
+ ESCROW_ENROLLMENT_INTENT_DOMAIN,
23332
24324
  ESCROW_ENVELOPE_VERSION,
23333
24325
  ESCROW_PIN_MAX_ATTEMPTS,
23334
24326
  ESCROW_RELEASE_INFO,
24327
+ EscrowEvidenceError,
23335
24328
  EscrowHoldRestartThrottledError,
23336
24329
  EscrowPinLockedError,
23337
24330
  EscrowPinMismatchError,
@@ -23359,6 +24352,7 @@ export {
23359
24352
  constantTimeEqualHex,
23360
24353
  countWords,
23361
24354
  createAdaptiveStorage,
24355
+ createEscrowEnrollmentIntent,
23362
24356
  createPasskeyCredential,
23363
24357
  createQrLoginSession,
23364
24358
  createSSSKeyManager,
@@ -23372,6 +24366,7 @@ export {
23372
24366
  deriveKeyFromPasskey,
23373
24367
  deriveKeyFromPassword,
23374
24368
  derivePinProof,
24369
+ encodeEscrowEnrollmentIntent,
23375
24370
  encryptEmailRelayPayload,
23376
24371
  encryptEscrowBlob,
23377
24372
  encryptShareForTransfer,
@@ -23384,6 +24379,7 @@ export {
23384
24379
  generatePinSalt,
23385
24380
  generateRecoveryPhrase,
23386
24381
  getDeviceShare,
24382
+ getEscrowBlobIdentity,
23387
24383
  getQrLoginSessionInfo,
23388
24384
  getShareVersion,
23389
24385
  hasDeviceShare,
@@ -23405,9 +24401,11 @@ export {
23405
24401
  reconstructFromShares,
23406
24402
  reconstructPrivateKey,
23407
24403
  recoveryPhraseToShare,
24404
+ resolveEscrowEnrollmentAction,
23408
24405
  sealEscrowRelease,
23409
24406
  setPublicComputerMode,
23410
24407
  shareToRecoveryPhrase,
24408
+ signEscrowEnrollmentIntent,
23411
24409
  splitAndVerify,
23412
24410
  splitPrivateKey,
23413
24411
  storeDeviceShare,
@@ -23415,6 +24413,11 @@ export {
23415
24413
  validatePin,
23416
24414
  validateRecoveryPhrase,
23417
24415
  verifyEnclaveAttestation,
24416
+ verifyEscrowAuthorityState,
24417
+ verifyEscrowCodeDelivery,
24418
+ verifyEscrowEnrollmentCommit,
24419
+ verifyEscrowHoldState,
23418
24420
  verifyNitroAttestationDocument,
23419
- verifyStoredShares
24421
+ verifyStoredShares,
24422
+ violatesCarryContactRule
23420
24423
  };