@reclaimprotocol/attestor-core 5.0.1-beta.13 → 5.0.1-beta.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/lib/external-rpc/index.js +17321 -3
  2. package/lib/index.d.ts +1 -0
  3. package/lib/index.js +15391 -11
  4. package/lib/scripts/build-browser-debug.d.ts +1 -0
  5. package/package.json +1 -1
  6. package/lib/avs/abis/avsDirectoryABI.js +0 -343
  7. package/lib/avs/abis/delegationABI.js +0 -4
  8. package/lib/avs/abis/registryABI.js +0 -728
  9. package/lib/avs/client/create-claim-on-avs.js +0 -168
  10. package/lib/avs/config.js +0 -26
  11. package/lib/avs/contracts/ReclaimServiceManager.js +0 -0
  12. package/lib/avs/contracts/common.js +0 -0
  13. package/lib/avs/contracts/factories/ReclaimServiceManager__factory.js +0 -1183
  14. package/lib/avs/contracts/factories/index.js +0 -4
  15. package/lib/avs/contracts/index.js +0 -6
  16. package/lib/avs/types/index.js +0 -0
  17. package/lib/avs/utils/contracts.js +0 -53
  18. package/lib/avs/utils/register.js +0 -74
  19. package/lib/avs/utils/tasks.js +0 -48
  20. package/lib/client/create-claim.js +0 -461
  21. package/lib/client/index.js +0 -3
  22. package/lib/client/tunnels/make-rpc-tcp-tunnel.js +0 -53
  23. package/lib/client/tunnels/make-rpc-tls-tunnel.js +0 -127
  24. package/lib/client/utils/attestor-pool.js +0 -24
  25. package/lib/client/utils/client-socket.js +0 -120
  26. package/lib/client/utils/message-handler.js +0 -97
  27. package/lib/config/index.js +0 -62
  28. package/lib/external-rpc/benchmark.js +0 -82
  29. package/lib/external-rpc/event-bus.js +0 -17
  30. package/lib/external-rpc/handle-incoming-msg.js +0 -241
  31. package/lib/external-rpc/jsc-polyfills/1.js +0 -80
  32. package/lib/external-rpc/jsc-polyfills/2.js +0 -15
  33. package/lib/external-rpc/jsc-polyfills/event.js +0 -19
  34. package/lib/external-rpc/jsc-polyfills/index.js +0 -2
  35. package/lib/external-rpc/jsc-polyfills/ws.js +0 -83
  36. package/lib/external-rpc/setup-browser.js +0 -33
  37. package/lib/external-rpc/setup-jsc.js +0 -22
  38. package/lib/external-rpc/types.js +0 -0
  39. package/lib/external-rpc/utils.js +0 -100
  40. package/lib/external-rpc/zk.js +0 -58
  41. package/lib/mechain/abis/governanceABI.js +0 -461
  42. package/lib/mechain/abis/taskABI.js +0 -512
  43. package/lib/mechain/client/create-claim-on-mechain.js +0 -33
  44. package/lib/mechain/client/index.js +0 -1
  45. package/lib/mechain/constants/index.js +0 -8
  46. package/lib/mechain/index.js +0 -2
  47. package/lib/mechain/types/index.js +0 -0
  48. package/lib/proto/api.js +0 -4250
  49. package/lib/proto/tee-bundle.js +0 -1296
  50. package/lib/providers/http/index.js +0 -640
  51. package/lib/providers/http/patch-parse5-tree.js +0 -34
  52. package/lib/providers/http/utils.js +0 -283
  53. package/lib/providers/index.js +0 -7
  54. package/lib/scripts/build-browser.js +0 -38
  55. package/lib/scripts/build-jsc.js +0 -47
  56. package/lib/scripts/build-lib.js +0 -47
  57. package/lib/scripts/check-avs-registration.js +0 -28
  58. package/lib/scripts/fallbacks/crypto.js +0 -4
  59. package/lib/scripts/fallbacks/empty.js +0 -4
  60. package/lib/scripts/fallbacks/re2.js +0 -7
  61. package/lib/scripts/fallbacks/snarkjs.js +0 -10
  62. package/lib/scripts/fallbacks/stwo.js +0 -159
  63. package/lib/scripts/generate-provider-types.js +0 -101
  64. package/lib/scripts/generate-receipt.js +0 -101
  65. package/lib/scripts/generate-toprf-keys.js +0 -24
  66. package/lib/scripts/jsc-cli-rpc.js +0 -35
  67. package/lib/scripts/register-avs-operator.js +0 -3
  68. package/lib/scripts/start-server.js +0 -11
  69. package/lib/scripts/update-avs-metadata.js +0 -20
  70. package/lib/scripts/utils.js +0 -10
  71. package/lib/scripts/whitelist-operator.js +0 -16
  72. package/lib/server/create-server.js +0 -105
  73. package/lib/server/handlers/claimTeeBundle.js +0 -232
  74. package/lib/server/handlers/claimTunnel.js +0 -80
  75. package/lib/server/handlers/completeClaimOnChain.js +0 -29
  76. package/lib/server/handlers/createClaimOnChain.js +0 -32
  77. package/lib/server/handlers/createTaskOnMechain.js +0 -57
  78. package/lib/server/handlers/createTunnel.js +0 -98
  79. package/lib/server/handlers/disconnectTunnel.js +0 -8
  80. package/lib/server/handlers/fetchCertificateBytes.js +0 -57
  81. package/lib/server/handlers/index.js +0 -25
  82. package/lib/server/handlers/init.js +0 -33
  83. package/lib/server/handlers/toprf.js +0 -19
  84. package/lib/server/index.js +0 -4
  85. package/lib/server/socket.js +0 -112
  86. package/lib/server/tunnels/make-tcp-tunnel.js +0 -202
  87. package/lib/server/utils/apm.js +0 -29
  88. package/lib/server/utils/assert-valid-claim-request.js +0 -354
  89. package/lib/server/utils/config-env.js +0 -4
  90. package/lib/server/utils/dns.js +0 -24
  91. package/lib/server/utils/gcp-attestation.js +0 -237
  92. package/lib/server/utils/generics.js +0 -45
  93. package/lib/server/utils/iso.js +0 -259
  94. package/lib/server/utils/keep-alive.js +0 -38
  95. package/lib/server/utils/nitro-attestation.js +0 -249
  96. package/lib/server/utils/oprf-raw.js +0 -61
  97. package/lib/server/utils/process-handshake.js +0 -233
  98. package/lib/server/utils/proxy-session.js +0 -6
  99. package/lib/server/utils/tee-oprf-mpc-verification.js +0 -86
  100. package/lib/server/utils/tee-oprf-verification.js +0 -151
  101. package/lib/server/utils/tee-transcript-reconstruction.js +0 -140
  102. package/lib/server/utils/tee-verification.js +0 -358
  103. package/lib/server/utils/validation.js +0 -45
  104. package/lib/types/bgp.js +0 -0
  105. package/lib/types/claims.js +0 -0
  106. package/lib/types/client.js +0 -0
  107. package/lib/types/general.js +0 -0
  108. package/lib/types/handlers.js +0 -0
  109. package/lib/types/index.js +0 -10
  110. package/lib/types/providers.gen.js +0 -16
  111. package/lib/types/providers.js +0 -0
  112. package/lib/types/rpc.js +0 -0
  113. package/lib/types/signatures.js +0 -0
  114. package/lib/types/tunnel.js +0 -0
  115. package/lib/types/zk.js +0 -0
  116. package/lib/utils/auth.js +0 -71
  117. package/lib/utils/b64-json.js +0 -17
  118. package/lib/utils/bgp-listener.js +0 -123
  119. package/lib/utils/claims.js +0 -89
  120. package/lib/utils/env.js +0 -19
  121. package/lib/utils/error.js +0 -54
  122. package/lib/utils/generics.js +0 -268
  123. package/lib/utils/http-parser.js +0 -201
  124. package/lib/utils/index.js +0 -13
  125. package/lib/utils/logger.js +0 -82
  126. package/lib/utils/prepare-packets.js +0 -69
  127. package/lib/utils/redactions.js +0 -135
  128. package/lib/utils/retries.js +0 -26
  129. package/lib/utils/signatures/eth.js +0 -31
  130. package/lib/utils/signatures/index.js +0 -12
  131. package/lib/utils/socket-base.js +0 -96
  132. package/lib/utils/tls.js +0 -58
  133. package/lib/utils/ws.js +0 -22
  134. package/lib/utils/zk.js +0 -625
@@ -1,151 +0,0 @@
1
- import bs58 from "bs58";
2
- import { AttestorError } from "#src/utils/error.js";
3
- import { makeDefaultOPRFOperator } from "#src/utils/zk.js";
4
- async function verifyOprfProofs(bundleData, logger) {
5
- if (!bundleData.oprfVerifications || bundleData.oprfVerifications.length === 0) {
6
- logger.debug("No OPRF verifications present in bundle");
7
- return [];
8
- }
9
- const { tOutputPayload } = bundleData;
10
- const consolidatedCiphertext = tOutputPayload.consolidatedResponseCiphertext;
11
- if (!consolidatedCiphertext || consolidatedCiphertext.length === 0) {
12
- throw new AttestorError("ERROR_INVALID_CLAIM", "No consolidated ciphertext for OPRF verification");
13
- }
14
- const results = [];
15
- logger.info(`Verifying ${bundleData.oprfVerifications.length} OPRF proofs`);
16
- for (const [idx, oprfData] of bundleData.oprfVerifications.entries()) {
17
- try {
18
- const result = await verifySingleOprfProof(
19
- oprfData,
20
- consolidatedCiphertext,
21
- idx,
22
- logger
23
- );
24
- results.push(result);
25
- } catch (error) {
26
- logger.error({ error, index: idx }, "OPRF proof verification failed");
27
- throw new AttestorError(
28
- "ERROR_INVALID_CLAIM",
29
- `OPRF verification failed at index ${idx}: ${error.message}`
30
- );
31
- }
32
- }
33
- logger.info(`Successfully verified ${results.length} OPRF proofs`);
34
- return results;
35
- }
36
- async function verifySingleOprfProof(oprfData, consolidatedCiphertext, index, logger) {
37
- const publicSignalsJson = JSON.parse(new TextDecoder().decode(oprfData.publicSignalsJson));
38
- const { proof, publicSignals, cipher } = publicSignalsJson;
39
- if (!proof || !publicSignals) {
40
- throw new Error("Missing proof or public signals in OPRF data");
41
- }
42
- const ciphertextChunk = consolidatedCiphertext.slice(
43
- oprfData.streamPos,
44
- oprfData.streamPos + oprfData.streamLength
45
- );
46
- const completePublicSignals = {
47
- out: publicSignals.out || Uint8Array.from([]),
48
- // Replace null input with extracted ciphertext
49
- in: ciphertextChunk,
50
- // Convert base64 nonces and counters
51
- noncesAndCounters: publicSignals.blocks?.map((block) => ({
52
- nonce: Buffer.from(block.nonce || "", "base64"),
53
- counter: block.counter || 0,
54
- boundary: block.boundary || ""
55
- })) || [],
56
- // Process TOPRF data
57
- toprf: publicSignals.toprf ? {
58
- ...publicSignals.toprf,
59
- // Convert domain separator from base64
60
- domainSeparator: publicSignals.toprf.domainSeparator ? Buffer.from(publicSignals.toprf.domainSeparator, "base64").toString("utf8") : "reclaim",
61
- // Convert output from base64
62
- output: publicSignals.toprf.output ? Buffer.from(publicSignals.toprf.output, "base64") : new Uint8Array(),
63
- // Convert response fields from base64
64
- responses: publicSignals.toprf.responses?.map((resp) => ({
65
- publicKeyShare: Buffer.from(resp.publicKeyShare || "", "base64"),
66
- evaluated: Buffer.from(resp.evaluated || "", "base64"),
67
- c: Buffer.from(resp.c || "", "base64"),
68
- r: Buffer.from(resp.r || "", "base64")
69
- })) || [],
70
- // Locations are already in correct format
71
- locations: publicSignals.toprf.locations || []
72
- } : void 0
73
- };
74
- const algorithm = cipher.replace("-toprf", "");
75
- const zkEngine = "gnark";
76
- const oprfOperator = makeDefaultOPRFOperator(algorithm, zkEngine, logger);
77
- const proofBytes = Buffer.from(proof, "base64");
78
- const isValid = await oprfOperator.groth16Verify(
79
- completePublicSignals,
80
- proofBytes,
81
- logger
82
- );
83
- if (!isValid) {
84
- throw new Error("OPRF proof verification failed");
85
- }
86
- logger.debug(`OPRF ${index}: Proof verified successfully`);
87
- const oprfOutput = completePublicSignals.toprf?.output;
88
- if (!oprfOutput || oprfOutput.length === 0) {
89
- throw new Error("No OPRF output found in verified proof");
90
- }
91
- const oprfLocation = completePublicSignals.toprf?.locations?.[0];
92
- if (!oprfLocation) {
93
- throw new Error("No OPRF location found in public signals");
94
- }
95
- logger.info(`OPRF #${index}: streamPos=${oprfData.streamPos}, locationPos=${oprfLocation.pos}, finalPos=${oprfData.streamPos + oprfLocation.pos}, len=${oprfLocation.len}`);
96
- return {
97
- // The position in the plaintext where to replace (stream position + OPRF location within chunk)
98
- position: oprfData.streamPos + oprfLocation.pos,
99
- length: oprfLocation.len,
100
- output: oprfOutput
101
- };
102
- }
103
- function replaceOprfRanges(plaintext, oprfResults, logger) {
104
- if (oprfResults.length === 0) {
105
- return plaintext;
106
- }
107
- const replacements = oprfResults.map((result) => {
108
- let outputBytes;
109
- let encodedOutput;
110
- if (result.isMPC) {
111
- encodedOutput = bs58.encode(result.output);
112
- outputBytes = new TextEncoder().encode(encodedOutput);
113
- } else {
114
- encodedOutput = Buffer.from(result.output).toString("base64");
115
- const truncated = encodedOutput.substring(0, result.length);
116
- outputBytes = new TextEncoder().encode(truncated);
117
- }
118
- return { result, outputBytes, encodedOutput };
119
- });
120
- replacements.sort((a, b) => a.result.position - b.result.position);
121
- let newSize = plaintext.length;
122
- for (const { result, outputBytes } of replacements) {
123
- const sizeDiff = outputBytes.length - result.length;
124
- newSize += sizeDiff;
125
- }
126
- logger.info(`Transcript size: ${plaintext.length} -> ${newSize} (${newSize - plaintext.length >= 0 ? "+" : ""}${newSize - plaintext.length} bytes)`);
127
- const newPlaintext = new Uint8Array(newSize);
128
- let srcPos = 0;
129
- let dstPos = 0;
130
- for (const [idx, { result, outputBytes, encodedOutput }] of replacements.entries()) {
131
- const segmentLength = result.position - srcPos;
132
- if (segmentLength > 0) {
133
- newPlaintext.set(plaintext.slice(srcPos, result.position), dstPos);
134
- dstPos += segmentLength;
135
- }
136
- const currentContent = plaintext.slice(result.position, result.position + result.length);
137
- logger.info(`OPRF #${idx} at pos ${result.position}: "${Buffer.from(currentContent).toString("utf8")}" (${result.length}b) -> "${encodedOutput}" (${outputBytes.length}b)${result.isMPC ? " [MPC/base58]" : ""}`);
138
- newPlaintext.set(outputBytes, dstPos);
139
- dstPos += outputBytes.length;
140
- srcPos = result.position + result.length;
141
- }
142
- if (srcPos < plaintext.length) {
143
- newPlaintext.set(plaintext.slice(srcPos), dstPos);
144
- }
145
- logger.info(`Replaced ${oprfResults.length} OPRF ranges in plaintext`);
146
- return newPlaintext;
147
- }
148
- export {
149
- replaceOprfRanges,
150
- verifyOprfProofs
151
- };
@@ -1,140 +0,0 @@
1
- import { AttestorError } from "#src/utils/error.js";
2
- import { REDACTION_CHAR_CODE } from "#src/utils/index.js";
3
- async function reconstructTlsTranscript(bundleData, logger, oprfResults) {
4
- try {
5
- const revealedRequest = reconstructRequest(bundleData, logger);
6
- const reconstructedResponse = await reconstructConsolidatedResponse(bundleData, logger, oprfResults);
7
- const certificateInfo = bundleData.kOutputPayload.certificateInfo;
8
- logger.info("TLS transcript reconstruction completed successfully", {
9
- requestSize: revealedRequest.length,
10
- responseSize: reconstructedResponse.length,
11
- hasCertificateInfo: !!certificateInfo
12
- });
13
- return {
14
- revealedRequest,
15
- reconstructedResponse,
16
- certificateInfo
17
- };
18
- } catch (error) {
19
- logger.error({ error }, "TLS transcript reconstruction failed");
20
- throw new AttestorError("ERROR_INVALID_CLAIM", `Transcript reconstruction failed: ${error.message}`);
21
- }
22
- }
23
- function reconstructRequest(bundleData, logger) {
24
- const { kOutputPayload } = bundleData;
25
- if (!kOutputPayload.requestRedactionRanges || kOutputPayload.requestRedactionRanges.length === 0) {
26
- logger.warn("No request redaction ranges - using redacted request as-is");
27
- return kOutputPayload.redactedRequest;
28
- }
29
- const revealedRequest = new Uint8Array(kOutputPayload.redactedRequest);
30
- const prettyRequest = new Uint8Array(revealedRequest);
31
- for (const range of kOutputPayload.requestRedactionRanges) {
32
- if (!range.type.includes("proof")) {
33
- const start = range.start;
34
- const length = range.length;
35
- for (let i = 0; i < length && start + i < prettyRequest.length; i++) {
36
- prettyRequest[start + i] = REDACTION_CHAR_CODE;
37
- }
38
- }
39
- }
40
- return prettyRequest;
41
- }
42
- async function reconstructConsolidatedResponse(bundleData, logger, oprfResults) {
43
- const { kOutputPayload, tOutputPayload } = bundleData;
44
- const consolidatedKeystream = kOutputPayload.consolidatedResponseKeystream;
45
- const consolidatedCiphertext = tOutputPayload.consolidatedResponseCiphertext;
46
- if (!consolidatedKeystream || consolidatedKeystream.length === 0) {
47
- throw new AttestorError("ERROR_INVALID_CLAIM", "No consolidated response keystream available");
48
- }
49
- if (!consolidatedCiphertext || consolidatedCiphertext.length === 0) {
50
- throw new AttestorError("ERROR_INVALID_CLAIM", "No consolidated response ciphertext available");
51
- }
52
- if (consolidatedKeystream.length !== consolidatedCiphertext.length) {
53
- logger.warn("Keystream and ciphertext length mismatch", {
54
- keystreamLength: consolidatedKeystream.length,
55
- ciphertextLength: consolidatedCiphertext.length
56
- });
57
- }
58
- const minLength = Math.min(consolidatedKeystream.length, consolidatedCiphertext.length);
59
- const reconstructedResponse = new Uint8Array(minLength);
60
- for (let i = 0; i < minLength; i++) {
61
- reconstructedResponse[i] = consolidatedKeystream[i] ^ consolidatedCiphertext[i];
62
- }
63
- logger.info(`Reconstructed response: ${reconstructedResponse.length} bytes, ${kOutputPayload.responseRedactionRanges?.length || 0} redaction ranges`);
64
- let processedResponse = applyResponseRedactionRanges(reconstructedResponse, kOutputPayload.responseRedactionRanges, logger);
65
- if (oprfResults && oprfResults.length > 0) {
66
- logger.info(`Applying ${oprfResults.length} OPRF replacements before trimming`);
67
- const { replaceOprfRanges } = await import("#src/server/utils/tee-oprf-verification.js");
68
- processedResponse = replaceOprfRanges(processedResponse, oprfResults, logger);
69
- }
70
- let leadingAsterisks = 0;
71
- for (const element of processedResponse) {
72
- if (element === REDACTION_CHAR_CODE) {
73
- leadingAsterisks++;
74
- } else {
75
- break;
76
- }
77
- }
78
- let trailingAsterisks = 0;
79
- for (let i = processedResponse.length - 1; i >= leadingAsterisks; i--) {
80
- if (processedResponse[i] === REDACTION_CHAR_CODE) {
81
- trailingAsterisks++;
82
- } else {
83
- break;
84
- }
85
- }
86
- const finalLength = processedResponse.length - leadingAsterisks - trailingAsterisks;
87
- logger.info(`After processing: ${processedResponse.length} bytes, ${leadingAsterisks} leading and ${trailingAsterisks} trailing asterisks trimmed, final: ${finalLength} bytes`);
88
- return processedResponse.slice(leadingAsterisks, processedResponse.length - trailingAsterisks);
89
- }
90
- function applyResponseRedactionRanges(response, redactionRanges, logger) {
91
- if (!redactionRanges || redactionRanges.length === 0) {
92
- return response;
93
- }
94
- const result = new Uint8Array(response);
95
- const consolidatedRanges = consolidateRedactionRanges(redactionRanges);
96
- if (logger) {
97
- logger.info(`Applying ${consolidatedRanges.length} redaction ranges to ${response.length} byte response`);
98
- }
99
- for (const [idx, range] of consolidatedRanges.entries()) {
100
- const rangeStart = range.start;
101
- const rangeEnd = range.start + range.length;
102
- if (rangeStart < 0 || rangeEnd > result.length) {
103
- if (logger) {
104
- logger.warn(`Redaction range #${idx} out of bounds: [${rangeStart}-${rangeEnd}] vs ${result.length}`);
105
- }
106
- continue;
107
- }
108
- if (logger && idx < 3) {
109
- logger.info(`Redaction range #${idx}: [${rangeStart}-${rangeEnd}]`);
110
- }
111
- for (let i = rangeStart; i < rangeEnd; i++) {
112
- result[i] = REDACTION_CHAR_CODE;
113
- }
114
- }
115
- return result;
116
- }
117
- function consolidateRedactionRanges(ranges) {
118
- if (ranges.length === 0) {
119
- return [];
120
- }
121
- const sortedRanges = [...ranges].sort((a, b) => a.start - b.start);
122
- const consolidated = [];
123
- let current = { ...sortedRanges[0] };
124
- for (let i = 1; i < sortedRanges.length; i++) {
125
- const next = sortedRanges[i];
126
- if (next.start <= current.start + current.length) {
127
- const endCurrent = current.start + current.length;
128
- const endNext = next.start + next.length;
129
- current.length = Math.max(endCurrent, endNext) - current.start;
130
- } else {
131
- consolidated.push(current);
132
- current = { ...next };
133
- }
134
- }
135
- consolidated.push(current);
136
- return consolidated;
137
- }
138
- export {
139
- reconstructTlsTranscript
140
- };
@@ -1,358 +0,0 @@
1
- import { ServiceSignatureType } from "#src/proto/api.js";
2
- import { BodyType, KOutputPayload, TOutputPayload, VerificationBundle } from "#src/proto/tee-bundle.js";
3
- import { validateGcpAttestationAndExtractKey } from "#src/server/utils/gcp-attestation.js";
4
- import { validateNitroAttestationAndExtractKey } from "#src/server/utils/nitro-attestation.js";
5
- import { AttestorError } from "#src/utils/error.js";
6
- import { SIGNATURES } from "#src/utils/signatures/index.js";
7
- async function verifyTeeBundle(bundleBytes, logger) {
8
- const bundle = parseVerificationBundle(bundleBytes);
9
- validateBundleCompleteness(bundle);
10
- const { teekKeyResult, teetKeyResult } = await extractPublicKeys(bundle, logger);
11
- await verifyTeeSignatures(bundle, teekKeyResult, teetKeyResult, logger);
12
- if (!bundle.teekSigned || !bundle.teetSigned) {
13
- throw new AttestorError("ERROR_INVALID_CLAIM", "Missing TEE signed messages");
14
- }
15
- const kOutputPayload = parseKOutputPayload(bundle.teekSigned);
16
- const tOutputPayload = parseTOutputPayload(bundle.teetSigned);
17
- validateTimestamps(kOutputPayload, tOutputPayload, logger);
18
- const teeSessionId = validateSessionIds(kOutputPayload, tOutputPayload, logger);
19
- logger.info("TEE bundle verification successful");
20
- return {
21
- teekSigned: bundle.teekSigned,
22
- teetSigned: bundle.teetSigned,
23
- kOutputPayload,
24
- tOutputPayload,
25
- teekPcr0: teekKeyResult.pcr0,
26
- teetPcr0: teetKeyResult.pcr0,
27
- teeSessionId
28
- };
29
- }
30
- function parseVerificationBundle(bundleBytes) {
31
- try {
32
- return VerificationBundle.decode(bundleBytes);
33
- } catch (error) {
34
- throw new Error(`Failed to parse verification bundle: ${error.message}`);
35
- }
36
- }
37
- function validateBundleCompleteness(bundle) {
38
- if (!bundle.teekSigned) {
39
- throw new Error("SECURITY ERROR: missing TEE_K signed message - verification bundle incomplete");
40
- }
41
- if (!bundle.teetSigned) {
42
- throw new Error("SECURITY ERROR: missing TEE_T signed message - verification bundle incomplete");
43
- }
44
- const hasAttestations = bundle.teekSigned.attestationReport?.report && bundle.teekSigned.attestationReport.report.length > 0 || bundle.teetSigned.attestationReport?.report && bundle.teetSigned.attestationReport.report.length > 0;
45
- const hasPublicKeys = bundle.teekSigned.ethAddress && bundle.teekSigned.ethAddress.length > 0 || bundle.teetSigned.ethAddress && bundle.teetSigned.ethAddress.length > 0;
46
- if (!hasAttestations && !hasPublicKeys) {
47
- throw new Error("SECURITY ERROR: bundle must have either Nitro attestations (production) or embedded public keys (development)");
48
- }
49
- if (bundle.teekSigned.bodyType !== BodyType.BODY_TYPE_K_OUTPUT) {
50
- throw new Error("Invalid TEE_K signed message: wrong body type");
51
- }
52
- if (bundle.teetSigned.bodyType !== BodyType.BODY_TYPE_T_OUTPUT) {
53
- throw new Error("Invalid TEE_T signed message: wrong body type");
54
- }
55
- if (!bundle.teekSigned.body || bundle.teekSigned.body.length === 0) {
56
- throw new Error("Invalid TEE_K signed message: empty body");
57
- }
58
- if (!bundle.teetSigned.body || bundle.teetSigned.body.length === 0) {
59
- throw new Error("Invalid TEE_T signed message: empty body");
60
- }
61
- if (!bundle.teekSigned.signature || bundle.teekSigned.signature.length === 0) {
62
- throw new Error("Invalid TEE_K signed message: missing signature");
63
- }
64
- if (!bundle.teetSigned.signature || bundle.teetSigned.signature.length === 0) {
65
- throw new Error("Invalid TEE_T signed message: missing signature");
66
- }
67
- }
68
- async function extractPublicKeys(bundle, logger) {
69
- const hasEmbeddedAttestations = bundle.teekSigned.attestationReport?.report && bundle.teekSigned.attestationReport.report.length > 0 && (bundle.teetSigned.attestationReport?.report && bundle.teetSigned.attestationReport.report.length > 0);
70
- let teekKeyResult;
71
- let teetKeyResult;
72
- if (hasEmbeddedAttestations) {
73
- logger.info("Using production mode: extracting keys from attestations");
74
- if (!bundle.teekSigned?.attestationReport?.report) {
75
- throw new Error("TEE_K embedded attestation report missing");
76
- }
77
- if (!bundle.teetSigned?.attestationReport?.report) {
78
- throw new Error("TEE_T embedded attestation report missing");
79
- }
80
- const teekAttestationType = bundle.teekSigned.attestationReport.type || "nitro";
81
- const teetAttestationType = bundle.teetSigned.attestationReport.type || "nitro";
82
- logger.info(`TEE_K attestation type: ${teekAttestationType}`);
83
- logger.info(`TEE_T attestation type: ${teetAttestationType}`);
84
- const teekAttestationBytes = bundle.teekSigned.attestationReport.report;
85
- const teetAttestationBytes = bundle.teetSigned.attestationReport.report;
86
- if (teekAttestationType === "gcp") {
87
- const gcpResult = await validateGcpAttestationAndExtractKey(teekAttestationBytes, logger);
88
- if (!gcpResult.isValid) {
89
- throw new Error(`TEE_K GCP attestation validation failed: ${gcpResult.errors.join(", ")}`);
90
- }
91
- if (!gcpResult.ethAddress) {
92
- throw new Error("TEE_K GCP attestation validation failed: no address");
93
- }
94
- if (gcpResult.userDataType !== "tee_k") {
95
- throw new Error(`TEE_K GCP attestation validation failed: wrong TEE type, expected tee_k, got ${gcpResult.userDataType}`);
96
- }
97
- teekKeyResult = {
98
- teeType: gcpResult.userDataType,
99
- ethAddress: "0x" + Buffer.from(gcpResult.ethAddress).toString("hex"),
100
- pcr0: gcpResult.pcr0 || "gcp-no-digest"
101
- };
102
- } else {
103
- const nitroResult = await validateNitroAttestationAndExtractKey(teekAttestationBytes);
104
- if (!nitroResult.isValid) {
105
- throw new Error(`TEE_K Nitro attestation validation failed: ${nitroResult.errors.join(", ")}`);
106
- }
107
- if (!nitroResult.ethAddress) {
108
- throw new Error("TEE_K Nitro attestation validation failed: no address");
109
- }
110
- if (nitroResult.userDataType !== "tee_k") {
111
- throw new Error(`TEE_K Nitro attestation validation failed: wrong TEE type, expected tee_k, got ${nitroResult.userDataType}`);
112
- }
113
- teekKeyResult = {
114
- teeType: nitroResult.userDataType,
115
- ethAddress: nitroResult.ethAddress,
116
- pcr0: nitroResult.pcr0
117
- };
118
- }
119
- if (teetAttestationType === "gcp") {
120
- const gcpResult = await validateGcpAttestationAndExtractKey(teetAttestationBytes, logger);
121
- if (!gcpResult.isValid) {
122
- throw new Error(`TEE_T GCP attestation validation failed: ${gcpResult.errors.join(", ")}`);
123
- }
124
- if (!gcpResult.ethAddress) {
125
- throw new Error("TEE_T GCP attestation validation failed: no address");
126
- }
127
- if (gcpResult.userDataType !== "tee_t") {
128
- throw new Error(`TEE_T GCP attestation validation failed: wrong TEE type, expected tee_t, got ${gcpResult.userDataType}`);
129
- }
130
- teetKeyResult = {
131
- teeType: gcpResult.userDataType,
132
- ethAddress: "0x" + Buffer.from(gcpResult.ethAddress).toString("hex"),
133
- pcr0: gcpResult.pcr0 || "gcp-no-digest"
134
- };
135
- if (!gcpResult.envVars?.EXPECTED_TEEK_PCR0) {
136
- throw new Error("TEE_T GCP attestation missing required EXPECTED_TEEK_PCR0 environment variable");
137
- }
138
- const expectedPcr0 = gcpResult.envVars.EXPECTED_TEEK_PCR0;
139
- const actualPcr0 = teekKeyResult.pcr0;
140
- logger.info(`Cross-validating TEE_K PCR0: expected=${expectedPcr0}, actual=${actualPcr0}`);
141
- if (expectedPcr0 !== actualPcr0) {
142
- throw new Error(`TEE cross-validation failed: TEE_T expects TEE_K PCR0 "${expectedPcr0}" but got "${actualPcr0}"`);
143
- }
144
- logger.info("TEE cross-validation successful: TEE_K PCR0 matches TEE_T expectation");
145
- } else {
146
- const nitroResult = await validateNitroAttestationAndExtractKey(teetAttestationBytes);
147
- if (!nitroResult.isValid) {
148
- throw new Error(`TEE_T Nitro attestation validation failed: ${nitroResult.errors.join(", ")}`);
149
- }
150
- if (!nitroResult.ethAddress) {
151
- throw new Error("TEE_T Nitro attestation validation failed: no address");
152
- }
153
- if (nitroResult.userDataType !== "tee_t") {
154
- throw new Error(`TEE_T Nitro attestation validation failed: wrong TEE type, expected tee_t, got ${nitroResult.userDataType}`);
155
- }
156
- teetKeyResult = {
157
- teeType: nitroResult.userDataType,
158
- ethAddress: nitroResult.ethAddress,
159
- pcr0: nitroResult.pcr0
160
- };
161
- }
162
- logger.info("Attestations validated successfully");
163
- } else {
164
- const standaloneEnabled = process.env.TEE_STANDALONE === "true" || process.env.TEE_STANDALONE === "1";
165
- if (!standaloneEnabled) {
166
- throw new Error("Missing attestation reports and standalone mode is not enabled (set TEE_STANDALONE=true to enable)");
167
- }
168
- const hasEmbeddedKeys = bundle.teekSigned.ethAddress && bundle.teekSigned.ethAddress.length > 0 && (bundle.teetSigned.ethAddress && bundle.teetSigned.ethAddress.length > 0);
169
- if (!hasEmbeddedKeys) {
170
- throw new Error("Missing attestation and no embedded ETH addresses for standalone mode");
171
- }
172
- logger.warn("STANDALONE MODE ENABLED: Using embedded ETH addresses without attestation verification");
173
- const teekAddress = Buffer.from(bundle.teekSigned.ethAddress).toString("utf8");
174
- teekKeyResult = {
175
- teeType: "tee_k",
176
- ethAddress: teekAddress,
177
- pcr0: "standalone-mode"
178
- // No PCR0 in standalone mode
179
- };
180
- logger.info(`TEE_K standalone address: ${teekAddress}`);
181
- const teetAddress = Buffer.from(bundle.teetSigned.ethAddress).toString("utf8");
182
- teetKeyResult = {
183
- teeType: "tee_t",
184
- ethAddress: teetAddress,
185
- pcr0: "standalone-mode"
186
- // No PCR0 in standalone mode
187
- };
188
- logger.info(`TEE_T standalone address: ${teetAddress}`);
189
- logger.info("Standalone mode key extraction successful");
190
- }
191
- return {
192
- teekKeyResult,
193
- teetKeyResult
194
- };
195
- }
196
- async function verifyTeeSignatures(bundle, teekKeyResult, teetKeyResult, logger) {
197
- if (!bundle.teekSigned) {
198
- throw new Error("TEE_K signed message is missing");
199
- }
200
- const teekResult = await verifyTeeSignature(
201
- bundle.teekSigned,
202
- teekKeyResult,
203
- "TEE_K",
204
- logger
205
- );
206
- if (!teekResult.isValid) {
207
- throw new Error(`TEE_K signature verification failed: ${teekResult.errors.join(", ")}`);
208
- }
209
- if (!bundle.teetSigned) {
210
- throw new Error("TEE_T signed message is missing");
211
- }
212
- const teetResult = await verifyTeeSignature(
213
- bundle.teetSigned,
214
- teetKeyResult,
215
- "TEE_T",
216
- logger
217
- );
218
- if (!teetResult.isValid) {
219
- throw new Error(`TEE_T signature verification failed: ${teetResult.errors.join(", ")}`);
220
- }
221
- logger.info("TEE signatures verified successfully");
222
- }
223
- async function verifyTeeSignature(signedMessage, extractedKey, teeType, logger) {
224
- const errors = [];
225
- if (!signedMessage) {
226
- return {
227
- isValid: false,
228
- errors: ["Signed message is null or undefined"]
229
- };
230
- }
231
- try {
232
- let ethAddress;
233
- if (extractedKey.ethAddress) {
234
- ethAddress = extractedKey.ethAddress;
235
- logger.debug(`${teeType} using ETH address from attestation: ${ethAddress}`);
236
- } else {
237
- return {
238
- isValid: false,
239
- errors: ["eth address is null"]
240
- };
241
- }
242
- const { verify: verifySig } = SIGNATURES[ServiceSignatureType.SERVICE_SIGNATURE_TYPE_ETH];
243
- const isValid = await verifySig(
244
- signedMessage.body,
245
- signedMessage.signature,
246
- ethAddress
247
- );
248
- if (!isValid) {
249
- errors.push(`${teeType} signature verification failed for address ${ethAddress}`);
250
- }
251
- logger.debug(`${teeType} signature verification result: ${isValid} for address ${ethAddress}`);
252
- return {
253
- isValid: errors.length === 0,
254
- errors,
255
- address: extractedKey.ethAddress
256
- };
257
- } catch (error) {
258
- errors.push(`${teeType} signature verification error: ${error.message}`);
259
- return {
260
- isValid: false,
261
- errors
262
- };
263
- }
264
- }
265
- function parseKOutputPayload(signedMessage) {
266
- const payload = KOutputPayload.decode(signedMessage.body);
267
- if (!payload.redactedRequest) {
268
- throw new Error("Missing redacted request in TEE_K payload");
269
- }
270
- if (!payload.consolidatedResponseKeystream || payload.consolidatedResponseKeystream.length === 0) {
271
- throw new Error("Missing consolidated response keystream in TEE_K payload");
272
- }
273
- if (!payload.certificateInfo) {
274
- throw new Error("Missing certificate info in TEE_K payload");
275
- }
276
- return payload;
277
- }
278
- function parseTOutputPayload(signedMessage) {
279
- const payload = TOutputPayload.decode(signedMessage.body);
280
- if (!payload.consolidatedResponseCiphertext || payload.consolidatedResponseCiphertext.length === 0) {
281
- throw new Error("Missing consolidated response ciphertext in TEE_T payload");
282
- }
283
- return payload;
284
- }
285
- function validateTimestamps(kPayload, tPayload, logger) {
286
- const now = Date.now();
287
- const kTimestamp = kPayload.timestampMs;
288
- const tTimestamp = tPayload.timestampMs;
289
- const kTimestampS = Math.floor(kTimestamp / 1e3);
290
- const tTimestampS = Math.floor(tTimestamp / 1e3);
291
- const nowS = Math.floor(now / 1e3);
292
- logger.info("Validating TEE timestamps", {
293
- kTimestampMs: kTimestamp,
294
- tTimestampMs: tTimestamp,
295
- kTimestampS,
296
- tTimestampS,
297
- nowS
298
- });
299
- const maxAgeMs = 10 * 60 * 1e3;
300
- const oldestAllowed = now - maxAgeMs;
301
- if (kTimestamp < oldestAllowed) {
302
- throw new Error(`TEE_K timestamp ${kTimestamp} is too old. Must be within 10 minutes of current time ${now}`);
303
- }
304
- if (tTimestamp < oldestAllowed) {
305
- throw new Error(`TEE_T timestamp ${tTimestamp} is too old. Must be within 10 minutes of current time ${now}`);
306
- }
307
- const maxFutureMs = 60 * 1e3;
308
- const maxAllowed = now + maxFutureMs;
309
- if (kTimestamp > maxAllowed) {
310
- throw new Error(`TEE_K timestamp ${kTimestamp} is in the future. Current time is ${now}`);
311
- }
312
- if (tTimestamp > maxAllowed) {
313
- throw new Error(`TEE_T timestamp ${tTimestamp} is in the future. Current time is ${now}`);
314
- }
315
- const timestampDiffMs = Math.abs(kTimestamp - tTimestamp);
316
- const maxDiffMs = 60 * 1e3;
317
- if (timestampDiffMs > maxDiffMs) {
318
- throw new Error(`TEE timestamps differ by ${timestampDiffMs}ms, which exceeds maximum allowed difference of ${maxDiffMs}ms (1 minute)`);
319
- }
320
- logger.info("TEE timestamp validation successful", {
321
- timestampDiffMs,
322
- ageKMs: now - kTimestamp,
323
- ageTMs: now - tTimestamp
324
- });
325
- }
326
- function validateSessionIds(kPayload, tPayload, logger) {
327
- const kSessionId = kPayload.sessionId;
328
- const tSessionId = tPayload.sessionId;
329
- logger.info("Validating TEE session IDs", {
330
- kSessionId,
331
- tSessionId
332
- });
333
- if (!kSessionId || kSessionId.length === 0) {
334
- throw new AttestorError(
335
- "ERROR_INVALID_CLAIM",
336
- "Missing session_id in TEE_K payload - required for cross-TEE binding"
337
- );
338
- }
339
- if (!tSessionId || tSessionId.length === 0) {
340
- throw new AttestorError(
341
- "ERROR_INVALID_CLAIM",
342
- "Missing session_id in TEE_T payload - required for cross-TEE binding"
343
- );
344
- }
345
- if (kSessionId !== tSessionId) {
346
- throw new AttestorError(
347
- "ERROR_INVALID_CLAIM",
348
- `Session ID mismatch: TEE_K session_id "${kSessionId}" does not match TEE_T session_id "${tSessionId}".`
349
- );
350
- }
351
- logger.info("TEE session ID validation successful", {
352
- sessionId: kSessionId
353
- });
354
- return kSessionId;
355
- }
356
- export {
357
- verifyTeeBundle
358
- };
@@ -1,45 +0,0 @@
1
- import { Ajv } from "ajv";
2
- import { PROVIDER_SCHEMAS } from "#src/types/providers.gen.js";
3
- import { AttestorError } from "#src/utils/error.js";
4
- const PROVIDER_VALIDATOR_MAP = {};
5
- const AJV = new Ajv({
6
- allErrors: true,
7
- strict: true,
8
- strictRequired: false,
9
- formats: {
10
- binary(data) {
11
- return data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data);
12
- },
13
- url(data) {
14
- try {
15
- new URL(data);
16
- return true;
17
- } catch {
18
- return false;
19
- }
20
- }
21
- }
22
- });
23
- function assertValidateProviderParams(name, params) {
24
- let validate = PROVIDER_VALIDATOR_MAP[name];
25
- if (!validate) {
26
- const schema = PROVIDER_SCHEMAS[name]?.parameters;
27
- if (!schema) {
28
- throw new AttestorError(
29
- "ERROR_BAD_REQUEST",
30
- `Invalid provider name "${String(name)}"`
31
- );
32
- }
33
- validate = AJV.compile(schema);
34
- }
35
- if (!validate?.(params)) {
36
- throw new AttestorError(
37
- "ERROR_BAD_REQUEST",
38
- "Params validation failed",
39
- { errors: JSON.stringify(validate.errors) }
40
- );
41
- }
42
- }
43
- export {
44
- assertValidateProviderParams
45
- };