@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.
- package/dist/escrow-attestation.d.ts +2 -9
- package/dist/escrow-attestation.d.ts.map +1 -1
- package/dist/escrow-authority.d.ts +112 -0
- package/dist/escrow-authority.d.ts.map +1 -0
- package/dist/escrow-authority.types.d.ts +51 -0
- package/dist/escrow-authority.types.d.ts.map +1 -0
- package/dist/escrow-crypto.d.ts +9 -1
- package/dist/escrow-crypto.d.ts.map +1 -1
- package/dist/escrow-enrollment-intent.d.ts +13 -0
- package/dist/escrow-enrollment-intent.d.ts.map +1 -0
- package/dist/escrow-enrollment-intent.types.d.ts +28 -0
- package/dist/escrow-enrollment-intent.types.d.ts.map +1 -0
- package/dist/escrow-nitro-attestation.d.ts +2 -0
- package/dist/escrow-nitro-attestation.d.ts.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/sss-key-manager.cjs.development.js +1592 -213
- package/dist/sss-key-manager.cjs.development.js.map +4 -4
- package/dist/sss-key-manager.cjs.production.min.js +28 -23
- package/dist/sss-key-manager.cjs.production.min.js.map +4 -4
- package/dist/sss-key-manager.esm.js +1107 -163
- package/dist/sss-key-manager.esm.js.map +4 -4
- package/dist/sss-strategy.d.ts.map +1 -1
- package/dist/types.d.ts +16 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/__fixtures__/escrow-authority-v1.json +96 -0
- package/src/__fixtures__/escrow-carry-contact-contract-v1.json +33 -0
- package/src/__fixtures__/escrow-enrollment-intent-v1.json +26 -0
- package/src/__fixtures__/nitro-attestation-real/staging-nsm-document.json +16 -0
- package/src/escrow-attestation.ts +10 -9
- package/src/escrow-authority.test.ts +643 -0
- package/src/escrow-authority.ts +597 -0
- package/src/escrow-authority.types.ts +56 -0
- package/src/escrow-crypto.ts +39 -1
- package/src/escrow-enrollment-intent.test.ts +224 -0
- package/src/escrow-enrollment-intent.ts +236 -0
- package/src/escrow-enrollment-intent.types.ts +28 -0
- package/src/escrow-nitro-attestation.test.ts +72 -0
- package/src/escrow-nitro-attestation.ts +34 -3
- package/src/index.ts +27 -0
- package/src/sss-authority.test.ts +889 -0
- package/src/sss-strategy.ts +479 -38
- 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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
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
|
|
1024
|
-
for (let i = 0; i <
|
|
1025
|
-
const k =
|
|
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(
|
|
1247
|
+
function defineLazy(object22, key, getter) {
|
|
1248
1248
|
let value = void 0;
|
|
1249
|
-
Object.defineProperty(
|
|
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(
|
|
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
|
|
1310
|
-
const promises =
|
|
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 <
|
|
1314
|
-
resolvedObj[
|
|
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
|
|
1854
|
+
const bytes3 = new Uint8Array(binaryString.length);
|
|
1855
1855
|
for (let i = 0; i < binaryString.length; i++) {
|
|
1856
|
-
|
|
1856
|
+
bytes3[i] = binaryString.charCodeAt(i);
|
|
1857
1857
|
}
|
|
1858
|
-
return
|
|
1858
|
+
return bytes3;
|
|
1859
1859
|
}
|
|
1860
1860
|
__name(base64ToUint8Array, "base64ToUint8Array");
|
|
1861
1861
|
__name2(base64ToUint8Array, "base64ToUint8Array");
|
|
1862
|
-
function uint8ArrayToBase64(
|
|
1862
|
+
function uint8ArrayToBase64(bytes3) {
|
|
1863
1863
|
let binaryString = "";
|
|
1864
|
-
for (let i = 0; i <
|
|
1865
|
-
binaryString += String.fromCharCode(
|
|
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(
|
|
1879
|
-
return uint8ArrayToBase64(
|
|
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
|
|
1888
|
+
const bytes3 = new Uint8Array(cleanHex.length / 2);
|
|
1889
1889
|
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
1890
|
-
|
|
1890
|
+
bytes3[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
|
|
1891
1891
|
}
|
|
1892
|
-
return
|
|
1892
|
+
return bytes3;
|
|
1893
1893
|
}
|
|
1894
1894
|
__name(hexToUint8Array, "hexToUint8Array");
|
|
1895
1895
|
__name2(hexToUint8Array, "hexToUint8Array");
|
|
1896
|
-
function uint8ArrayToHex(
|
|
1897
|
-
return Array.from(
|
|
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(() =>
|
|
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(() =>
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
3601
|
-
for (const k of
|
|
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(
|
|
3611
|
-
numKeys:
|
|
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((
|
|
8374
|
-
return
|
|
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(() =>
|
|
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(() =>
|
|
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(() =>
|
|
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
|
|
14152
|
+
function base6422(params) {
|
|
14153
14153
|
return /* @__PURE__ */ _base64(ZodBase64, params);
|
|
14154
14154
|
}
|
|
14155
|
-
__name(
|
|
14156
|
-
__name2(
|
|
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
|
|
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(
|
|
14212
|
-
__name2(
|
|
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
|
|
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(
|
|
14533
|
-
__name2(
|
|
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
|
|
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 (
|
|
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 (
|
|
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 &&
|
|
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((
|
|
18533
|
+
var encodeBase64Url = /* @__PURE__ */ __name2((bytes3) => {
|
|
18523
18534
|
let output = "";
|
|
18524
|
-
for (let index = 0; index <
|
|
18525
|
-
const first =
|
|
18526
|
-
const second =
|
|
18527
|
-
const third =
|
|
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((
|
|
18569
|
-
message: `must be canonical base64url for exactly ${
|
|
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
|
|
19179
|
+
const bytes3 = new Uint8Array(buf);
|
|
19169
19180
|
let binary = "";
|
|
19170
|
-
for (let i = 0; i <
|
|
19171
|
-
binary += String.fromCharCode(
|
|
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
|
|
19189
|
+
const bytes3 = new Uint8Array(binary.length);
|
|
19179
19190
|
for (let i = 0; i < binary.length; i++) {
|
|
19180
|
-
|
|
19191
|
+
bytes3[i] = binary.charCodeAt(i);
|
|
19181
19192
|
}
|
|
19182
|
-
return
|
|
19193
|
+
return bytes3;
|
|
19183
19194
|
}
|
|
19184
19195
|
__name(base64ToBuffer, "base64ToBuffer");
|
|
19185
19196
|
function hexToBytes(hex2) {
|
|
19186
|
-
const
|
|
19197
|
+
const bytes3 = new Uint8Array(hex2.length / 2);
|
|
19187
19198
|
for (let i = 0; i < hex2.length; i += 2) {
|
|
19188
|
-
|
|
19199
|
+
bytes3[i / 2] = parseInt(hex2.slice(i, i + 2), 16);
|
|
19189
19200
|
}
|
|
19190
|
-
return
|
|
19201
|
+
return bytes3;
|
|
19191
19202
|
}
|
|
19192
19203
|
__name(hexToBytes, "hexToBytes");
|
|
19193
|
-
function bytesToHex(
|
|
19194
|
-
return Array.from(
|
|
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
|
|
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(
|
|
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
|
|
19372
|
+
const bytes3 = new Uint8Array(buf);
|
|
19362
19373
|
let binary = "";
|
|
19363
|
-
for (let i = 0; i <
|
|
19364
|
-
binary += String.fromCharCode(
|
|
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
|
|
19382
|
+
const bytes3 = new Uint8Array(binary.length);
|
|
19372
19383
|
for (let i = 0; i < binary.length; i++) {
|
|
19373
|
-
|
|
19384
|
+
bytes3[i] = binary.charCodeAt(i);
|
|
19374
19385
|
}
|
|
19375
|
-
return
|
|
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(
|
|
19784
|
-
return Array.from(
|
|
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
|
|
19789
|
-
for (let i = 0; i <
|
|
19790
|
-
|
|
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
|
|
19803
|
+
return bytes3;
|
|
19793
19804
|
}
|
|
19794
19805
|
__name(bitsToBytes, "bitsToBytes");
|
|
19795
19806
|
async function computeChecksum(data) {
|
|
19796
|
-
const
|
|
19797
|
-
const hashBits = bytesToBits(new Uint8Array(
|
|
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
|
|
19831
|
-
const checksumBits2 = Math.floor(
|
|
19832
|
-
const totalBits =
|
|
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 =
|
|
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
|
|
20130
|
-
const copy = new Uint8Array(new ArrayBuffer(
|
|
20131
|
-
copy.set(
|
|
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
|
|
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
|
|
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((
|
|
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
|
|
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(
|
|
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(
|
|
20308
|
-
const iv = base64ToBuffer(
|
|
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(
|
|
20322
|
+
{ name: "AES-GCM", iv, additionalData: getAad(envelope2) },
|
|
20312
20323
|
aesKey,
|
|
20313
|
-
base64ToBuffer(
|
|
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
|
|
20327
|
-
const copy = new Uint8Array(new ArrayBuffer(
|
|
20328
|
-
copy.set(
|
|
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((
|
|
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
|
|
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(
|
|
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(
|
|
20482
|
-
const iv = base64ToBuffer(
|
|
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(
|
|
20514
|
+
{ name: "AES-GCM", iv, additionalData: getAad2(envelope2) },
|
|
20486
20515
|
aesKey,
|
|
20487
|
-
base64ToBuffer(
|
|
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 (
|
|
20502
|
-
await decryptFromEnvelope(
|
|
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 (
|
|
20514
|
-
await decryptFromEnvelope(
|
|
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((
|
|
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
|
|
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
|
|
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
|
|
21611
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
21671
|
-
if (
|
|
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 (!
|
|
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
|
-
|
|
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 : {
|
|
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
|
-
|
|
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
|
-
() =>
|
|
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
|
-
|
|
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
|
|
22230
|
-
|
|
22231
|
-
)
|
|
22232
|
-
|
|
22233
|
-
|
|
22234
|
-
|
|
22235
|
-
|
|
22236
|
-
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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
|
};
|