@reclaimprotocol/js-sdk 5.1.0-dev.1 → 5.2.0
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/README.md +50 -19
- package/dist/index.d.ts +58 -18
- package/dist/index.js +455 -857
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -84,7 +84,7 @@ var require_package = __commonJS({
|
|
|
84
84
|
"package.json"(exports2, module2) {
|
|
85
85
|
module2.exports = {
|
|
86
86
|
name: "@reclaimprotocol/js-sdk",
|
|
87
|
-
version: "5.
|
|
87
|
+
version: "5.2.0",
|
|
88
88
|
description: "Designed to request proofs from the Reclaim protocol and manage the flow of claims and witness interactions.",
|
|
89
89
|
main: "dist/index.js",
|
|
90
90
|
types: "dist/index.d.ts",
|
|
@@ -200,7 +200,6 @@ __export(index_exports, {
|
|
|
200
200
|
clearDeviceCache: () => clearDeviceCache,
|
|
201
201
|
createLinkWithTemplateData: () => createLinkWithTemplateData,
|
|
202
202
|
createSignDataForClaim: () => createSignDataForClaim,
|
|
203
|
-
createTrustedDataFromProofData: () => createTrustedDataFromProofData,
|
|
204
203
|
fetchProviderConfigs: () => fetchProviderConfigs,
|
|
205
204
|
fetchProviderHashRequirementsBy: () => fetchProviderHashRequirementsBy,
|
|
206
205
|
fetchStatusUrl: () => fetchStatusUrl,
|
|
@@ -213,7 +212,6 @@ __export(index_exports, {
|
|
|
213
212
|
getProviderHashRequirementSpecFromProviderConfig: () => getProviderHashRequirementSpecFromProviderConfig,
|
|
214
213
|
getProviderHashRequirementsFromSpec: () => getProviderHashRequirementsFromSpec,
|
|
215
214
|
getProviderParamsAsCanonicalizedString: () => getProviderParamsAsCanonicalizedString,
|
|
216
|
-
getPublicDataFromProofs: () => getPublicDataFromProofs,
|
|
217
215
|
getShortenedUrl: () => getShortenedUrl,
|
|
218
216
|
hashProofClaimParams: () => hashProofClaimParams,
|
|
219
217
|
hashRequestSpec: () => hashRequestSpec,
|
|
@@ -222,6 +220,7 @@ __export(index_exports, {
|
|
|
222
220
|
isHttpProviderClaimParams: () => isHttpProviderClaimParams,
|
|
223
221
|
isMobileDevice: () => isMobileDevice,
|
|
224
222
|
recoverSignersOfSignedClaim: () => recoverSignersOfSignedClaim,
|
|
223
|
+
runTeeVerification: () => runTeeVerification,
|
|
225
224
|
takePairsWhereValueIsArray: () => takePairsWhereValueIsArray,
|
|
226
225
|
takeTemplateParametersFromProofs: () => takeTemplateParametersFromProofs,
|
|
227
226
|
transformForOnchain: () => transformForOnchain,
|
|
@@ -240,7 +239,7 @@ var RECLAIM_EXTENSION_ACTIONS = {
|
|
|
240
239
|
};
|
|
241
240
|
|
|
242
241
|
// src/Reclaim.ts
|
|
243
|
-
var
|
|
242
|
+
var import_ethers6 = require("ethers");
|
|
244
243
|
var import_canonicalize3 = __toESM(require("canonicalize"));
|
|
245
244
|
|
|
246
245
|
// src/utils/errors.ts
|
|
@@ -340,62 +339,6 @@ var logger_default = {
|
|
|
340
339
|
setLogLevel
|
|
341
340
|
};
|
|
342
341
|
|
|
343
|
-
// src/utils/helper.ts
|
|
344
|
-
var logger2 = logger_default.logger;
|
|
345
|
-
function escapeRegExp(string) {
|
|
346
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
347
|
-
}
|
|
348
|
-
function replaceAll(str, find, replace) {
|
|
349
|
-
if (find === "") return str;
|
|
350
|
-
return str.replace(new RegExp(escapeRegExp(find), "g"), replace);
|
|
351
|
-
}
|
|
352
|
-
function scheduleIntervalEndingTask(sessionId, intervals, onFailureCallback, timeout = 1e3 * 60 * 10) {
|
|
353
|
-
setTimeout(() => {
|
|
354
|
-
if (intervals.has(sessionId)) {
|
|
355
|
-
const message = "Interval ended without receiving proofs";
|
|
356
|
-
onFailureCallback(new TimeoutError(message));
|
|
357
|
-
logger2.info(message);
|
|
358
|
-
clearInterval(intervals.get(sessionId));
|
|
359
|
-
intervals.delete(sessionId);
|
|
360
|
-
}
|
|
361
|
-
}, timeout);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// src/utils/constants.ts
|
|
365
|
-
var BACKEND_BASE_URL = "https://api.reclaimprotocol.org";
|
|
366
|
-
function setBackendBaseUrl(url) {
|
|
367
|
-
BACKEND_BASE_URL = url;
|
|
368
|
-
}
|
|
369
|
-
var constants = {
|
|
370
|
-
// Default callback URL for Reclaim protocol
|
|
371
|
-
get DEFAULT_RECLAIM_CALLBACK_URL() {
|
|
372
|
-
return `${BACKEND_BASE_URL}/api/sdk/callback?callbackId=`;
|
|
373
|
-
},
|
|
374
|
-
// Default error callback URL for Reclaim protocol
|
|
375
|
-
get DEFAULT_RECLAIM_CANCEL_CALLBACK_URL() {
|
|
376
|
-
return `${BACKEND_BASE_URL}/api/sdk/error-callback?callbackId=`;
|
|
377
|
-
},
|
|
378
|
-
// Default status URL for Reclaim sessions
|
|
379
|
-
get DEFAULT_RECLAIM_STATUS_URL() {
|
|
380
|
-
return `${BACKEND_BASE_URL}/api/sdk/session/`;
|
|
381
|
-
},
|
|
382
|
-
// Default attestors URL for Reclaim sessions
|
|
383
|
-
get DEFAULT_ATTESTORS_URL() {
|
|
384
|
-
return `${BACKEND_BASE_URL}/api/attestors`;
|
|
385
|
-
},
|
|
386
|
-
DEFAULT_PROVIDER_CONFIGS_URL(providerId, exactProviderVersionString, allowedTags) {
|
|
387
|
-
return `${BACKEND_BASE_URL}/api/providers/${providerId}/configs?versionNumber=${exactProviderVersionString || ""}&allowedTags=${(allowedTags == null ? void 0 : allowedTags.join(",")) || ""}`;
|
|
388
|
-
},
|
|
389
|
-
// Default portal URL
|
|
390
|
-
DEFAULT_PORTAL_URL: "https://portal.reclaimprotocol.org",
|
|
391
|
-
// Default sharepage URL
|
|
392
|
-
DEFAULT_APP_SHARE_PAGE_URL: "https://share.reclaimprotocol.org/verifier",
|
|
393
|
-
// URL for sharing Reclaim templates
|
|
394
|
-
RECLAIM_SHARE_URL: "https://share.reclaimprotocol.org/verifier/?template=",
|
|
395
|
-
// Chrome extension URL for Reclaim Protocol
|
|
396
|
-
CHROME_EXTENSION_URL: "https://chromewebstore.google.com/detail/reclaim-extension/oafieibbbcepkmenknelhmgaoahamdeh"
|
|
397
|
-
};
|
|
398
|
-
|
|
399
342
|
// src/utils/validationUtils.ts
|
|
400
343
|
var import_ethers = require("ethers");
|
|
401
344
|
var import_canonicalize2 = __toESM(require("canonicalize"));
|
|
@@ -410,43 +353,43 @@ function canonicalStringify(params) {
|
|
|
410
353
|
}
|
|
411
354
|
|
|
412
355
|
// src/utils/validationUtils.ts
|
|
413
|
-
var
|
|
356
|
+
var logger2 = logger_default.logger;
|
|
414
357
|
function validateFunctionParams(params, functionName) {
|
|
415
358
|
params.forEach(({ input, paramName, isString }) => {
|
|
416
359
|
if (input == null) {
|
|
417
|
-
|
|
360
|
+
logger2.info(`Validation failed: ${paramName} in ${functionName} is null or undefined`);
|
|
418
361
|
throw new InvalidParamError(`${paramName} passed to ${functionName} must not be null or undefined.`);
|
|
419
362
|
}
|
|
420
363
|
if (isString && typeof input !== "string") {
|
|
421
|
-
|
|
364
|
+
logger2.info(`Validation failed: ${paramName} in ${functionName} is not a string`);
|
|
422
365
|
throw new InvalidParamError(`${paramName} passed to ${functionName} must be a string.`);
|
|
423
366
|
}
|
|
424
367
|
if (isString && input.trim() === "") {
|
|
425
|
-
|
|
368
|
+
logger2.info(`Validation failed: ${paramName} in ${functionName} is an empty string`);
|
|
426
369
|
throw new InvalidParamError(`${paramName} passed to ${functionName} must not be an empty string.`);
|
|
427
370
|
}
|
|
428
371
|
});
|
|
429
372
|
}
|
|
430
373
|
function validateFunctionParamsWithFn(param, functionName) {
|
|
431
374
|
if (!param.isValid()) {
|
|
432
|
-
|
|
375
|
+
logger2.info(`Validation failed: ${param.paramName} in ${functionName} is not valid`);
|
|
433
376
|
throw new InvalidParamError(`${param.paramName} passed to ${functionName} must be valid.`);
|
|
434
377
|
}
|
|
435
378
|
}
|
|
436
379
|
function validateParameters(parameters) {
|
|
437
380
|
try {
|
|
438
381
|
if (typeof parameters !== "object" || parameters === null) {
|
|
439
|
-
|
|
382
|
+
logger2.info(`Parameters validation failed: Provided parameters is not an object`);
|
|
440
383
|
throw new InavlidParametersError(`The provided parameters is not an object`);
|
|
441
384
|
}
|
|
442
385
|
for (const [key, value] of Object.entries(parameters)) {
|
|
443
386
|
if (typeof key !== "string" || typeof value !== "string") {
|
|
444
|
-
|
|
387
|
+
logger2.info(`Parameters validation failed: Provided parameters is not an object of key value pairs of string and string`);
|
|
445
388
|
throw new InavlidParametersError(`The provided parameters is not an object of key value pairs of string and string`);
|
|
446
389
|
}
|
|
447
390
|
}
|
|
448
391
|
} catch (e) {
|
|
449
|
-
|
|
392
|
+
logger2.info(`Parameters validation failed: ${e.message}`);
|
|
450
393
|
throw new InavlidParametersError(`Invalid parameters passed to validateParameters.`, e);
|
|
451
394
|
}
|
|
452
395
|
}
|
|
@@ -454,7 +397,7 @@ function validateURL(url, functionName) {
|
|
|
454
397
|
try {
|
|
455
398
|
new URL(url);
|
|
456
399
|
} catch (e) {
|
|
457
|
-
|
|
400
|
+
logger2.info(`URL validation failed for ${url} in ${functionName}: ${e.message}`);
|
|
458
401
|
throw new InvalidParamError(`Invalid URL format ${url} passed to ${functionName}.`, e);
|
|
459
402
|
}
|
|
460
403
|
}
|
|
@@ -467,7 +410,7 @@ function validateRedirectionMethod(method, functionName) {
|
|
|
467
410
|
throw new Error(`Invalid redirection method ${method} passed to ${functionName}.`);
|
|
468
411
|
}
|
|
469
412
|
} catch (e) {
|
|
470
|
-
|
|
413
|
+
logger2.info(`Redirection method validation failed for ${method} in ${functionName}: ${e.message}`);
|
|
471
414
|
throw new InvalidParamError(`Invalid redirection method ${method} passed to ${functionName}.`, e);
|
|
472
415
|
}
|
|
473
416
|
}
|
|
@@ -489,16 +432,16 @@ function validateRedirectionBody(records, functionName) {
|
|
|
489
432
|
throw new Error("Redirection body must be an array of objects with name, and value");
|
|
490
433
|
}
|
|
491
434
|
} catch (e) {
|
|
492
|
-
|
|
435
|
+
logger2.info(`Redirection body validation failed for ${records} in ${functionName}: ${e.message}`);
|
|
493
436
|
throw new InvalidParamError(`Invalid redirection body ${records} passed to ${functionName}.`, e);
|
|
494
437
|
}
|
|
495
438
|
}
|
|
496
439
|
function validateSignature(providerId, signature, applicationId, timestamp) {
|
|
497
440
|
try {
|
|
498
|
-
|
|
441
|
+
logger2.info(`Starting signature validation for providerId: ${providerId}, applicationId: ${applicationId}, timestamp: ${timestamp}`);
|
|
499
442
|
const message = (0, import_canonicalize2.default)({ providerId, timestamp });
|
|
500
443
|
if (!message) {
|
|
501
|
-
|
|
444
|
+
logger2.info("Failed to canonicalize message for signature validation");
|
|
502
445
|
throw new Error("Failed to canonicalize message");
|
|
503
446
|
}
|
|
504
447
|
const messageHash = import_ethers.ethers.keccak256(new TextEncoder().encode(message));
|
|
@@ -507,12 +450,12 @@ function validateSignature(providerId, signature, applicationId, timestamp) {
|
|
|
507
450
|
import_ethers.ethers.hexlify(signature)
|
|
508
451
|
).toLowerCase();
|
|
509
452
|
if (import_ethers.ethers.getAddress(appId) !== import_ethers.ethers.getAddress(applicationId)) {
|
|
510
|
-
|
|
453
|
+
logger2.info(`Signature validation failed: Mismatch between derived appId (${appId}) and provided applicationId (${applicationId})`);
|
|
511
454
|
throw new InvalidSignatureError(`Signature does not match the application id: ${appId}`);
|
|
512
455
|
}
|
|
513
|
-
|
|
456
|
+
logger2.info(`Signature validated successfully for applicationId: ${applicationId}`);
|
|
514
457
|
} catch (err) {
|
|
515
|
-
|
|
458
|
+
logger2.info(`Signature validation failed: ${err.message}`);
|
|
516
459
|
if (err instanceof InvalidSignatureError) {
|
|
517
460
|
throw err;
|
|
518
461
|
}
|
|
@@ -528,16 +471,16 @@ function validateContext(context) {
|
|
|
528
471
|
JSON.parse(canonicalStringify(context));
|
|
529
472
|
return;
|
|
530
473
|
} catch (e) {
|
|
531
|
-
|
|
474
|
+
logger2.info(`Context validation failed: Provided JSON serializable context is not valid`);
|
|
532
475
|
throw new InvalidParamError(`The provided context is not valid`);
|
|
533
476
|
}
|
|
534
477
|
}
|
|
535
478
|
if (!context.contextAddress) {
|
|
536
|
-
|
|
479
|
+
logger2.info(`Context validation failed: Provided context address in context is not valid`);
|
|
537
480
|
throw new InvalidParamError(`The provided context address in context is not valid`);
|
|
538
481
|
}
|
|
539
482
|
if (!context.contextMessage) {
|
|
540
|
-
|
|
483
|
+
logger2.info(`Context validation failed: Provided context message in context is not valid`);
|
|
541
484
|
throw new InvalidParamError(`The provided context message in context is not valid`);
|
|
542
485
|
}
|
|
543
486
|
validateFunctionParams([
|
|
@@ -593,11 +536,120 @@ function hashObject(o) {
|
|
|
593
536
|
const messageHash = import_ethers.ethers.keccak256(new TextEncoder().encode(canonicalData));
|
|
594
537
|
return messageHash;
|
|
595
538
|
} catch (e) {
|
|
596
|
-
|
|
539
|
+
logger2.info(`Failed to hash object: ${e.message}`);
|
|
597
540
|
throw new Error(`Failed to hash object: ${e.message}`);
|
|
598
541
|
}
|
|
599
542
|
}
|
|
600
543
|
|
|
544
|
+
// src/utils/helper.ts
|
|
545
|
+
var logger3 = logger_default.logger;
|
|
546
|
+
function escapeRegExp(string) {
|
|
547
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
548
|
+
}
|
|
549
|
+
function replaceAll(str, find, replace) {
|
|
550
|
+
if (find === "") return str;
|
|
551
|
+
return str.replace(new RegExp(escapeRegExp(find), "g"), replace);
|
|
552
|
+
}
|
|
553
|
+
function scheduleIntervalEndingTask(sessionId, intervals, onFailureCallback, timeout = 1e3 * 60 * 10) {
|
|
554
|
+
setTimeout(() => {
|
|
555
|
+
if (intervals.has(sessionId)) {
|
|
556
|
+
const message = "Interval ended without receiving proofs";
|
|
557
|
+
onFailureCallback(new TimeoutError(message));
|
|
558
|
+
logger3.info(message);
|
|
559
|
+
clearInterval(intervals.get(sessionId));
|
|
560
|
+
intervals.delete(sessionId);
|
|
561
|
+
}
|
|
562
|
+
}, timeout);
|
|
563
|
+
}
|
|
564
|
+
var createVerifyProofResultSuccess = (proofs, isTeeAttestationVerified) => {
|
|
565
|
+
return {
|
|
566
|
+
isVerified: true,
|
|
567
|
+
isTeeAttestationVerified,
|
|
568
|
+
error: void 0,
|
|
569
|
+
data: proofs.map(createTrustedDataFromProofData),
|
|
570
|
+
publicData: getPublicDataFromProofs(proofs)
|
|
571
|
+
};
|
|
572
|
+
};
|
|
573
|
+
var createVerifyProofResultFailure = (error, isTeeAttestationVerified) => {
|
|
574
|
+
return {
|
|
575
|
+
isVerified: false,
|
|
576
|
+
isTeeAttestationVerified,
|
|
577
|
+
error,
|
|
578
|
+
data: [],
|
|
579
|
+
publicData: []
|
|
580
|
+
};
|
|
581
|
+
};
|
|
582
|
+
function createTrustedDataFromProofData(proof) {
|
|
583
|
+
try {
|
|
584
|
+
const context = JSON.parse(proof.claimData.context);
|
|
585
|
+
const _a = context, { extractedParameters } = _a, rest = __objRest(_a, ["extractedParameters"]);
|
|
586
|
+
return {
|
|
587
|
+
context: rest,
|
|
588
|
+
extractedParameters: extractedParameters != null ? extractedParameters : {}
|
|
589
|
+
};
|
|
590
|
+
} catch (e) {
|
|
591
|
+
return {
|
|
592
|
+
context: {},
|
|
593
|
+
extractedParameters: {}
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function getPublicDataFromProofs(proofs) {
|
|
598
|
+
const data = [];
|
|
599
|
+
const seenData = /* @__PURE__ */ new Set();
|
|
600
|
+
for (const proof of proofs) {
|
|
601
|
+
const publicData = proof.publicData;
|
|
602
|
+
if (publicData === null || publicData === void 0) {
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
try {
|
|
606
|
+
const hash = hashObject(publicData);
|
|
607
|
+
if (seenData.has(hash)) {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
seenData.add(hash);
|
|
611
|
+
} catch (_) {
|
|
612
|
+
}
|
|
613
|
+
data.push(publicData);
|
|
614
|
+
}
|
|
615
|
+
return data;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// src/utils/constants.ts
|
|
619
|
+
var BACKEND_BASE_URL = "https://api.reclaimprotocol.org";
|
|
620
|
+
function setBackendBaseUrl(url) {
|
|
621
|
+
BACKEND_BASE_URL = url;
|
|
622
|
+
}
|
|
623
|
+
var constants = {
|
|
624
|
+
// Default callback URL for Reclaim protocol
|
|
625
|
+
get DEFAULT_RECLAIM_CALLBACK_URL() {
|
|
626
|
+
return `${BACKEND_BASE_URL}/api/sdk/callback?callbackId=`;
|
|
627
|
+
},
|
|
628
|
+
// Default error callback URL for Reclaim protocol
|
|
629
|
+
get DEFAULT_RECLAIM_CANCEL_CALLBACK_URL() {
|
|
630
|
+
return `${BACKEND_BASE_URL}/api/sdk/error-callback?callbackId=`;
|
|
631
|
+
},
|
|
632
|
+
// Default status URL for Reclaim sessions
|
|
633
|
+
get DEFAULT_RECLAIM_STATUS_URL() {
|
|
634
|
+
return `${BACKEND_BASE_URL}/api/sdk/session/`;
|
|
635
|
+
},
|
|
636
|
+
// Default attestors URL for Reclaim sessions
|
|
637
|
+
get DEFAULT_ATTESTORS_URL() {
|
|
638
|
+
return `${BACKEND_BASE_URL}/api/attestors`;
|
|
639
|
+
},
|
|
640
|
+
DEFAULT_PROVIDER_CONFIGS_URL(providerId, exactProviderVersionString, allowedTags) {
|
|
641
|
+
return `${BACKEND_BASE_URL}/api/providers/${providerId}/configs?versionNumber=${exactProviderVersionString || ""}&allowedTags=${(allowedTags == null ? void 0 : allowedTags.join(",")) || ""}`;
|
|
642
|
+
},
|
|
643
|
+
// Default portal URL
|
|
644
|
+
DEFAULT_PORTAL_URL: "https://portal.reclaimprotocol.org",
|
|
645
|
+
// Default sharepage URL
|
|
646
|
+
DEFAULT_APP_SHARE_PAGE_URL: "https://share.reclaimprotocol.org/verifier",
|
|
647
|
+
// URL for sharing Reclaim templates
|
|
648
|
+
RECLAIM_SHARE_URL: "https://share.reclaimprotocol.org/verifier/?template=",
|
|
649
|
+
// Chrome extension URL for Reclaim Protocol
|
|
650
|
+
CHROME_EXTENSION_URL: "https://chromewebstore.google.com/detail/reclaim-extension/oafieibbbcepkmenknelhmgaoahamdeh"
|
|
651
|
+
};
|
|
652
|
+
|
|
601
653
|
// src/utils/fetch.ts
|
|
602
654
|
var import_fetch_retry = __toESM(require("fetch-retry"));
|
|
603
655
|
var MAX_RETRIES = 3;
|
|
@@ -1395,6 +1447,20 @@ function clearDeviceCache() {
|
|
|
1395
1447
|
cachedMobileType = null;
|
|
1396
1448
|
}
|
|
1397
1449
|
|
|
1450
|
+
// src/utils/attestationNonce.ts
|
|
1451
|
+
var import_ethers4 = require("ethers");
|
|
1452
|
+
var ATTESTATION_NONCE_DOMAIN = "RECLAIM_TEE_NONCE_V1";
|
|
1453
|
+
function generateAttestationNonce(appSecret, applicationId, sessionId, timestamp) {
|
|
1454
|
+
const noncePayload = [
|
|
1455
|
+
ATTESTATION_NONCE_DOMAIN,
|
|
1456
|
+
applicationId,
|
|
1457
|
+
sessionId,
|
|
1458
|
+
timestamp,
|
|
1459
|
+
appSecret
|
|
1460
|
+
].join(":");
|
|
1461
|
+
return import_ethers4.ethers.keccak256(import_ethers4.ethers.toUtf8Bytes(noncePayload)).replace(/^0x/i, "");
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1398
1464
|
// src/utils/providerUtils.ts
|
|
1399
1465
|
var logger7 = logger_default.logger;
|
|
1400
1466
|
function fetchProviderHashRequirementsBy(providerId, exactProviderVersionString, allowedTags, proofs) {
|
|
@@ -1622,724 +1688,336 @@ function assertValidateProof(proofs, config) {
|
|
|
1622
1688
|
}
|
|
1623
1689
|
|
|
1624
1690
|
// src/utils/verifyTee.ts
|
|
1625
|
-
var
|
|
1626
|
-
var import_ethers4 = require("ethers");
|
|
1627
|
-
|
|
1628
|
-
// src/utils/amdCerts.ts
|
|
1629
|
-
var AMD_CERTS = {
|
|
1630
|
-
"Milan": `-----BEGIN CERTIFICATE-----
|
|
1631
|
-
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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-
upK4YNJmq36IJp7ZWIm5nK2fJNkYEHW0m/NXcIA9U2iHl5bAQ5cbGp97/FaOJ4Vm
|
|
1642
|
-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
-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
-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
-
oDf+BF4XI/IIHJL2N5uKy3+gkSB2Xl6prohgVmqRFvBW9OTCEa32WhXu0t1Z1abE
|
|
1646
|
-
KDZ3LpZt9/Crg6zyPpXDLR/tLHHpSaPRj7CTzHieKMTz+Q6RrCCQcHGfaAD/ETNY
|
|
1647
|
-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
-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
-
v6fiOWx4U8uWhRzHs8nvDAdcS4LW31tPlA9BeOK/BGimQTu7hM5MDFZL0C9dWK5p
|
|
1650
|
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-
6Pokji9h55m05Dxob8XtD4gW6oFLo9Icg7XqdOr9Iip5RBIPxy7rKk/ReqGs9KH7
|
|
1666
|
-
0YPk
|
|
1667
|
-
-----END CERTIFICATE-----
|
|
1668
|
-
-----BEGIN CERTIFICATE-----
|
|
1669
|
-
MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC
|
|
1670
|
-
BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS
|
|
1671
|
-
BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg
|
|
1672
|
-
Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp
|
|
1673
|
-
Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy
|
|
1674
|
-
MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS
|
|
1675
|
-
BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j
|
|
1676
|
-
ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG
|
|
1677
|
-
9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg
|
|
1678
|
-
W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta
|
|
1679
|
-
1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2
|
|
1680
|
-
SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0
|
|
1681
|
-
60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05
|
|
1682
|
-
gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg
|
|
1683
|
-
bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs
|
|
1684
|
-
+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi
|
|
1685
|
-
Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ
|
|
1686
|
-
eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18
|
|
1687
|
-
fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j
|
|
1688
|
-
WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI
|
|
1689
|
-
rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG
|
|
1690
|
-
KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG
|
|
1691
|
-
SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI
|
|
1692
|
-
AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel
|
|
1693
|
-
ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw
|
|
1694
|
-
STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK
|
|
1695
|
-
dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq
|
|
1696
|
-
zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp
|
|
1697
|
-
KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e
|
|
1698
|
-
pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq
|
|
1699
|
-
HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh
|
|
1700
|
-
3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn
|
|
1701
|
-
JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH
|
|
1702
|
-
CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4
|
|
1703
|
-
AFZEAwoKCQ==
|
|
1704
|
-
-----END CERTIFICATE-----`,
|
|
1705
|
-
"Genoa": `-----BEGIN CERTIFICATE-----
|
|
1706
|
-
MIIGjzCCBD6gAwIBAgIDAgEBMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC
|
|
1707
|
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-
gM6VNoowHwYDVR0jBBgwFoAUn135/g3Y81rQMxol74EpT74xqFswEgYDVR0TAQH/
|
|
1727
|
-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
-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
-
AQEKMDmgDzANBglghkgBZQMEAgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQME
|
|
1730
|
-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
-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
-
+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
-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
-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
-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
-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
-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
-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
-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
-
xmEqaiI7xS58pR6vZ8BeXMGPPQqgOm/oBzOypVR3iCG6MFdjsTNA6M8P7GCZe1p7
|
|
1741
|
-
2cko
|
|
1742
|
-
-----END CERTIFICATE-----
|
|
1743
|
-
-----BEGIN CERTIFICATE-----
|
|
1744
|
-
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
-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
-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
-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
-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
-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
-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
-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
-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
-
/L2PVQGhK3YdfEBgpF/JiwWFBsT/fXDhzA01p3LkcT/7LdjcRfKXjHl+0Qq/M4dZ
|
|
1754
|
-
kh6QDoUeKzNBLDcBKDDGWo3v35NyrxbA1DnkYwUKU5AAk4P94tKXLp80oxt84ahy
|
|
1755
|
-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
-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
-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
-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
-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
-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
-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
-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
-
WSY254cCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSfXfn+Ddjz
|
|
1764
|
-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
-
KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvR2Vub2EvY3JsMEYGCSqG
|
|
1766
|
-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
-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
-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
-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
-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
-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
-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
-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
-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
-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
-
HdS38ijiJmm9vl50RGUeOAXjSuInGR4bsRufeGPB9peTa9BcBOeTWzstqTUB/F/q
|
|
1777
|
-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
|
|
1778
|
-
/wMz1R1BHg==
|
|
1779
|
-
-----END CERTIFICATE-----`
|
|
1780
|
-
};
|
|
1781
|
-
|
|
1782
|
-
// src/utils/verifyTee.ts
|
|
1783
|
-
var crlCache = {};
|
|
1691
|
+
var import_ethers5 = require("ethers");
|
|
1784
1692
|
var logger9 = logger_default.logger;
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1693
|
+
var EXPECTED_ISSUER = "https://confidentialcomputing.googleapis.com";
|
|
1694
|
+
var EXPECTED_HW_MODEL = "GCP_AMD_SEV";
|
|
1695
|
+
var EXPECTED_TEE_PROVIDER = "gcp";
|
|
1696
|
+
var EXPECTED_TEE_TECHNOLOGY = "amd-sev";
|
|
1697
|
+
var TOKEN_CLOCK_SKEW_S = 60;
|
|
1698
|
+
var NONCE_TIMESTAMP_MAX_SKEW_MS = 10 * 60 * 1e3;
|
|
1699
|
+
var BROWSER_ENVIRONMENT_ERROR = "TEE attestation verification is only supported in non-browser environments. Run verifyTeeAttestation on your server or API route.";
|
|
1700
|
+
function assert(condition, message) {
|
|
1701
|
+
if (!condition) {
|
|
1702
|
+
throw new Error(message);
|
|
1788
1703
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1704
|
+
}
|
|
1705
|
+
function isBrowserEnvironment() {
|
|
1706
|
+
if (typeof window !== "undefined" || typeof document !== "undefined") {
|
|
1707
|
+
return true;
|
|
1791
1708
|
}
|
|
1792
|
-
if (typeof
|
|
1793
|
-
return
|
|
1709
|
+
if (typeof navigator !== "undefined" && typeof process === "undefined") {
|
|
1710
|
+
return true;
|
|
1794
1711
|
}
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
let result = "";
|
|
1799
|
-
const chunkSize = 32768;
|
|
1800
|
-
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
1801
|
-
const chunk = bytes.subarray(i, i + chunkSize);
|
|
1802
|
-
result += String.fromCharCode(...chunk);
|
|
1712
|
+
const workerGlobalScope = globalThis.WorkerGlobalScope;
|
|
1713
|
+
if (typeof workerGlobalScope !== "undefined" && typeof self !== "undefined" && self instanceof workerGlobalScope) {
|
|
1714
|
+
return true;
|
|
1803
1715
|
}
|
|
1804
|
-
return
|
|
1716
|
+
return false;
|
|
1805
1717
|
}
|
|
1806
|
-
function
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
result[i] = binary.charCodeAt(i);
|
|
1718
|
+
function assertNonBrowserEnvironment() {
|
|
1719
|
+
if (isBrowserEnvironment()) {
|
|
1720
|
+
throw new Error(BROWSER_ENVIRONMENT_ERROR);
|
|
1810
1721
|
}
|
|
1811
|
-
return result;
|
|
1812
1722
|
}
|
|
1813
|
-
function
|
|
1814
|
-
|
|
1815
|
-
const binary = atob(base64);
|
|
1816
|
-
return binaryStringToUint8Array(binary);
|
|
1817
|
-
}
|
|
1818
|
-
if (typeof Buffer !== "undefined") {
|
|
1819
|
-
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
1820
|
-
}
|
|
1821
|
-
throw new Error("Base64 decoding is not supported in this environment");
|
|
1723
|
+
function normalizeHex(value) {
|
|
1724
|
+
return (value || "").trim().replace(/^0x/i, "").toLowerCase();
|
|
1822
1725
|
}
|
|
1823
|
-
function
|
|
1824
|
-
|
|
1825
|
-
let hex = "";
|
|
1826
|
-
for (let i = 0; i < view.length; i++) {
|
|
1827
|
-
hex += view[i].toString(16).padStart(2, "0");
|
|
1828
|
-
}
|
|
1829
|
-
return hex;
|
|
1726
|
+
function isHex(value) {
|
|
1727
|
+
return /^[0-9a-f]+$/i.test(value);
|
|
1830
1728
|
}
|
|
1831
|
-
function
|
|
1832
|
-
const
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
result.set(arr, offset);
|
|
1837
|
-
offset += arr.length;
|
|
1729
|
+
function decodeBase64Url(input) {
|
|
1730
|
+
const normalized = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
1731
|
+
const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
|
|
1732
|
+
if (typeof Buffer !== "undefined") {
|
|
1733
|
+
return new Uint8Array(Buffer.from(padded, "base64"));
|
|
1838
1734
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1735
|
+
if (typeof atob === "function") {
|
|
1736
|
+
const binary = atob(padded);
|
|
1737
|
+
const bytes = new Uint8Array(binary.length);
|
|
1738
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
1739
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1740
|
+
}
|
|
1741
|
+
return bytes;
|
|
1742
|
+
}
|
|
1743
|
+
throw new Error("Base64 decoding is not supported in this environment");
|
|
1845
1744
|
}
|
|
1846
|
-
function
|
|
1847
|
-
|
|
1848
|
-
while (cleaned.startsWith("0") && cleaned.length > 1) cleaned = cleaned.substring(1);
|
|
1849
|
-
return cleaned;
|
|
1745
|
+
function decodeUtf8(bytes) {
|
|
1746
|
+
return new TextDecoder().decode(bytes);
|
|
1850
1747
|
}
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
if (typeof window !== "undefined" && ((_a = window.crypto) == null ? void 0 : _a.subtle)) return window.crypto.subtle;
|
|
1855
|
-
if (isNode) return require("crypto").webcrypto.subtle;
|
|
1856
|
-
throw new Error("No WebCrypto subtle implementation found in this environment");
|
|
1857
|
-
};
|
|
1858
|
-
function parseCert(buffer) {
|
|
1859
|
-
const bytes = toUint8Array(buffer);
|
|
1860
|
-
const asn1 = import_node_forge.default.asn1.fromDer(uint8ArrayToBinaryString(bytes));
|
|
1861
|
-
const certSeq = asn1.value;
|
|
1862
|
-
const tbsAsn1 = certSeq[0];
|
|
1863
|
-
const sigAlgAsn1 = certSeq[1];
|
|
1864
|
-
const sigValueAsn1 = certSeq[2];
|
|
1865
|
-
const tbsFields = tbsAsn1.value;
|
|
1866
|
-
let idx = 0;
|
|
1867
|
-
if (tbsFields[idx].tagClass === 128) idx++;
|
|
1868
|
-
const serialAsn1 = tbsFields[idx++];
|
|
1869
|
-
const serialNumber = import_node_forge.default.util.bytesToHex(serialAsn1.value);
|
|
1870
|
-
idx++;
|
|
1871
|
-
idx++;
|
|
1872
|
-
const validityAsn1 = tbsFields[idx++];
|
|
1873
|
-
idx++;
|
|
1874
|
-
const spkiAsn1 = tbsFields[idx];
|
|
1875
|
-
if (!validityAsn1 || !Array.isArray(validityAsn1.value) || validityAsn1.value.length < 2) {
|
|
1876
|
-
throw new Error("Certificate validity window is malformed");
|
|
1877
|
-
}
|
|
1878
|
-
const notBeforeNode = validityAsn1.value[0];
|
|
1879
|
-
const notAfterNode = validityAsn1.value[1];
|
|
1880
|
-
const notBefore = notBeforeNode ? parseAsn1Time(notBeforeNode) : void 0;
|
|
1881
|
-
const notAfter = notAfterNode ? parseAsn1Time(notAfterNode) : void 0;
|
|
1882
|
-
const sigRaw = typeof sigValueAsn1.value === "string" ? sigValueAsn1.value : "";
|
|
1883
|
-
const signature = binaryStringToUint8Array(sigRaw.substring(1));
|
|
1884
|
-
const sigAlgOid = import_node_forge.default.asn1.derToOid(sigAlgAsn1.value[0].value);
|
|
1748
|
+
function decodeJwt(token) {
|
|
1749
|
+
const parts = token.split(".");
|
|
1750
|
+
assert(parts.length === 3, "attestation token is not a JWT");
|
|
1885
1751
|
return {
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
spkiDer: binaryStringToUint8Array(import_node_forge.default.asn1.toDer(spkiAsn1).getBytes()),
|
|
1891
|
-
notBefore,
|
|
1892
|
-
notAfter
|
|
1752
|
+
header: JSON.parse(decodeUtf8(decodeBase64Url(parts[0]))),
|
|
1753
|
+
payload: JSON.parse(decodeUtf8(decodeBase64Url(parts[1]))),
|
|
1754
|
+
signingInput: `${parts[0]}.${parts[1]}`,
|
|
1755
|
+
signature: decodeBase64Url(parts[2])
|
|
1893
1756
|
};
|
|
1894
1757
|
}
|
|
1895
|
-
function
|
|
1758
|
+
function getFetch() {
|
|
1759
|
+
const fetchFn = globalThis.fetch;
|
|
1760
|
+
assert(fetchFn, "fetch is not available in this environment");
|
|
1761
|
+
return fetchFn.bind(globalThis);
|
|
1762
|
+
}
|
|
1763
|
+
function getSubtleCrypto() {
|
|
1764
|
+
var _a, _b, _c;
|
|
1765
|
+
if ((_a = globalThis.crypto) == null ? void 0 : _a.subtle) {
|
|
1766
|
+
return globalThis.crypto.subtle;
|
|
1767
|
+
}
|
|
1768
|
+
const nodeCrypto = typeof process !== "undefined" && ((_b = process.versions) == null ? void 0 : _b.node) ? require("crypto") : void 0;
|
|
1769
|
+
if ((_c = nodeCrypto == null ? void 0 : nodeCrypto.webcrypto) == null ? void 0 : _c.subtle) {
|
|
1770
|
+
return nodeCrypto.webcrypto.subtle;
|
|
1771
|
+
}
|
|
1772
|
+
throw new Error("WebCrypto subtle is not available in this environment");
|
|
1773
|
+
}
|
|
1774
|
+
function fetchJson(url) {
|
|
1896
1775
|
return __async(this, null, function* () {
|
|
1897
|
-
const
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
let importParams;
|
|
1901
|
-
let verifyParams;
|
|
1902
|
-
if (sigAlgOid === "1.2.840.113549.1.1.10") {
|
|
1903
|
-
importParams = { name: "RSA-PSS", hash: "SHA-384" };
|
|
1904
|
-
verifyParams = { name: "RSA-PSS", saltLength: 48 };
|
|
1905
|
-
} else if (sigAlgOid === "1.2.840.113549.1.1.11" || sigAlgOid === "1.2.840.113549.1.1.12" || sigAlgOid === "1.2.840.113549.1.1.5") {
|
|
1906
|
-
importParams = { name: "RSASSA-PKCS1-v1_5", hash: sigAlgOid === "1.2.840.113549.1.1.12" ? "SHA-384" : "SHA-256" };
|
|
1907
|
-
verifyParams = { name: "RSASSA-PKCS1-v1_5" };
|
|
1908
|
-
} else if (sigAlgOid === "1.2.840.10045.4.3.3") {
|
|
1909
|
-
importParams = { name: "ECDSA", namedCurve: "P-384" };
|
|
1910
|
-
verifyParams = { name: "ECDSA", hash: "SHA-384" };
|
|
1911
|
-
} else {
|
|
1912
|
-
importParams = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
|
|
1913
|
-
verifyParams = { name: "RSASSA-PKCS1-v1_5" };
|
|
1776
|
+
const response = yield getFetch()(url);
|
|
1777
|
+
if (!response.ok) {
|
|
1778
|
+
throw new Error(`GET ${url} returned ${response.status} ${response.statusText}`);
|
|
1914
1779
|
}
|
|
1915
|
-
|
|
1916
|
-
const isValid = yield cryptoSubtle.verify(verifyParams, key, signature, data);
|
|
1917
|
-
if (!isValid) throw new Error(`Signature verification failed (OID: ${sigAlgOid}, ImportParams: ${JSON.stringify(importParams)})`);
|
|
1780
|
+
return response.json();
|
|
1918
1781
|
});
|
|
1919
1782
|
}
|
|
1920
|
-
|
|
1921
|
-
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjiL30OjPuxa+GC1I7SAcBv2u2pMt
|
|
1922
|
-
h9WbP33IvB3eFww+C1hoW0fwdZPiq4FxBtKNiZuFpmYuFngW/nJteBu9kQ==
|
|
1923
|
-
-----END PUBLIC KEY-----
|
|
1924
|
-
`;
|
|
1925
|
-
function verifyTeeAttestation(proof, expectedApplicationId) {
|
|
1783
|
+
function sha256Hex(input) {
|
|
1926
1784
|
return __async(this, null, function* () {
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
if (!teeAttestation) {
|
|
1930
|
-
throw new Error("Missing teeAttestation in proof");
|
|
1931
|
-
}
|
|
1932
|
-
if (typeof teeAttestation === "string") {
|
|
1933
|
-
teeAttestation = JSON.parse(teeAttestation);
|
|
1934
|
-
}
|
|
1935
|
-
let expectedNonceSignature;
|
|
1936
|
-
let nonceDataObj;
|
|
1937
|
-
try {
|
|
1938
|
-
const context = JSON.parse(proof.claimData.context);
|
|
1939
|
-
expectedNonceSignature = context.attestationNonce;
|
|
1940
|
-
nonceDataObj = context.attestationNonceData;
|
|
1941
|
-
} catch (e) {
|
|
1942
|
-
throw new Error("Failed to parse proof context to extract attestationNonce");
|
|
1943
|
-
}
|
|
1944
|
-
if (!expectedNonceSignature || !nonceDataObj) {
|
|
1945
|
-
throw new Error("Proof context is missing attestationNonce or attestationNonceData");
|
|
1946
|
-
}
|
|
1947
|
-
if (teeAttestation.nonce !== expectedNonceSignature) {
|
|
1948
|
-
throw new Error(`Nonce Mismatch! Expected signature ${expectedNonceSignature}, got ${teeAttestation.nonce}`);
|
|
1949
|
-
}
|
|
1950
|
-
const { applicationId, sessionId, timestamp } = nonceDataObj;
|
|
1951
|
-
if (expectedApplicationId && applicationId.toLowerCase() !== expectedApplicationId.toLowerCase()) {
|
|
1952
|
-
throw new Error(`Application ID Mismatch! Expected ${expectedApplicationId}, but proof context contains ${applicationId}`);
|
|
1953
|
-
}
|
|
1954
|
-
const expectedNonceData = `${applicationId}:${sessionId}:${timestamp}`;
|
|
1955
|
-
const nonceMsg = import_ethers4.ethers.getBytes(import_ethers4.ethers.keccak256(new TextEncoder().encode(expectedNonceData)));
|
|
1956
|
-
const recoveredAddress = import_ethers4.ethers.verifyMessage(nonceMsg, expectedNonceSignature);
|
|
1957
|
-
if (recoveredAddress.toLowerCase() !== applicationId.toLowerCase()) {
|
|
1958
|
-
throw new Error(`Nonce signature verification failed: recovered ${recoveredAddress}, expected ${applicationId}`);
|
|
1959
|
-
}
|
|
1960
|
-
try {
|
|
1961
|
-
const context = JSON.parse(proof.claimData.context);
|
|
1962
|
-
const paramSessionId = context.attestationNonceData.sessionId;
|
|
1963
|
-
if (!paramSessionId) {
|
|
1964
|
-
throw new Error(`Proof parameters are missing proxySessionId or sessionId`);
|
|
1965
|
-
}
|
|
1966
|
-
if (paramSessionId.toString() !== sessionId.toString()) {
|
|
1967
|
-
throw new Error(`Session ID Mismatch! Expected ${sessionId}, but proof parameters contain ${paramSessionId}`);
|
|
1968
|
-
}
|
|
1969
|
-
const claimTimestampMs = proof.claimData.timestampS * 1e3;
|
|
1970
|
-
const nonceTimestampMs = parseInt(timestamp, 10);
|
|
1971
|
-
const diffMs = Math.abs(claimTimestampMs - nonceTimestampMs);
|
|
1972
|
-
const TEN_MINUTES_MS = 10 * 60 * 1e3;
|
|
1973
|
-
if (diffMs > TEN_MINUTES_MS) {
|
|
1974
|
-
throw new Error(`Timestamp Skew Too Large! claimData.timestampS and attestationNonce timestamp differ by ${Math.round(diffMs / 1e3)}s (limit: 600s)`);
|
|
1975
|
-
}
|
|
1976
|
-
} catch (e) {
|
|
1977
|
-
if (e instanceof Error && (e.message.includes("Session ID Mismatch!") || e.message.includes("Timestamp Skew"))) {
|
|
1978
|
-
throw e;
|
|
1979
|
-
}
|
|
1980
|
-
throw new Error(`Failed to cross-verify session ID: ${e.message}`);
|
|
1981
|
-
}
|
|
1982
|
-
const reportBuffer = base64ToUint8Array(teeAttestation.snp_report);
|
|
1983
|
-
const report = parseAttestationReport(reportBuffer);
|
|
1984
|
-
if (report.isDebugEnabled) {
|
|
1985
|
-
throw new Error("POLICY CHECK FAILED: Debug mode is ALLOWED. Environment is compromised.");
|
|
1986
|
-
}
|
|
1987
|
-
const certBuffer = base64ToUint8Array(teeAttestation.vlek_cert);
|
|
1988
|
-
yield verifyAMDChain(certBuffer);
|
|
1989
|
-
verifyTCB(certBuffer, report);
|
|
1990
|
-
yield verifyHardwareSignature(reportBuffer, certBuffer);
|
|
1991
|
-
yield verifyReportData(teeAttestation, proof.claimData.context, report);
|
|
1992
|
-
return true;
|
|
1993
|
-
} catch (error) {
|
|
1994
|
-
logger9.error("TEE attestation verification failed:", error);
|
|
1995
|
-
return false;
|
|
1996
|
-
}
|
|
1785
|
+
const digest = yield getSubtleCrypto().digest("SHA-256", new TextEncoder().encode(input));
|
|
1786
|
+
return Array.from(new Uint8Array(digest), (value) => value.toString(16).padStart(2, "0")).join("");
|
|
1997
1787
|
});
|
|
1998
1788
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
const policy = view.getBigUint64(8, true);
|
|
2005
|
-
const isDebugEnabled = (policy & BigInt(1) << BigInt(19)) !== BigInt(0);
|
|
2006
|
-
const reported_tcb = {
|
|
2007
|
-
bootloader: buffer[56],
|
|
2008
|
-
tee: buffer[57],
|
|
2009
|
-
snp: buffer[62],
|
|
2010
|
-
microcode: buffer[63]
|
|
2011
|
-
};
|
|
2012
|
-
const reportData = arrayBufferToHex(buffer.subarray(80, 144));
|
|
2013
|
-
return { policy, isDebugEnabled, reported_tcb, reportData };
|
|
2014
|
-
}
|
|
2015
|
-
function getExtValue(certAsn1, oidString) {
|
|
2016
|
-
const tbsCert = certAsn1.value[0];
|
|
2017
|
-
if (!tbsCert || !tbsCert.value) return null;
|
|
2018
|
-
const extBlockWrapper = tbsCert.value.find((node) => node.tagClass === import_node_forge.default.asn1.Class.CONTEXT_SPECIFIC && node.type === 3);
|
|
2019
|
-
if (!extBlockWrapper || !extBlockWrapper.value || !extBlockWrapper.value.length) return null;
|
|
2020
|
-
const extSequence = extBlockWrapper.value[0];
|
|
2021
|
-
for (const ext of extSequence.value) {
|
|
2022
|
-
const extIdAsn1 = ext.value[0];
|
|
2023
|
-
const extIdStr = import_node_forge.default.asn1.derToOid(extIdAsn1.value);
|
|
2024
|
-
if (extIdStr === oidString) {
|
|
2025
|
-
const extValueAsn1 = ext.value[ext.value.length - 1];
|
|
2026
|
-
const rawOctetStringBytes = extValueAsn1.value;
|
|
2027
|
-
try {
|
|
2028
|
-
const innerAsn1 = import_node_forge.default.asn1.fromDer(import_node_forge.default.util.createBuffer(rawOctetStringBytes));
|
|
2029
|
-
if (innerAsn1.type === 2) {
|
|
2030
|
-
const bytes = innerAsn1.value;
|
|
2031
|
-
if (typeof bytes === "string" && bytes.length > 0) {
|
|
2032
|
-
return bytes.charCodeAt(bytes.length - 1);
|
|
2033
|
-
} else {
|
|
2034
|
-
throw new Error(`Extension ${oidString} INTEGER value is empty or invalid`);
|
|
2035
|
-
}
|
|
2036
|
-
} else {
|
|
2037
|
-
throw new Error(`Extension ${oidString} does not contain an INTEGER, found type ${innerAsn1.type}`);
|
|
2038
|
-
}
|
|
2039
|
-
} catch (e) {
|
|
2040
|
-
throw new Error(`Failed to strictly parse AMD TCB extension ${oidString}: ${e.message}`);
|
|
2041
|
-
}
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
return null;
|
|
2045
|
-
}
|
|
2046
|
-
function verifyTCB(vlekCertBuffer, report) {
|
|
2047
|
-
const certAsn1 = import_node_forge.default.asn1.fromDer(import_node_forge.default.util.createBuffer(uint8ArrayToBinaryString(vlekCertBuffer)));
|
|
2048
|
-
const OID_BOOTLOADER = "1.3.6.1.4.1.3704.1.3.1";
|
|
2049
|
-
const OID_TEE = "1.3.6.1.4.1.3704.1.3.2";
|
|
2050
|
-
const OID_SNP = "1.3.6.1.4.1.3704.1.3.3";
|
|
2051
|
-
const OID_MICROCODE = "1.3.6.1.4.1.3704.1.3.8";
|
|
2052
|
-
const certTcb = {
|
|
2053
|
-
bootloader: getExtValue(certAsn1, OID_BOOTLOADER),
|
|
2054
|
-
tee: getExtValue(certAsn1, OID_TEE),
|
|
2055
|
-
snp: getExtValue(certAsn1, OID_SNP),
|
|
2056
|
-
microcode: getExtValue(certAsn1, OID_MICROCODE)
|
|
2057
|
-
};
|
|
2058
|
-
if (certTcb.bootloader !== null && report.reported_tcb.bootloader < certTcb.bootloader) {
|
|
2059
|
-
throw new Error(`TCB Downgrade! Bootloader reported ${report.reported_tcb.bootloader}, but certificate requires ${certTcb.bootloader}`);
|
|
2060
|
-
}
|
|
2061
|
-
if (certTcb.tee !== null && report.reported_tcb.tee < certTcb.tee) {
|
|
2062
|
-
throw new Error(`TCB Downgrade! TEE reported ${report.reported_tcb.tee}, but certificate requires ${certTcb.tee}`);
|
|
2063
|
-
}
|
|
2064
|
-
if (certTcb.snp !== null && report.reported_tcb.snp < certTcb.snp) {
|
|
2065
|
-
throw new Error(`TCB Downgrade! SNP reported ${report.reported_tcb.snp}, but certificate requires ${certTcb.snp}`);
|
|
2066
|
-
}
|
|
2067
|
-
if (certTcb.microcode !== null && report.reported_tcb.microcode < certTcb.microcode) {
|
|
2068
|
-
throw new Error(`TCB Downgrade! Microcode reported ${report.reported_tcb.microcode}, but certificate requires ${certTcb.microcode}`);
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
function parseAsn1Time(node) {
|
|
2072
|
-
const s = node.value;
|
|
2073
|
-
if (node.type === import_node_forge.default.asn1.Type.UTCTIME) {
|
|
2074
|
-
const yr = parseInt(s.substring(0, 2), 10);
|
|
2075
|
-
return new Date(Date.UTC(
|
|
2076
|
-
yr >= 50 ? 1900 + yr : 2e3 + yr,
|
|
2077
|
-
parseInt(s.substring(2, 4), 10) - 1,
|
|
2078
|
-
parseInt(s.substring(4, 6), 10),
|
|
2079
|
-
parseInt(s.substring(6, 8), 10),
|
|
2080
|
-
parseInt(s.substring(8, 10), 10),
|
|
2081
|
-
parseInt(s.substring(10, 12), 10)
|
|
2082
|
-
));
|
|
2083
|
-
} else {
|
|
2084
|
-
return new Date(Date.UTC(
|
|
2085
|
-
parseInt(s.substring(0, 4), 10),
|
|
2086
|
-
parseInt(s.substring(4, 6), 10) - 1,
|
|
2087
|
-
parseInt(s.substring(6, 8), 10),
|
|
2088
|
-
parseInt(s.substring(8, 10), 10),
|
|
2089
|
-
parseInt(s.substring(10, 12), 10),
|
|
2090
|
-
parseInt(s.substring(12, 14), 10)
|
|
2091
|
-
));
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
function verifyCRL(crlBuf, arkPem, vlekSerial) {
|
|
1789
|
+
var JWKS_CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
1790
|
+
var cachedJwksUri = null;
|
|
1791
|
+
var cachedJwksKeys = null;
|
|
1792
|
+
var cachedJwksAt = 0;
|
|
1793
|
+
function verifyJwtSignature(token, issuer) {
|
|
2095
1794
|
return __async(this, null, function* () {
|
|
2096
|
-
const
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
const thisUpdateAsn1 = tbsFields[fi++];
|
|
2115
|
-
let nextUpdateAsn1 = null;
|
|
2116
|
-
if (fi < tbsFields.length && tbsFields[fi].tagClass === import_node_forge.default.asn1.Class.UNIVERSAL && (tbsFields[fi].type === import_node_forge.default.asn1.Type.UTCTIME || tbsFields[fi].type === import_node_forge.default.asn1.Type.GENERALIZEDTIME)) {
|
|
2117
|
-
nextUpdateAsn1 = tbsFields[fi++];
|
|
2118
|
-
}
|
|
2119
|
-
let revokedSeq = null;
|
|
2120
|
-
if (fi < tbsFields.length && tbsFields[fi].tagClass === import_node_forge.default.asn1.Class.UNIVERSAL && tbsFields[fi].type === import_node_forge.default.asn1.Type.SEQUENCE) {
|
|
2121
|
-
revokedSeq = tbsFields[fi];
|
|
2122
|
-
}
|
|
2123
|
-
const now = /* @__PURE__ */ new Date();
|
|
2124
|
-
const thisUpdate = parseAsn1Time(thisUpdateAsn1);
|
|
2125
|
-
if (thisUpdate > now) {
|
|
2126
|
-
throw new Error(`CRL is not yet valid: thisUpdate is ${thisUpdate.toISOString()}`);
|
|
2127
|
-
}
|
|
2128
|
-
if (nextUpdateAsn1) {
|
|
2129
|
-
const nextUpdate = parseAsn1Time(nextUpdateAsn1);
|
|
2130
|
-
if (nextUpdate < now) {
|
|
2131
|
-
throw new Error(`CRL has expired: nextUpdate was ${nextUpdate.toISOString()}`);
|
|
2132
|
-
}
|
|
2133
|
-
}
|
|
2134
|
-
const crlIssuerDer = import_node_forge.default.asn1.toDer(issuerAsn1).getBytes();
|
|
2135
|
-
const arkForgeCert = import_node_forge.default.pki.certificateFromPem(arkPem);
|
|
2136
|
-
const arkCertAsn1 = import_node_forge.default.pki.certificateToAsn1(arkForgeCert);
|
|
2137
|
-
const arkSubjectAsn1 = arkCertAsn1.value[0].value[5];
|
|
2138
|
-
const arkSubjectDer = import_node_forge.default.asn1.toDer(arkSubjectAsn1).getBytes();
|
|
2139
|
-
if (crlIssuerDer !== arkSubjectDer) {
|
|
2140
|
-
throw new Error("CRL issuer does not match AMD ARK certificate subject \u2014 chain mismatch");
|
|
2141
|
-
}
|
|
2142
|
-
const tbsDerBuf = binaryStringToUint8Array(import_node_forge.default.asn1.toDer(tbsAsn1).getBytes());
|
|
2143
|
-
const sigRaw = typeof sigBitsAsn1.value === "string" ? sigBitsAsn1.value : "";
|
|
2144
|
-
const sigBuf = binaryStringToUint8Array(sigRaw.substring(1));
|
|
2145
|
-
const cryptoSubtle = getSubtleCrypto();
|
|
2146
|
-
const spkiBuf = binaryStringToUint8Array(
|
|
2147
|
-
import_node_forge.default.asn1.toDer(import_node_forge.default.pki.publicKeyToAsn1(arkForgeCert.publicKey)).getBytes()
|
|
2148
|
-
);
|
|
2149
|
-
const arkCryptoKey = yield cryptoSubtle.importKey(
|
|
2150
|
-
"spki",
|
|
2151
|
-
spkiBuf,
|
|
2152
|
-
{ name: "RSA-PSS", hash: "SHA-384" },
|
|
1795
|
+
const { header, payload, signingInput, signature } = decodeJwt(token);
|
|
1796
|
+
assert(header.alg === "RS256", `unexpected attestation signing algorithm: ${header.alg}`);
|
|
1797
|
+
assert(typeof header.kid === "string" && header.kid.length > 0, "attestation token kid is missing");
|
|
1798
|
+
const isCacheFresh = cachedJwksKeys && Date.now() - cachedJwksAt < JWKS_CACHE_TTL_MS;
|
|
1799
|
+
if (!isCacheFresh) {
|
|
1800
|
+
const oidc = yield fetchJson(`${issuer}/.well-known/openid-configuration`);
|
|
1801
|
+
assert(typeof (oidc == null ? void 0 : oidc.jwks_uri) === "string" && oidc.jwks_uri.length > 0, "issuer JWKS URI is missing");
|
|
1802
|
+
cachedJwksUri = oidc.jwks_uri;
|
|
1803
|
+
const jwks = yield fetchJson(cachedJwksUri);
|
|
1804
|
+
cachedJwksKeys = (jwks == null ? void 0 : jwks.keys) || [];
|
|
1805
|
+
cachedJwksAt = Date.now();
|
|
1806
|
+
}
|
|
1807
|
+
const jwk = cachedJwksKeys.find((key) => key.kid === header.kid);
|
|
1808
|
+
assert(jwk, `no JWKS key found for kid ${header.kid}`);
|
|
1809
|
+
const cryptoKey = yield getSubtleCrypto().importKey(
|
|
1810
|
+
"jwk",
|
|
1811
|
+
jwk,
|
|
1812
|
+
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
|
|
2153
1813
|
false,
|
|
2154
1814
|
["verify"]
|
|
2155
1815
|
);
|
|
2156
|
-
const isValid = yield
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
tbsDerBuf
|
|
1816
|
+
const isValid = yield getSubtleCrypto().verify(
|
|
1817
|
+
"RSASSA-PKCS1-v1_5",
|
|
1818
|
+
cryptoKey,
|
|
1819
|
+
signature,
|
|
1820
|
+
new TextEncoder().encode(signingInput)
|
|
2162
1821
|
);
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
}
|
|
2166
|
-
const targetSerial = normalizeSerial(vlekSerial);
|
|
2167
|
-
if (revokedSeq && Array.isArray(revokedSeq.value)) {
|
|
2168
|
-
for (const entry of revokedSeq.value) {
|
|
2169
|
-
if (!Array.isArray(entry.value) || entry.value.length < 2) continue;
|
|
2170
|
-
const serialAsn1 = entry.value[0];
|
|
2171
|
-
if (serialAsn1.type !== import_node_forge.default.asn1.Type.INTEGER || typeof serialAsn1.value !== "string") continue;
|
|
2172
|
-
const serialHex = import_node_forge.default.util.bytesToHex(serialAsn1.value);
|
|
2173
|
-
if (normalizeSerial(serialHex) === targetSerial) {
|
|
2174
|
-
throw new Error("\u{1F6A8} VLEK Certificate is REVOKED per AMD CRL! This hardware may be compromised.");
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
1822
|
+
assert(isValid, "JWT signature verification failed");
|
|
1823
|
+
return payload;
|
|
2178
1824
|
});
|
|
2179
1825
|
}
|
|
2180
|
-
function
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
1826
|
+
function isNonceContextData(obj) {
|
|
1827
|
+
if (!obj || typeof obj !== "object") return false;
|
|
1828
|
+
const o = obj;
|
|
1829
|
+
return typeof o.applicationId === "string" && o.applicationId.length > 0 && typeof o.sessionId === "string" && o.sessionId.length > 0 && typeof o.timestamp === "string" && o.timestamp.length > 0;
|
|
1830
|
+
}
|
|
1831
|
+
function parseProofContext(proof) {
|
|
1832
|
+
let parsedContext;
|
|
1833
|
+
try {
|
|
1834
|
+
parsedContext = JSON.parse(proof.claimData.context);
|
|
1835
|
+
} catch (e) {
|
|
1836
|
+
throw new Error("Malformed proof: claimData.context is not valid JSON");
|
|
2184
1837
|
}
|
|
2185
|
-
if (
|
|
2186
|
-
throw new Error(
|
|
1838
|
+
if (!parsedContext || typeof parsedContext !== "object") {
|
|
1839
|
+
throw new Error("Malformed proof: claimData.context is not a JSON object");
|
|
2187
1840
|
}
|
|
1841
|
+
const ctx = parsedContext;
|
|
1842
|
+
const expectedNonce = ctx.attestationNonce;
|
|
1843
|
+
assert(typeof expectedNonce === "string" && expectedNonce.length > 0, "Proof context is missing attestationNonce");
|
|
1844
|
+
const nonceDataObj = ctx.attestationNonceData;
|
|
1845
|
+
assert(isNonceContextData(nonceDataObj), "Proof context is missing or has invalid attestationNonceData (requires applicationId, sessionId, timestamp)");
|
|
1846
|
+
return { parsedContext: ctx, nonceDataObj, expectedNonce };
|
|
2188
1847
|
}
|
|
2189
|
-
function
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
const ask = parseCert(binaryStringToUint8Array(askDer));
|
|
2206
|
-
const ark = parseCert(binaryStringToUint8Array(arkDer));
|
|
2207
|
-
assertCertValidity("ASK", ask);
|
|
2208
|
-
assertCertValidity("ARK", ark);
|
|
2209
|
-
try {
|
|
2210
|
-
yield verifySignature(certs[1], ark.tbsDer, ark.signature, ark.sigAlgOid);
|
|
2211
|
-
} catch (e) {
|
|
2212
|
-
throw new Error(`AMD ARK self-signature verification failed: ${e.message}`);
|
|
2213
|
-
}
|
|
2214
|
-
try {
|
|
2215
|
-
yield verifySignature(certs[1], ask.tbsDer, ask.signature, ask.sigAlgOid);
|
|
2216
|
-
} catch (e) {
|
|
2217
|
-
throw new Error(`AMD ASK-by-ARK signature verification failed: ${e.message}`);
|
|
2218
|
-
}
|
|
2219
|
-
try {
|
|
2220
|
-
yield verifySignature(certs[0], vlek.tbsDer, vlek.signature, vlek.sigAlgOid);
|
|
2221
|
-
} catch (e) {
|
|
2222
|
-
throw new Error(`VLEK-by-ASK signature verification failed: ${e.message}`);
|
|
2223
|
-
}
|
|
2224
|
-
matchedChain = true;
|
|
2225
|
-
let crlBuf;
|
|
2226
|
-
const now = Date.now();
|
|
2227
|
-
if (crlCache[processor] && now - crlCache[processor].fetchedAt < 36e5) {
|
|
2228
|
-
crlBuf = crlCache[processor].buffer;
|
|
2229
|
-
} else {
|
|
2230
|
-
const crlUrl = `https://kdsintf.amd.com/vlek/v1/${processor}/crl`;
|
|
2231
|
-
const controller = new AbortController();
|
|
2232
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
2233
|
-
const crlResp = yield fetch(crlUrl, { signal: controller.signal });
|
|
2234
|
-
clearTimeout(timeoutId);
|
|
2235
|
-
if (!crlResp.ok) continue;
|
|
2236
|
-
crlBuf = new Uint8Array(yield crlResp.arrayBuffer());
|
|
2237
|
-
crlCache[processor] = { buffer: crlBuf, fetchedAt: now };
|
|
2238
|
-
}
|
|
2239
|
-
if (vlek.serialNumber && crlBuf) {
|
|
2240
|
-
yield verifyCRL(crlBuf, certs[1], vlek.serialNumber);
|
|
2241
|
-
}
|
|
2242
|
-
chainVerified = true;
|
|
2243
|
-
break;
|
|
2244
|
-
} catch (e) {
|
|
2245
|
-
if (matchedChain) {
|
|
2246
|
-
throw e;
|
|
2247
|
-
}
|
|
2248
|
-
continue;
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
if (!chainVerified) {
|
|
2252
|
-
throw new Error("VLEK Certificate failed verification against all known AMD Root of Trust chains!");
|
|
1848
|
+
function verifyApplicationAndSessionBinding(proof, parsedContext, nonceDataObj, expectedApplicationId) {
|
|
1849
|
+
var _a;
|
|
1850
|
+
const { applicationId, sessionId, timestamp } = nonceDataObj;
|
|
1851
|
+
if (expectedApplicationId) {
|
|
1852
|
+
assert(
|
|
1853
|
+
applicationId.toLowerCase() === expectedApplicationId.toLowerCase(),
|
|
1854
|
+
`Application ID Mismatch! Expected ${expectedApplicationId}, but proof context contains ${applicationId}`
|
|
1855
|
+
);
|
|
1856
|
+
}
|
|
1857
|
+
let parsedParameters = {};
|
|
1858
|
+
if (proof.claimData.parameters) {
|
|
1859
|
+
try {
|
|
1860
|
+
const parsed = JSON.parse(proof.claimData.parameters);
|
|
1861
|
+
parsedParameters = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
1862
|
+
} catch (e) {
|
|
1863
|
+
throw new Error("Malformed proof: claimData.parameters is not valid JSON");
|
|
2253
1864
|
}
|
|
2254
|
-
}
|
|
1865
|
+
}
|
|
1866
|
+
const contextSessionId = parsedContext == null ? void 0 : parsedContext.reclaimSessionId;
|
|
1867
|
+
const parameterSessionId = (_a = parsedParameters == null ? void 0 : parsedParameters.proxySessionId) != null ? _a : parsedParameters == null ? void 0 : parsedParameters.sessionId;
|
|
1868
|
+
if (contextSessionId && contextSessionId.toString() !== sessionId.toString()) {
|
|
1869
|
+
throw new Error(`Session ID Mismatch! Expected ${sessionId}, but proof context contains reclaimSessionId=${contextSessionId}`);
|
|
1870
|
+
}
|
|
1871
|
+
if (parameterSessionId && parameterSessionId.toString() !== sessionId.toString()) {
|
|
1872
|
+
throw new Error(`Session ID Mismatch! Expected ${sessionId}, but proof parameters contain ${parameterSessionId}`);
|
|
1873
|
+
}
|
|
1874
|
+
if (!contextSessionId && !parameterSessionId) {
|
|
1875
|
+
throw new Error("Proof is missing reclaimSessionId and proxySessionId/sessionId for attestation nonce verification");
|
|
1876
|
+
}
|
|
1877
|
+
const claimTimestampMs = proof.claimData.timestampS * 1e3;
|
|
1878
|
+
const nonceTimestampMs = parseInt(timestamp, 10);
|
|
1879
|
+
const diffMs = Math.abs(claimTimestampMs - nonceTimestampMs);
|
|
1880
|
+
if (diffMs > NONCE_TIMESTAMP_MAX_SKEW_MS) {
|
|
1881
|
+
throw new Error(`Timestamp Skew Too Large! claimData.timestampS and attestationNonce timestamp differ by ${Math.round(diffMs / 1e3)}s (limit: 600s)`);
|
|
1882
|
+
}
|
|
2255
1883
|
}
|
|
2256
|
-
function
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
1884
|
+
function verifyNonceMaterial(expectedNonce, nonceDataObj, expectedAppSecret) {
|
|
1885
|
+
const cleanExpectedNonce = normalizeHex(expectedNonce);
|
|
1886
|
+
const { applicationId, sessionId, timestamp } = nonceDataObj;
|
|
1887
|
+
assert(cleanExpectedNonce.length > 0, "Proof context attestationNonce is empty");
|
|
1888
|
+
assert(isHex(cleanExpectedNonce), "Proof context attestationNonce is not valid hex");
|
|
1889
|
+
if (expectedAppSecret) {
|
|
1890
|
+
const recomputedNonce = generateAttestationNonce(expectedAppSecret, applicationId, sessionId, timestamp);
|
|
1891
|
+
assert(
|
|
1892
|
+
recomputedNonce === cleanExpectedNonce,
|
|
1893
|
+
"Attestation nonce verification failed: app secret, application ID, session ID, or timestamp do not match"
|
|
1894
|
+
);
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
if (cleanExpectedNonce.length > 74) {
|
|
1898
|
+
const legacyNonceData = `${applicationId}:${sessionId}:${timestamp}`;
|
|
1899
|
+
const nonceMsg = import_ethers5.ethers.getBytes(import_ethers5.ethers.keccak256(new TextEncoder().encode(legacyNonceData)));
|
|
1900
|
+
const recoveredAddress = import_ethers5.ethers.verifyMessage(
|
|
1901
|
+
nonceMsg,
|
|
1902
|
+
expectedNonce.startsWith("0x") ? expectedNonce : `0x${expectedNonce}`
|
|
2272
1903
|
);
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
throw new Error("Hardware ECDSA signature is malformed: non-zero padding bytes detected in the structural signature coordinates.");
|
|
2277
|
-
}
|
|
2278
|
-
const r48 = rBE.subarray(rBE.length - 48);
|
|
2279
|
-
const s48 = sBE.subarray(sBE.length - 48);
|
|
2280
|
-
const rawSignature = concatUint8Arrays([r48, s48]);
|
|
2281
|
-
const isValid = yield cryptoSubtle.verify(
|
|
2282
|
-
{ name: "ECDSA", hash: { name: "SHA-384" } },
|
|
2283
|
-
importedKey,
|
|
2284
|
-
rawSignature,
|
|
2285
|
-
signedData
|
|
1904
|
+
assert(
|
|
1905
|
+
recoveredAddress.toLowerCase() === applicationId.toLowerCase(),
|
|
1906
|
+
`Nonce signature verification failed: recovered ${recoveredAddress}, expected ${applicationId}`
|
|
2286
1907
|
);
|
|
2287
|
-
|
|
2288
|
-
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
throw new Error("App secret is required to verify hash-based attestation nonces");
|
|
1911
|
+
}
|
|
1912
|
+
function assertTokenFresh(claims) {
|
|
1913
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1914
|
+
if (typeof claims.nbf === "number" && now + TOKEN_CLOCK_SKEW_S < claims.nbf) {
|
|
1915
|
+
throw new Error(`Attestation token is not valid before ${claims.nbf}`);
|
|
1916
|
+
}
|
|
1917
|
+
if (typeof claims.exp === "number" && now - TOKEN_CLOCK_SKEW_S > claims.exp) {
|
|
1918
|
+
throw new Error(`Attestation token expired at ${claims.exp}`);
|
|
1919
|
+
}
|
|
1920
|
+
if (typeof claims.iat === "number" && claims.iat > now + TOKEN_CLOCK_SKEW_S) {
|
|
1921
|
+
throw new Error(`Attestation token issued-at ${claims.iat} is in the future`);
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
function assertAudienceClaim(aud) {
|
|
1925
|
+
if (typeof aud === "string") {
|
|
1926
|
+
assert(aud.length > 0, "attestation token audience is empty");
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
if (Array.isArray(aud)) {
|
|
1930
|
+
assert(aud.length > 0, "attestation token audience is empty");
|
|
1931
|
+
assert(aud.every((entry) => typeof entry === "string" && entry.length > 0), "attestation token audience contains invalid entries");
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
throw new Error("attestation token audience is missing");
|
|
1935
|
+
}
|
|
1936
|
+
function assertProofShape(teeAttestation) {
|
|
1937
|
+
var _a, _b, _c;
|
|
1938
|
+
if (teeAttestation.error) {
|
|
1939
|
+
throw new Error(`${teeAttestation.error.code}: ${teeAttestation.error.message}`);
|
|
1940
|
+
}
|
|
1941
|
+
assert(teeAttestation.proof_version === "v2", `unexpected proof version: ${teeAttestation.proof_version}`);
|
|
1942
|
+
assert(teeAttestation.tee_provider === EXPECTED_TEE_PROVIDER, `unexpected tee provider: ${teeAttestation.tee_provider}`);
|
|
1943
|
+
assert(teeAttestation.tee_technology === EXPECTED_TEE_TECHNOLOGY, `unexpected tee technology: ${teeAttestation.tee_technology}`);
|
|
1944
|
+
assert(typeof teeAttestation.nonce === "string" && teeAttestation.nonce.length > 0, "tee attestation nonce missing");
|
|
1945
|
+
assert(typeof teeAttestation.timestamp === "string" && teeAttestation.timestamp.length > 0, "tee attestation timestamp missing");
|
|
1946
|
+
assert(!Number.isNaN(Date.parse(teeAttestation.timestamp)), "tee attestation timestamp is invalid");
|
|
1947
|
+
assert(typeof ((_a = teeAttestation.workload) == null ? void 0 : _a.image_digest) === "string" && teeAttestation.workload.image_digest.length > 0, "workload image digest missing");
|
|
1948
|
+
assert(typeof ((_b = teeAttestation.verifier) == null ? void 0 : _b.image_digest) === "string" && teeAttestation.verifier.image_digest.length > 0, "verifier image digest missing");
|
|
1949
|
+
assert(typeof ((_c = teeAttestation.attestation) == null ? void 0 : _c.token) === "string" && teeAttestation.attestation.token.length > 0, "attestation token missing");
|
|
1950
|
+
}
|
|
1951
|
+
function verifyGcpClaims(teeAttestation, expectedNonce) {
|
|
1952
|
+
return __async(this, null, function* () {
|
|
1953
|
+
var _a;
|
|
1954
|
+
const claims = yield verifyJwtSignature(teeAttestation.attestation.token, EXPECTED_ISSUER);
|
|
1955
|
+
assert(claims.iss === EXPECTED_ISSUER, `unexpected issuer: ${claims.iss}`);
|
|
1956
|
+
assertAudienceClaim(claims.aud);
|
|
1957
|
+
assert(Array.isArray(claims.eat_nonce), "eat_nonce claim missing");
|
|
1958
|
+
const digestBinding = yield sha256Hex(
|
|
1959
|
+
`${teeAttestation.workload.image_digest}
|
|
1960
|
+
${teeAttestation.verifier.image_digest}`
|
|
1961
|
+
);
|
|
1962
|
+
assert(claims.eat_nonce.includes(expectedNonce), "request nonce is not present in attestation token");
|
|
1963
|
+
assert(claims.eat_nonce.includes(digestBinding), "digest-binding nonce is not present in attestation token");
|
|
1964
|
+
assert(claims.hwmodel === EXPECTED_HW_MODEL, `unexpected hwmodel: ${claims.hwmodel}`);
|
|
1965
|
+
assert(claims.secboot === true, "secure boot claim is not true");
|
|
1966
|
+
assert((_a = claims.submods) == null ? void 0 : _a.gce, "gce submod claim missing");
|
|
1967
|
+
assertTokenFresh(claims);
|
|
1968
|
+
});
|
|
1969
|
+
}
|
|
1970
|
+
function verifyTeeAttestation(proof, appSecret) {
|
|
1971
|
+
return __async(this, null, function* () {
|
|
1972
|
+
assertNonBrowserEnvironment();
|
|
1973
|
+
try {
|
|
1974
|
+
const appId = new import_ethers5.ethers.Wallet(appSecret).address;
|
|
1975
|
+
let teeAttestation = proof.teeAttestation;
|
|
1976
|
+
if (!teeAttestation) {
|
|
1977
|
+
throw new Error("Missing teeAttestation in proof");
|
|
1978
|
+
}
|
|
1979
|
+
if (typeof teeAttestation === "string") {
|
|
1980
|
+
teeAttestation = JSON.parse(teeAttestation);
|
|
1981
|
+
}
|
|
1982
|
+
assertProofShape(teeAttestation);
|
|
1983
|
+
const { parsedContext, nonceDataObj, expectedNonce } = parseProofContext(proof);
|
|
1984
|
+
verifyApplicationAndSessionBinding(proof, parsedContext, nonceDataObj, appId);
|
|
1985
|
+
verifyNonceMaterial(expectedNonce, nonceDataObj, appSecret);
|
|
1986
|
+
const cleanExpectedNonce = normalizeHex(expectedNonce);
|
|
1987
|
+
const cleanTeeNonce = normalizeHex(teeAttestation.nonce);
|
|
1988
|
+
assert(cleanTeeNonce.length > 0, "TEE attestation nonce is empty");
|
|
1989
|
+
assert(isHex(cleanTeeNonce), "TEE attestation nonce is not valid hex");
|
|
1990
|
+
assert(cleanTeeNonce === cleanExpectedNonce, `Nonce Mismatch! Expected ${cleanExpectedNonce}, got ${cleanTeeNonce}`);
|
|
1991
|
+
yield verifyGcpClaims(teeAttestation, cleanExpectedNonce);
|
|
1992
|
+
return { isVerified: true };
|
|
1993
|
+
} catch (error) {
|
|
1994
|
+
logger9.error("TEE attestation verification failed:", error);
|
|
1995
|
+
return {
|
|
1996
|
+
isVerified: false,
|
|
1997
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1998
|
+
};
|
|
2289
1999
|
}
|
|
2290
2000
|
});
|
|
2291
2001
|
}
|
|
2292
|
-
function
|
|
2002
|
+
function runTeeVerification(proofs, config) {
|
|
2293
2003
|
return __async(this, null, function* () {
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
};
|
|
2309
|
-
const importedCosignKey = yield cryptoSubtle.importKey(
|
|
2310
|
-
"spki",
|
|
2311
|
-
base64ToUint8Array(
|
|
2312
|
-
COSIGN_PUBLIC_KEY.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace(/\s+/g, "")
|
|
2313
|
-
),
|
|
2314
|
-
{ name: "ECDSA", namedCurve: "P-256" },
|
|
2315
|
-
true,
|
|
2316
|
-
["verify"]
|
|
2317
|
-
);
|
|
2318
|
-
const pubKeySpkiDer = yield cryptoSubtle.exportKey("spki", importedCosignKey);
|
|
2319
|
-
const pubKeyHashBuffer = yield cryptoSubtle.digest("SHA-256", pubKeySpkiDer);
|
|
2320
|
-
const pubKeyHashBytes = new Uint8Array(pubKeyHashBuffer);
|
|
2321
|
-
const nonceHex = (teeAttestation.nonce || nonce).replace(/^0x/i, "");
|
|
2322
|
-
const nonceBytes = new Uint8Array(nonceHex.length / 2);
|
|
2323
|
-
for (let i = 0; i < nonceBytes.length; i++) nonceBytes[i] = parseInt(nonceHex.substring(i * 2, i * 2 + 2), 16);
|
|
2324
|
-
const workloadBytes = extractDigestBytes(teeAttestation.workload_digest);
|
|
2325
|
-
const verifierBytes = extractDigestBytes(teeAttestation.verifier_digest);
|
|
2326
|
-
const domainSep = new TextEncoder().encode("POPCORN_TEE_REPORT_DATA_V1");
|
|
2327
|
-
const version = new Uint8Array([1]);
|
|
2328
|
-
const payload = new Uint8Array(
|
|
2329
|
-
domainSep.length + version.length + workloadBytes.length + verifierBytes.length + pubKeyHashBytes.length + nonceBytes.length
|
|
2004
|
+
const hasTeeData = proofs.every((proof) => {
|
|
2005
|
+
if (proof.teeAttestation) return true;
|
|
2006
|
+
try {
|
|
2007
|
+
const context = JSON.parse(proof.claimData.context);
|
|
2008
|
+
return !!(context == null ? void 0 : context.attestationNonce);
|
|
2009
|
+
} catch (e) {
|
|
2010
|
+
return false;
|
|
2011
|
+
}
|
|
2012
|
+
});
|
|
2013
|
+
if (!hasTeeData) {
|
|
2014
|
+
throw new TeeVerificationError("TEE verification requested but one or more proofs are missing TEE attestation data");
|
|
2015
|
+
}
|
|
2016
|
+
const teeResults = yield Promise.all(
|
|
2017
|
+
proofs.map((proof) => verifyTeeAttestation(proof, config.appSecret))
|
|
2330
2018
|
);
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
payload.set(chunk, offset);
|
|
2334
|
-
offset += chunk.length;
|
|
2335
|
-
}
|
|
2336
|
-
const hashBuffer = yield cryptoSubtle.digest("SHA-256", payload);
|
|
2337
|
-
const hashHex = arrayBufferToHex(hashBuffer);
|
|
2338
|
-
const expected64ByteHex = hashHex + hashHex;
|
|
2339
|
-
if (report.reportData !== expected64ByteHex) {
|
|
2340
|
-
throw new Error(`REPORT_DATA Mismatch! Hardware report is not bound to these image digests or nonce.
|
|
2341
|
-
Expected: ${expected64ByteHex}
|
|
2342
|
-
Got: ${report.reportData}`);
|
|
2019
|
+
if (!teeResults.every((r) => r.isVerified)) {
|
|
2020
|
+
throw new TeeVerificationError("TEE attestation verification failed for one or more proofs");
|
|
2343
2021
|
}
|
|
2344
2022
|
});
|
|
2345
2023
|
}
|
|
@@ -2362,103 +2040,22 @@ function verifyProof(proofOrProofs, config) {
|
|
|
2362
2040
|
yield assertVerifiedProof(proof, attestors);
|
|
2363
2041
|
}
|
|
2364
2042
|
yield assertValidateProof(proofs, config);
|
|
2365
|
-
let
|
|
2366
|
-
if (config.
|
|
2367
|
-
|
|
2368
|
-
if (!hasTeeData) {
|
|
2369
|
-
const teeError = new TeeVerificationError("TEE verification requested but one or more proofs are missing TEE attestation data");
|
|
2370
|
-
logger10.error(teeError.message);
|
|
2371
|
-
const errorResult = {
|
|
2372
|
-
isVerified: false,
|
|
2373
|
-
isTeeVerified: false,
|
|
2374
|
-
error: teeError,
|
|
2375
|
-
data: [],
|
|
2376
|
-
publicData: []
|
|
2377
|
-
};
|
|
2378
|
-
return errorResult;
|
|
2379
|
-
}
|
|
2380
|
-
try {
|
|
2381
|
-
const teeResults = yield Promise.all(proofs.map((proof) => verifyTeeAttestation(proof)));
|
|
2382
|
-
isTeeVerified = teeResults.every((r) => r === true);
|
|
2383
|
-
if (!isTeeVerified) {
|
|
2384
|
-
const teeError = new TeeVerificationError("TEE attestation verification failed for one or more proofs");
|
|
2385
|
-
logger10.error(teeError.message);
|
|
2386
|
-
const errorResult = {
|
|
2387
|
-
isVerified: false,
|
|
2388
|
-
isTeeVerified: false,
|
|
2389
|
-
error: teeError,
|
|
2390
|
-
data: [],
|
|
2391
|
-
publicData: []
|
|
2392
|
-
};
|
|
2393
|
-
return errorResult;
|
|
2394
|
-
}
|
|
2395
|
-
} catch (error) {
|
|
2396
|
-
const teeError = new TeeVerificationError("Error verifying TEE attestation", error);
|
|
2397
|
-
logger10.error(teeError.message);
|
|
2398
|
-
const errorResult = {
|
|
2399
|
-
isVerified: false,
|
|
2400
|
-
isTeeVerified: false,
|
|
2401
|
-
error: teeError,
|
|
2402
|
-
data: [],
|
|
2403
|
-
publicData: []
|
|
2404
|
-
};
|
|
2405
|
-
return errorResult;
|
|
2406
|
-
}
|
|
2043
|
+
let isTeeAttestationVerified;
|
|
2044
|
+
if (config.teeAttestation && "dangerouslyDisableContentValidation" in config && config.dangerouslyDisableContentValidation) {
|
|
2045
|
+
logger10.warn("teeAttestation is enabled but content validation is disabled \u2014 TEE attestation alone does not guarantee proof contents are valid");
|
|
2407
2046
|
}
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
error: void 0
|
|
2414
|
-
};
|
|
2415
|
-
return result;
|
|
2047
|
+
if (config.teeAttestation) {
|
|
2048
|
+
yield runTeeVerification(proofs, config.teeAttestation);
|
|
2049
|
+
isTeeAttestationVerified = true;
|
|
2050
|
+
}
|
|
2051
|
+
return createVerifyProofResultSuccess(proofs, isTeeAttestationVerified);
|
|
2416
2052
|
} catch (error) {
|
|
2417
2053
|
logger10.error("Error in validating proof:", error);
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
2421
|
-
data: [],
|
|
2422
|
-
publicData: []
|
|
2423
|
-
};
|
|
2054
|
+
const _error = error instanceof Error ? error : new Error(String(error));
|
|
2055
|
+
return createVerifyProofResultFailure(_error);
|
|
2424
2056
|
}
|
|
2425
2057
|
});
|
|
2426
2058
|
}
|
|
2427
|
-
function createTrustedDataFromProofData(proof) {
|
|
2428
|
-
try {
|
|
2429
|
-
const context = JSON.parse(proof.claimData.context);
|
|
2430
|
-
const _a = context, { extractedParameters } = _a, rest = __objRest(_a, ["extractedParameters"]);
|
|
2431
|
-
return {
|
|
2432
|
-
context: rest,
|
|
2433
|
-
extractedParameters: extractedParameters != null ? extractedParameters : {}
|
|
2434
|
-
};
|
|
2435
|
-
} catch (e) {
|
|
2436
|
-
return {
|
|
2437
|
-
context: {},
|
|
2438
|
-
extractedParameters: {}
|
|
2439
|
-
};
|
|
2440
|
-
}
|
|
2441
|
-
}
|
|
2442
|
-
function getPublicDataFromProofs(proofs) {
|
|
2443
|
-
const data = [];
|
|
2444
|
-
const seenData = /* @__PURE__ */ new Set();
|
|
2445
|
-
for (const proof of proofs) {
|
|
2446
|
-
const publicData = proof.publicData;
|
|
2447
|
-
if (publicData === null || publicData === void 0) {
|
|
2448
|
-
continue;
|
|
2449
|
-
}
|
|
2450
|
-
try {
|
|
2451
|
-
const hash = hashObject(publicData);
|
|
2452
|
-
if (seenData.has(hash)) {
|
|
2453
|
-
continue;
|
|
2454
|
-
}
|
|
2455
|
-
seenData.add(hash);
|
|
2456
|
-
} catch (_) {
|
|
2457
|
-
}
|
|
2458
|
-
data.push(publicData);
|
|
2459
|
-
}
|
|
2460
|
-
return data;
|
|
2461
|
-
}
|
|
2462
2059
|
function transformForOnchain(proof) {
|
|
2463
2060
|
const claimInfoBuilder = /* @__PURE__ */ new Map([
|
|
2464
2061
|
["context", proof.claimData.context],
|
|
@@ -2714,11 +2311,13 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
2714
2311
|
proofRequestInstance.resolvedProviderVersion = data.resolvedProviderVersion;
|
|
2715
2312
|
proofRequestInstance.context.reclaimSessionId = data.sessionId;
|
|
2716
2313
|
if (options == null ? void 0 : options.acceptTeeAttestation) {
|
|
2717
|
-
const
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2314
|
+
const attestationNonce = generateAttestationNonce(
|
|
2315
|
+
appSecret,
|
|
2316
|
+
applicationId,
|
|
2317
|
+
data.sessionId,
|
|
2318
|
+
proofRequestInstance.timeStamp
|
|
2319
|
+
);
|
|
2320
|
+
proofRequestInstance.setAttestationContext(attestationNonce, {
|
|
2722
2321
|
applicationId,
|
|
2723
2322
|
sessionId: data.sessionId,
|
|
2724
2323
|
timestamp: proofRequestInstance.timeStamp
|
|
@@ -3239,13 +2838,13 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
3239
2838
|
generateSignature(applicationSecret) {
|
|
3240
2839
|
return __async(this, null, function* () {
|
|
3241
2840
|
try {
|
|
3242
|
-
const wallet = new
|
|
2841
|
+
const wallet = new import_ethers6.ethers.Wallet(applicationSecret);
|
|
3243
2842
|
const canonicalData = (0, import_canonicalize3.default)({ providerId: this.providerId, timestamp: this.timeStamp });
|
|
3244
2843
|
if (!canonicalData) {
|
|
3245
2844
|
throw new SignatureGeneratingError("Failed to canonicalize data for signing.");
|
|
3246
2845
|
}
|
|
3247
|
-
const messageHash =
|
|
3248
|
-
return yield wallet.signMessage(
|
|
2846
|
+
const messageHash = import_ethers6.ethers.keccak256(new TextEncoder().encode(canonicalData));
|
|
2847
|
+
return yield wallet.signMessage(import_ethers6.ethers.getBytes(messageHash));
|
|
3249
2848
|
} catch (err) {
|
|
3250
2849
|
logger10.info(`Error generating proof request for applicationId: ${this.applicationId}, providerId: ${this.providerId}, timeStamp: ${this.timeStamp}`);
|
|
3251
2850
|
throw new SignatureGeneratingError(`Error generating signature for applicationId: ${this.applicationId}`);
|
|
@@ -3913,7 +3512,6 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
3913
3512
|
clearDeviceCache,
|
|
3914
3513
|
createLinkWithTemplateData,
|
|
3915
3514
|
createSignDataForClaim,
|
|
3916
|
-
createTrustedDataFromProofData,
|
|
3917
3515
|
fetchProviderConfigs,
|
|
3918
3516
|
fetchProviderHashRequirementsBy,
|
|
3919
3517
|
fetchStatusUrl,
|
|
@@ -3926,7 +3524,6 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
3926
3524
|
getProviderHashRequirementSpecFromProviderConfig,
|
|
3927
3525
|
getProviderHashRequirementsFromSpec,
|
|
3928
3526
|
getProviderParamsAsCanonicalizedString,
|
|
3929
|
-
getPublicDataFromProofs,
|
|
3930
3527
|
getShortenedUrl,
|
|
3931
3528
|
hashProofClaimParams,
|
|
3932
3529
|
hashRequestSpec,
|
|
@@ -3935,6 +3532,7 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
3935
3532
|
isHttpProviderClaimParams,
|
|
3936
3533
|
isMobileDevice,
|
|
3937
3534
|
recoverSignersOfSignedClaim,
|
|
3535
|
+
runTeeVerification,
|
|
3938
3536
|
takePairsWhereValueIsArray,
|
|
3939
3537
|
takeTemplateParametersFromProofs,
|
|
3940
3538
|
transformForOnchain,
|