@learncard/sss-key-manager 0.2.1 → 0.2.2

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 (44) 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 +112 -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 +1592 -213
  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 +1107 -163
  22. package/dist/sss-key-manager.esm.js.map +4 -4
  23. package/dist/sss-strategy.d.ts.map +1 -1
  24. package/dist/types.d.ts +16 -1
  25. package/dist/types.d.ts.map +1 -1
  26. package/package.json +2 -2
  27. package/src/__fixtures__/escrow-authority-v1.json +96 -0
  28. package/src/__fixtures__/escrow-carry-contact-contract-v1.json +33 -0
  29. package/src/__fixtures__/escrow-enrollment-intent-v1.json +26 -0
  30. package/src/__fixtures__/nitro-attestation-real/staging-nsm-document.json +16 -0
  31. package/src/escrow-attestation.ts +10 -9
  32. package/src/escrow-authority.test.ts +643 -0
  33. package/src/escrow-authority.ts +597 -0
  34. package/src/escrow-authority.types.ts +56 -0
  35. package/src/escrow-crypto.ts +39 -1
  36. package/src/escrow-enrollment-intent.test.ts +224 -0
  37. package/src/escrow-enrollment-intent.ts +236 -0
  38. package/src/escrow-enrollment-intent.types.ts +28 -0
  39. package/src/escrow-nitro-attestation.test.ts +72 -0
  40. package/src/escrow-nitro-attestation.ts +34 -3
  41. package/src/index.ts +27 -0
  42. package/src/sss-authority.test.ts +889 -0
  43. package/src/sss-strategy.ts +479 -38
  44. package/src/types.ts +22 -3
@@ -568,7 +568,7 @@ var require_types_cjs_development = __commonJS({
568
568
  _function: /* @__PURE__ */ __name(() => _function, "_function"),
569
569
  any: /* @__PURE__ */ __name(() => any, "any"),
570
570
  array: /* @__PURE__ */ __name(() => array, "array"),
571
- base64: /* @__PURE__ */ __name(() => base642, "base64"),
571
+ base64: /* @__PURE__ */ __name(() => base6422, "base64"),
572
572
  base64url: /* @__PURE__ */ __name(() => base64url2, "base64url"),
573
573
  bigint: /* @__PURE__ */ __name(() => bigint2, "bigint"),
574
574
  boolean: /* @__PURE__ */ __name(() => boolean2, "boolean"),
@@ -609,7 +609,7 @@ var require_types_cjs_development = __commonJS({
609
609
  gt: /* @__PURE__ */ __name(() => _gt, "gt"),
610
610
  gte: /* @__PURE__ */ __name(() => _gte, "gte"),
611
611
  guid: /* @__PURE__ */ __name(() => guid2, "guid"),
612
- hash: /* @__PURE__ */ __name(() => hash, "hash"),
612
+ hash: /* @__PURE__ */ __name(() => hash2, "hash"),
613
613
  hex: /* @__PURE__ */ __name(() => hex22, "hex"),
614
614
  hostname: /* @__PURE__ */ __name(() => hostname2, "hostname"),
615
615
  httpUrl: /* @__PURE__ */ __name(() => httpUrl, "httpUrl"),
@@ -658,7 +658,7 @@ var require_types_cjs_development = __commonJS({
658
658
  nullable: /* @__PURE__ */ __name(() => nullable, "nullable"),
659
659
  nullish: /* @__PURE__ */ __name(() => nullish2, "nullish"),
660
660
  number: /* @__PURE__ */ __name(() => number2, "number"),
661
- object: /* @__PURE__ */ __name(() => object, "object"),
661
+ object: /* @__PURE__ */ __name(() => object2, "object"),
662
662
  optional: /* @__PURE__ */ __name(() => optional, "optional"),
663
663
  overwrite: /* @__PURE__ */ __name(() => _overwrite, "overwrite"),
664
664
  parse: /* @__PURE__ */ __name(() => parse2, "parse"),
@@ -1020,9 +1020,9 @@ var require_types_cjs_development = __commonJS({
1020
1020
  inst._zod.traits.add(name);
1021
1021
  initializer3(inst, def);
1022
1022
  const proto = _.prototype;
1023
- const keys = Object.keys(proto);
1024
- for (let i = 0; i < keys.length; i++) {
1025
- const k = keys[i];
1023
+ const keys2 = Object.keys(proto);
1024
+ for (let i = 0; i < keys2.length; i++) {
1025
+ const k = keys2[i];
1026
1026
  if (!(k in inst)) {
1027
1027
  inst[k] = proto[k].bind(inst);
1028
1028
  }
@@ -1244,9 +1244,9 @@ var require_types_cjs_development = __commonJS({
1244
1244
  __name(floatSafeRemainder, "floatSafeRemainder");
1245
1245
  __name2(floatSafeRemainder, "floatSafeRemainder");
1246
1246
  var EVALUATING = /* @__PURE__ */ Symbol("evaluating");
1247
- function defineLazy(object2, key, getter) {
1247
+ function defineLazy(object22, key, getter) {
1248
1248
  let value = void 0;
1249
- Object.defineProperty(object2, key, {
1249
+ Object.defineProperty(object22, key, {
1250
1250
  get() {
1251
1251
  if (value === EVALUATING) {
1252
1252
  return void 0;
@@ -1258,7 +1258,7 @@ var require_types_cjs_development = __commonJS({
1258
1258
  return value;
1259
1259
  },
1260
1260
  set(v) {
1261
- Object.defineProperty(object2, key, {
1261
+ Object.defineProperty(object22, key, {
1262
1262
  value: v
1263
1263
  // configurable: true,
1264
1264
  });
@@ -1306,12 +1306,12 @@ var require_types_cjs_development = __commonJS({
1306
1306
  __name(getElementAtPath, "getElementAtPath");
1307
1307
  __name2(getElementAtPath, "getElementAtPath");
1308
1308
  function promiseAllObject(promisesObj) {
1309
- const keys = Object.keys(promisesObj);
1310
- const promises = keys.map((key) => promisesObj[key]);
1309
+ const keys2 = Object.keys(promisesObj);
1310
+ const promises = keys2.map((key) => promisesObj[key]);
1311
1311
  return Promise.all(promises).then((results) => {
1312
1312
  const resolvedObj = {};
1313
- for (let i = 0; i < keys.length; i++) {
1314
- resolvedObj[keys[i]] = results[i];
1313
+ for (let i = 0; i < keys2.length; i++) {
1314
+ resolvedObj[keys2[i]] = results[i];
1315
1315
  }
1316
1316
  return resolvedObj;
1317
1317
  });
@@ -1851,18 +1851,18 @@ var require_types_cjs_development = __commonJS({
1851
1851
  __name2(cleanEnum, "cleanEnum");
1852
1852
  function base64ToUint8Array(base643) {
1853
1853
  const binaryString = atob(base643);
1854
- const bytes2 = new Uint8Array(binaryString.length);
1854
+ const bytes3 = new Uint8Array(binaryString.length);
1855
1855
  for (let i = 0; i < binaryString.length; i++) {
1856
- bytes2[i] = binaryString.charCodeAt(i);
1856
+ bytes3[i] = binaryString.charCodeAt(i);
1857
1857
  }
1858
- return bytes2;
1858
+ return bytes3;
1859
1859
  }
1860
1860
  __name(base64ToUint8Array, "base64ToUint8Array");
1861
1861
  __name2(base64ToUint8Array, "base64ToUint8Array");
1862
- function uint8ArrayToBase64(bytes2) {
1862
+ function uint8ArrayToBase64(bytes3) {
1863
1863
  let binaryString = "";
1864
- for (let i = 0; i < bytes2.length; i++) {
1865
- binaryString += String.fromCharCode(bytes2[i]);
1864
+ for (let i = 0; i < bytes3.length; i++) {
1865
+ binaryString += String.fromCharCode(bytes3[i]);
1866
1866
  }
1867
1867
  return btoa(binaryString);
1868
1868
  }
@@ -1875,8 +1875,8 @@ var require_types_cjs_development = __commonJS({
1875
1875
  }
1876
1876
  __name(base64urlToUint8Array, "base64urlToUint8Array");
1877
1877
  __name2(base64urlToUint8Array, "base64urlToUint8Array");
1878
- function uint8ArrayToBase64url(bytes2) {
1879
- return uint8ArrayToBase64(bytes2).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
1878
+ function uint8ArrayToBase64url(bytes3) {
1879
+ return uint8ArrayToBase64(bytes3).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
1880
1880
  }
1881
1881
  __name(uint8ArrayToBase64url, "uint8ArrayToBase64url");
1882
1882
  __name2(uint8ArrayToBase64url, "uint8ArrayToBase64url");
@@ -1885,16 +1885,16 @@ var require_types_cjs_development = __commonJS({
1885
1885
  if (cleanHex.length % 2 !== 0) {
1886
1886
  throw new Error("Invalid hex string length");
1887
1887
  }
1888
- const bytes2 = new Uint8Array(cleanHex.length / 2);
1888
+ const bytes3 = new Uint8Array(cleanHex.length / 2);
1889
1889
  for (let i = 0; i < cleanHex.length; i += 2) {
1890
- bytes2[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
1890
+ bytes3[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
1891
1891
  }
1892
- return bytes2;
1892
+ return bytes3;
1893
1893
  }
1894
1894
  __name(hexToUint8Array, "hexToUint8Array");
1895
1895
  __name2(hexToUint8Array, "hexToUint8Array");
1896
- function uint8ArrayToHex(bytes2) {
1897
- return Array.from(bytes2).map((b) => b.toString(16).padStart(2, "0")).join("");
1896
+ function uint8ArrayToHex(bytes3) {
1897
+ return Array.from(bytes3).map((b) => b.toString(16).padStart(2, "0")).join("");
1898
1898
  }
1899
1899
  __name(uint8ArrayToHex, "uint8ArrayToHex");
1900
1900
  __name2(uint8ArrayToHex, "uint8ArrayToHex");
@@ -2143,7 +2143,7 @@ var require_types_cjs_development = __commonJS({
2143
2143
  var safeDecodeAsync = /* @__PURE__ */ _safeDecodeAsync($ZodRealError);
2144
2144
  var regexes_exports = {};
2145
2145
  __export(regexes_exports, {
2146
- base64: /* @__PURE__ */ __name(() => base64, "base64"),
2146
+ base64: /* @__PURE__ */ __name(() => base642, "base64"),
2147
2147
  base64url: /* @__PURE__ */ __name(() => base64url, "base64url"),
2148
2148
  bigint: /* @__PURE__ */ __name(() => bigint, "bigint"),
2149
2149
  boolean: /* @__PURE__ */ __name(() => boolean, "boolean"),
@@ -2166,7 +2166,7 @@ var require_types_cjs_development = __commonJS({
2166
2166
  html5Email: /* @__PURE__ */ __name(() => html5Email, "html5Email"),
2167
2167
  httpProtocol: /* @__PURE__ */ __name(() => httpProtocol, "httpProtocol"),
2168
2168
  idnEmail: /* @__PURE__ */ __name(() => idnEmail, "idnEmail"),
2169
- integer: /* @__PURE__ */ __name(() => integer, "integer"),
2169
+ integer: /* @__PURE__ */ __name(() => integer3, "integer"),
2170
2170
  ipv4: /* @__PURE__ */ __name(() => ipv4, "ipv4"),
2171
2171
  ipv6: /* @__PURE__ */ __name(() => ipv6, "ipv6"),
2172
2172
  ksuid: /* @__PURE__ */ __name(() => ksuid, "ksuid"),
@@ -2240,7 +2240,7 @@ var require_types_cjs_development = __commonJS({
2240
2240
  }, "mac");
2241
2241
  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
2242
  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}=))?$/;
2243
+ var base642 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
2244
2244
  var base64url = /^[A-Za-z0-9_-]*$/;
2245
2245
  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
2246
  var domain = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
@@ -2277,7 +2277,7 @@ var require_types_cjs_development = __commonJS({
2277
2277
  return new RegExp(`^${regex}$`);
2278
2278
  }, "string");
2279
2279
  var bigint = /^-?\d+n?$/;
2280
- var integer = /^-?\d+$/;
2280
+ var integer3 = /^-?\d+$/;
2281
2281
  var number = /^-?\d+(?:\.\d+)?$/;
2282
2282
  var boolean = /^(?:true|false)$/i;
2283
2283
  var _null = /^null$/i;
@@ -2411,7 +2411,7 @@ var require_types_cjs_development = __commonJS({
2411
2411
  bag.minimum = minimum;
2412
2412
  bag.maximum = maximum;
2413
2413
  if (isInt)
2414
- bag.pattern = integer;
2414
+ bag.pattern = integer3;
2415
2415
  });
2416
2416
  inst._zod.check = (payload) => {
2417
2417
  const input = payload.value;
@@ -3251,7 +3251,7 @@ var require_types_cjs_development = __commonJS({
3251
3251
  __name(isValidBase64, "isValidBase64");
3252
3252
  __name2(isValidBase64, "isValidBase64");
3253
3253
  var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => {
3254
- def.pattern ?? (def.pattern = base64);
3254
+ def.pattern ?? (def.pattern = base642);
3255
3255
  $ZodStringFormat.init(inst, def);
3256
3256
  inst._zod.bag.contentEncoding = "base64";
3257
3257
  inst._zod.check = (payload) => {
@@ -3597,8 +3597,8 @@ var require_types_cjs_development = __commonJS({
3597
3597
  __name(handlePropertyResult, "handlePropertyResult");
3598
3598
  __name2(handlePropertyResult, "handlePropertyResult");
3599
3599
  function normalizeDef(def) {
3600
- const keys = Object.keys(def.shape);
3601
- for (const k of keys) {
3600
+ const keys2 = Object.keys(def.shape);
3601
+ for (const k of keys2) {
3602
3602
  if (!def.shape?.[k]?._zod?.traits?.has("$ZodType")) {
3603
3603
  throw new Error(`Invalid element at key "${k}": expected a Zod schema`);
3604
3604
  }
@@ -3606,9 +3606,9 @@ var require_types_cjs_development = __commonJS({
3606
3606
  const okeys = optionalKeys(def.shape);
3607
3607
  return {
3608
3608
  ...def,
3609
- keys,
3610
- keySet: new Set(keys),
3611
- numKeys: keys.length,
3609
+ keys: keys2,
3610
+ keySet: new Set(keys2),
3611
+ numKeys: keys2.length,
3612
3612
  optionalKeys: new Set(okeys)
3613
3613
  };
3614
3614
  }
@@ -8370,8 +8370,8 @@ var require_types_cjs_development = __commonJS({
8370
8370
  }
8371
8371
  __name(ko_default, "ko_default");
8372
8372
  __name2(ko_default, "default");
8373
- var capitalizeFirstCharacter = /* @__PURE__ */ __name2((text) => {
8374
- return text.charAt(0).toUpperCase() + text.slice(1);
8373
+ var capitalizeFirstCharacter = /* @__PURE__ */ __name2((text2) => {
8374
+ return text2.charAt(0).toUpperCase() + text2.slice(1);
8375
8375
  }, "capitalizeFirstCharacter");
8376
8376
  function getUnitTypeFromNumber(number4) {
8377
8377
  const abs = Math.abs(number4);
@@ -13491,7 +13491,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13491
13491
  _function: /* @__PURE__ */ __name(() => _function, "_function"),
13492
13492
  any: /* @__PURE__ */ __name(() => any, "any"),
13493
13493
  array: /* @__PURE__ */ __name(() => array, "array"),
13494
- base64: /* @__PURE__ */ __name(() => base642, "base64"),
13494
+ base64: /* @__PURE__ */ __name(() => base6422, "base64"),
13495
13495
  base64url: /* @__PURE__ */ __name(() => base64url2, "base64url"),
13496
13496
  bigint: /* @__PURE__ */ __name(() => bigint2, "bigint"),
13497
13497
  boolean: /* @__PURE__ */ __name(() => boolean2, "boolean"),
@@ -13516,7 +13516,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13516
13516
  float64: /* @__PURE__ */ __name(() => float64, "float64"),
13517
13517
  function: /* @__PURE__ */ __name(() => _function, "function"),
13518
13518
  guid: /* @__PURE__ */ __name(() => guid2, "guid"),
13519
- hash: /* @__PURE__ */ __name(() => hash, "hash"),
13519
+ hash: /* @__PURE__ */ __name(() => hash2, "hash"),
13520
13520
  hex: /* @__PURE__ */ __name(() => hex22, "hex"),
13521
13521
  hostname: /* @__PURE__ */ __name(() => hostname2, "hostname"),
13522
13522
  httpUrl: /* @__PURE__ */ __name(() => httpUrl, "httpUrl"),
@@ -13548,7 +13548,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13548
13548
  nullable: /* @__PURE__ */ __name(() => nullable, "nullable"),
13549
13549
  nullish: /* @__PURE__ */ __name(() => nullish2, "nullish"),
13550
13550
  number: /* @__PURE__ */ __name(() => number2, "number"),
13551
- object: /* @__PURE__ */ __name(() => object, "object"),
13551
+ object: /* @__PURE__ */ __name(() => object2, "object"),
13552
13552
  optional: /* @__PURE__ */ __name(() => optional, "optional"),
13553
13553
  partialRecord: /* @__PURE__ */ __name(() => partialRecord, "partialRecord"),
13554
13554
  pipe: /* @__PURE__ */ __name(() => pipe, "pipe"),
@@ -14149,11 +14149,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14149
14149
  $ZodBase64.init(inst, def);
14150
14150
  ZodStringFormat.init(inst, def);
14151
14151
  });
14152
- function base642(params) {
14152
+ function base6422(params) {
14153
14153
  return /* @__PURE__ */ _base64(ZodBase64, params);
14154
14154
  }
14155
- __name(base642, "base642");
14156
- __name2(base642, "base64");
14155
+ __name(base6422, "base642");
14156
+ __name2(base6422, "base64");
14157
14157
  var ZodBase64URL = /* @__PURE__ */ $constructor("ZodBase64URL", (inst, def) => {
14158
14158
  $ZodBase64URL.init(inst, def);
14159
14159
  ZodStringFormat.init(inst, def);
@@ -14200,7 +14200,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14200
14200
  }
14201
14201
  __name(hex22, "hex2");
14202
14202
  __name2(hex22, "hex");
14203
- function hash(alg, params) {
14203
+ function hash2(alg, params) {
14204
14204
  const enc = params?.enc ?? "hex";
14205
14205
  const format = `${alg}_${enc}`;
14206
14206
  const regex = regexes_exports[format];
@@ -14208,8 +14208,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14208
14208
  throw new Error(`Unrecognized hash format: ${format}`);
14209
14209
  return /* @__PURE__ */ _stringFormat(ZodCustomStringFormat, format, regex, params);
14210
14210
  }
14211
- __name(hash, "hash");
14212
- __name2(hash, "hash");
14211
+ __name(hash2, "hash");
14212
+ __name2(hash2, "hash");
14213
14213
  var ZodNumber = /* @__PURE__ */ $constructor("ZodNumber", (inst, def) => {
14214
14214
  $ZodNumber.init(inst, def);
14215
14215
  ZodType.init(inst, def);
@@ -14521,7 +14521,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14521
14521
  }
14522
14522
  });
14523
14523
  });
14524
- function object(shape, params) {
14524
+ function object2(shape, params) {
14525
14525
  const def = {
14526
14526
  type: "object",
14527
14527
  shape: shape ?? {},
@@ -14529,8 +14529,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14529
14529
  };
14530
14530
  return new ZodObject(def);
14531
14531
  }
14532
- __name(object, "object");
14533
- __name2(object, "object");
14532
+ __name(object2, "object");
14533
+ __name2(object2, "object");
14534
14534
  function strictObject(shape, params) {
14535
14535
  return new ZodObject({
14536
14536
  type: "object",
@@ -14725,11 +14725,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14725
14725
  inst._zod.processJSONSchema = (ctx, json2, params) => enumProcessor(inst, ctx, json2, params);
14726
14726
  inst.enum = def.entries;
14727
14727
  inst.options = Object.values(def.entries);
14728
- const keys = new Set(Object.keys(def.entries));
14728
+ const keys2 = new Set(Object.keys(def.entries));
14729
14729
  inst.extract = (values, params) => {
14730
14730
  const newEntries = {};
14731
14731
  for (const value of values) {
14732
- if (keys.has(value)) {
14732
+ if (keys2.has(value)) {
14733
14733
  newEntries[value] = def.entries[value];
14734
14734
  } else
14735
14735
  throw new Error(`Key ${value} not found in enum`);
@@ -14744,7 +14744,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14744
14744
  inst.exclude = (values, params) => {
14745
14745
  const newEntries = { ...def.entries };
14746
14746
  for (const value of values) {
14747
- if (keys.has(value)) {
14747
+ if (keys2.has(value)) {
14748
14748
  delete newEntries[value];
14749
14749
  } else
14750
14750
  throw new Error(`Key ${value} not found in enum`);
@@ -15880,7 +15880,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15880
15880
  verificationMethod: external_exports.string(),
15881
15881
  jws: external_exports.string().optional()
15882
15882
  }).catchall(external_exports.any()).superRefine((proof, ctx) => {
15883
- if (proof.created === void 0 && (proof.type !== "DataIntegrityProof" || proof.cryptosuite !== "ecdsa-rdfc-2019")) {
15883
+ if (proof.created === void 0 && // These standardized Data Integrity suites make created optional.
15884
+ // Keep the prior requirement for legacy and unknown proof suites.
15885
+ // https://www.w3.org/TR/vc-di-eddsa/#proof-configuration-eddsa-jcs-2022
15886
+ // https://www.w3.org/TR/vc-di-bbs/#proof-configuration
15887
+ (proof.type !== "DataIntegrityProof" || ![
15888
+ "ecdsa-rdfc-2019",
15889
+ "ecdsa-jcs-2019",
15890
+ "ecdsa-sd-2023",
15891
+ "eddsa-rdfc-2022",
15892
+ "eddsa-jcs-2022",
15893
+ "bbs-2023"
15894
+ ].includes(proof.cryptosuite))) {
15884
15895
  ctx.addIssue({
15885
15896
  code: "custom",
15886
15897
  path: ["created"],
@@ -18519,12 +18530,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18519
18530
  }
18520
18531
  return table;
18521
18532
  })();
18522
- var encodeBase64Url = /* @__PURE__ */ __name2((bytes2) => {
18533
+ var encodeBase64Url = /* @__PURE__ */ __name2((bytes3) => {
18523
18534
  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];
18535
+ for (let index = 0; index < bytes3.length; index += 3) {
18536
+ const first = bytes3[index];
18537
+ const second = bytes3[index + 1];
18538
+ const third = bytes3[index + 2];
18528
18539
  output += BASE64URL_ALPHABET[first >> 2];
18529
18540
  output += BASE64URL_ALPHABET[(first & 3) << 4 | (second ?? 0) >> 4];
18530
18541
  if (second === void 0) break;
@@ -18565,8 +18576,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18565
18576
  }, "isCanonicalBase64Url");
18566
18577
  var decodedBase64UrlByteLength = /* @__PURE__ */ __name2((value) => decodeBase64Url(value)?.length ?? null, "decodedBase64UrlByteLength");
18567
18578
  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`
18579
+ var canonicalBase64Url = /* @__PURE__ */ __name2((bytes3) => external_exports.string().refine((value) => isCanonicalBase64Url(value, bytes3), {
18580
+ message: `must be canonical base64url for exactly ${bytes3} bytes`
18570
18581
  }), "canonicalBase64Url");
18571
18582
  var ShareLinkIdValidator = canonicalBase64Url(SHARE_LINK_ID_BYTES);
18572
18583
  var ShareContentKeyValidator = canonicalBase64Url(SHARE_CONTENT_KEY_BYTES);
@@ -19165,37 +19176,37 @@ var DEFAULT_KDF_PARAMS = {
19165
19176
  parallelism: ARGON2_PARALLELISM
19166
19177
  };
19167
19178
  function bufferToBase64(buf) {
19168
- const bytes2 = new Uint8Array(buf);
19179
+ const bytes3 = new Uint8Array(buf);
19169
19180
  let binary = "";
19170
- for (let i = 0; i < bytes2.byteLength; i++) {
19171
- binary += String.fromCharCode(bytes2[i]);
19181
+ for (let i = 0; i < bytes3.byteLength; i++) {
19182
+ binary += String.fromCharCode(bytes3[i]);
19172
19183
  }
19173
19184
  return btoa(binary);
19174
19185
  }
19175
19186
  __name(bufferToBase64, "bufferToBase64");
19176
19187
  function base64ToBuffer(b64) {
19177
19188
  const binary = atob(b64);
19178
- const bytes2 = new Uint8Array(binary.length);
19189
+ const bytes3 = new Uint8Array(binary.length);
19179
19190
  for (let i = 0; i < binary.length; i++) {
19180
- bytes2[i] = binary.charCodeAt(i);
19191
+ bytes3[i] = binary.charCodeAt(i);
19181
19192
  }
19182
- return bytes2;
19193
+ return bytes3;
19183
19194
  }
19184
19195
  __name(base64ToBuffer, "base64ToBuffer");
19185
19196
  function hexToBytes(hex2) {
19186
- const bytes2 = new Uint8Array(hex2.length / 2);
19197
+ const bytes3 = new Uint8Array(hex2.length / 2);
19187
19198
  for (let i = 0; i < hex2.length; i += 2) {
19188
- bytes2[i / 2] = parseInt(hex2.slice(i, i + 2), 16);
19199
+ bytes3[i / 2] = parseInt(hex2.slice(i, i + 2), 16);
19189
19200
  }
19190
- return bytes2;
19201
+ return bytes3;
19191
19202
  }
19192
19203
  __name(hexToBytes, "hexToBytes");
19193
- function bytesToHex(bytes2) {
19194
- return Array.from(bytes2).map((b) => b.toString(16).padStart(2, "0")).join("");
19204
+ function bytesToHex(bytes3) {
19205
+ return Array.from(bytes3).map((b) => b.toString(16).padStart(2, "0")).join("");
19195
19206
  }
19196
19207
  __name(bytesToHex, "bytesToHex");
19197
19208
  async function deriveKeyFromPassword(password, salt, params = DEFAULT_KDF_PARAMS) {
19198
- const hash = await argon2id({
19209
+ const hash2 = await argon2id({
19199
19210
  password,
19200
19211
  salt,
19201
19212
  iterations: params.timeCost,
@@ -19204,7 +19215,7 @@ async function deriveKeyFromPassword(password, salt, params = DEFAULT_KDF_PARAMS
19204
19215
  hashLength: ARGON2_HASH_LENGTH,
19205
19216
  outputType: "binary"
19206
19217
  });
19207
- return Uint8Array.from(hash);
19218
+ return Uint8Array.from(hash2);
19208
19219
  }
19209
19220
  __name(deriveKeyFromPassword, "deriveKeyFromPassword");
19210
19221
  async function encryptWithPassword(plaintext, password) {
@@ -19358,21 +19369,21 @@ async function getOrCreateMasterKey() {
19358
19369
  }
19359
19370
  __name(getOrCreateMasterKey, "getOrCreateMasterKey");
19360
19371
  function bufferToBase642(buf) {
19361
- const bytes2 = new Uint8Array(buf);
19372
+ const bytes3 = new Uint8Array(buf);
19362
19373
  let binary = "";
19363
- for (let i = 0; i < bytes2.byteLength; i++) {
19364
- binary += String.fromCharCode(bytes2[i]);
19374
+ for (let i = 0; i < bytes3.byteLength; i++) {
19375
+ binary += String.fromCharCode(bytes3[i]);
19365
19376
  }
19366
19377
  return btoa(binary);
19367
19378
  }
19368
19379
  __name(bufferToBase642, "bufferToBase64");
19369
19380
  function base64ToBuffer2(b64) {
19370
19381
  const binary = atob(b64);
19371
- const bytes2 = new Uint8Array(binary.length);
19382
+ const bytes3 = new Uint8Array(binary.length);
19372
19383
  for (let i = 0; i < binary.length; i++) {
19373
- bytes2[i] = binary.charCodeAt(i);
19384
+ bytes3[i] = binary.charCodeAt(i);
19374
19385
  }
19375
- return bytes2.buffer;
19386
+ return bytes3.buffer;
19376
19387
  }
19377
19388
  __name(base64ToBuffer2, "base64ToBuffer");
19378
19389
  async function encryptShare(share, id) {
@@ -19780,21 +19791,21 @@ async function getWordlist() {
19780
19791
  return wordlistPromise;
19781
19792
  }
19782
19793
  __name(getWordlist, "getWordlist");
19783
- function bytesToBits(bytes2) {
19784
- return Array.from(bytes2).map((b) => b.toString(2).padStart(8, "0")).join("");
19794
+ function bytesToBits(bytes3) {
19795
+ return Array.from(bytes3).map((b) => b.toString(2).padStart(8, "0")).join("");
19785
19796
  }
19786
19797
  __name(bytesToBits, "bytesToBits");
19787
19798
  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);
19799
+ const bytes3 = new Uint8Array(Math.ceil(bits.length / 8));
19800
+ for (let i = 0; i < bytes3.length; i++) {
19801
+ bytes3[i] = parseInt(bits.slice(i * 8, (i + 1) * 8).padEnd(8, "0"), 2);
19791
19802
  }
19792
- return bytes2;
19803
+ return bytes3;
19793
19804
  }
19794
19805
  __name(bitsToBytes, "bitsToBytes");
19795
19806
  async function computeChecksum(data) {
19796
- const hash = await crypto.subtle.digest("SHA-256", data);
19797
- const hashBits = bytesToBits(new Uint8Array(hash));
19807
+ const hash2 = await crypto.subtle.digest("SHA-256", data);
19808
+ const hashBits = bytesToBits(new Uint8Array(hash2));
19798
19809
  const checksumLength = Math.floor(data.length / 4);
19799
19810
  return hashBits.slice(0, checksumLength);
19800
19811
  }
@@ -19827,12 +19838,12 @@ async function recoveryPhraseToShare(phrase) {
19827
19838
  bits += index.toString(2).padStart(11, "0");
19828
19839
  }
19829
19840
  let dataByteCount = 0;
19830
- for (let bytes2 = 1; bytes2 <= 64; bytes2++) {
19831
- const checksumBits2 = Math.floor(bytes2 / 4);
19832
- const totalBits = bytes2 * 8 + checksumBits2;
19841
+ for (let bytes3 = 1; bytes3 <= 64; bytes3++) {
19842
+ const checksumBits2 = Math.floor(bytes3 / 4);
19843
+ const totalBits = bytes3 * 8 + checksumBits2;
19833
19844
  const wordsNeeded = Math.ceil(totalBits / 11);
19834
19845
  if (wordsNeeded === words.length) {
19835
- dataByteCount = bytes2;
19846
+ dataByteCount = bytes3;
19836
19847
  } else if (wordsNeeded > words.length) {
19837
19848
  break;
19838
19849
  }
@@ -20126,9 +20137,9 @@ __name(createSSSKeyManager, "createSSSKeyManager");
20126
20137
  var EMAIL_RELAY_ENVELOPE_VERSION = 1;
20127
20138
  var EMAIL_RELAY_ALGORITHM = "P-256-HKDF-SHA256-AES-256-GCM";
20128
20139
  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);
20140
+ const bytes3 = new TextEncoder().encode(value);
20141
+ const copy = new Uint8Array(new ArrayBuffer(bytes3.byteLength));
20142
+ copy.set(bytes3);
20132
20143
  return copy;
20133
20144
  }, "encodeUtf8");
20134
20145
  var EMAIL_RELAY_INFO = encodeUtf8("learncard-email-relay-v1");
@@ -20147,7 +20158,7 @@ var parseBranding = /* @__PURE__ */ __name((value) => {
20147
20158
  if (value === void 0) return void 0;
20148
20159
  if (!isRecord(value)) throw new Error("Invalid email relay branding");
20149
20160
  const branding = {};
20150
- const keys = [
20161
+ const keys2 = [
20151
20162
  "brandName",
20152
20163
  "logoUrl",
20153
20164
  "logoAlt",
@@ -20159,7 +20170,7 @@ var parseBranding = /* @__PURE__ */ __name((value) => {
20159
20170
  "fromDomain",
20160
20171
  "copyrightHolder"
20161
20172
  ];
20162
- for (const key of keys) {
20173
+ for (const key of keys2) {
20163
20174
  const field = value[key];
20164
20175
  if (field === void 0) continue;
20165
20176
  if (typeof field !== "string" || field.length > 2048) {
@@ -20212,7 +20223,7 @@ var parseEmailRelayPlaintext = /* @__PURE__ */ __name((value) => {
20212
20223
  branding: parseBranding(value.branding)
20213
20224
  };
20214
20225
  }, "parseEmailRelayPlaintext");
20215
- var getAad = /* @__PURE__ */ __name((envelope) => encodeUtf8(`${envelope.version}|${envelope.algorithm}|${envelope.keyId}`), "getAad");
20226
+ var getAad = /* @__PURE__ */ __name((envelope2) => encodeUtf8(`${envelope2.version}|${envelope2.algorithm}|${envelope2.keyId}`), "getAad");
20216
20227
  var deriveAesKey = /* @__PURE__ */ __name(async (sharedSecret, salt, usage) => {
20217
20228
  const keyMaterial = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, [
20218
20229
  "deriveKey"
@@ -20284,7 +20295,7 @@ var encryptEmailRelayPayload = /* @__PURE__ */ __name(async (input, publicKeyBas
20284
20295
  };
20285
20296
  }, "encryptEmailRelayPayload");
20286
20297
  var decryptEmailRelayPayload = /* @__PURE__ */ __name(async (value, privateKeyBase64) => {
20287
- const envelope = parseEmailRelayEnvelope(value);
20298
+ const envelope2 = parseEmailRelayEnvelope(value);
20288
20299
  const privateKey = await crypto.subtle.importKey(
20289
20300
  "pkcs8",
20290
20301
  base64ToBuffer(privateKeyBase64.trim()).buffer,
@@ -20294,7 +20305,7 @@ var decryptEmailRelayPayload = /* @__PURE__ */ __name(async (value, privateKeyBa
20294
20305
  );
20295
20306
  const ephemeralPublicKey = await crypto.subtle.importKey(
20296
20307
  "raw",
20297
- base64ToBuffer(envelope.ephemeralPublicKey).buffer,
20308
+ base64ToBuffer(envelope2.ephemeralPublicKey).buffer,
20298
20309
  { name: "ECDH", namedCurve: "P-256" },
20299
20310
  false,
20300
20311
  []
@@ -20304,13 +20315,13 @@ var decryptEmailRelayPayload = /* @__PURE__ */ __name(async (value, privateKeyBa
20304
20315
  privateKey,
20305
20316
  256
20306
20317
  );
20307
- const salt = base64ToBuffer(envelope.salt);
20308
- const iv = base64ToBuffer(envelope.iv);
20318
+ const salt = base64ToBuffer(envelope2.salt);
20319
+ const iv = base64ToBuffer(envelope2.iv);
20309
20320
  const aesKey = await deriveAesKey(sharedSecret, salt, "decrypt");
20310
20321
  const decrypted = await crypto.subtle.decrypt(
20311
- { name: "AES-GCM", iv, additionalData: getAad(envelope) },
20322
+ { name: "AES-GCM", iv, additionalData: getAad(envelope2) },
20312
20323
  aesKey,
20313
- base64ToBuffer(envelope.ciphertext)
20324
+ base64ToBuffer(envelope2.ciphertext)
20314
20325
  );
20315
20326
  const parsed = JSON.parse(new TextDecoder().decode(decrypted));
20316
20327
  return parseEmailRelayPlaintext(parsed);
@@ -20322,10 +20333,11 @@ var ESCROW_ALGORITHM = "P-256-HKDF-SHA256-AES-256-GCM";
20322
20333
  var ESCROW_BLOB_INFO = "learncard-escrow-blob-v1";
20323
20334
  var ESCROW_RELEASE_INFO = "learncard-escrow-release-v1";
20324
20335
  var ESCROW_CLIENT_KEY_ID = "client";
20336
+ var RECOVERY_LANGUAGES = ["en", "es", "fr", "ar"];
20325
20337
  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);
20338
+ const bytes3 = new TextEncoder().encode(value);
20339
+ const copy = new Uint8Array(new ArrayBuffer(bytes3.byteLength));
20340
+ copy.set(bytes3);
20329
20341
  return copy;
20330
20342
  }, "encodeUtf8");
20331
20343
  var isRecord2 = /* @__PURE__ */ __name((value) => typeof value === "object" && value !== null && !Array.isArray(value), "isRecord");
@@ -20361,6 +20373,22 @@ var parseEscrowBlobPlaintext = /* @__PURE__ */ __name((value) => {
20361
20373
  if (typeof value.shareVersion !== "number" || !Number.isInteger(value.shareVersion) || value.shareVersion <= 0) {
20362
20374
  throw new Error("Invalid escrow shareVersion");
20363
20375
  }
20376
+ if (value.recoveryEmail !== void 0 !== (value.recoveryLanguage !== void 0)) {
20377
+ throw new Error("Invalid escrow recoveryEmail");
20378
+ }
20379
+ let recoveryContact = {};
20380
+ if (value.recoveryEmail !== void 0) {
20381
+ const email = requireString2(value, "recoveryEmail", { min: 3, max: 254 }).trim().toLowerCase();
20382
+ const at = email.split("@");
20383
+ 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(".")) {
20384
+ throw new Error("Invalid escrow recoveryEmail");
20385
+ }
20386
+ const language = requireString2(value, "recoveryLanguage", { min: 2, max: 2 });
20387
+ if (!RECOVERY_LANGUAGES.includes(language)) {
20388
+ throw new Error("Invalid escrow recoveryLanguage");
20389
+ }
20390
+ recoveryContact = { recoveryEmail: email, recoveryLanguage: language };
20391
+ }
20364
20392
  return {
20365
20393
  version: ESCROW_ENVELOPE_VERSION,
20366
20394
  recoveryShare: requireString2(value, "recoveryShare", {
@@ -20376,7 +20404,8 @@ var parseEscrowBlobPlaintext = /* @__PURE__ */ __name((value) => {
20376
20404
  max: 64,
20377
20405
  pattern: /^[0-9a-f]{64}$/i
20378
20406
  }).toLowerCase()
20379
- } : {}
20407
+ } : {},
20408
+ ...recoveryContact
20380
20409
  };
20381
20410
  }, "parseEscrowBlobPlaintext");
20382
20411
  var parseEscrowReleasePlaintext = /* @__PURE__ */ __name((value) => {
@@ -20387,7 +20416,7 @@ var parseEscrowReleasePlaintext = /* @__PURE__ */ __name((value) => {
20387
20416
  holdId: requireString2(value, "holdId", { max: 128, pattern: /^[A-Za-z0-9._-]+$/ })
20388
20417
  };
20389
20418
  }, "parseEscrowReleasePlaintext");
20390
- var getAad2 = /* @__PURE__ */ __name((envelope) => encodeUtf82(`${envelope.version}|${envelope.algorithm}|${envelope.keyId}`), "getAad");
20419
+ var getAad2 = /* @__PURE__ */ __name((envelope2) => encodeUtf82(`${envelope2.version}|${envelope2.algorithm}|${envelope2.keyId}`), "getAad");
20391
20420
  var deriveAesKey2 = /* @__PURE__ */ __name(async (sharedSecret, salt, usage, info) => {
20392
20421
  const keyMaterial = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, [
20393
20422
  "deriveKey"
@@ -20458,7 +20487,7 @@ var encryptToRecipient = /* @__PURE__ */ __name(async (plaintextJson, spki, keyI
20458
20487
  };
20459
20488
  }, "encryptToRecipient");
20460
20489
  var decryptFromEnvelope = /* @__PURE__ */ __name(async (value, pkcs8, info) => {
20461
- const envelope = parseEscrowEnvelope(value);
20490
+ const envelope2 = parseEscrowEnvelope(value);
20462
20491
  const privateKey = await crypto.subtle.importKey(
20463
20492
  "pkcs8",
20464
20493
  base64ToBuffer(pkcs8.trim()),
@@ -20468,7 +20497,7 @@ var decryptFromEnvelope = /* @__PURE__ */ __name(async (value, pkcs8, info) => {
20468
20497
  );
20469
20498
  const ephemeralPublicKey = await crypto.subtle.importKey(
20470
20499
  "raw",
20471
- base64ToBuffer(envelope.ephemeralPublicKey),
20500
+ base64ToBuffer(envelope2.ephemeralPublicKey),
20472
20501
  { name: "ECDH", namedCurve: "P-256" },
20473
20502
  false,
20474
20503
  []
@@ -20478,13 +20507,13 @@ var decryptFromEnvelope = /* @__PURE__ */ __name(async (value, pkcs8, info) => {
20478
20507
  privateKey,
20479
20508
  256
20480
20509
  );
20481
- const salt = base64ToBuffer(envelope.salt);
20482
- const iv = base64ToBuffer(envelope.iv);
20510
+ const salt = base64ToBuffer(envelope2.salt);
20511
+ const iv = base64ToBuffer(envelope2.iv);
20483
20512
  const aesKey = await deriveAesKey2(sharedSecret, salt, "decrypt", info);
20484
20513
  const decrypted = await crypto.subtle.decrypt(
20485
- { name: "AES-GCM", iv, additionalData: getAad2(envelope) },
20514
+ { name: "AES-GCM", iv, additionalData: getAad2(envelope2) },
20486
20515
  aesKey,
20487
- base64ToBuffer(envelope.ciphertext)
20516
+ base64ToBuffer(envelope2.ciphertext)
20488
20517
  );
20489
20518
  const parsed = JSON.parse(new TextDecoder().decode(decrypted));
20490
20519
  return parsed;
@@ -20498,8 +20527,8 @@ var encryptEscrowBlob = /* @__PURE__ */ __name(async (input, enclavePublicKeySpk
20498
20527
  ESCROW_BLOB_INFO
20499
20528
  );
20500
20529
  }, "encryptEscrowBlob");
20501
- var decryptEscrowBlob = /* @__PURE__ */ __name(async (envelope, enclavePrivateKeyPkcs8B64) => parseEscrowBlobPlaintext(
20502
- await decryptFromEnvelope(envelope, enclavePrivateKeyPkcs8B64, ESCROW_BLOB_INFO)
20530
+ var decryptEscrowBlob = /* @__PURE__ */ __name(async (envelope2, enclavePrivateKeyPkcs8B64) => parseEscrowBlobPlaintext(
20531
+ await decryptFromEnvelope(envelope2, enclavePrivateKeyPkcs8B64, ESCROW_BLOB_INFO)
20503
20532
  ), "decryptEscrowBlob");
20504
20533
  var sealEscrowRelease = /* @__PURE__ */ __name(async (input, clientEphemeralPublicKeySpkiB64) => {
20505
20534
  const plaintext = parseEscrowReleasePlaintext({ ...input, version: ESCROW_ENVELOPE_VERSION });
@@ -20510,10 +20539,196 @@ var sealEscrowRelease = /* @__PURE__ */ __name(async (input, clientEphemeralPubl
20510
20539
  ESCROW_RELEASE_INFO
20511
20540
  );
20512
20541
  }, "sealEscrowRelease");
20513
- var openEscrowRelease = /* @__PURE__ */ __name(async (envelope, clientEphemeralPrivateKeyPkcs8B64) => parseEscrowReleasePlaintext(
20514
- await decryptFromEnvelope(envelope, clientEphemeralPrivateKeyPkcs8B64, ESCROW_RELEASE_INFO)
20542
+ var openEscrowRelease = /* @__PURE__ */ __name(async (envelope2, clientEphemeralPrivateKeyPkcs8B64) => parseEscrowReleasePlaintext(
20543
+ await decryptFromEnvelope(envelope2, clientEphemeralPrivateKeyPkcs8B64, ESCROW_RELEASE_INFO)
20515
20544
  ), "openEscrowRelease");
20516
20545
 
20546
+ // src/escrow-enrollment-intent.ts
20547
+ import { ExtendedPoint, getPublicKeyAsync, signAsync } from "@noble/ed25519";
20548
+ var ESCROW_ENROLLMENT_INTENT_DOMAIN = "learncard-escrow-enrollment-intent-v2\0";
20549
+ var BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
20550
+ var FIELDS = [
20551
+ "version",
20552
+ "tenant",
20553
+ "did",
20554
+ "authorityGeneration",
20555
+ "expectedEnrollmentVersion",
20556
+ "action",
20557
+ "sourceBlobIdentity",
20558
+ "targetBlobIdentity",
20559
+ "pinAction",
20560
+ "shareVersion",
20561
+ "enrollmentEpoch",
20562
+ "requestId",
20563
+ "allowRewrap",
20564
+ "recoveryEmail",
20565
+ "recoveryLanguage"
20566
+ ];
20567
+ var ACTIONS = ["enroll", "replace", "disable", "carry"];
20568
+ var PIN_ACTIONS = ["set", "change", "clear", "keep"];
20569
+ var RECOVERY_LANGUAGES2 = ["en", "es", "fr", "ar"];
20570
+ var requireIntent = /* @__PURE__ */ __name((condition) => {
20571
+ if (!condition) throw new Error("Invalid escrow enrollment intent");
20572
+ }, "requireIntent");
20573
+ var matches = /* @__PURE__ */ __name((value, pattern) => typeof value === "string" && pattern.exec(value)?.[0] === value, "matches");
20574
+ var utf8 = /* @__PURE__ */ __name((value) => new TextEncoder().encode(value), "utf8");
20575
+ var integer = /* @__PURE__ */ __name((value, width) => {
20576
+ requireIntent(
20577
+ Number.isSafeInteger(value) && value >= 0 && (width === 8 || value <= 4294967295)
20578
+ );
20579
+ const bytes3 = new Uint8Array(width);
20580
+ const view = new DataView(bytes3.buffer);
20581
+ if (width === 4) view.setUint32(0, value, false);
20582
+ else view.setBigUint64(0, BigInt(value), false);
20583
+ return bytes3;
20584
+ }, "integer");
20585
+ var join = /* @__PURE__ */ __name((fields) => {
20586
+ const result = new Uint8Array(fields.reduce((length, field) => length + field.length, 0));
20587
+ let offset = 0;
20588
+ for (const field of fields) {
20589
+ result.set(field, offset);
20590
+ offset += field.length;
20591
+ }
20592
+ return result;
20593
+ }, "join");
20594
+ var hashBytes = /* @__PURE__ */ __name((value) => {
20595
+ if (value === null) return new Uint8Array();
20596
+ requireIntent(matches(value, /^[0-9a-f]{64}$/));
20597
+ return new Uint8Array(hexToBytes(value));
20598
+ }, "hashBytes");
20599
+ var didPublicKey = /* @__PURE__ */ __name((did) => {
20600
+ requireIntent(typeof did === "string" && did.startsWith("did:key:z") && did.length === 56);
20601
+ let value = 0n;
20602
+ for (const char of did.slice(9)) {
20603
+ const digit = BASE58.indexOf(char);
20604
+ requireIntent(digit >= 0);
20605
+ value = value * 58n + BigInt(digit);
20606
+ }
20607
+ const bytes3 = new Uint8Array(34);
20608
+ for (let i = 33; i >= 0; i--) {
20609
+ bytes3[i] = Number(value & 255n);
20610
+ value >>= 8n;
20611
+ }
20612
+ requireIntent(value === 0n && bytes3[0] === 237 && bytes3[1] === 1);
20613
+ const key = bytes3.slice(2);
20614
+ try {
20615
+ requireIntent(!ExtendedPoint.fromHex(key, false).isSmallOrder());
20616
+ } catch {
20617
+ throw new Error("Invalid escrow enrollment intent");
20618
+ }
20619
+ return key;
20620
+ }, "didPublicKey");
20621
+ var encodeEscrowEnrollmentIntent = /* @__PURE__ */ __name((intent) => {
20622
+ requireIntent(typeof intent === "object" && intent !== null && !Array.isArray(intent));
20623
+ requireIntent(
20624
+ Object.keys(intent).length === FIELDS.length && FIELDS.every((field) => Object.prototype.hasOwnProperty.call(intent, field))
20625
+ );
20626
+ requireIntent(intent.version === 2 && typeof intent.allowRewrap === "boolean");
20627
+ requireIntent(matches(intent.tenant, /^[A-Za-z0-9_-]{1,128}$/));
20628
+ requireIntent(matches(intent.authorityGeneration, /^[A-Za-z0-9_-]{1,128}$/));
20629
+ requireIntent(matches(intent.requestId, /^[A-Za-z0-9._-]{1,112}$/));
20630
+ didPublicKey(intent.did);
20631
+ const action = ACTIONS.indexOf(intent.action);
20632
+ const pin = PIN_ACTIONS.indexOf(intent.pinAction);
20633
+ requireIntent(action >= 0 && pin >= 0 && intent.shareVersion > 0 && intent.enrollmentEpoch > 0);
20634
+ const source = hashBytes(intent.sourceBlobIdentity);
20635
+ const target = hashBytes(intent.targetBlobIdentity);
20636
+ if (intent.action === "enroll") requireIntent(source.length === 0 && target.length === 32);
20637
+ else if (intent.action === "disable")
20638
+ requireIntent(
20639
+ source.length === 32 && target.length === 0 && intent.pinAction === "keep" && !intent.allowRewrap
20640
+ );
20641
+ else
20642
+ requireIntent(
20643
+ source.length === 32 && target.length === 32 && intent.sourceBlobIdentity !== intent.targetBlobIdentity
20644
+ );
20645
+ if (intent.action === "carry") requireIntent(intent.pinAction === "keep");
20646
+ if (intent.recoveryEmail === null) {
20647
+ requireIntent(
20648
+ intent.recoveryLanguage === null && (intent.action === "disable" || intent.action === "carry")
20649
+ );
20650
+ } else {
20651
+ requireIntent(intent.action === "enroll" || intent.action === "replace");
20652
+ requireIntent(
20653
+ matches(intent.recoveryEmail, /^[\x21-\x7e]{3,254}$/) && intent.recoveryEmail === intent.recoveryEmail.trim().toLowerCase()
20654
+ );
20655
+ const at = intent.recoveryEmail.split("@");
20656
+ requireIntent(
20657
+ at.length === 2 && at[0].length > 0 && at[1].length > 0 && at[1].includes(".") && !at[1].startsWith(".") && !at[1].endsWith(".")
20658
+ );
20659
+ requireIntent(
20660
+ intent.recoveryLanguage !== null && RECOVERY_LANGUAGES2.includes(intent.recoveryLanguage)
20661
+ );
20662
+ }
20663
+ const fields = [
20664
+ integer(intent.version, 4),
20665
+ utf8(intent.tenant),
20666
+ utf8(intent.did),
20667
+ utf8(intent.authorityGeneration),
20668
+ integer(intent.expectedEnrollmentVersion, 8),
20669
+ Uint8Array.of(action),
20670
+ source,
20671
+ target,
20672
+ Uint8Array.of(pin),
20673
+ integer(intent.shareVersion, 4),
20674
+ integer(intent.enrollmentEpoch, 8),
20675
+ utf8(intent.requestId),
20676
+ Uint8Array.of(Number(intent.allowRewrap)),
20677
+ intent.recoveryEmail === null ? new Uint8Array() : utf8(intent.recoveryEmail),
20678
+ intent.recoveryLanguage === null ? new Uint8Array() : utf8(intent.recoveryLanguage)
20679
+ ];
20680
+ return join([
20681
+ utf8(ESCROW_ENROLLMENT_INTENT_DOMAIN),
20682
+ ...fields.flatMap((field) => [integer(field.length, 4), field])
20683
+ ]);
20684
+ }, "encodeEscrowEnrollmentIntent");
20685
+ var signEscrowEnrollmentIntent = /* @__PURE__ */ __name(async (intent, privateKey) => {
20686
+ encodeEscrowEnrollmentIntent(intent);
20687
+ const snapshot = { ...intent };
20688
+ const message = encodeEscrowEnrollmentIntent(snapshot);
20689
+ requireIntent(matches(privateKey, /^[0-9a-f]{64}$/i));
20690
+ const seed = hexToBytes(privateKey);
20691
+ try {
20692
+ const publicKey = await getPublicKeyAsync(seed);
20693
+ const expected = didPublicKey(snapshot.did);
20694
+ requireIntent(publicKey.every((byte, index) => byte === expected[index]));
20695
+ const signature = await signAsync(message, seed);
20696
+ return { intent: snapshot, signature: bufferToBase64(new Uint8Array(signature).buffer) };
20697
+ } finally {
20698
+ seed.fill(0);
20699
+ }
20700
+ }, "signEscrowEnrollmentIntent");
20701
+ var getEscrowBlobIdentity = /* @__PURE__ */ __name(async (input) => {
20702
+ const envelope2 = parseEscrowEnvelope(input);
20703
+ const decode2 = /* @__PURE__ */ __name((value) => {
20704
+ const bytes3 = new Uint8Array(base64ToBuffer(value));
20705
+ requireIntent(bufferToBase64(bytes3.buffer) === value);
20706
+ return bytes3;
20707
+ }, "decode");
20708
+ const point = decode2(envelope2.ephemeralPublicKey);
20709
+ const salt = decode2(envelope2.salt);
20710
+ const iv = decode2(envelope2.iv);
20711
+ const ciphertext = decode2(envelope2.ciphertext);
20712
+ requireIntent(
20713
+ point.length === 65 && point[0] === 4 && salt.length === 32 && iv.length === 12 && ciphertext.length >= 16
20714
+ );
20715
+ await crypto.subtle.importKey("raw", point, { name: "ECDH", namedCurve: "P-256" }, false, []);
20716
+ const fields = [
20717
+ Uint8Array.of(1),
20718
+ utf8(envelope2.algorithm),
20719
+ utf8(envelope2.keyId),
20720
+ point.slice(1, 33),
20721
+ salt,
20722
+ iv,
20723
+ ciphertext
20724
+ ];
20725
+ const message = join([
20726
+ utf8("learncard-escrow-blob-id-v1\0"),
20727
+ ...fields.flatMap((field) => [integer(field.length, 8), field])
20728
+ ]);
20729
+ return bytesToHex(new Uint8Array(await crypto.subtle.digest("SHA-256", message)));
20730
+ }, "getEscrowBlobIdentity");
20731
+
20517
20732
  // src/escrow-nitro-attestation.ts
20518
20733
  import "reflect-metadata";
20519
20734
  import { decode, encode } from "cborg";
@@ -20537,15 +20752,15 @@ var AWS_ROOT_SHA256 = "641a0321a3e244efe456463195d606317ed7cdcc3c1756e09893f3c68
20537
20752
  var fail = /* @__PURE__ */ __name((reason) => {
20538
20753
  throw new NitroAttestationError(reason);
20539
20754
  }, "fail");
20540
- var hex = /* @__PURE__ */ __name((bytes2) => Array.from(bytes2, (byte) => byte.toString(16).padStart(2, "0")).join(""), "hex");
20755
+ var hex = /* @__PURE__ */ __name((bytes3) => Array.from(bytes3, (byte) => byte.toString(16).padStart(2, "0")).join(""), "hex");
20541
20756
  var bytes = /* @__PURE__ */ __name((value, reason) => value instanceof Uint8Array && value.length > 0 ? value : fail(reason), "bytes");
20542
20757
  var map = /* @__PURE__ */ __name((value, reason) => value instanceof Map ? value : fail(reason), "map");
20543
- var decodeCbor = /* @__PURE__ */ __name((value) => {
20758
+ var decodeCbor = /* @__PURE__ */ __name((value, { allowIndefinite = false } = {}) => {
20544
20759
  try {
20545
20760
  return decode(value, {
20546
20761
  useMaps: true,
20547
20762
  rejectDuplicateMapKeys: true,
20548
- allowIndefinite: false,
20763
+ allowIndefinite,
20549
20764
  allowUndefined: false,
20550
20765
  allowNaN: false,
20551
20766
  allowInfinity: false,
@@ -20603,7 +20818,7 @@ var verifyNitroAttestationDocument = /* @__PURE__ */ __name(async (documentB64,
20603
20818
  const payloadBytes = bytes(payloadValue, "cose");
20604
20819
  const signature = bytes(signatureValue, "signature");
20605
20820
  if (signature.length !== 96) fail("signature");
20606
- const payload = map(decodeCbor(payloadBytes), "cbor");
20821
+ const payload = map(decodeCbor(payloadBytes, { allowIndefinite: true }), "cbor");
20607
20822
  const moduleId = payload.get("module_id");
20608
20823
  const timestamp = payload.get("timestamp");
20609
20824
  if (typeof moduleId !== "string" || !moduleId || payload.get("digest") !== "SHA384")
@@ -20693,13 +20908,36 @@ var verifyNitroAttestationDocument = /* @__PURE__ */ __name(async (documentB64,
20693
20908
  } catch {
20694
20909
  return fail("user-data");
20695
20910
  }
20911
+ const rawLedgerKey = payload.get("public_key");
20912
+ let ledgerPublicKey;
20913
+ let measurementHash;
20914
+ if (rawLedgerKey !== void 0 && rawLedgerKey !== null) {
20915
+ const ledger = new Uint8Array(bytes(rawLedgerKey, "user-data"));
20916
+ if (ledger.length !== 65 || ledger[0] !== 4) fail("user-data");
20917
+ try {
20918
+ await crypto.subtle.importKey(
20919
+ "raw",
20920
+ ledger,
20921
+ { name: "ECDSA", namedCurve: "P-256" },
20922
+ false,
20923
+ ["verify"]
20924
+ );
20925
+ } catch {
20926
+ return fail("user-data");
20927
+ }
20928
+ const tuple = new Uint8Array(144);
20929
+ for (let i = 0; i < 3; i++) tuple.set(bytes(rawPcrs.get(i), "pcr"), i * 48);
20930
+ measurementHash = hex(new Uint8Array(await crypto.subtle.digest("SHA-256", tuple)));
20931
+ ledgerPublicKey = bufferToBase64(ledger.buffer);
20932
+ }
20696
20933
  return {
20697
20934
  escrowPublicKeySpkiB64: bufferToBase64(new Uint8Array(spki).buffer),
20698
20935
  keyId: newFormat?.keyId,
20699
20936
  previousKeyIds: newFormat?.previousKeyIds ?? [],
20700
20937
  pcrs,
20701
20938
  timestamp,
20702
- moduleId
20939
+ moduleId,
20940
+ ...ledgerPublicKey ? { ledgerPublicKey, measurementHash } : {}
20703
20941
  };
20704
20942
  }, "verifyNitroAttestationDocument");
20705
20943
 
@@ -20753,7 +20991,13 @@ var verifyEnclaveAttestation = /* @__PURE__ */ __name(async (attestation, policy
20753
20991
  publicKey: verified.escrowPublicKeySpkiB64,
20754
20992
  keyId: attestation.keyId,
20755
20993
  previousKeyIds: verified.previousKeyIds,
20756
- mode: "nitro"
20994
+ mode: "nitro",
20995
+ ...verified.keyId && verified.ledgerPublicKey && verified.measurementHash ? {
20996
+ authority: {
20997
+ ledgerPublicKey: verified.ledgerPublicKey,
20998
+ measurement: verified.measurementHash
20999
+ }
21000
+ } : {}
20757
21001
  };
20758
21002
  }
20759
21003
  const publicKey = normalizePublicKey(attestation.publicKey);
@@ -20775,6 +21019,393 @@ var verifyEnclaveAttestation = /* @__PURE__ */ __name(async (attestation, policy
20775
21019
  };
20776
21020
  }, "verifyEnclaveAttestation");
20777
21021
 
21022
+ // src/escrow-authority.types.ts
21023
+ var EscrowEvidenceError = class extends Error {
21024
+ static {
21025
+ __name(this, "EscrowEvidenceError");
21026
+ }
21027
+ constructor() {
21028
+ super("Automatic recovery evidence could not be verified.");
21029
+ this.name = "EscrowEvidenceError";
21030
+ }
21031
+ };
21032
+
21033
+ // src/escrow-authority.ts
21034
+ var fail2 = /* @__PURE__ */ __name(() => {
21035
+ throw new EscrowEvidenceError();
21036
+ }, "fail");
21037
+ var requireEvidence = /* @__PURE__ */ __name((condition) => {
21038
+ if (!condition) fail2();
21039
+ }, "requireEvidence");
21040
+ var object = /* @__PURE__ */ __name((value) => {
21041
+ if (!value || typeof value !== "object" || Array.isArray(value)) return fail2();
21042
+ return value;
21043
+ }, "object");
21044
+ var keys = /* @__PURE__ */ __name((value, names) => {
21045
+ requireEvidence(
21046
+ Object.keys(value).length === names.length && names.every((name) => Object.prototype.hasOwnProperty.call(value, name))
21047
+ );
21048
+ }, "keys");
21049
+ var integer2 = /* @__PURE__ */ __name((value, minimum = 0, maximum = Number.MAX_SAFE_INTEGER) => {
21050
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < minimum || value > maximum)
21051
+ return fail2();
21052
+ return value;
21053
+ }, "integer");
21054
+ var text = /* @__PURE__ */ __name((value, maximum) => {
21055
+ if (typeof value !== "string" || !value.length || value.length > maximum) return fail2();
21056
+ return value;
21057
+ }, "text");
21058
+ var bytes2 = /* @__PURE__ */ __name((value, length) => {
21059
+ if (!Array.isArray(value) || value.length !== length || !value.every(
21060
+ (byte) => typeof byte === "number" && Number.isInteger(byte) && byte >= 0 && byte <= 255
21061
+ ))
21062
+ return fail2();
21063
+ return Uint8Array.from(value);
21064
+ }, "bytes");
21065
+ var base64 = /* @__PURE__ */ __name((value, maximum) => {
21066
+ const encoded = text(value, maximum);
21067
+ try {
21068
+ const decoded = new Uint8Array(base64ToBuffer(encoded));
21069
+ requireEvidence(bufferToBase64(decoded.buffer) === encoded);
21070
+ return decoded;
21071
+ } catch {
21072
+ return fail2();
21073
+ }
21074
+ }, "base64");
21075
+ var frame = /* @__PURE__ */ __name((value) => {
21076
+ const result = new Uint8Array(4 + value.length);
21077
+ new DataView(result.buffer).setUint32(0, value.length, false);
21078
+ result.set(value, 4);
21079
+ return result;
21080
+ }, "frame");
21081
+ var join2 = /* @__PURE__ */ __name((...values) => {
21082
+ const result = new Uint8Array(values.reduce((n, v) => n + v.length, 0));
21083
+ let offset = 0;
21084
+ for (const value of values) {
21085
+ result.set(value, offset);
21086
+ offset += value.length;
21087
+ }
21088
+ return result;
21089
+ }, "join");
21090
+ var hash = /* @__PURE__ */ __name(async (value) => bytesToHex(new Uint8Array(await crypto.subtle.digest("SHA-256", new Uint8Array(value)))), "hash");
21091
+ var utf82 = /* @__PURE__ */ __name((value) => new TextEncoder().encode(value), "utf8");
21092
+ var escrowAuthorityMeasurements = /* @__PURE__ */ __name(async (policy) => {
21093
+ if (policy.mode !== "nitro") return [];
21094
+ const hashes = [];
21095
+ for (const pin of policy.pinnedMeasurements) {
21096
+ const tuple = [pin.pcr0, pin.pcr1, pin.pcr2];
21097
+ if (!tuple.every((pcr) => typeof pcr === "string" && /^[0-9a-f]{96}$/i.test(pcr))) continue;
21098
+ if (pin.pcr0 === "00".repeat(48)) continue;
21099
+ const encoded = tuple.join("");
21100
+ const decoded = Uint8Array.from(
21101
+ { length: 144 },
21102
+ (_, i) => parseInt(encoded.slice(i * 2, i * 2 + 2), 16)
21103
+ );
21104
+ hashes.push(await hash(decoded));
21105
+ }
21106
+ return hashes;
21107
+ }, "escrowAuthorityMeasurements");
21108
+ var P256_ORDER = BigInt("0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
21109
+ var verifyRecord = /* @__PURE__ */ __name(async (input, kind, identity, historical) => {
21110
+ requireEvidence(identity.mode === "nitro" && !!identity.authority);
21111
+ const raw = object(input);
21112
+ keys(raw, ["version", "kind", "keyId", "measurement", "payload", "signature"]);
21113
+ requireEvidence(raw.version === 1 && raw.kind === kind);
21114
+ const keyId = text(raw.keyId, 128);
21115
+ requireEvidence(
21116
+ historical ? [identity.keyId, ...identity.previousKeyIds].includes(keyId) : keyId === identity.keyId
21117
+ );
21118
+ const measurement = bytes2(raw.measurement, 32);
21119
+ requireEvidence(
21120
+ historical ? historical.allowedMeasurements.includes(bytesToHex(measurement)) : bytesToHex(measurement) === identity.authority?.measurement
21121
+ );
21122
+ const payloadB64 = text(raw.payload, kind === "snapshot" ? 49152 : 8192);
21123
+ const signatureB64 = text(raw.signature, 88);
21124
+ const payload = base64(payloadB64, kind === "snapshot" ? 49152 : 8192);
21125
+ const signature = base64(signatureB64, 88);
21126
+ const ledgerKey = base64(identity.authority?.ledgerPublicKey, 88);
21127
+ requireEvidence(ledgerKey.length === 65 && ledgerKey[0] === 4);
21128
+ requireEvidence(signature.length === 64);
21129
+ const s = BigInt(`0x${bytesToHex(signature.slice(32))}`);
21130
+ requireEvidence(s > 0n && s <= P256_ORDER / 2n);
21131
+ const message = join2(
21132
+ utf82("learncard-authority-signed-record-v1\0"),
21133
+ Uint8Array.of(kind === "snapshot" ? 2 : 1),
21134
+ frame(utf82(keyId)),
21135
+ measurement,
21136
+ frame(payload)
21137
+ );
21138
+ try {
21139
+ const key = await crypto.subtle.importKey(
21140
+ "raw",
21141
+ ledgerKey,
21142
+ { name: "ECDSA", namedCurve: "P-256" },
21143
+ false,
21144
+ ["verify"]
21145
+ );
21146
+ requireEvidence(
21147
+ await crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, key, signature, message)
21148
+ );
21149
+ const decoded = JSON.parse(
21150
+ new TextDecoder("utf-8", { fatal: true }).decode(payload)
21151
+ );
21152
+ const record = {
21153
+ version: 1,
21154
+ kind,
21155
+ keyId,
21156
+ measurement: Array.from(measurement),
21157
+ payload: payloadB64,
21158
+ signature: signatureB64
21159
+ };
21160
+ return { record, payload: decoded };
21161
+ } catch {
21162
+ return fail2();
21163
+ }
21164
+ }, "verifyRecord");
21165
+ var envelope = /* @__PURE__ */ __name((value) => {
21166
+ try {
21167
+ const e = parseEscrowEnvelope(value);
21168
+ return {
21169
+ version: e.version,
21170
+ algorithm: e.algorithm,
21171
+ keyId: e.keyId,
21172
+ ephemeralPublicKey: e.ephemeralPublicKey,
21173
+ salt: e.salt,
21174
+ iv: e.iv,
21175
+ ciphertext: e.ciphertext
21176
+ };
21177
+ } catch {
21178
+ return fail2();
21179
+ }
21180
+ }, "envelope");
21181
+ var state = /* @__PURE__ */ __name((value) => {
21182
+ const raw = object(value);
21183
+ keys(raw, [
21184
+ "tenant",
21185
+ "authorityGeneration",
21186
+ "did",
21187
+ "enrollmentVersion",
21188
+ "enrollmentEpoch",
21189
+ "status",
21190
+ "envelope",
21191
+ "shareVersion",
21192
+ "hasPin",
21193
+ "pinGeneration",
21194
+ "attemptsUsed"
21195
+ ]);
21196
+ if (raw.status !== "empty" && raw.status !== "active" && raw.status !== "disabled")
21197
+ return fail2();
21198
+ if (typeof raw.hasPin !== "boolean") return fail2();
21199
+ const result = {
21200
+ tenant: text(raw.tenant, 128),
21201
+ authorityGeneration: text(raw.authorityGeneration, 128),
21202
+ did: text(raw.did, 8192),
21203
+ enrollmentVersion: integer2(raw.enrollmentVersion),
21204
+ enrollmentEpoch: integer2(raw.enrollmentEpoch),
21205
+ status: raw.status,
21206
+ envelope: raw.envelope === null ? null : envelope(raw.envelope),
21207
+ shareVersion: raw.shareVersion === null ? null : integer2(raw.shareVersion, 1, 4294967295),
21208
+ hasPin: raw.hasPin,
21209
+ pinGeneration: integer2(raw.pinGeneration),
21210
+ attemptsUsed: integer2(raw.attemptsUsed, 0, 10)
21211
+ };
21212
+ requireEvidence(
21213
+ result.pinGeneration <= result.enrollmentVersion && result.enrollmentEpoch <= result.enrollmentVersion && (!result.hasPin || result.pinGeneration > 0) && (result.hasPin || result.attemptsUsed === 0)
21214
+ );
21215
+ if (result.status === "empty")
21216
+ requireEvidence(
21217
+ result.enrollmentVersion === 0 && result.enrollmentEpoch === 0 && !result.envelope && result.shareVersion === null && !result.hasPin
21218
+ );
21219
+ else requireEvidence(result.enrollmentVersion > 0 && result.enrollmentEpoch > 0);
21220
+ if (result.status === "active")
21221
+ requireEvidence(!!result.envelope && result.shareVersion !== null);
21222
+ else requireEvidence(result.envelope === null && result.shareVersion === null);
21223
+ return result;
21224
+ }, "state");
21225
+ var verifyEscrowAuthorityState = /* @__PURE__ */ __name(async (response, nonce, tenant, did, identity) => {
21226
+ requireEvidence(/^[0-9a-f]{64}$/.test(nonce) && nonce.length === 64);
21227
+ const raw = object(response);
21228
+ keys(raw, ["state", "proof"]);
21229
+ const { payload } = await verifyRecord(raw.proof, "snapshot", identity);
21230
+ if (!Array.isArray(payload) || payload.length !== 2 || payload[0] !== nonce) return fail2();
21231
+ const signed = state(payload[1]);
21232
+ requireEvidence(signed.tenant === tenant && signed.did === did);
21233
+ requireEvidence(JSON.stringify(signed) === JSON.stringify(state(raw.state)));
21234
+ if (signed.envelope) {
21235
+ requireEvidence(
21236
+ [identity.keyId, ...identity.previousKeyIds].includes(signed.envelope.keyId)
21237
+ );
21238
+ await getEscrowBlobIdentity(signed.envelope);
21239
+ }
21240
+ return signed;
21241
+ }, "verifyEscrowAuthorityState");
21242
+ var verifyEscrowEnrollmentCommit = /* @__PURE__ */ __name(async (response, intent, identity) => {
21243
+ const raw = object(response);
21244
+ const { record, payload } = await verifyRecord(raw.receipt, "commit", identity);
21245
+ const data = object(payload);
21246
+ keys(data, ["commit", "eventHash", "requestDigest", "resultBlobIdentity", "enrollmentEpoch"]);
21247
+ const commit = object(data.commit);
21248
+ keys(commit, ["schemaVersion", "binding", "executionId", "revisions", "transactionDigest"]);
21249
+ const binding = object(commit.binding);
21250
+ keys(binding, ["tenant", "generation", "didHash"]);
21251
+ const revisions = object(commit.revisions);
21252
+ keys(revisions, ["operation", "enrollment"]);
21253
+ requireEvidence(
21254
+ commit.schemaVersion === 1 && binding.tenant === intent.tenant && binding.generation === intent.authorityGeneration
21255
+ );
21256
+ requireEvidence(bytesToHex(bytes2(binding.didHash, 32)) === await hash(utf82(intent.did)));
21257
+ requireEvidence(
21258
+ integer2(revisions.enrollment, 1) === intent.expectedEnrollmentVersion + 1 && integer2(revisions.operation, 1) >= integer2(revisions.enrollment, 1)
21259
+ );
21260
+ bytes2(commit.executionId, 16);
21261
+ bytes2(commit.transactionDigest, 32);
21262
+ bytes2(data.eventHash, 32);
21263
+ requireEvidence(integer2(data.enrollmentEpoch, 1) === intent.enrollmentEpoch);
21264
+ requireEvidence(
21265
+ bytesToHex(bytes2(data.requestDigest, 32)) === await hash(encodeEscrowEnrollmentIntent(intent))
21266
+ );
21267
+ if (intent.action === "disable") {
21268
+ requireEvidence(
21269
+ (raw.envelope === null || raw.envelope === void 0) && data.resultBlobIdentity === null
21270
+ );
21271
+ return { envelope: null, receipt: record };
21272
+ }
21273
+ const output = envelope(raw.envelope);
21274
+ requireEvidence(
21275
+ output.keyId === identity.keyId && bytesToHex(bytes2(data.resultBlobIdentity, 32)) === await getEscrowBlobIdentity(output)
21276
+ );
21277
+ return { envelope: output, receipt: record };
21278
+ }, "verifyEscrowEnrollmentCommit");
21279
+ var holdDigest = /* @__PURE__ */ __name(async (tenant, record) => {
21280
+ const h = record.hold;
21281
+ const didHash = await hash(utf82(h.did));
21282
+ return hash(
21283
+ utf82(
21284
+ JSON.stringify([
21285
+ "learncard-hold-v1",
21286
+ tenant,
21287
+ h.holdId,
21288
+ didHash,
21289
+ h.shareVersion,
21290
+ h.blobHash,
21291
+ h.enrollmentEpoch,
21292
+ h.releasePolicy,
21293
+ h.clientEphemeralPublicKey,
21294
+ h.createdLo,
21295
+ h.createdHi,
21296
+ record.holdDurationMs,
21297
+ h.policyVersion,
21298
+ record.ledgerSeq
21299
+ ])
21300
+ )
21301
+ );
21302
+ }, "holdDigest");
21303
+ var verifyEscrowHoldRecord = /* @__PURE__ */ __name(async (record, expected, identity, historical) => {
21304
+ requireEvidence(
21305
+ record.hold.holdId === expected.holdId && record.hold.did === expected.did && record.hold.clientEphemeralPublicKey === expected.clientEphemeralPublicKey && record.hold.blobHash === expected.blobHash
21306
+ );
21307
+ let signed;
21308
+ try {
21309
+ signed = JSON.parse(
21310
+ new TextDecoder("utf-8", { fatal: true }).decode(base64(record.hold.signature, 8192))
21311
+ );
21312
+ } catch {
21313
+ return fail2();
21314
+ }
21315
+ const { record: receipt, payload } = await verifyRecord(signed, "commit", identity, historical);
21316
+ const data = object(payload);
21317
+ keys(data, ["commit", "eventHash", "requestDigest", "resultBlobIdentity", "enrollmentEpoch"]);
21318
+ const commit = object(data.commit);
21319
+ keys(commit, ["schemaVersion", "binding", "executionId", "revisions", "transactionDigest"]);
21320
+ const binding = object(commit.binding);
21321
+ keys(binding, ["tenant", "generation", "didHash"]);
21322
+ requireEvidence(commit.schemaVersion === 1 && binding.tenant === expected.tenant);
21323
+ const authorityGeneration = text(binding.generation, 128);
21324
+ if (historical) requireEvidence(authorityGeneration === historical.authorityGeneration);
21325
+ requireEvidence(bytesToHex(bytes2(binding.didHash, 32)) === await hash(utf82(expected.did)));
21326
+ requireEvidence(integer2(data.enrollmentEpoch, 1) === record.hold.enrollmentEpoch);
21327
+ requireEvidence(bytesToHex(bytes2(data.resultBlobIdentity, 32)) === record.hold.blobHash);
21328
+ const digest = await holdDigest(expected.tenant, record);
21329
+ requireEvidence(bytesToHex(bytes2(data.requestDigest, 32)) === digest);
21330
+ return { receipt, authorityGeneration, holdDigest: digest };
21331
+ }, "verifyEscrowHoldRecord");
21332
+ var verifyEscrowHoldConfirmation = /* @__PURE__ */ __name(async (receipt, record, expected, identity) => {
21333
+ const raw = object(receipt);
21334
+ const kind = raw.kind;
21335
+ if (kind !== "commit" && kind !== "snapshot") return fail2();
21336
+ const { record: verified, payload } = await verifyRecord(raw, kind, identity);
21337
+ if (kind === "snapshot") {
21338
+ if (!Array.isArray(payload) || payload.length !== 7) return fail2();
21339
+ const [tag, holdId, digestBytes] = payload;
21340
+ requireEvidence(
21341
+ tag === "learncard-authority-hold-snapshot-v2" && holdId === record.hold.holdId && payload[6] === expected.authorityGeneration
21342
+ );
21343
+ requireEvidence(
21344
+ bytesToHex(bytes2(digestBytes, 32)) === await holdDigest(expected.tenant, record)
21345
+ );
21346
+ requireEvidence(payload[3] === "pending");
21347
+ integer2(payload[4], 1);
21348
+ return verified;
21349
+ }
21350
+ const data = object(payload);
21351
+ keys(data, ["commit", "eventHash", "requestDigest", "resultBlobIdentity", "enrollmentEpoch"]);
21352
+ const commit = object(data.commit);
21353
+ keys(commit, ["schemaVersion", "binding", "executionId", "revisions", "transactionDigest"]);
21354
+ const binding = object(commit.binding);
21355
+ keys(binding, ["tenant", "generation", "didHash"]);
21356
+ requireEvidence(
21357
+ commit.schemaVersion === 1 && binding.tenant === expected.tenant && binding.generation === expected.authorityGeneration
21358
+ );
21359
+ requireEvidence(bytesToHex(bytes2(binding.didHash, 32)) === await hash(utf82(expected.did)));
21360
+ requireEvidence(integer2(data.enrollmentEpoch, 1) === record.hold.enrollmentEpoch);
21361
+ requireEvidence(bytesToHex(bytes2(data.resultBlobIdentity, 32)) === record.hold.blobHash);
21362
+ bytes2(data.requestDigest, 32);
21363
+ return verified;
21364
+ }, "verifyEscrowHoldConfirmation");
21365
+ var resolveEscrowEnrollmentAction = /* @__PURE__ */ __name((current, input) => {
21366
+ const disabling = input === "disable";
21367
+ const pinAction = disabling ? "keep" : input === "set" ? current.hasPin ? "change" : "set" : input === "clear" ? current.hasPin ? "clear" : "keep" : "keep";
21368
+ const action = disabling ? "disable" : current.status !== "active" ? "enroll" : current.hasPin && pinAction === "keep" ? "carry" : "replace";
21369
+ return { action, pinAction, bindsRecoveryContact: action === "enroll" || action === "replace" };
21370
+ }, "resolveEscrowEnrollmentAction");
21371
+ var violatesCarryContactRule = /* @__PURE__ */ __name((action, target) => action === "carry" && !!target && (!!target.recoveryEmail || !!target.recoveryLanguage), "violatesCarryContactRule");
21372
+ var createEscrowEnrollmentIntent = /* @__PURE__ */ __name(async (input) => {
21373
+ const current = state(input.state);
21374
+ const target = input.target === null ? null : envelope(input.target);
21375
+ const disabling = input.pinAction === "disable";
21376
+ requireEvidence(
21377
+ current.enrollmentVersion < Number.MAX_SAFE_INTEGER && (disabling || current.enrollmentEpoch < Number.MAX_SAFE_INTEGER)
21378
+ );
21379
+ if (disabling && current.status !== "active") return fail2();
21380
+ if (current.status === "disabled" && current.hasPin && input.pinAction === "automatic")
21381
+ return fail2();
21382
+ const { action, pinAction, bindsRecoveryContact } = resolveEscrowEnrollmentAction(
21383
+ current,
21384
+ input.pinAction
21385
+ );
21386
+ requireEvidence(!disabling || input.target === null);
21387
+ requireEvidence(disabling || input.target !== null);
21388
+ if (action === "carry") requireEvidence(input.shareVersion > (current.shareVersion ?? 0));
21389
+ const intent = {
21390
+ version: 2,
21391
+ tenant: current.tenant,
21392
+ did: current.did,
21393
+ authorityGeneration: current.authorityGeneration,
21394
+ expectedEnrollmentVersion: current.enrollmentVersion,
21395
+ action,
21396
+ sourceBlobIdentity: current.envelope ? await getEscrowBlobIdentity(current.envelope) : null,
21397
+ targetBlobIdentity: target ? await getEscrowBlobIdentity(target) : null,
21398
+ pinAction,
21399
+ shareVersion: disabling ? current.shareVersion : input.shareVersion,
21400
+ enrollmentEpoch: current.enrollmentEpoch + (disabling ? 0 : 1),
21401
+ requestId: crypto.randomUUID(),
21402
+ allowRewrap: !disabling,
21403
+ recoveryEmail: bindsRecoveryContact ? input.recoveryEmail : null,
21404
+ recoveryLanguage: bindsRecoveryContact ? input.recoveryLanguage : null
21405
+ };
21406
+ return signEscrowEnrollmentIntent(intent, input.privateKey);
21407
+ }, "createEscrowEnrollmentIntent");
21408
+
20778
21409
  // src/types.ts
20779
21410
  var import_types = __toESM(require_dist());
20780
21411
  var EscrowPinUnavailableError = class extends Error {
@@ -21519,6 +22150,10 @@ function createSSSStrategy(config) {
21519
22150
  const localWrites = /* @__PURE__ */ new Set();
21520
22151
  const guardedStorage = /* @__PURE__ */ __name(() => {
21521
22152
  const generation = storageGeneration;
22153
+ const assertCurrent = /* @__PURE__ */ __name(() => {
22154
+ if (generation !== storageGeneration)
22155
+ throw new Error("This recovery request was cancelled.");
22156
+ }, "assertCurrent");
21522
22157
  const write = /* @__PURE__ */ __name((operation) => {
21523
22158
  if (generation !== storageGeneration) {
21524
22159
  return Promise.reject(new Error("This recovery request was cancelled."));
@@ -21529,6 +22164,7 @@ function createSSSStrategy(config) {
21529
22164
  }, "write");
21530
22165
  const guarded = {
21531
22166
  ...storage,
22167
+ assertCurrent,
21532
22168
  storeDeviceShare: /* @__PURE__ */ __name((share, id) => write(() => storage.storeDeviceShare(share, id)), "storeDeviceShare"),
21533
22169
  storeShareVersion: /* @__PURE__ */ __name((version, id) => write(() => storage.storeShareVersion(version, id)), "storeShareVersion"),
21534
22170
  clearAllShares: /* @__PURE__ */ __name((id) => write(() => storage.clearAllShares(id)), "clearAllShares"),
@@ -21546,6 +22182,7 @@ function createSSSStrategy(config) {
21546
22182
  let lastServerSnapshot = null;
21547
22183
  let escrowEnrollmentInFlight;
21548
22184
  let staleEscrowReenrollAttempted = false;
22185
+ let verifiedEscrowEnvelope;
21549
22186
  let unenrolledRotation;
21550
22187
  const escrowRequest = /* @__PURE__ */ __name(async (path, init, pinAttempt = false) => {
21551
22188
  const response = await fetch(`${serverUrl}/keys/escrow${path}`, {
@@ -21604,16 +22241,89 @@ function createSSSStrategy(config) {
21604
22241
  );
21605
22242
  return verifyEnclaveAttestation(attestation, config.escrow.attestation, nonce);
21606
22243
  }, "fetchVerifiedEnclaveKey");
21607
- const enrollEscrow = /* @__PURE__ */ __name(async (token, providerType, privateKey, primaryDid, shares, shareVersion, signDidAuthVp, verifiedKey, pinMaterial, clearPin = false) => {
22244
+ const fetchEscrowHoldStatus = /* @__PURE__ */ __name(async (params) => {
22245
+ const query = new URLSearchParams(
22246
+ "holdId" in params ? { holdId: params.holdId } : { providerType: params.providerType }
22247
+ );
22248
+ const secret = "holdId" in params ? params.resumeToken : params.token;
22249
+ const result = await escrowRequest(`/status?${query}`, {
22250
+ method: "GET",
22251
+ headers: {
22252
+ ...buildHeaders("", void 0, tenantId),
22253
+ "X-Auth-Token": secret
22254
+ }
22255
+ });
22256
+ return result.hold;
22257
+ }, "fetchEscrowHoldStatus");
22258
+ const recoveryExpectations = /* @__PURE__ */ __name((holdId, expected) => {
22259
+ if (!expected || expected.holdId !== holdId || expected.tenant !== (tenantId ?? "learncard") || !expected.did || !expected.clientEphemeralPublicKey || !expected.blobHash || !expected.authorityGeneration || !expected.holdDigest || !expected.ledgerPublicKey || !expected.keyId)
22260
+ throw new EscrowEvidenceError();
22261
+ return { ...expected };
22262
+ }, "recoveryExpectations");
22263
+ const verifyPendingHold = /* @__PURE__ */ __name(async (status, expected, identity) => {
22264
+ 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)
22265
+ throw new EscrowEvidenceError();
22266
+ const record = status.holdRecord;
22267
+ const verified = await verifyEscrowHoldRecord(record, expected, identity, {
22268
+ historical: true,
22269
+ allowedMeasurements: await escrowAuthorityMeasurements(config.escrow.attestation),
22270
+ authorityGeneration: expected.authorityGeneration
22271
+ });
22272
+ if (verified.holdDigest !== expected.holdDigest || verified.receipt.keyId !== expected.keyId)
22273
+ throw new EscrowEvidenceError();
22274
+ return record;
22275
+ }, "verifyPendingHold");
22276
+ const fetchAuthorityState = /* @__PURE__ */ __name(async (token, providerType, privateKey, did, signer, identity, assertCurrent) => {
22277
+ assertCurrent();
22278
+ if (identity.mode !== "nitro" || !identity.authority)
22279
+ throw new Error("The enclave does not support verified enrollment.");
22280
+ const nonce = Array.from(
22281
+ crypto.getRandomValues(new Uint8Array(32)),
22282
+ (byte) => byte.toString(16).padStart(2, "0")
22283
+ ).join("");
22284
+ const vp = await requestFreshDidAuthVp(serverUrl, privateKey, did, signer, tenantId);
22285
+ assertCurrent();
22286
+ const response = await escrowRequest("/authority-state", {
22287
+ method: "POST",
22288
+ headers: buildHeaders(token, vp, tenantId),
22289
+ body: JSON.stringify({ authToken: token, providerType, nonce })
22290
+ });
22291
+ const result = await verifyEscrowAuthorityState(
22292
+ response,
22293
+ nonce,
22294
+ tenantId ?? "learncard",
22295
+ did,
22296
+ identity
22297
+ );
22298
+ assertCurrent();
22299
+ return result;
22300
+ }, "fetchAuthorityState");
22301
+ const enrollEscrow = /* @__PURE__ */ __name(async (token, providerType, privateKey, primaryDid, shares, shareVersion, signDidAuthVp, verifiedKey, pinMaterial, clearPin = false, assertCurrent = guardedStorage().assertCurrent, recoveryEmail = "", recoveryLanguage = "") => {
21608
22302
  if (!config.escrow?.enabled) throw new Error("Escrow enrollment is disabled");
21609
22303
  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(
22304
+ const identity = verifiedKey ?? await fetchVerifiedEnclaveKey();
22305
+ assertCurrent();
22306
+ const { publicKey, keyId } = identity;
22307
+ const authority = config.escrow.attestation.mode === "nitro" ? await fetchAuthorityState(
22308
+ token,
22309
+ providerType,
22310
+ privateKey,
22311
+ primaryDid,
22312
+ signDidAuthVp,
22313
+ identity,
22314
+ assertCurrent
22315
+ ) : void 0;
22316
+ const bindsRecoveryContact = !authority || resolveEscrowEnrollmentAction(
22317
+ authority,
22318
+ pinMaterial ? "set" : clearPin ? "clear" : "automatic"
22319
+ ).bindsRecoveryContact;
22320
+ const envelope2 = await encryptEscrowBlob(
21612
22321
  {
21613
22322
  recoveryShare: shares.recoveryShare,
21614
22323
  did: primaryDid,
21615
22324
  shareVersion,
21616
- ...pinMaterial ? { pinVerifier: pinMaterial.pinVerifier } : {}
22325
+ ...pinMaterial ? { pinVerifier: pinMaterial.pinVerifier } : {},
22326
+ ...authority && recoveryEmail && bindsRecoveryContact ? { recoveryEmail, recoveryLanguage } : {}
21617
22327
  },
21618
22328
  publicKey,
21619
22329
  keyId
@@ -21625,19 +22335,40 @@ function createSSSStrategy(config) {
21625
22335
  signDidAuthVp,
21626
22336
  tenantId
21627
22337
  );
21628
- await escrowRequest("", {
22338
+ const signedIntent = authority ? await createEscrowEnrollmentIntent({
22339
+ state: authority,
22340
+ target: envelope2,
22341
+ shareVersion,
22342
+ privateKey,
22343
+ pinAction: pinMaterial ? "set" : clearPin ? "clear" : "automatic",
22344
+ recoveryEmail,
22345
+ recoveryLanguage
22346
+ }) : void 0;
22347
+ assertCurrent();
22348
+ verifiedEscrowEnvelope = void 0;
22349
+ const response = await escrowRequest("", {
21629
22350
  method: "POST",
21630
22351
  headers: buildHeaders(token, vp, tenantId),
21631
22352
  body: JSON.stringify({
21632
22353
  authToken: token,
21633
22354
  providerType,
21634
- envelope,
22355
+ envelope: envelope2,
21635
22356
  shareVersion,
21636
22357
  enclaveKeyId: keyId,
21637
22358
  ...pinMaterial ? { pinSalt: pinMaterial.pinSalt } : {},
21638
- ...clearPin ? { clearPin: true } : {}
22359
+ ...(signedIntent ? signedIntent.intent.pinAction === "clear" : clearPin) ? { clearPin: true } : {},
22360
+ ...signedIntent ? { signedIntent, sourceEnvelope: authority?.envelope ?? null } : {}
21639
22361
  })
21640
22362
  });
22363
+ if (signedIntent) {
22364
+ const committed = await verifyEscrowEnrollmentCommit(
22365
+ response,
22366
+ signedIntent.intent,
22367
+ identity
22368
+ );
22369
+ assertCurrent();
22370
+ verifiedEscrowEnvelope = committed.envelope ?? void 0;
22371
+ }
21641
22372
  }, "enrollEscrow");
21642
22373
  const tryEnrollEscrow = /* @__PURE__ */ __name(async (...args) => {
21643
22374
  if (!config.escrow?.enabled) return;
@@ -21652,9 +22383,12 @@ function createSSSStrategy(config) {
21652
22383
  }
21653
22384
  }
21654
22385
  }, "tryEnrollEscrow");
21655
- const ensureEscrowEnrollmentUnguarded = /* @__PURE__ */ __name(async (self, params, storage2, forceRotate = false) => {
22386
+ const ensureEscrowEnrollmentUnguarded = /* @__PURE__ */ __name(async (self, params, storage2, forceRotate = false, allowDisabledEnrollment = false) => {
22387
+ let clearPinForEnrollment = forceRotate;
21656
22388
  if (!config.escrow?.enabled) return { enrolled: false, reason: "disabled" };
21657
22389
  const pin = params.options?.pin;
22390
+ const recoveryEmail = params.options?.recoveryEmail ?? "";
22391
+ const recoveryLanguage = params.options?.recoveryLanguage ?? "";
21658
22392
  if (pin !== void 0) {
21659
22393
  const validation = validatePin(pin);
21660
22394
  if (!validation.ok)
@@ -21663,12 +22397,35 @@ function createSSSStrategy(config) {
21663
22397
  );
21664
22398
  }
21665
22399
  const status = await self.fetchServerKeyStatus(params.token, params.providerType);
22400
+ storage2.assertCurrent();
21666
22401
  if (status.escrowOptedOut) return { enrolled: false, reason: "opted-out" };
21667
- const currentlyEnrolled = status.shareVersion !== null && status.recoveryMethods.some(
22402
+ let currentlyEnrolled = status.shareVersion !== null && status.recoveryMethods.some(
21668
22403
  (method) => method.type === "escrow" && method.shareVersion === status.shareVersion && (Boolean(method.confirmedAt) || "confirmationStatus" in method && method.confirmationStatus === "confirmed")
21669
22404
  );
21670
- const repairingStaleBlob = pin === void 0 && !forceRotate && currentlyEnrolled && !!status.escrowStale && !staleEscrowReenrollAttempted;
21671
- if (!forceRotate && pin === void 0 && currentlyEnrolled && !repairingStaleBlob)
22405
+ let verifiedKey;
22406
+ if (config.escrow.attestation.mode === "nitro") {
22407
+ if (!status.primaryDid || !params.signDidAuthVp)
22408
+ throw new Error("Owner proof signing is required for escrow enrollment.");
22409
+ verifiedKey = await fetchVerifiedEnclaveKey();
22410
+ const current = await fetchAuthorityState(
22411
+ params.token,
22412
+ params.providerType,
22413
+ params.privateKey,
22414
+ status.primaryDid,
22415
+ params.signDidAuthVp,
22416
+ verifiedKey,
22417
+ storage2.assertCurrent
22418
+ );
22419
+ if (current.status === "disabled") {
22420
+ verifiedEscrowEnvelope = void 0;
22421
+ if (!allowDisabledEnrollment) return { enrolled: false, reason: "opted-out" };
22422
+ clearPinForEnrollment ||= current.hasPin && pin === void 0;
22423
+ }
22424
+ currentlyEnrolled = currentlyEnrolled && current.status === "active" && current.shareVersion === status.shareVersion && current.envelope?.keyId === verifiedKey.keyId;
22425
+ if (currentlyEnrolled) verifiedEscrowEnvelope = current.envelope ?? void 0;
22426
+ }
22427
+ const repairingStaleBlob = pin === void 0 && !clearPinForEnrollment && currentlyEnrolled && !!status.escrowStale && !staleEscrowReenrollAttempted;
22428
+ if (!clearPinForEnrollment && pin === void 0 && currentlyEnrolled && !repairingStaleBlob)
21672
22429
  return { enrolled: true, changed: false };
21673
22430
  if (repairingStaleBlob) staleEscrowReenrollAttempted = true;
21674
22431
  if (!status.primaryDid) throw new Error("Cannot enroll escrow without a primary DID");
@@ -21676,7 +22433,7 @@ function createSSSStrategy(config) {
21676
22433
  throw new Error("DID proof signing is required for escrow enrollment");
21677
22434
  }
21678
22435
  try {
21679
- if (!forceRotate && pin === void 0 && unenrolledRotation && unenrolledRotation.shareVersion === status.shareVersion && unenrolledRotation.primaryDid === status.primaryDid) {
22436
+ if (!clearPinForEnrollment && pin === void 0 && unenrolledRotation && unenrolledRotation.shareVersion === status.shareVersion && unenrolledRotation.primaryDid === status.primaryDid) {
21680
22437
  const retry = unenrolledRotation;
21681
22438
  await enrollEscrow(
21682
22439
  params.token,
@@ -21688,12 +22445,16 @@ function createSSSStrategy(config) {
21688
22445
  params.signDidAuthVp,
21689
22446
  void 0,
21690
22447
  void 0,
21691
- retry.clearPin
22448
+ retry.clearPin,
22449
+ storage2.assertCurrent,
22450
+ recoveryEmail,
22451
+ recoveryLanguage
21692
22452
  );
21693
22453
  unenrolledRotation = void 0;
21694
22454
  return { enrolled: true, changed: true, shareVersion: retry.shareVersion };
21695
22455
  }
21696
- const verifiedKey = await fetchVerifiedEnclaveKey();
22456
+ verifiedKey ??= await fetchVerifiedEnclaveKey();
22457
+ storage2.assertCurrent();
21697
22458
  const pinSalt = pin !== void 0 ? generatePinSalt() : void 0;
21698
22459
  const pinMaterial = pin !== void 0 && pinSalt !== void 0 ? { pinSalt, pinVerifier: await derivePinProof(pin, pinSalt) } : void 0;
21699
22460
  const storageId = activeStorageId;
@@ -21722,7 +22483,12 @@ function createSSSStrategy(config) {
21722
22483
  authShare: shares.authShare,
21723
22484
  primaryDid: status.primaryDid
21724
22485
  };
21725
- unenrolledRotation = pinMaterial ? void 0 : { shares, shareVersion, primaryDid: status.primaryDid, clearPin: forceRotate };
22486
+ unenrolledRotation = pinMaterial ? void 0 : {
22487
+ shares,
22488
+ shareVersion,
22489
+ primaryDid: status.primaryDid,
22490
+ clearPin: clearPinForEnrollment
22491
+ };
21726
22492
  await enrollEscrow(
21727
22493
  params.token,
21728
22494
  params.providerType,
@@ -21733,12 +22499,15 @@ function createSSSStrategy(config) {
21733
22499
  params.signDidAuthVp,
21734
22500
  verifiedKey,
21735
22501
  pinMaterial,
21736
- forceRotate
22502
+ clearPinForEnrollment,
22503
+ storage2.assertCurrent,
22504
+ recoveryEmail,
22505
+ recoveryLanguage
21737
22506
  );
21738
22507
  unenrolledRotation = void 0;
21739
22508
  return { enrolled: true, changed: true, shareVersion };
21740
22509
  } catch (err) {
21741
- if (!repairingStaleBlob) throw err;
22510
+ if (!repairingStaleBlob || config.escrow.attestation.mode === "nitro") throw err;
21742
22511
  try {
21743
22512
  if (config.onEscrowError) config.onEscrowError(err);
21744
22513
  else console.warn("SSS: escrow enrollment failed");
@@ -21779,6 +22548,7 @@ function createSSSStrategy(config) {
21779
22548
  },
21780
22549
  // --- User scoping ---
21781
22550
  setActiveUser(userId) {
22551
+ verifiedEscrowEnvelope = void 0;
21782
22552
  if (activeStorageId !== `sss-device-share:${userId}`) storageGeneration++;
21783
22553
  activeStorageId = `sss-device-share:${userId}`;
21784
22554
  lastServerSnapshot = null;
@@ -21823,6 +22593,7 @@ function createSSSStrategy(config) {
21823
22593
  );
21824
22594
  }
21825
22595
  storageGeneration++;
22596
+ verifiedEscrowEnvelope = void 0;
21826
22597
  await Promise.allSettled(localWrites);
21827
22598
  await clearPendingShareCandidates(storage, storageId);
21828
22599
  return storage.clearAllShares(storageId);
@@ -22121,8 +22892,58 @@ function createSSSStrategy(config) {
22121
22892
  return { state: enrolled ? "enrolled" : "not-enrolled", escrowPin: status.escrowPin };
22122
22893
  },
22123
22894
  async disableEscrowRecovery(params) {
22895
+ const assertCurrent = guardedStorage().assertCurrent;
22124
22896
  const status = await this.fetchServerKeyStatus(params.token, params.providerType);
22125
22897
  if (!status.primaryDid) throw new Error("Cannot disable escrow without a primary DID");
22898
+ if (config.escrow?.attestation.mode === "nitro") {
22899
+ const identity = await fetchVerifiedEnclaveKey();
22900
+ const current = await fetchAuthorityState(
22901
+ params.token,
22902
+ params.providerType,
22903
+ params.privateKey,
22904
+ status.primaryDid,
22905
+ params.signDidAuthVp,
22906
+ identity,
22907
+ assertCurrent
22908
+ );
22909
+ if (current.status === "disabled") {
22910
+ verifiedEscrowEnvelope = void 0;
22911
+ return;
22912
+ }
22913
+ const signedIntent = await createEscrowEnrollmentIntent({
22914
+ state: current,
22915
+ target: null,
22916
+ shareVersion: current.shareVersion ?? 0,
22917
+ privateKey: params.privateKey,
22918
+ pinAction: "disable",
22919
+ // Disable always nulls these server-side regardless of input.
22920
+ recoveryEmail: "",
22921
+ recoveryLanguage: ""
22922
+ });
22923
+ const vp2 = await requestFreshDidAuthVp(
22924
+ serverUrl,
22925
+ params.privateKey,
22926
+ status.primaryDid,
22927
+ params.signDidAuthVp,
22928
+ tenantId
22929
+ );
22930
+ assertCurrent();
22931
+ const response = await escrowRequest("/disable", {
22932
+ method: "POST",
22933
+ headers: buildHeaders(params.token, vp2, tenantId),
22934
+ body: JSON.stringify({
22935
+ authToken: params.token,
22936
+ providerType: params.providerType,
22937
+ optOut: true,
22938
+ signedIntent,
22939
+ sourceEnvelope: current.envelope
22940
+ })
22941
+ });
22942
+ await verifyEscrowEnrollmentCommit(response, signedIntent.intent, identity);
22943
+ assertCurrent();
22944
+ verifiedEscrowEnvelope = void 0;
22945
+ return;
22946
+ }
22126
22947
  const vp = await requestFreshDidAuthVp(
22127
22948
  serverUrl,
22128
22949
  params.privateKey,
@@ -22163,13 +22984,29 @@ function createSSSStrategy(config) {
22163
22984
  providerType: params.providerType
22164
22985
  })
22165
22986
  });
22166
- return ensureEscrowEnrollmentUnguarded(this, params, storage2);
22987
+ return ensureEscrowEnrollmentUnguarded(this, params, storage2, false, true);
22167
22988
  });
22168
22989
  },
22169
22990
  async setEscrowPin({ pin, ...params }) {
22170
22991
  const storage2 = guardedStorage();
22171
22992
  const result = await withRotationLock(
22172
- () => ensureEscrowEnrollmentUnguarded(this, { ...params, options: { pin } }, storage2)
22993
+ () => (
22994
+ // P9.1: preserve the caller's recoveryEmail/recoveryLanguage — do
22995
+ // not replace `params.options` wholesale, or nitro enroll/replace
22996
+ // loses its required recovery contact.
22997
+ ensureEscrowEnrollmentUnguarded(
22998
+ this,
22999
+ {
23000
+ ...params,
23001
+ options: {
23002
+ pin,
23003
+ recoveryEmail: params.options?.recoveryEmail,
23004
+ recoveryLanguage: params.options?.recoveryLanguage
23005
+ }
23006
+ },
23007
+ storage2
23008
+ )
23009
+ )
22173
23010
  );
22174
23011
  if (!result.enrolled || !result.changed) {
22175
23012
  throw new Error("Automatic recovery is not available for this account.");
@@ -22201,8 +23038,15 @@ function createSSSStrategy(config) {
22201
23038
  escrowEnrollmentInFlight = void 0;
22202
23039
  }
22203
23040
  },
23041
+ getVerifiedEscrowEnvelope() {
23042
+ return verifiedEscrowEnvelope ? { ...verifiedEscrowEnvelope } : null;
23043
+ },
22204
23044
  /** Start or explicitly restart a hold with fresh client-bound resume secrets. */
22205
23045
  async startEscrowRecovery(params) {
23046
+ const expectedDid = params.expectedDid;
23047
+ const nitro = config.escrow?.attestation.mode === "nitro";
23048
+ if (nitro && (!expectedDid || (params.tenantId ?? tenantId) !== tenantId))
23049
+ throw new EscrowEvidenceError();
22206
23050
  const sessionProof = params.recoverySessionToken !== void 0;
22207
23051
  if (sessionProof ? params.token !== void 0 || params.providerType !== void 0 : !params.token || !params.providerType) {
22208
23052
  throw new Error("Provide exactly one identity proof");
@@ -22222,25 +23066,101 @@ function createSSSStrategy(config) {
22222
23066
  },
22223
23067
  params.options?.releasePolicy === "pin"
22224
23068
  );
22225
- return { ...result, clientEphemeralPrivateKey: pair.privateKey };
23069
+ let expectations;
23070
+ if (nitro && result.resumeToken !== null) {
23071
+ const identity = await fetchVerifiedEnclaveKey();
23072
+ if (identity.mode !== "nitro" || !identity.authority || !result.holdRecord || !expectedDid)
23073
+ throw new EscrowEvidenceError();
23074
+ const verified = await verifyEscrowHoldRecord(
23075
+ result.holdRecord,
23076
+ {
23077
+ tenant: tenantId ?? "learncard",
23078
+ did: expectedDid,
23079
+ holdId: result.holdId,
23080
+ clientEphemeralPublicKey: pair.publicKey,
23081
+ blobHash: result.holdRecord.hold.blobHash
23082
+ },
23083
+ identity
23084
+ );
23085
+ expectations = {
23086
+ tenant: tenantId ?? "learncard",
23087
+ did: expectedDid,
23088
+ holdId: result.holdId,
23089
+ clientEphemeralPublicKey: pair.publicKey,
23090
+ blobHash: result.holdRecord.hold.blobHash,
23091
+ authorityGeneration: verified.authorityGeneration,
23092
+ holdDigest: verified.holdDigest,
23093
+ ledgerPublicKey: identity.authority.ledgerPublicKey,
23094
+ keyId: verified.receipt.keyId
23095
+ };
23096
+ }
23097
+ return { ...result, clientEphemeralPrivateKey: pair.privateKey, expectations };
22226
23098
  },
22227
23099
  /** Read hold status; secrets (auth or resume token) never travel in a URL. */
22228
23100
  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
- }
23101
+ const expected = config.escrow?.attestation.mode === "nitro" && "holdId" in params ? recoveryExpectations(params.holdId, params.expectations) : void 0;
23102
+ const status = await fetchEscrowHoldStatus(params);
23103
+ if (expected && status)
23104
+ await verifyPendingHold(status, expected, await fetchVerifiedEnclaveKey());
23105
+ return status;
23106
+ },
23107
+ /** Submit the confirmation code emailed by the enclave for an authority-mode hold. */
23108
+ async confirmEscrowRecovery(params) {
23109
+ let pending;
23110
+ if (config.escrow?.attestation.mode === "nitro") {
23111
+ const expected = recoveryExpectations(params.holdId, params.expectations);
23112
+ if ((params.tenantId ?? tenantId) !== tenantId) throw new EscrowEvidenceError();
23113
+ const identity = await fetchVerifiedEnclaveKey();
23114
+ const status = await fetchEscrowHoldStatus({
23115
+ holdId: params.holdId,
23116
+ resumeToken: params.resumeToken
23117
+ });
23118
+ if (!status?.holdRecord) {
23119
+ throw new EscrowEvidenceError();
22241
23120
  }
22242
- );
22243
- return result.hold;
23121
+ const holdRecord = await verifyPendingHold(status, expected, identity);
23122
+ pending = { identity, holdRecord, expected };
23123
+ }
23124
+ const confirm = /* @__PURE__ */ __name(() => escrowRequest("/confirm", {
23125
+ method: "POST",
23126
+ headers: buildHeaders("", void 0, params.tenantId ?? tenantId),
23127
+ body: JSON.stringify({
23128
+ holdId: params.holdId,
23129
+ resumeToken: params.resumeToken,
23130
+ code: params.code
23131
+ })
23132
+ }), "confirm");
23133
+ const result = await confirm();
23134
+ if (pending) {
23135
+ if (!result.receipt) {
23136
+ throw new EscrowEvidenceError();
23137
+ }
23138
+ const receipt = await verifyEscrowHoldConfirmation(
23139
+ result.receipt,
23140
+ pending.holdRecord,
23141
+ pending.expected,
23142
+ pending.identity
23143
+ );
23144
+ if (receipt.kind === "commit") {
23145
+ const readback = await confirm();
23146
+ const snapshot = await verifyEscrowHoldConfirmation(
23147
+ readback.receipt,
23148
+ pending.holdRecord,
23149
+ pending.expected,
23150
+ pending.identity
23151
+ );
23152
+ if (snapshot.kind !== "snapshot") throw new EscrowEvidenceError();
23153
+ }
23154
+ }
23155
+ return { success: true };
23156
+ },
23157
+ /** Request a fresh confirmation code for a hold awaiting confirmation. */
23158
+ async resendEscrowRecoveryCode(params) {
23159
+ return escrowRequest("/resend", {
23160
+ method: "POST",
23161
+ headers: buildHeaders("", void 0, params.tenantId ?? tenantId),
23162
+ body: JSON.stringify({ holdId: params.holdId, resumeToken: params.resumeToken })
23163
+ });
22244
23164
  },
22245
23165
  /** Cancel a pending hold with a fresh DID-owner proof. */
22246
23166
  async cancelEscrowRecovery(params) {
@@ -22272,8 +23192,19 @@ function createSSSStrategy(config) {
22272
23192
  const start = input.method === "escrow-pin" ? await this.startEscrowRecovery({
22273
23193
  token,
22274
23194
  providerType,
23195
+ expectedDid: input.expectedDid,
22275
23196
  options: { releasePolicy: "pin" }
22276
23197
  }) : input;
23198
+ const expected = config.escrow?.attestation.mode === "nitro" ? recoveryExpectations(start.holdId, start.expectations) : void 0;
23199
+ if (expected) {
23200
+ if (!start.resumeToken) throw new EscrowEvidenceError();
23201
+ const status = await fetchEscrowHoldStatus({
23202
+ holdId: start.holdId,
23203
+ resumeToken: start.resumeToken
23204
+ });
23205
+ if (!status) throw new EscrowEvidenceError();
23206
+ await verifyPendingHold(status, expected, await fetchVerifiedEnclaveKey());
23207
+ }
22277
23208
  let pinProof;
22278
23209
  if (input.method === "escrow-pin") {
22279
23210
  if (!("pinSalt" in start) || !start.pinSalt || !start.resumeToken)
@@ -22297,6 +23228,8 @@ function createSSSStrategy(config) {
22297
23228
  material.sealedShare,
22298
23229
  start.clientEphemeralPrivateKey
22299
23230
  );
23231
+ if (expected && (plaintext.did !== expected.did || plaintext.holdId !== expected.holdId))
23232
+ throw new EscrowEvidenceError();
22300
23233
  if (plaintext.holdId !== start.holdId || plaintext.did !== material.primaryDid || plaintext.shareVersion !== material.shareVersion) {
22301
23234
  throw new Error("Escrow recovery material does not match the request");
22302
23235
  }
@@ -22710,10 +23643,10 @@ function createSSSStrategy(config) {
22710
23643
  const pending = pendingPhraseConfirmation;
22711
23644
  if (!pending) throw new Error("Generate a new recovery phrase to continue");
22712
23645
  const phraseWords = pending.phrase.trim().toLowerCase().split(/\s+/);
22713
- const matches = input.challengeWords.length === pending.challengeWordIndices.length && pending.challengeWordIndices.every(
23646
+ const matches2 = input.challengeWords.length === pending.challengeWordIndices.length && pending.challengeWordIndices.every(
22714
23647
  (wordIndex, answerIndex) => input.challengeWords[answerIndex]?.trim().toLowerCase() === phraseWords[wordIndex]
22715
23648
  );
22716
- if (!matches) {
23649
+ if (!matches2) {
22717
23650
  throw new Error("Those words do not match. Check your phrase and try again.");
22718
23651
  }
22719
23652
  } else if (input.method === "backup") {
@@ -23099,6 +24032,7 @@ function createSSSStrategy(config) {
23099
24032
  },
23100
24033
  async cleanup() {
23101
24034
  storageGeneration++;
24035
+ verifiedEscrowEnvelope = void 0;
23102
24036
  await Promise.allSettled(localWrites);
23103
24037
  }
23104
24038
  };
@@ -23329,9 +24263,11 @@ export {
23329
24263
  ESCROW_ALGORITHM,
23330
24264
  ESCROW_BLOB_INFO,
23331
24265
  ESCROW_CLIENT_KEY_ID,
24266
+ ESCROW_ENROLLMENT_INTENT_DOMAIN,
23332
24267
  ESCROW_ENVELOPE_VERSION,
23333
24268
  ESCROW_PIN_MAX_ATTEMPTS,
23334
24269
  ESCROW_RELEASE_INFO,
24270
+ EscrowEvidenceError,
23335
24271
  EscrowHoldRestartThrottledError,
23336
24272
  EscrowPinLockedError,
23337
24273
  EscrowPinMismatchError,
@@ -23359,6 +24295,7 @@ export {
23359
24295
  constantTimeEqualHex,
23360
24296
  countWords,
23361
24297
  createAdaptiveStorage,
24298
+ createEscrowEnrollmentIntent,
23362
24299
  createPasskeyCredential,
23363
24300
  createQrLoginSession,
23364
24301
  createSSSKeyManager,
@@ -23372,6 +24309,7 @@ export {
23372
24309
  deriveKeyFromPasskey,
23373
24310
  deriveKeyFromPassword,
23374
24311
  derivePinProof,
24312
+ encodeEscrowEnrollmentIntent,
23375
24313
  encryptEmailRelayPayload,
23376
24314
  encryptEscrowBlob,
23377
24315
  encryptShareForTransfer,
@@ -23384,6 +24322,7 @@ export {
23384
24322
  generatePinSalt,
23385
24323
  generateRecoveryPhrase,
23386
24324
  getDeviceShare,
24325
+ getEscrowBlobIdentity,
23387
24326
  getQrLoginSessionInfo,
23388
24327
  getShareVersion,
23389
24328
  hasDeviceShare,
@@ -23405,9 +24344,11 @@ export {
23405
24344
  reconstructFromShares,
23406
24345
  reconstructPrivateKey,
23407
24346
  recoveryPhraseToShare,
24347
+ resolveEscrowEnrollmentAction,
23408
24348
  sealEscrowRelease,
23409
24349
  setPublicComputerMode,
23410
24350
  shareToRecoveryPhrase,
24351
+ signEscrowEnrollmentIntent,
23411
24352
  splitAndVerify,
23412
24353
  splitPrivateKey,
23413
24354
  storeDeviceShare,
@@ -23415,6 +24356,9 @@ export {
23415
24356
  validatePin,
23416
24357
  validateRecoveryPhrase,
23417
24358
  verifyEnclaveAttestation,
24359
+ verifyEscrowAuthorityState,
24360
+ verifyEscrowEnrollmentCommit,
23418
24361
  verifyNitroAttestationDocument,
23419
- verifyStoredShares
24362
+ verifyStoredShares,
24363
+ violatesCarryContactRule
23420
24364
  };