@reclaimprotocol/js-sdk 5.1.0 → 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 -16
- package/dist/index.js +328 -712
- 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",
|
|
@@ -220,6 +220,7 @@ __export(index_exports, {
|
|
|
220
220
|
isHttpProviderClaimParams: () => isHttpProviderClaimParams,
|
|
221
221
|
isMobileDevice: () => isMobileDevice,
|
|
222
222
|
recoverSignersOfSignedClaim: () => recoverSignersOfSignedClaim,
|
|
223
|
+
runTeeVerification: () => runTeeVerification,
|
|
223
224
|
takePairsWhereValueIsArray: () => takePairsWhereValueIsArray,
|
|
224
225
|
takeTemplateParametersFromProofs: () => takeTemplateParametersFromProofs,
|
|
225
226
|
transformForOnchain: () => transformForOnchain,
|
|
@@ -238,7 +239,7 @@ var RECLAIM_EXTENSION_ACTIONS = {
|
|
|
238
239
|
};
|
|
239
240
|
|
|
240
241
|
// src/Reclaim.ts
|
|
241
|
-
var
|
|
242
|
+
var import_ethers6 = require("ethers");
|
|
242
243
|
var import_canonicalize3 = __toESM(require("canonicalize"));
|
|
243
244
|
|
|
244
245
|
// src/utils/errors.ts
|
|
@@ -560,19 +561,19 @@ function scheduleIntervalEndingTask(sessionId, intervals, onFailureCallback, tim
|
|
|
560
561
|
}
|
|
561
562
|
}, timeout);
|
|
562
563
|
}
|
|
563
|
-
var createVerifyProofResultSuccess = (proofs,
|
|
564
|
+
var createVerifyProofResultSuccess = (proofs, isTeeAttestationVerified) => {
|
|
564
565
|
return {
|
|
565
566
|
isVerified: true,
|
|
566
|
-
|
|
567
|
+
isTeeAttestationVerified,
|
|
567
568
|
error: void 0,
|
|
568
569
|
data: proofs.map(createTrustedDataFromProofData),
|
|
569
570
|
publicData: getPublicDataFromProofs(proofs)
|
|
570
571
|
};
|
|
571
572
|
};
|
|
572
|
-
var createVerifyProofResultFailure = (error,
|
|
573
|
+
var createVerifyProofResultFailure = (error, isTeeAttestationVerified) => {
|
|
573
574
|
return {
|
|
574
575
|
isVerified: false,
|
|
575
|
-
|
|
576
|
+
isTeeAttestationVerified,
|
|
576
577
|
error,
|
|
577
578
|
data: [],
|
|
578
579
|
publicData: []
|
|
@@ -1446,6 +1447,20 @@ function clearDeviceCache() {
|
|
|
1446
1447
|
cachedMobileType = null;
|
|
1447
1448
|
}
|
|
1448
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
|
+
|
|
1449
1464
|
// src/utils/providerUtils.ts
|
|
1450
1465
|
var logger7 = logger_default.logger;
|
|
1451
1466
|
function fetchProviderHashRequirementsBy(providerId, exactProviderVersionString, allowedTags, proofs) {
|
|
@@ -1673,724 +1688,336 @@ function assertValidateProof(proofs, config) {
|
|
|
1673
1688
|
}
|
|
1674
1689
|
|
|
1675
1690
|
// src/utils/verifyTee.ts
|
|
1676
|
-
var
|
|
1677
|
-
var import_ethers4 = require("ethers");
|
|
1678
|
-
|
|
1679
|
-
// src/utils/amdCerts.ts
|
|
1680
|
-
var AMD_CERTS = {
|
|
1681
|
-
"Milan": `-----BEGIN CERTIFICATE-----
|
|
1682
|
-
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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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
-
XZAmS3l+rXgSCeS1Q0+1olcnFSJpiwfssfhoutJqePyicu+OhkX131PMeO/VOtH3
|
|
1692
|
-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
-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
-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
-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
-
oDf+BF4XI/IIHJL2N5uKy3+gkSB2Xl6prohgVmqRFvBW9OTCEa32WhXu0t1Z1abE
|
|
1697
|
-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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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
-
cHM6Ly9rZHNpbnRmLmFtZC5jb20vdmxlay92MS9NaWxhbi9jcmwwRgYJKoZIhvcN
|
|
1705
|
-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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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
|
|
1717
|
-
0YPk
|
|
1718
|
-
-----END CERTIFICATE-----
|
|
1719
|
-
-----BEGIN CERTIFICATE-----
|
|
1720
|
-
MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC
|
|
1721
|
-
BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS
|
|
1722
|
-
BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg
|
|
1723
|
-
Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp
|
|
1724
|
-
Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy
|
|
1725
|
-
MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS
|
|
1726
|
-
BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j
|
|
1727
|
-
ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG
|
|
1728
|
-
9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg
|
|
1729
|
-
W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta
|
|
1730
|
-
1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2
|
|
1731
|
-
SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0
|
|
1732
|
-
60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05
|
|
1733
|
-
gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg
|
|
1734
|
-
bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs
|
|
1735
|
-
+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi
|
|
1736
|
-
Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ
|
|
1737
|
-
eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18
|
|
1738
|
-
fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j
|
|
1739
|
-
WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI
|
|
1740
|
-
rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG
|
|
1741
|
-
KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG
|
|
1742
|
-
SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI
|
|
1743
|
-
AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel
|
|
1744
|
-
ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw
|
|
1745
|
-
STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK
|
|
1746
|
-
dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq
|
|
1747
|
-
zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp
|
|
1748
|
-
KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e
|
|
1749
|
-
pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq
|
|
1750
|
-
HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh
|
|
1751
|
-
3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn
|
|
1752
|
-
JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH
|
|
1753
|
-
CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4
|
|
1754
|
-
AFZEAwoKCQ==
|
|
1755
|
-
-----END CERTIFICATE-----`,
|
|
1756
|
-
"Genoa": `-----BEGIN CERTIFICATE-----
|
|
1757
|
-
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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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
-
jZO7V4v6Ft84p71P6GXfOVEQgCuatiszfIwFrRQk/cmU7HuJadBq6XtYE+qBJMju
|
|
1768
|
-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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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
-
fAXF9sgvYtQjcaR9wlGr8ySRtZ2YJWofuR7zgYFJPEXRwAnbAR/05hBmog7CMt1F
|
|
1776
|
-
9YKSmku6JfRecY8CAwEAAaOBozCBoDAdBgNVHQ4EFgQUhEdjn8HQNI9bN2NAKL9z
|
|
1777
|
-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
-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
-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
-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
-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
-
4f2+VN5Xs1NNdptn2nq/SKu5aKnLS5XGWCnHfMSKZ7vqHLKMa0Wxfm+4JahOItQ3
|
|
1783
|
-
+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
-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
-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
-
hAflCiugBsNeJivx0j7B/HazAvxkLPTCkIdmQJccezF5PCgmMW0SeP4cMb5Ewzv/
|
|
1787
|
-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
-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
-
G+tIMyRc+3Y3yRAYchFNOFHyS6R2C0KTy1nRSYwBUdQtGaQ0rE3e5Mulcidh4qkI
|
|
1790
|
-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
-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
|
|
1792
|
-
2cko
|
|
1793
|
-
-----END CERTIFICATE-----
|
|
1794
|
-
-----BEGIN CERTIFICATE-----
|
|
1795
|
-
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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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
-
c3iUMyhNeGToOvgx/iUo4gGpG61NDpkEUvIzuKcaMx8IdTpWg2DF6SwF0IgVMffn
|
|
1808
|
-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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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
-
KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvR2Vub2EvY3JsMEYGCSqG
|
|
1817
|
-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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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
|
|
1829
|
-
/wMz1R1BHg==
|
|
1830
|
-
-----END CERTIFICATE-----`
|
|
1831
|
-
};
|
|
1832
|
-
|
|
1833
|
-
// src/utils/verifyTee.ts
|
|
1834
|
-
var crlCache = {};
|
|
1691
|
+
var import_ethers5 = require("ethers");
|
|
1835
1692
|
var logger9 = logger_default.logger;
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
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);
|
|
1839
1703
|
}
|
|
1840
|
-
|
|
1841
|
-
|
|
1704
|
+
}
|
|
1705
|
+
function isBrowserEnvironment() {
|
|
1706
|
+
if (typeof window !== "undefined" || typeof document !== "undefined") {
|
|
1707
|
+
return true;
|
|
1842
1708
|
}
|
|
1843
|
-
if (typeof
|
|
1844
|
-
return
|
|
1709
|
+
if (typeof navigator !== "undefined" && typeof process === "undefined") {
|
|
1710
|
+
return true;
|
|
1845
1711
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
let result = "";
|
|
1850
|
-
const chunkSize = 32768;
|
|
1851
|
-
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
1852
|
-
const chunk = bytes.subarray(i, i + chunkSize);
|
|
1853
|
-
result += String.fromCharCode(...chunk);
|
|
1712
|
+
const workerGlobalScope = globalThis.WorkerGlobalScope;
|
|
1713
|
+
if (typeof workerGlobalScope !== "undefined" && typeof self !== "undefined" && self instanceof workerGlobalScope) {
|
|
1714
|
+
return true;
|
|
1854
1715
|
}
|
|
1855
|
-
return
|
|
1716
|
+
return false;
|
|
1856
1717
|
}
|
|
1857
|
-
function
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
result[i] = binary.charCodeAt(i);
|
|
1718
|
+
function assertNonBrowserEnvironment() {
|
|
1719
|
+
if (isBrowserEnvironment()) {
|
|
1720
|
+
throw new Error(BROWSER_ENVIRONMENT_ERROR);
|
|
1861
1721
|
}
|
|
1862
|
-
return result;
|
|
1863
1722
|
}
|
|
1864
|
-
function
|
|
1865
|
-
|
|
1866
|
-
const binary = atob(base64);
|
|
1867
|
-
return binaryStringToUint8Array(binary);
|
|
1868
|
-
}
|
|
1869
|
-
if (typeof Buffer !== "undefined") {
|
|
1870
|
-
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
1871
|
-
}
|
|
1872
|
-
throw new Error("Base64 decoding is not supported in this environment");
|
|
1723
|
+
function normalizeHex(value) {
|
|
1724
|
+
return (value || "").trim().replace(/^0x/i, "").toLowerCase();
|
|
1873
1725
|
}
|
|
1874
|
-
function
|
|
1875
|
-
|
|
1876
|
-
let hex = "";
|
|
1877
|
-
for (let i = 0; i < view.length; i++) {
|
|
1878
|
-
hex += view[i].toString(16).padStart(2, "0");
|
|
1879
|
-
}
|
|
1880
|
-
return hex;
|
|
1726
|
+
function isHex(value) {
|
|
1727
|
+
return /^[0-9a-f]+$/i.test(value);
|
|
1881
1728
|
}
|
|
1882
|
-
function
|
|
1883
|
-
const
|
|
1884
|
-
const
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
result.set(arr, offset);
|
|
1888
|
-
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"));
|
|
1889
1734
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
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");
|
|
1896
1744
|
}
|
|
1897
|
-
function
|
|
1898
|
-
|
|
1899
|
-
while (cleaned.startsWith("0") && cleaned.length > 1) cleaned = cleaned.substring(1);
|
|
1900
|
-
return cleaned;
|
|
1745
|
+
function decodeUtf8(bytes) {
|
|
1746
|
+
return new TextDecoder().decode(bytes);
|
|
1901
1747
|
}
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
if (typeof window !== "undefined" && ((_a = window.crypto) == null ? void 0 : _a.subtle)) return window.crypto.subtle;
|
|
1906
|
-
if (isNode) return require("crypto").webcrypto.subtle;
|
|
1907
|
-
throw new Error("No WebCrypto subtle implementation found in this environment");
|
|
1908
|
-
};
|
|
1909
|
-
function parseCert(buffer) {
|
|
1910
|
-
const bytes = toUint8Array(buffer);
|
|
1911
|
-
const asn1 = import_node_forge.default.asn1.fromDer(uint8ArrayToBinaryString(bytes));
|
|
1912
|
-
const certSeq = asn1.value;
|
|
1913
|
-
const tbsAsn1 = certSeq[0];
|
|
1914
|
-
const sigAlgAsn1 = certSeq[1];
|
|
1915
|
-
const sigValueAsn1 = certSeq[2];
|
|
1916
|
-
const tbsFields = tbsAsn1.value;
|
|
1917
|
-
let idx = 0;
|
|
1918
|
-
if (tbsFields[idx].tagClass === 128) idx++;
|
|
1919
|
-
const serialAsn1 = tbsFields[idx++];
|
|
1920
|
-
const serialNumber = import_node_forge.default.util.bytesToHex(serialAsn1.value);
|
|
1921
|
-
idx++;
|
|
1922
|
-
idx++;
|
|
1923
|
-
const validityAsn1 = tbsFields[idx++];
|
|
1924
|
-
idx++;
|
|
1925
|
-
const spkiAsn1 = tbsFields[idx];
|
|
1926
|
-
if (!validityAsn1 || !Array.isArray(validityAsn1.value) || validityAsn1.value.length < 2) {
|
|
1927
|
-
throw new Error("Certificate validity window is malformed");
|
|
1928
|
-
}
|
|
1929
|
-
const notBeforeNode = validityAsn1.value[0];
|
|
1930
|
-
const notAfterNode = validityAsn1.value[1];
|
|
1931
|
-
const notBefore = notBeforeNode ? parseAsn1Time(notBeforeNode) : void 0;
|
|
1932
|
-
const notAfter = notAfterNode ? parseAsn1Time(notAfterNode) : void 0;
|
|
1933
|
-
const sigRaw = typeof sigValueAsn1.value === "string" ? sigValueAsn1.value : "";
|
|
1934
|
-
const signature = binaryStringToUint8Array(sigRaw.substring(1));
|
|
1935
|
-
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");
|
|
1936
1751
|
return {
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
spkiDer: binaryStringToUint8Array(import_node_forge.default.asn1.toDer(spkiAsn1).getBytes()),
|
|
1942
|
-
notBefore,
|
|
1943
|
-
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])
|
|
1944
1756
|
};
|
|
1945
1757
|
}
|
|
1946
|
-
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) {
|
|
1947
1775
|
return __async(this, null, function* () {
|
|
1948
|
-
const
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
let importParams;
|
|
1952
|
-
let verifyParams;
|
|
1953
|
-
if (sigAlgOid === "1.2.840.113549.1.1.10") {
|
|
1954
|
-
importParams = { name: "RSA-PSS", hash: "SHA-384" };
|
|
1955
|
-
verifyParams = { name: "RSA-PSS", saltLength: 48 };
|
|
1956
|
-
} 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") {
|
|
1957
|
-
importParams = { name: "RSASSA-PKCS1-v1_5", hash: sigAlgOid === "1.2.840.113549.1.1.12" ? "SHA-384" : "SHA-256" };
|
|
1958
|
-
verifyParams = { name: "RSASSA-PKCS1-v1_5" };
|
|
1959
|
-
} else if (sigAlgOid === "1.2.840.10045.4.3.3") {
|
|
1960
|
-
importParams = { name: "ECDSA", namedCurve: "P-384" };
|
|
1961
|
-
verifyParams = { name: "ECDSA", hash: "SHA-384" };
|
|
1962
|
-
} else {
|
|
1963
|
-
importParams = { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" };
|
|
1964
|
-
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}`);
|
|
1965
1779
|
}
|
|
1966
|
-
|
|
1967
|
-
const isValid = yield cryptoSubtle.verify(verifyParams, key, signature, data);
|
|
1968
|
-
if (!isValid) throw new Error(`Signature verification failed (OID: ${sigAlgOid}, ImportParams: ${JSON.stringify(importParams)})`);
|
|
1780
|
+
return response.json();
|
|
1969
1781
|
});
|
|
1970
1782
|
}
|
|
1971
|
-
|
|
1972
|
-
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjiL30OjPuxa+GC1I7SAcBv2u2pMt
|
|
1973
|
-
h9WbP33IvB3eFww+C1hoW0fwdZPiq4FxBtKNiZuFpmYuFngW/nJteBu9kQ==
|
|
1974
|
-
-----END PUBLIC KEY-----
|
|
1975
|
-
`;
|
|
1976
|
-
function verifyTeeAttestation(proof, expectedApplicationId) {
|
|
1783
|
+
function sha256Hex(input) {
|
|
1977
1784
|
return __async(this, null, function* () {
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
if (!teeAttestation) {
|
|
1981
|
-
throw new Error("Missing teeAttestation in proof");
|
|
1982
|
-
}
|
|
1983
|
-
if (typeof teeAttestation === "string") {
|
|
1984
|
-
teeAttestation = JSON.parse(teeAttestation);
|
|
1985
|
-
}
|
|
1986
|
-
let expectedNonceSignature;
|
|
1987
|
-
let nonceDataObj;
|
|
1988
|
-
try {
|
|
1989
|
-
const context = JSON.parse(proof.claimData.context);
|
|
1990
|
-
expectedNonceSignature = context.attestationNonce;
|
|
1991
|
-
nonceDataObj = context.attestationNonceData;
|
|
1992
|
-
} catch (e) {
|
|
1993
|
-
throw new Error("Failed to parse proof context to extract attestationNonce");
|
|
1994
|
-
}
|
|
1995
|
-
if (!expectedNonceSignature || !nonceDataObj) {
|
|
1996
|
-
throw new Error("Proof context is missing attestationNonce or attestationNonceData");
|
|
1997
|
-
}
|
|
1998
|
-
if (teeAttestation.nonce !== expectedNonceSignature) {
|
|
1999
|
-
throw new Error(`Nonce Mismatch! Expected signature ${expectedNonceSignature}, got ${teeAttestation.nonce}`);
|
|
2000
|
-
}
|
|
2001
|
-
const { applicationId, sessionId, timestamp } = nonceDataObj;
|
|
2002
|
-
if (expectedApplicationId && applicationId.toLowerCase() !== expectedApplicationId.toLowerCase()) {
|
|
2003
|
-
throw new Error(`Application ID Mismatch! Expected ${expectedApplicationId}, but proof context contains ${applicationId}`);
|
|
2004
|
-
}
|
|
2005
|
-
const expectedNonceData = `${applicationId}:${sessionId}:${timestamp}`;
|
|
2006
|
-
const nonceMsg = import_ethers4.ethers.getBytes(import_ethers4.ethers.keccak256(new TextEncoder().encode(expectedNonceData)));
|
|
2007
|
-
const recoveredAddress = import_ethers4.ethers.verifyMessage(nonceMsg, expectedNonceSignature);
|
|
2008
|
-
if (recoveredAddress.toLowerCase() !== applicationId.toLowerCase()) {
|
|
2009
|
-
throw new Error(`Nonce signature verification failed: recovered ${recoveredAddress}, expected ${applicationId}`);
|
|
2010
|
-
}
|
|
2011
|
-
try {
|
|
2012
|
-
const context = JSON.parse(proof.claimData.context);
|
|
2013
|
-
const paramSessionId = context.attestationNonceData.sessionId;
|
|
2014
|
-
if (!paramSessionId) {
|
|
2015
|
-
throw new Error(`Proof parameters are missing proxySessionId or sessionId`);
|
|
2016
|
-
}
|
|
2017
|
-
if (paramSessionId.toString() !== sessionId.toString()) {
|
|
2018
|
-
throw new Error(`Session ID Mismatch! Expected ${sessionId}, but proof parameters contain ${paramSessionId}`);
|
|
2019
|
-
}
|
|
2020
|
-
const claimTimestampMs = proof.claimData.timestampS * 1e3;
|
|
2021
|
-
const nonceTimestampMs = parseInt(timestamp, 10);
|
|
2022
|
-
const diffMs = Math.abs(claimTimestampMs - nonceTimestampMs);
|
|
2023
|
-
const TEN_MINUTES_MS = 10 * 60 * 1e3;
|
|
2024
|
-
if (diffMs > TEN_MINUTES_MS) {
|
|
2025
|
-
throw new Error(`Timestamp Skew Too Large! claimData.timestampS and attestationNonce timestamp differ by ${Math.round(diffMs / 1e3)}s (limit: 600s)`);
|
|
2026
|
-
}
|
|
2027
|
-
} catch (e) {
|
|
2028
|
-
if (e instanceof Error && (e.message.includes("Session ID Mismatch!") || e.message.includes("Timestamp Skew"))) {
|
|
2029
|
-
throw e;
|
|
2030
|
-
}
|
|
2031
|
-
throw new Error(`Failed to cross-verify session ID: ${e.message}`);
|
|
2032
|
-
}
|
|
2033
|
-
const reportBuffer = base64ToUint8Array(teeAttestation.snp_report);
|
|
2034
|
-
const report = parseAttestationReport(reportBuffer);
|
|
2035
|
-
if (report.isDebugEnabled) {
|
|
2036
|
-
throw new Error("POLICY CHECK FAILED: Debug mode is ALLOWED. Environment is compromised.");
|
|
2037
|
-
}
|
|
2038
|
-
const certBuffer = base64ToUint8Array(teeAttestation.vlek_cert);
|
|
2039
|
-
yield verifyAMDChain(certBuffer);
|
|
2040
|
-
verifyTCB(certBuffer, report);
|
|
2041
|
-
yield verifyHardwareSignature(reportBuffer, certBuffer);
|
|
2042
|
-
yield verifyReportData(teeAttestation, proof.claimData.context, report);
|
|
2043
|
-
return true;
|
|
2044
|
-
} catch (error) {
|
|
2045
|
-
logger9.error("TEE attestation verification failed:", error);
|
|
2046
|
-
return false;
|
|
2047
|
-
}
|
|
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("");
|
|
2048
1787
|
});
|
|
2049
1788
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
const policy = view.getBigUint64(8, true);
|
|
2056
|
-
const isDebugEnabled = (policy & BigInt(1) << BigInt(19)) !== BigInt(0);
|
|
2057
|
-
const reported_tcb = {
|
|
2058
|
-
bootloader: buffer[56],
|
|
2059
|
-
tee: buffer[57],
|
|
2060
|
-
snp: buffer[62],
|
|
2061
|
-
microcode: buffer[63]
|
|
2062
|
-
};
|
|
2063
|
-
const reportData = arrayBufferToHex(buffer.subarray(80, 144));
|
|
2064
|
-
return { policy, isDebugEnabled, reported_tcb, reportData };
|
|
2065
|
-
}
|
|
2066
|
-
function getExtValue(certAsn1, oidString) {
|
|
2067
|
-
const tbsCert = certAsn1.value[0];
|
|
2068
|
-
if (!tbsCert || !tbsCert.value) return null;
|
|
2069
|
-
const extBlockWrapper = tbsCert.value.find((node) => node.tagClass === import_node_forge.default.asn1.Class.CONTEXT_SPECIFIC && node.type === 3);
|
|
2070
|
-
if (!extBlockWrapper || !extBlockWrapper.value || !extBlockWrapper.value.length) return null;
|
|
2071
|
-
const extSequence = extBlockWrapper.value[0];
|
|
2072
|
-
for (const ext of extSequence.value) {
|
|
2073
|
-
const extIdAsn1 = ext.value[0];
|
|
2074
|
-
const extIdStr = import_node_forge.default.asn1.derToOid(extIdAsn1.value);
|
|
2075
|
-
if (extIdStr === oidString) {
|
|
2076
|
-
const extValueAsn1 = ext.value[ext.value.length - 1];
|
|
2077
|
-
const rawOctetStringBytes = extValueAsn1.value;
|
|
2078
|
-
try {
|
|
2079
|
-
const innerAsn1 = import_node_forge.default.asn1.fromDer(import_node_forge.default.util.createBuffer(rawOctetStringBytes));
|
|
2080
|
-
if (innerAsn1.type === 2) {
|
|
2081
|
-
const bytes = innerAsn1.value;
|
|
2082
|
-
if (typeof bytes === "string" && bytes.length > 0) {
|
|
2083
|
-
return bytes.charCodeAt(bytes.length - 1);
|
|
2084
|
-
} else {
|
|
2085
|
-
throw new Error(`Extension ${oidString} INTEGER value is empty or invalid`);
|
|
2086
|
-
}
|
|
2087
|
-
} else {
|
|
2088
|
-
throw new Error(`Extension ${oidString} does not contain an INTEGER, found type ${innerAsn1.type}`);
|
|
2089
|
-
}
|
|
2090
|
-
} catch (e) {
|
|
2091
|
-
throw new Error(`Failed to strictly parse AMD TCB extension ${oidString}: ${e.message}`);
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
}
|
|
2095
|
-
return null;
|
|
2096
|
-
}
|
|
2097
|
-
function verifyTCB(vlekCertBuffer, report) {
|
|
2098
|
-
const certAsn1 = import_node_forge.default.asn1.fromDer(import_node_forge.default.util.createBuffer(uint8ArrayToBinaryString(vlekCertBuffer)));
|
|
2099
|
-
const OID_BOOTLOADER = "1.3.6.1.4.1.3704.1.3.1";
|
|
2100
|
-
const OID_TEE = "1.3.6.1.4.1.3704.1.3.2";
|
|
2101
|
-
const OID_SNP = "1.3.6.1.4.1.3704.1.3.3";
|
|
2102
|
-
const OID_MICROCODE = "1.3.6.1.4.1.3704.1.3.8";
|
|
2103
|
-
const certTcb = {
|
|
2104
|
-
bootloader: getExtValue(certAsn1, OID_BOOTLOADER),
|
|
2105
|
-
tee: getExtValue(certAsn1, OID_TEE),
|
|
2106
|
-
snp: getExtValue(certAsn1, OID_SNP),
|
|
2107
|
-
microcode: getExtValue(certAsn1, OID_MICROCODE)
|
|
2108
|
-
};
|
|
2109
|
-
if (certTcb.bootloader !== null && report.reported_tcb.bootloader < certTcb.bootloader) {
|
|
2110
|
-
throw new Error(`TCB Downgrade! Bootloader reported ${report.reported_tcb.bootloader}, but certificate requires ${certTcb.bootloader}`);
|
|
2111
|
-
}
|
|
2112
|
-
if (certTcb.tee !== null && report.reported_tcb.tee < certTcb.tee) {
|
|
2113
|
-
throw new Error(`TCB Downgrade! TEE reported ${report.reported_tcb.tee}, but certificate requires ${certTcb.tee}`);
|
|
2114
|
-
}
|
|
2115
|
-
if (certTcb.snp !== null && report.reported_tcb.snp < certTcb.snp) {
|
|
2116
|
-
throw new Error(`TCB Downgrade! SNP reported ${report.reported_tcb.snp}, but certificate requires ${certTcb.snp}`);
|
|
2117
|
-
}
|
|
2118
|
-
if (certTcb.microcode !== null && report.reported_tcb.microcode < certTcb.microcode) {
|
|
2119
|
-
throw new Error(`TCB Downgrade! Microcode reported ${report.reported_tcb.microcode}, but certificate requires ${certTcb.microcode}`);
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
function parseAsn1Time(node) {
|
|
2123
|
-
const s = node.value;
|
|
2124
|
-
if (node.type === import_node_forge.default.asn1.Type.UTCTIME) {
|
|
2125
|
-
const yr = parseInt(s.substring(0, 2), 10);
|
|
2126
|
-
return new Date(Date.UTC(
|
|
2127
|
-
yr >= 50 ? 1900 + yr : 2e3 + yr,
|
|
2128
|
-
parseInt(s.substring(2, 4), 10) - 1,
|
|
2129
|
-
parseInt(s.substring(4, 6), 10),
|
|
2130
|
-
parseInt(s.substring(6, 8), 10),
|
|
2131
|
-
parseInt(s.substring(8, 10), 10),
|
|
2132
|
-
parseInt(s.substring(10, 12), 10)
|
|
2133
|
-
));
|
|
2134
|
-
} else {
|
|
2135
|
-
return new Date(Date.UTC(
|
|
2136
|
-
parseInt(s.substring(0, 4), 10),
|
|
2137
|
-
parseInt(s.substring(4, 6), 10) - 1,
|
|
2138
|
-
parseInt(s.substring(6, 8), 10),
|
|
2139
|
-
parseInt(s.substring(8, 10), 10),
|
|
2140
|
-
parseInt(s.substring(10, 12), 10),
|
|
2141
|
-
parseInt(s.substring(12, 14), 10)
|
|
2142
|
-
));
|
|
2143
|
-
}
|
|
2144
|
-
}
|
|
2145
|
-
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) {
|
|
2146
1794
|
return __async(this, null, function* () {
|
|
2147
|
-
const
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
}
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
const thisUpdateAsn1 = tbsFields[fi++];
|
|
2166
|
-
let nextUpdateAsn1 = null;
|
|
2167
|
-
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)) {
|
|
2168
|
-
nextUpdateAsn1 = tbsFields[fi++];
|
|
2169
|
-
}
|
|
2170
|
-
let revokedSeq = null;
|
|
2171
|
-
if (fi < tbsFields.length && tbsFields[fi].tagClass === import_node_forge.default.asn1.Class.UNIVERSAL && tbsFields[fi].type === import_node_forge.default.asn1.Type.SEQUENCE) {
|
|
2172
|
-
revokedSeq = tbsFields[fi];
|
|
2173
|
-
}
|
|
2174
|
-
const now = /* @__PURE__ */ new Date();
|
|
2175
|
-
const thisUpdate = parseAsn1Time(thisUpdateAsn1);
|
|
2176
|
-
if (thisUpdate > now) {
|
|
2177
|
-
throw new Error(`CRL is not yet valid: thisUpdate is ${thisUpdate.toISOString()}`);
|
|
2178
|
-
}
|
|
2179
|
-
if (nextUpdateAsn1) {
|
|
2180
|
-
const nextUpdate = parseAsn1Time(nextUpdateAsn1);
|
|
2181
|
-
if (nextUpdate < now) {
|
|
2182
|
-
throw new Error(`CRL has expired: nextUpdate was ${nextUpdate.toISOString()}`);
|
|
2183
|
-
}
|
|
2184
|
-
}
|
|
2185
|
-
const crlIssuerDer = import_node_forge.default.asn1.toDer(issuerAsn1).getBytes();
|
|
2186
|
-
const arkForgeCert = import_node_forge.default.pki.certificateFromPem(arkPem);
|
|
2187
|
-
const arkCertAsn1 = import_node_forge.default.pki.certificateToAsn1(arkForgeCert);
|
|
2188
|
-
const arkSubjectAsn1 = arkCertAsn1.value[0].value[5];
|
|
2189
|
-
const arkSubjectDer = import_node_forge.default.asn1.toDer(arkSubjectAsn1).getBytes();
|
|
2190
|
-
if (crlIssuerDer !== arkSubjectDer) {
|
|
2191
|
-
throw new Error("CRL issuer does not match AMD ARK certificate subject \u2014 chain mismatch");
|
|
2192
|
-
}
|
|
2193
|
-
const tbsDerBuf = binaryStringToUint8Array(import_node_forge.default.asn1.toDer(tbsAsn1).getBytes());
|
|
2194
|
-
const sigRaw = typeof sigBitsAsn1.value === "string" ? sigBitsAsn1.value : "";
|
|
2195
|
-
const sigBuf = binaryStringToUint8Array(sigRaw.substring(1));
|
|
2196
|
-
const cryptoSubtle = getSubtleCrypto();
|
|
2197
|
-
const spkiBuf = binaryStringToUint8Array(
|
|
2198
|
-
import_node_forge.default.asn1.toDer(import_node_forge.default.pki.publicKeyToAsn1(arkForgeCert.publicKey)).getBytes()
|
|
2199
|
-
);
|
|
2200
|
-
const arkCryptoKey = yield cryptoSubtle.importKey(
|
|
2201
|
-
"spki",
|
|
2202
|
-
spkiBuf,
|
|
2203
|
-
{ 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" },
|
|
2204
1813
|
false,
|
|
2205
1814
|
["verify"]
|
|
2206
1815
|
);
|
|
2207
|
-
const isValid = yield
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
tbsDerBuf
|
|
1816
|
+
const isValid = yield getSubtleCrypto().verify(
|
|
1817
|
+
"RSASSA-PKCS1-v1_5",
|
|
1818
|
+
cryptoKey,
|
|
1819
|
+
signature,
|
|
1820
|
+
new TextEncoder().encode(signingInput)
|
|
2213
1821
|
);
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
}
|
|
2217
|
-
const targetSerial = normalizeSerial(vlekSerial);
|
|
2218
|
-
if (revokedSeq && Array.isArray(revokedSeq.value)) {
|
|
2219
|
-
for (const entry of revokedSeq.value) {
|
|
2220
|
-
if (!Array.isArray(entry.value) || entry.value.length < 2) continue;
|
|
2221
|
-
const serialAsn1 = entry.value[0];
|
|
2222
|
-
if (serialAsn1.type !== import_node_forge.default.asn1.Type.INTEGER || typeof serialAsn1.value !== "string") continue;
|
|
2223
|
-
const serialHex = import_node_forge.default.util.bytesToHex(serialAsn1.value);
|
|
2224
|
-
if (normalizeSerial(serialHex) === targetSerial) {
|
|
2225
|
-
throw new Error("\u{1F6A8} VLEK Certificate is REVOKED per AMD CRL! This hardware may be compromised.");
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
1822
|
+
assert(isValid, "JWT signature verification failed");
|
|
1823
|
+
return payload;
|
|
2229
1824
|
});
|
|
2230
1825
|
}
|
|
2231
|
-
function
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
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");
|
|
2235
1837
|
}
|
|
2236
|
-
if (
|
|
2237
|
-
throw new Error(
|
|
1838
|
+
if (!parsedContext || typeof parsedContext !== "object") {
|
|
1839
|
+
throw new Error("Malformed proof: claimData.context is not a JSON object");
|
|
2238
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 };
|
|
2239
1847
|
}
|
|
2240
|
-
function
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
const ask = parseCert(binaryStringToUint8Array(askDer));
|
|
2257
|
-
const ark = parseCert(binaryStringToUint8Array(arkDer));
|
|
2258
|
-
assertCertValidity("ASK", ask);
|
|
2259
|
-
assertCertValidity("ARK", ark);
|
|
2260
|
-
try {
|
|
2261
|
-
yield verifySignature(certs[1], ark.tbsDer, ark.signature, ark.sigAlgOid);
|
|
2262
|
-
} catch (e) {
|
|
2263
|
-
throw new Error(`AMD ARK self-signature verification failed: ${e.message}`);
|
|
2264
|
-
}
|
|
2265
|
-
try {
|
|
2266
|
-
yield verifySignature(certs[1], ask.tbsDer, ask.signature, ask.sigAlgOid);
|
|
2267
|
-
} catch (e) {
|
|
2268
|
-
throw new Error(`AMD ASK-by-ARK signature verification failed: ${e.message}`);
|
|
2269
|
-
}
|
|
2270
|
-
try {
|
|
2271
|
-
yield verifySignature(certs[0], vlek.tbsDer, vlek.signature, vlek.sigAlgOid);
|
|
2272
|
-
} catch (e) {
|
|
2273
|
-
throw new Error(`VLEK-by-ASK signature verification failed: ${e.message}`);
|
|
2274
|
-
}
|
|
2275
|
-
matchedChain = true;
|
|
2276
|
-
let crlBuf;
|
|
2277
|
-
const now = Date.now();
|
|
2278
|
-
if (crlCache[processor] && now - crlCache[processor].fetchedAt < 36e5) {
|
|
2279
|
-
crlBuf = crlCache[processor].buffer;
|
|
2280
|
-
} else {
|
|
2281
|
-
const crlUrl = `https://kdsintf.amd.com/vlek/v1/${processor}/crl`;
|
|
2282
|
-
const controller = new AbortController();
|
|
2283
|
-
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
2284
|
-
const crlResp = yield fetch(crlUrl, { signal: controller.signal });
|
|
2285
|
-
clearTimeout(timeoutId);
|
|
2286
|
-
if (!crlResp.ok) continue;
|
|
2287
|
-
crlBuf = new Uint8Array(yield crlResp.arrayBuffer());
|
|
2288
|
-
crlCache[processor] = { buffer: crlBuf, fetchedAt: now };
|
|
2289
|
-
}
|
|
2290
|
-
if (vlek.serialNumber && crlBuf) {
|
|
2291
|
-
yield verifyCRL(crlBuf, certs[1], vlek.serialNumber);
|
|
2292
|
-
}
|
|
2293
|
-
chainVerified = true;
|
|
2294
|
-
break;
|
|
2295
|
-
} catch (e) {
|
|
2296
|
-
if (matchedChain) {
|
|
2297
|
-
throw e;
|
|
2298
|
-
}
|
|
2299
|
-
continue;
|
|
2300
|
-
}
|
|
2301
|
-
}
|
|
2302
|
-
if (!chainVerified) {
|
|
2303
|
-
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");
|
|
2304
1864
|
}
|
|
2305
|
-
}
|
|
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
|
+
}
|
|
2306
1883
|
}
|
|
2307
|
-
function
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
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}`
|
|
1903
|
+
);
|
|
1904
|
+
assert(
|
|
1905
|
+
recoveredAddress.toLowerCase() === applicationId.toLowerCase(),
|
|
1906
|
+
`Nonce signature verification failed: recovered ${recoveredAddress}, expected ${applicationId}`
|
|
2323
1907
|
);
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
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}`
|
|
2337
1961
|
);
|
|
2338
|
-
|
|
2339
|
-
|
|
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
|
+
};
|
|
2340
1999
|
}
|
|
2341
2000
|
});
|
|
2342
2001
|
}
|
|
2343
|
-
function
|
|
2002
|
+
function runTeeVerification(proofs, config) {
|
|
2344
2003
|
return __async(this, null, function* () {
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
};
|
|
2360
|
-
const importedCosignKey = yield cryptoSubtle.importKey(
|
|
2361
|
-
"spki",
|
|
2362
|
-
base64ToUint8Array(
|
|
2363
|
-
COSIGN_PUBLIC_KEY.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace(/\s+/g, "")
|
|
2364
|
-
),
|
|
2365
|
-
{ name: "ECDSA", namedCurve: "P-256" },
|
|
2366
|
-
true,
|
|
2367
|
-
["verify"]
|
|
2368
|
-
);
|
|
2369
|
-
const pubKeySpkiDer = yield cryptoSubtle.exportKey("spki", importedCosignKey);
|
|
2370
|
-
const pubKeyHashBuffer = yield cryptoSubtle.digest("SHA-256", pubKeySpkiDer);
|
|
2371
|
-
const pubKeyHashBytes = new Uint8Array(pubKeyHashBuffer);
|
|
2372
|
-
const nonceHex = (teeAttestation.nonce || nonce).replace(/^0x/i, "");
|
|
2373
|
-
const nonceBytes = new Uint8Array(nonceHex.length / 2);
|
|
2374
|
-
for (let i = 0; i < nonceBytes.length; i++) nonceBytes[i] = parseInt(nonceHex.substring(i * 2, i * 2 + 2), 16);
|
|
2375
|
-
const workloadBytes = extractDigestBytes(teeAttestation.workload_digest);
|
|
2376
|
-
const verifierBytes = extractDigestBytes(teeAttestation.verifier_digest);
|
|
2377
|
-
const domainSep = new TextEncoder().encode("POPCORN_TEE_REPORT_DATA_V1");
|
|
2378
|
-
const version = new Uint8Array([1]);
|
|
2379
|
-
const payload = new Uint8Array(
|
|
2380
|
-
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))
|
|
2381
2018
|
);
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
payload.set(chunk, offset);
|
|
2385
|
-
offset += chunk.length;
|
|
2386
|
-
}
|
|
2387
|
-
const hashBuffer = yield cryptoSubtle.digest("SHA-256", payload);
|
|
2388
|
-
const hashHex = arrayBufferToHex(hashBuffer);
|
|
2389
|
-
const expected64ByteHex = hashHex + hashHex;
|
|
2390
|
-
if (report.reportData !== expected64ByteHex) {
|
|
2391
|
-
throw new Error(`REPORT_DATA Mismatch! Hardware report is not bound to these image digests or nonce.
|
|
2392
|
-
Expected: ${expected64ByteHex}
|
|
2393
|
-
Got: ${report.reportData}`);
|
|
2019
|
+
if (!teeResults.every((r) => r.isVerified)) {
|
|
2020
|
+
throw new TeeVerificationError("TEE attestation verification failed for one or more proofs");
|
|
2394
2021
|
}
|
|
2395
2022
|
});
|
|
2396
2023
|
}
|
|
@@ -2413,29 +2040,15 @@ function verifyProof(proofOrProofs, config) {
|
|
|
2413
2040
|
yield assertVerifiedProof(proof, attestors);
|
|
2414
2041
|
}
|
|
2415
2042
|
yield assertValidateProof(proofs, config);
|
|
2416
|
-
let
|
|
2417
|
-
if (config.
|
|
2418
|
-
|
|
2419
|
-
if (!hasTeeData) {
|
|
2420
|
-
const teeError = new TeeVerificationError("TEE verification requested but one or more proofs are missing TEE attestation data");
|
|
2421
|
-
logger10.error(teeError.message);
|
|
2422
|
-
return createVerifyProofResultFailure(teeError, false);
|
|
2423
|
-
}
|
|
2424
|
-
try {
|
|
2425
|
-
const teeResults = yield Promise.all(proofs.map((proof) => verifyTeeAttestation(proof)));
|
|
2426
|
-
isTeeVerified = teeResults.every((r) => r === true);
|
|
2427
|
-
if (!isTeeVerified) {
|
|
2428
|
-
const teeError = new TeeVerificationError("TEE attestation verification failed for one or more proofs");
|
|
2429
|
-
logger10.error(teeError.message);
|
|
2430
|
-
return createVerifyProofResultFailure(teeError, false);
|
|
2431
|
-
}
|
|
2432
|
-
} catch (error) {
|
|
2433
|
-
const teeError = new TeeVerificationError("Error verifying TEE attestation", error);
|
|
2434
|
-
logger10.error(teeError.message);
|
|
2435
|
-
return createVerifyProofResultFailure(teeError, false);
|
|
2436
|
-
}
|
|
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");
|
|
2437
2046
|
}
|
|
2438
|
-
|
|
2047
|
+
if (config.teeAttestation) {
|
|
2048
|
+
yield runTeeVerification(proofs, config.teeAttestation);
|
|
2049
|
+
isTeeAttestationVerified = true;
|
|
2050
|
+
}
|
|
2051
|
+
return createVerifyProofResultSuccess(proofs, isTeeAttestationVerified);
|
|
2439
2052
|
} catch (error) {
|
|
2440
2053
|
logger10.error("Error in validating proof:", error);
|
|
2441
2054
|
const _error = error instanceof Error ? error : new Error(String(error));
|
|
@@ -2698,11 +2311,13 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
2698
2311
|
proofRequestInstance.resolvedProviderVersion = data.resolvedProviderVersion;
|
|
2699
2312
|
proofRequestInstance.context.reclaimSessionId = data.sessionId;
|
|
2700
2313
|
if (options == null ? void 0 : options.acceptTeeAttestation) {
|
|
2701
|
-
const
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2314
|
+
const attestationNonce = generateAttestationNonce(
|
|
2315
|
+
appSecret,
|
|
2316
|
+
applicationId,
|
|
2317
|
+
data.sessionId,
|
|
2318
|
+
proofRequestInstance.timeStamp
|
|
2319
|
+
);
|
|
2320
|
+
proofRequestInstance.setAttestationContext(attestationNonce, {
|
|
2706
2321
|
applicationId,
|
|
2707
2322
|
sessionId: data.sessionId,
|
|
2708
2323
|
timestamp: proofRequestInstance.timeStamp
|
|
@@ -3223,13 +2838,13 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
3223
2838
|
generateSignature(applicationSecret) {
|
|
3224
2839
|
return __async(this, null, function* () {
|
|
3225
2840
|
try {
|
|
3226
|
-
const wallet = new
|
|
2841
|
+
const wallet = new import_ethers6.ethers.Wallet(applicationSecret);
|
|
3227
2842
|
const canonicalData = (0, import_canonicalize3.default)({ providerId: this.providerId, timestamp: this.timeStamp });
|
|
3228
2843
|
if (!canonicalData) {
|
|
3229
2844
|
throw new SignatureGeneratingError("Failed to canonicalize data for signing.");
|
|
3230
2845
|
}
|
|
3231
|
-
const messageHash =
|
|
3232
|
-
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));
|
|
3233
2848
|
} catch (err) {
|
|
3234
2849
|
logger10.info(`Error generating proof request for applicationId: ${this.applicationId}, providerId: ${this.providerId}, timeStamp: ${this.timeStamp}`);
|
|
3235
2850
|
throw new SignatureGeneratingError(`Error generating signature for applicationId: ${this.applicationId}`);
|
|
@@ -3917,6 +3532,7 @@ var ReclaimProofRequest = class _ReclaimProofRequest {
|
|
|
3917
3532
|
isHttpProviderClaimParams,
|
|
3918
3533
|
isMobileDevice,
|
|
3919
3534
|
recoverSignersOfSignedClaim,
|
|
3535
|
+
runTeeVerification,
|
|
3920
3536
|
takePairsWhereValueIsArray,
|
|
3921
3537
|
takeTemplateParametersFromProofs,
|
|
3922
3538
|
transformForOnchain,
|