@reclaimprotocol/attestor-core 5.0.1-beta.2 → 5.0.1-beta.22

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 (145) hide show
  1. package/browser/resources/attestor-browser.min.mjs +4512 -0
  2. package/lib/avs/abis/avsDirectoryABI.js +338 -341
  3. package/lib/avs/abis/delegationABI.js +1 -4
  4. package/lib/avs/abis/registryABI.js +719 -722
  5. package/lib/avs/client/create-claim-on-avs.js +129 -157
  6. package/lib/avs/config.js +18 -24
  7. package/lib/avs/contracts/ReclaimServiceManager.js +1 -0
  8. package/lib/avs/contracts/common.js +1 -0
  9. package/lib/avs/contracts/factories/ReclaimServiceManager__factory.js +1139 -1156
  10. package/lib/avs/contracts/factories/index.js +4 -4
  11. package/lib/avs/contracts/index.js +2 -6
  12. package/lib/avs/types/index.js +1 -0
  13. package/lib/avs/utils/contracts.js +30 -50
  14. package/lib/avs/utils/register.js +75 -70
  15. package/lib/avs/utils/tasks.js +38 -45
  16. package/lib/client/create-claim.js +402 -431
  17. package/lib/client/tunnels/make-rpc-tcp-tunnel.js +46 -48
  18. package/lib/client/tunnels/make-rpc-tls-tunnel.js +125 -121
  19. package/lib/client/utils/attestor-pool.js +23 -22
  20. package/lib/client/utils/client-socket.js +86 -109
  21. package/lib/client/utils/message-handler.js +79 -89
  22. package/lib/config/index.js +40 -58
  23. package/lib/external-rpc/benchmark.js +61 -74
  24. package/lib/external-rpc/event-bus.js +12 -15
  25. package/lib/external-rpc/handle-incoming-msg.js +216 -225
  26. package/lib/external-rpc/jsc-polyfills/1.js +70 -68
  27. package/lib/external-rpc/jsc-polyfills/2.js +17 -12
  28. package/lib/external-rpc/jsc-polyfills/event.js +10 -15
  29. package/lib/external-rpc/jsc-polyfills/index.js +2 -2
  30. package/lib/external-rpc/jsc-polyfills/ws.js +77 -79
  31. package/lib/external-rpc/setup-browser.js +28 -28
  32. package/lib/external-rpc/setup-jsc.js +17 -17
  33. package/lib/external-rpc/types.js +1 -0
  34. package/lib/external-rpc/utils.js +89 -89
  35. package/lib/external-rpc/zk.js +55 -50
  36. package/lib/index.js +2 -6
  37. package/lib/mechain/abis/governanceABI.js +457 -460
  38. package/lib/mechain/abis/taskABI.js +502 -505
  39. package/lib/mechain/client/create-claim-on-mechain.js +24 -29
  40. package/lib/mechain/constants/index.js +3 -8
  41. package/lib/mechain/types/index.js +1 -0
  42. package/lib/proto/api.js +4200 -4087
  43. package/lib/proto/tee-bundle.js +1261 -1241
  44. package/lib/providers/http/index.js +616 -603
  45. package/lib/providers/http/patch-parse5-tree.js +27 -29
  46. package/lib/providers/http/utils.js +289 -248
  47. package/lib/providers/index.js +3 -6
  48. package/lib/server/create-server.js +89 -91
  49. package/lib/server/handlers/claimTeeBundle.js +231 -211
  50. package/lib/server/handlers/claimTunnel.js +66 -73
  51. package/lib/server/handlers/completeClaimOnChain.js +20 -25
  52. package/lib/server/handlers/createClaimOnChain.js +21 -27
  53. package/lib/server/handlers/createTaskOnMechain.js +40 -50
  54. package/lib/server/handlers/createTunnel.js +85 -90
  55. package/lib/server/handlers/disconnectTunnel.js +4 -7
  56. package/lib/server/handlers/fetchCertificateBytes.js +37 -53
  57. package/lib/server/handlers/index.js +21 -24
  58. package/lib/server/handlers/init.js +27 -28
  59. package/lib/server/handlers/toprf.js +13 -16
  60. package/lib/server/socket.js +97 -100
  61. package/lib/server/tunnels/make-tcp-tunnel.js +161 -186
  62. package/lib/server/utils/apm.js +32 -25
  63. package/lib/server/utils/assert-valid-claim-request.js +305 -334
  64. package/lib/server/utils/config-env.js +2 -2
  65. package/lib/server/utils/dns.js +12 -18
  66. package/lib/server/utils/gcp-attestation.js +233 -181
  67. package/lib/server/utils/generics.d.ts +1 -1
  68. package/lib/server/utils/generics.js +43 -37
  69. package/lib/server/utils/iso.js +253 -256
  70. package/lib/server/utils/keep-alive.js +36 -36
  71. package/lib/server/utils/nitro-attestation.js +295 -220
  72. package/lib/server/utils/oprf-raw.js +48 -55
  73. package/lib/server/utils/process-handshake.js +200 -218
  74. package/lib/server/utils/proxy-session.js +5 -5
  75. package/lib/server/utils/tee-oprf-mpc-verification.js +82 -78
  76. package/lib/server/utils/tee-oprf-verification.js +165 -142
  77. package/lib/server/utils/tee-transcript-reconstruction.js +176 -129
  78. package/lib/server/utils/tee-verification.js +397 -334
  79. package/lib/server/utils/validation.js +30 -37
  80. package/lib/types/bgp.js +1 -0
  81. package/lib/types/claims.js +1 -0
  82. package/lib/types/client.js +1 -0
  83. package/lib/types/general.js +1 -0
  84. package/lib/types/handlers.js +1 -0
  85. package/lib/types/providers.d.ts +3 -2
  86. package/lib/types/providers.gen.js +9 -15
  87. package/lib/types/providers.js +1 -0
  88. package/lib/types/rpc.js +1 -0
  89. package/lib/types/signatures.d.ts +1 -2
  90. package/lib/types/signatures.js +1 -0
  91. package/lib/types/tunnel.js +1 -0
  92. package/lib/types/zk.js +1 -0
  93. package/lib/utils/auth.js +54 -66
  94. package/lib/utils/b64-json.js +15 -15
  95. package/lib/utils/bgp-listener.js +107 -111
  96. package/lib/utils/claims.js +89 -80
  97. package/lib/utils/env.js +13 -17
  98. package/lib/utils/error.js +43 -47
  99. package/lib/utils/generics.js +284 -235
  100. package/lib/utils/http-parser.js +232 -187
  101. package/lib/utils/logger.js +80 -71
  102. package/lib/utils/prepare-packets.js +69 -67
  103. package/lib/utils/redactions.js +163 -121
  104. package/lib/utils/retries.js +22 -24
  105. package/lib/utils/signatures/eth.js +29 -28
  106. package/lib/utils/signatures/index.js +5 -10
  107. package/lib/utils/socket-base.js +84 -88
  108. package/lib/utils/tls.js +28 -28
  109. package/lib/utils/ws.js +19 -19
  110. package/lib/utils/zk.js +542 -582
  111. package/package.json +12 -5
  112. package/lib/external-rpc/global.d.js +0 -0
  113. package/lib/scripts/build-browser.d.ts +0 -1
  114. package/lib/scripts/build-jsc.d.ts +0 -1
  115. package/lib/scripts/build-lib.d.ts +0 -1
  116. package/lib/scripts/check-avs-registration.d.ts +0 -1
  117. package/lib/scripts/check-avs-registration.js +0 -28
  118. package/lib/scripts/fallbacks/crypto.d.ts +0 -1
  119. package/lib/scripts/fallbacks/crypto.js +0 -4
  120. package/lib/scripts/fallbacks/empty.d.ts +0 -3
  121. package/lib/scripts/fallbacks/empty.js +0 -4
  122. package/lib/scripts/fallbacks/re2.d.ts +0 -1
  123. package/lib/scripts/fallbacks/re2.js +0 -7
  124. package/lib/scripts/fallbacks/snarkjs.d.ts +0 -1
  125. package/lib/scripts/fallbacks/snarkjs.js +0 -10
  126. package/lib/scripts/fallbacks/stwo.d.ts +0 -6
  127. package/lib/scripts/fallbacks/stwo.js +0 -159
  128. package/lib/scripts/generate-provider-types.d.ts +0 -5
  129. package/lib/scripts/generate-provider-types.js +0 -101
  130. package/lib/scripts/generate-receipt.d.ts +0 -9
  131. package/lib/scripts/generate-receipt.js +0 -101
  132. package/lib/scripts/generate-toprf-keys.d.ts +0 -1
  133. package/lib/scripts/generate-toprf-keys.js +0 -24
  134. package/lib/scripts/jsc-cli-rpc.d.ts +0 -1
  135. package/lib/scripts/jsc-cli-rpc.js +0 -35
  136. package/lib/scripts/register-avs-operator.d.ts +0 -1
  137. package/lib/scripts/register-avs-operator.js +0 -3
  138. package/lib/scripts/start-server.d.ts +0 -1
  139. package/lib/scripts/start-server.js +0 -11
  140. package/lib/scripts/update-avs-metadata.d.ts +0 -1
  141. package/lib/scripts/update-avs-metadata.js +0 -20
  142. package/lib/scripts/utils.d.ts +0 -1
  143. package/lib/scripts/utils.js +0 -10
  144. package/lib/scripts/whitelist-operator.d.ts +0 -1
  145. package/lib/scripts/whitelist-operator.js +0 -16
@@ -1,233 +1,215 @@
1
- import {
2
- concatenateUint8Arrays,
3
- getSignatureDataTls12,
4
- getSignatureDataTls13,
5
- PACKET_TYPE,
6
- parseCertificates,
7
- parseClientHello,
8
- parseServerCertificateVerify,
9
- parseServerHello,
10
- processServerKeyShare,
11
- SUPPORTED_RECORD_TYPE_MAP,
12
- uint8ArrayToDataView,
13
- verifyCertificateChain,
14
- verifyCertificateSignature
15
- } from "@reclaimprotocol/tls";
1
+ import { concatenateUint8Arrays, getSignatureDataTls12, getSignatureDataTls13, PACKET_TYPE, parseCertificates, parseClientHello, parseServerCertificateVerify, parseServerHello, processServerKeyShare, SUPPORTED_RECORD_TYPE_MAP, uint8ArrayToDataView, verifyCertificateChain, verifyCertificateSignature } from '@reclaimprotocol/tls';
16
2
  import { TranscriptMessageSenderType } from "../../proto/api.js";
17
3
  import { decryptDirect } from "../../utils/index.js";
18
4
  const RECORD_LENGTH_BYTES = 3;
19
- async function processHandshake(receipt, logger) {
20
- const certificates = [];
21
- const handshakeRawMessages = [];
22
- let currentPacketIdx = 0;
23
- let cipherSuite = void 0;
24
- let tlsVersion = void 0;
25
- let serverRandom = void 0;
26
- let clientRandom = void 0;
27
- let serverFinishedIdx = -1;
28
- let clientFinishedIdx = -1;
29
- let certVerified = false;
30
- let certVerifyHandled = false;
31
- let hostname = void 0;
32
- let clientChangeCipherSpecMsgIdx = -1;
33
- let serverChangeCipherSpecMsgIdx = -1;
34
- let incompletePkt = void 0;
35
- while (serverFinishedIdx < 0 || clientFinishedIdx < 0) {
36
- const packetIdx = currentPacketIdx++;
37
- if (packetIdx >= receipt.length) {
38
- throw new Error(
39
- "Receipt over but server finish: " + serverFinishedIdx + ", client finish: " + clientFinishedIdx
40
- );
41
- }
42
- const { message, reveal, sender } = receipt[packetIdx];
43
- if (message[0] === PACKET_TYPE["CHANGE_CIPHER_SPEC"]) {
44
- if (sender === TranscriptMessageSenderType.TRANSCRIPT_MESSAGE_SENDER_TYPE_CLIENT) {
45
- clientChangeCipherSpecMsgIdx = packetIdx;
46
- logger.trace("found client change cipher spec message");
47
- } else {
48
- serverChangeCipherSpecMsgIdx = packetIdx;
49
- logger.trace("found server change cipher spec message");
50
- }
51
- continue;
52
- }
53
- let plaintext = getWithoutHeader(message);
54
- if (!plaintext) {
55
- throw new Error("incomplete TLS record encountered");
5
+ /**
6
+ * Verifies server cert chain and removes handshake messages from transcript
7
+ * @param receipt
8
+ * @param logger
9
+ */
10
+ export async function processHandshake(receipt, logger) {
11
+ const certificates = [];
12
+ const handshakeRawMessages = [];
13
+ let currentPacketIdx = 0;
14
+ let cipherSuite = undefined;
15
+ let tlsVersion = undefined;
16
+ let serverRandom = undefined;
17
+ let clientRandom = undefined;
18
+ let serverFinishedIdx = -1;
19
+ let clientFinishedIdx = -1;
20
+ let certVerified = false;
21
+ let certVerifyHandled = false;
22
+ let hostname = undefined;
23
+ let clientChangeCipherSpecMsgIdx = -1;
24
+ let serverChangeCipherSpecMsgIdx = -1;
25
+ let incompletePkt = undefined;
26
+ while (serverFinishedIdx < 0 || clientFinishedIdx < 0) {
27
+ const packetIdx = currentPacketIdx++;
28
+ if (packetIdx >= receipt.length) {
29
+ throw new Error('Receipt over but server finish: ' + serverFinishedIdx
30
+ + ', client finish: ' + clientFinishedIdx);
31
+ }
32
+ const { message, reveal, sender } = receipt[packetIdx];
33
+ // skip change cipher spec message
34
+ if (message[0] === PACKET_TYPE['CHANGE_CIPHER_SPEC']) {
35
+ if (sender === TranscriptMessageSenderType
36
+ .TRANSCRIPT_MESSAGE_SENDER_TYPE_CLIENT) {
37
+ clientChangeCipherSpecMsgIdx = packetIdx;
38
+ logger.trace('found client change cipher spec message');
39
+ }
40
+ else {
41
+ serverChangeCipherSpecMsgIdx = packetIdx;
42
+ logger.trace('found server change cipher spec message');
43
+ }
44
+ continue;
45
+ }
46
+ let plaintext = getWithoutHeader(message);
47
+ if (!plaintext) {
48
+ throw new Error('incomplete TLS record encountered');
49
+ }
50
+ if (
51
+ // decrypt if wrapped record or after change cipher spec message,
52
+ // after which records are encrypted
53
+ message[0] === PACKET_TYPE['WRAPPED_RECORD']
54
+ || (serverChangeCipherSpecMsgIdx > 0
55
+ && sender === TranscriptMessageSenderType
56
+ .TRANSCRIPT_MESSAGE_SENDER_TYPE_SERVER)
57
+ || (clientChangeCipherSpecMsgIdx > 0
58
+ && sender === TranscriptMessageSenderType
59
+ .TRANSCRIPT_MESSAGE_SENDER_TYPE_CLIENT)) { // encrypted
60
+ if (!tlsVersion || !cipherSuite) {
61
+ throw new Error('Could not find cipherSuite to use & got enc record');
62
+ }
63
+ if (!reveal?.directReveal?.key) {
64
+ throw new Error('no direct reveal for handshake packet: ' + packetIdx);
65
+ }
66
+ const recordHeader = message.slice(0, 5);
67
+ ({ plaintext } = await decryptDirect(reveal?.directReveal, cipherSuite, recordHeader, tlsVersion, plaintext));
68
+ if (tlsVersion === 'TLS1_3') {
69
+ plaintext = plaintext.slice(0, -1);
70
+ }
71
+ }
72
+ if (incompletePkt) {
73
+ const incSender = receipt[incompletePkt.packetIdx].sender;
74
+ if (incSender !== sender) {
75
+ throw new Error('Missing follow up to incomplete packet at idx: '
76
+ + incompletePkt.packetIdx);
77
+ }
78
+ plaintext = concatenateUint8Arrays([
79
+ incompletePkt.remainingBytes,
80
+ plaintext
81
+ ]);
82
+ incompletePkt = undefined;
83
+ }
84
+ const handshakeMessages = [];
85
+ // each handshake packet may contain multiple handshake messages
86
+ for (let offset = 0; offset < plaintext.length;) {
87
+ const type = plaintext[offset];
88
+ // +1 for the record type byte
89
+ const content = readWithLength(plaintext.slice(offset + 1), RECORD_LENGTH_BYTES);
90
+ if (!content) {
91
+ incompletePkt = {
92
+ remainingBytes: plaintext.slice(offset),
93
+ packetIdx,
94
+ };
95
+ break;
96
+ }
97
+ handshakeMessages.push({
98
+ type,
99
+ content,
100
+ contentWithHeader: plaintext.slice(offset, offset + 1 + RECORD_LENGTH_BYTES + content.length),
101
+ packetIdx,
102
+ });
103
+ offset += 1 + RECORD_LENGTH_BYTES + content.length;
104
+ }
105
+ for (const msg of handshakeMessages) {
106
+ await processHandshakeMessage(msg);
107
+ handshakeRawMessages.push(msg.contentWithHeader);
108
+ }
56
109
  }
57
- if (
58
- // decrypt if wrapped record or after change cipher spec message,
59
- // after which records are encrypted
60
- message[0] === PACKET_TYPE["WRAPPED_RECORD"] || serverChangeCipherSpecMsgIdx > 0 && sender === TranscriptMessageSenderType.TRANSCRIPT_MESSAGE_SENDER_TYPE_SERVER || clientChangeCipherSpecMsgIdx > 0 && sender === TranscriptMessageSenderType.TRANSCRIPT_MESSAGE_SENDER_TYPE_CLIENT
61
- ) {
62
- if (!tlsVersion || !cipherSuite) {
63
- throw new Error("Could not find cipherSuite to use & got enc record");
64
- }
65
- if (!reveal?.directReveal?.key) {
66
- throw new Error(
67
- "no direct reveal for handshake packet: " + packetIdx
68
- );
69
- }
70
- const recordHeader = message.slice(0, 5);
71
- ({ plaintext } = await decryptDirect(
72
- reveal?.directReveal,
73
- cipherSuite,
74
- recordHeader,
75
- tlsVersion,
76
- plaintext
77
- ));
78
- if (tlsVersion === "TLS1_3") {
79
- plaintext = plaintext.slice(0, -1);
80
- }
110
+ if (!certVerified) {
111
+ throw new Error('No provider certificates received');
81
112
  }
82
- if (incompletePkt) {
83
- const incSender = receipt[incompletePkt.packetIdx].sender;
84
- if (incSender !== sender) {
85
- throw new Error(
86
- "Missing follow up to incomplete packet at idx: " + incompletePkt.packetIdx
87
- );
88
- }
89
- plaintext = concatenateUint8Arrays([
90
- incompletePkt.remainingBytes,
91
- plaintext
92
- ]);
93
- incompletePkt = void 0;
113
+ if (tlsVersion === 'TLS1_3' && serverFinishedIdx < 0) {
114
+ throw new Error('server finished message not found');
94
115
  }
95
- const handshakeMessages = [];
96
- for (let offset = 0; offset < plaintext.length; ) {
97
- const type = plaintext[offset];
98
- const content = readWithLength(plaintext.slice(offset + 1), RECORD_LENGTH_BYTES);
99
- if (!content) {
100
- incompletePkt = {
101
- remainingBytes: plaintext.slice(offset),
102
- packetIdx
103
- };
104
- break;
105
- }
106
- handshakeMessages.push({
107
- type,
108
- content,
109
- contentWithHeader: plaintext.slice(
110
- offset,
111
- offset + 1 + RECORD_LENGTH_BYTES + content.length
112
- ),
113
- packetIdx
114
- });
115
- offset += 1 + RECORD_LENGTH_BYTES + content.length;
116
+ if (tlsVersion === 'TLS1_3' && !certVerifyHandled) {
117
+ throw new Error('TLS1.3 cert verify packet not received');
116
118
  }
117
- for (const msg of handshakeMessages) {
118
- await processHandshakeMessage(msg);
119
- handshakeRawMessages.push(msg.contentWithHeader);
119
+ if (tlsVersion === 'TLS1_2' && (serverChangeCipherSpecMsgIdx < 0 || clientChangeCipherSpecMsgIdx < 0)) {
120
+ throw new Error('change cipher spec message not found');
120
121
  }
121
- }
122
- if (!certVerified) {
123
- throw new Error("No provider certificates received");
124
- }
125
- if (tlsVersion === "TLS1_3" && serverFinishedIdx < 0) {
126
- throw new Error("server finished message not found");
127
- }
128
- if (tlsVersion === "TLS1_3" && !certVerifyHandled) {
129
- throw new Error("TLS1.3 cert verify packet not received");
130
- }
131
- if (tlsVersion === "TLS1_2" && (serverChangeCipherSpecMsgIdx < 0 || clientChangeCipherSpecMsgIdx < 0)) {
132
- throw new Error("change cipher spec message not found");
133
- }
134
- const nextMsgIndex = Math.max(serverFinishedIdx, clientFinishedIdx) + 1;
135
- return {
136
- tlsVersion,
137
- cipherSuite,
138
- hostname,
139
- nextMsgIndex
140
- };
141
- async function processHandshakeMessage({ type, content, contentWithHeader, packetIdx }) {
142
- switch (type) {
143
- case SUPPORTED_RECORD_TYPE_MAP.CLIENT_HELLO:
144
- const clientHello = parseClientHello(contentWithHeader);
145
- clientRandom = clientHello.serverRandom;
146
- const { SERVER_NAME: sni } = clientHello.extensions;
147
- hostname = sni?.serverName;
148
- if (!hostname) {
149
- throw new Error("client hello has no SNI");
150
- }
151
- break;
152
- case SUPPORTED_RECORD_TYPE_MAP.SERVER_HELLO:
153
- const serverHello = await parseServerHello(content);
154
- cipherSuite = serverHello.cipherSuite;
155
- tlsVersion = serverHello.serverTlsVersion;
156
- serverRandom = serverHello.serverRandom;
157
- logger.info(
158
- { serverTLSVersion: tlsVersion, cipherSuite },
159
- "extracted server hello params"
160
- );
161
- break;
162
- case SUPPORTED_RECORD_TYPE_MAP.CERTIFICATE:
163
- const parseResult = parseCertificates(content, { version: tlsVersion });
164
- certificates.push(...parseResult.certificates);
165
- await verifyCertificateChain(certificates, hostname, logger);
166
- logger.info({ hostname }, "verified provider certificate chain");
167
- certVerified = true;
168
- break;
169
- case SUPPORTED_RECORD_TYPE_MAP.CERTIFICATE_VERIFY:
170
- const signature = parseServerCertificateVerify(content);
171
- if (!certificates?.length) {
172
- throw new Error("No provider certificates received");
122
+ const nextMsgIndex = Math.max(serverFinishedIdx, clientFinishedIdx) + 1;
123
+ return {
124
+ tlsVersion: tlsVersion,
125
+ cipherSuite: cipherSuite,
126
+ hostname: hostname,
127
+ nextMsgIndex,
128
+ };
129
+ async function processHandshakeMessage({ type, content, contentWithHeader, packetIdx }) {
130
+ switch (type) {
131
+ case SUPPORTED_RECORD_TYPE_MAP.CLIENT_HELLO:
132
+ const clientHello = parseClientHello(contentWithHeader);
133
+ clientRandom = clientHello.serverRandom;
134
+ const { SERVER_NAME: sni } = clientHello.extensions;
135
+ hostname = sni?.serverName;
136
+ if (!hostname) {
137
+ throw new Error('client hello has no SNI');
138
+ }
139
+ break;
140
+ case SUPPORTED_RECORD_TYPE_MAP.SERVER_HELLO:
141
+ const serverHello = await parseServerHello(content);
142
+ cipherSuite = serverHello.cipherSuite;
143
+ tlsVersion = serverHello.serverTlsVersion;
144
+ serverRandom = serverHello.serverRandom;
145
+ logger.info({ serverTLSVersion: tlsVersion, cipherSuite }, 'extracted server hello params');
146
+ break;
147
+ case SUPPORTED_RECORD_TYPE_MAP.CERTIFICATE:
148
+ const parseResult = parseCertificates(content, { version: tlsVersion });
149
+ certificates.push(...parseResult.certificates);
150
+ await verifyCertificateChain(certificates, hostname, logger);
151
+ logger.info({ hostname }, 'verified provider certificate chain');
152
+ certVerified = true;
153
+ break;
154
+ case SUPPORTED_RECORD_TYPE_MAP.CERTIFICATE_VERIFY:
155
+ const signature = parseServerCertificateVerify(content);
156
+ if (!certificates?.length) {
157
+ throw new Error('No provider certificates received');
158
+ }
159
+ const signatureData = await getSignatureDataTls13(handshakeRawMessages, cipherSuite);
160
+ await verifyCertificateSignature({
161
+ ...signature,
162
+ publicKey: certificates[0].getPublicKey(),
163
+ signatureData,
164
+ });
165
+ certVerifyHandled = true;
166
+ break;
167
+ case SUPPORTED_RECORD_TYPE_MAP.SERVER_KEY_SHARE:
168
+ if (!certificates?.length) {
169
+ throw new Error('No provider certificates received');
170
+ }
171
+ const keyShare = await processServerKeyShare(content);
172
+ const signatureData12 = await getSignatureDataTls12({
173
+ clientRandom: clientRandom,
174
+ serverRandom: serverRandom,
175
+ curveType: keyShare.publicKeyType,
176
+ publicKey: keyShare.publicKey,
177
+ });
178
+ // verify signature
179
+ await verifyCertificateSignature({
180
+ signature: keyShare.signatureBytes,
181
+ algorithm: keyShare.signatureAlgorithm,
182
+ publicKey: certificates[0].getPublicKey(),
183
+ signatureData: signatureData12,
184
+ publicKeyType: keyShare.publicKeyType
185
+ });
186
+ await verifyCertificateChain(certificates, hostname, logger);
187
+ logger.info({ hostname }, 'verified provider certificate chain');
188
+ certVerified = true;
189
+ break;
190
+ case SUPPORTED_RECORD_TYPE_MAP.FINISHED:
191
+ const packet = receipt[packetIdx];
192
+ if (packet.sender === TranscriptMessageSenderType.TRANSCRIPT_MESSAGE_SENDER_TYPE_CLIENT) {
193
+ clientFinishedIdx = packetIdx;
194
+ }
195
+ else {
196
+ serverFinishedIdx = packetIdx;
197
+ }
198
+ break;
173
199
  }
174
- const signatureData = await getSignatureDataTls13(
175
- handshakeRawMessages,
176
- cipherSuite
177
- );
178
- await verifyCertificateSignature({
179
- ...signature,
180
- publicKey: certificates[0].getPublicKey(),
181
- signatureData
182
- });
183
- certVerifyHandled = true;
184
- break;
185
- case SUPPORTED_RECORD_TYPE_MAP.SERVER_KEY_SHARE:
186
- if (!certificates?.length) {
187
- throw new Error("No provider certificates received");
188
- }
189
- const keyShare = await processServerKeyShare(content);
190
- const signatureData12 = await getSignatureDataTls12(
191
- {
192
- clientRandom,
193
- serverRandom,
194
- curveType: keyShare.publicKeyType,
195
- publicKey: keyShare.publicKey
196
- }
197
- );
198
- await verifyCertificateSignature({
199
- signature: keyShare.signatureBytes,
200
- algorithm: keyShare.signatureAlgorithm,
201
- publicKey: certificates[0].getPublicKey(),
202
- signatureData: signatureData12,
203
- publicKeyType: keyShare.publicKeyType
204
- });
205
- await verifyCertificateChain(certificates, hostname, logger);
206
- logger.info({ hostname }, "verified provider certificate chain");
207
- certVerified = true;
208
- break;
209
- case SUPPORTED_RECORD_TYPE_MAP.FINISHED:
210
- const packet = receipt[packetIdx];
211
- if (packet.sender === TranscriptMessageSenderType.TRANSCRIPT_MESSAGE_SENDER_TYPE_CLIENT) {
212
- clientFinishedIdx = packetIdx;
213
- } else {
214
- serverFinishedIdx = packetIdx;
215
- }
216
- break;
217
200
  }
218
- }
219
201
  }
220
202
  function getWithoutHeader(message) {
221
- return readWithLength(message.slice(3), 2);
203
+ // strip the record header (xx 03 03 xx xx)
204
+ return readWithLength(message.slice(3), 2);
222
205
  }
223
206
  function readWithLength(data, lengthBytes = 2) {
224
- const dataView = uint8ArrayToDataView(data);
225
- const length = lengthBytes === 1 ? dataView.getUint8(0) : dataView.getUint16(lengthBytes === 3 ? 1 : 0);
226
- if (data.length < lengthBytes + length) {
227
- return void 0;
228
- }
229
- return data.slice(lengthBytes, lengthBytes + length);
207
+ const dataView = uint8ArrayToDataView(data);
208
+ const length = lengthBytes === 1
209
+ ? dataView.getUint8(0)
210
+ : dataView.getUint16(lengthBytes === 3 ? 1 : 0);
211
+ if (data.length < lengthBytes + length) {
212
+ return undefined;
213
+ }
214
+ return data.slice(lengthBytes, lengthBytes + length);
230
215
  }
231
- export {
232
- processHandshake
233
- };
@@ -1,6 +1,6 @@
1
- function isValidProxySessionId(sessionId) {
2
- return typeof sessionId === "string" && sessionId.length >= 8 && sessionId.length < 15 && /^[a-z0-9]+$/.test(sessionId);
1
+ export function isValidProxySessionId(sessionId) {
2
+ return typeof sessionId === 'string'
3
+ && sessionId.length >= 8
4
+ && sessionId.length < 15
5
+ && /^[a-z0-9]+$/.test(sessionId);
3
6
  }
4
- export {
5
- isValidProxySessionId
6
- };
@@ -1,86 +1,90 @@
1
+ /**
2
+ * TEE OPRF MPC Verification
3
+ * Verifies OPRF MPC outputs from TEE_K and TEE_T match
4
+ *
5
+ * Unlike ZK OPRF which requires proof verification, OPRF MPC outputs
6
+ * are already trusted because they are included in TEE-signed payloads.
7
+ * This module verifies that both TEEs computed identical outputs.
8
+ */
1
9
  import { AttestorError } from "../../utils/error.js";
2
- function verifyOprfMpcOutputs(kPayload, tPayload, logger) {
3
- const kOutputs = kPayload.oprfOutputs || [];
4
- const tOutputs = tPayload.oprfOutputs || [];
5
- if (kOutputs.length === 0 && tOutputs.length === 0) {
6
- logger.debug("No OPRF MPC outputs to verify");
7
- return [];
8
- }
9
- if (kOutputs.length !== tOutputs.length) {
10
- throw new AttestorError(
11
- "ERROR_INVALID_CLAIM",
12
- `OPRF MPC count mismatch: TEE_K has ${kOutputs.length}, TEE_T has ${tOutputs.length}`
13
- );
14
- }
15
- logger.info(`Verifying ${kOutputs.length} OPRF MPC outputs`);
16
- const results = [];
17
- for (const [i, kOut] of kOutputs.entries()) {
18
- const tOut = tOutputs[i];
19
- if (kOut.tlsStart < 0 || tOut.tlsStart < 0) {
20
- throw new AttestorError(
21
- "ERROR_INVALID_CLAIM",
22
- `OPRF MPC invalid position at index ${i}: negative start position`
23
- );
10
+ /**
11
+ * Verifies OPRF MPC outputs from TEE_K and TEE_T match
12
+ * Returns verified outputs for transcript replacement (same format as ZK OPRF)
13
+ */
14
+ export function verifyOprfMpcOutputs(kPayload, tPayload, logger) {
15
+ const kOutputs = kPayload.oprfOutputs || [];
16
+ const tOutputs = tPayload.oprfOutputs || [];
17
+ // Empty is valid - no OPRF MPC was requested
18
+ if (kOutputs.length === 0 && tOutputs.length === 0) {
19
+ logger.debug('No OPRF MPC outputs to verify');
20
+ return [];
24
21
  }
25
- if (kOut.tlsLength <= 0 || kOut.tlsLength > 64 || tOut.tlsLength <= 0 || tOut.tlsLength > 64) {
26
- throw new AttestorError(
27
- "ERROR_INVALID_CLAIM",
28
- `OPRF MPC invalid length at index ${i}: must be 1-64 bytes (TEE_K: ${kOut.tlsLength}, TEE_T: ${tOut.tlsLength})`
29
- );
22
+ // Count must match between TEE_K and TEE_T
23
+ if (kOutputs.length !== tOutputs.length) {
24
+ throw new AttestorError('ERROR_INVALID_CLAIM', `OPRF MPC count mismatch: TEE_K has ${kOutputs.length}, TEE_T has ${tOutputs.length}`);
30
25
  }
31
- if (kOut.hashOutput.length !== 32 || tOut.hashOutput.length !== 32) {
32
- throw new AttestorError(
33
- "ERROR_INVALID_CLAIM",
34
- `OPRF MPC invalid hash size at index ${i}: expected 32 bytes (TEE_K: ${kOut.hashOutput.length}, TEE_T: ${tOut.hashOutput.length})`
35
- );
26
+ logger.info(`Verifying ${kOutputs.length} OPRF MPC outputs`);
27
+ const results = [];
28
+ for (const [i, kOut] of kOutputs.entries()) {
29
+ const tOut = tOutputs[i];
30
+ // Validate position bounds (must be non-negative)
31
+ if (kOut.tlsStart < 0 || tOut.tlsStart < 0) {
32
+ throw new AttestorError('ERROR_INVALID_CLAIM', `OPRF MPC invalid position at index ${i}: negative start position`);
33
+ }
34
+ // Validate length constraints (must be positive and <= 64 bytes, matching TEE validation)
35
+ if (kOut.tlsLength <= 0 || kOut.tlsLength > 64 || tOut.tlsLength <= 0 || tOut.tlsLength > 64) {
36
+ throw new AttestorError('ERROR_INVALID_CLAIM', `OPRF MPC invalid length at index ${i}: must be 1-64 bytes ` +
37
+ `(TEE_K: ${kOut.tlsLength}, TEE_T: ${tOut.tlsLength})`);
38
+ }
39
+ // Validate hash output size (must be exactly 32 bytes for SHA256)
40
+ if (kOut.hashOutput.length !== 32 || tOut.hashOutput.length !== 32) {
41
+ throw new AttestorError('ERROR_INVALID_CLAIM', `OPRF MPC invalid hash size at index ${i}: expected 32 bytes ` +
42
+ `(TEE_K: ${kOut.hashOutput.length}, TEE_T: ${tOut.hashOutput.length})`);
43
+ }
44
+ // Verify positions match
45
+ if (kOut.tlsStart !== tOut.tlsStart || kOut.tlsLength !== tOut.tlsLength) {
46
+ throw new AttestorError('ERROR_INVALID_CLAIM', `OPRF MPC position mismatch at index ${i}: ` +
47
+ `TEE_K [${kOut.tlsStart}:${kOut.tlsStart + kOut.tlsLength}] vs ` +
48
+ `TEE_T [${tOut.tlsStart}:${tOut.tlsStart + tOut.tlsLength}]`);
49
+ }
50
+ // Verify hash outputs match (hash = SHA256(CMAC), so this implies CMAC matched too)
51
+ if (!buffersEqual(kOut.hashOutput, tOut.hashOutput)) {
52
+ throw new AttestorError('ERROR_INVALID_CLAIM', `OPRF MPC hash mismatch at index ${i}: outputs differ between TEE_K and TEE_T`);
53
+ }
54
+ // Log the actual output data for debugging
55
+ const hashOutputHex = Buffer.from(kOut.hashOutput).toString('hex');
56
+ const hashOutputBase64 = Buffer.from(kOut.hashOutput).toString('base64');
57
+ logger.info({
58
+ index: i,
59
+ position: kOut.tlsStart,
60
+ length: kOut.tlsLength,
61
+ hashOutputLen: kOut.hashOutput.length,
62
+ hashOutputHex: hashOutputHex.substring(0, 32) + '...',
63
+ hashOutputBase64Preview: hashOutputBase64.substring(0, 20) + '...'
64
+ }, 'OPRF MPC output verified');
65
+ // Return in same format as ZK OPRF for unified replacement
66
+ // MPC OPRF uses full hash length (not truncated like TOPRF)
67
+ results.push({
68
+ position: kOut.tlsStart,
69
+ length: kOut.tlsLength,
70
+ output: new Uint8Array(kOut.hashOutput), // Use SHA256(CMAC) as the replacement value
71
+ isMPC: true
72
+ });
36
73
  }
37
- if (kOut.tlsStart !== tOut.tlsStart || kOut.tlsLength !== tOut.tlsLength) {
38
- throw new AttestorError(
39
- "ERROR_INVALID_CLAIM",
40
- `OPRF MPC position mismatch at index ${i}: TEE_K [${kOut.tlsStart}:${kOut.tlsStart + kOut.tlsLength}] vs TEE_T [${tOut.tlsStart}:${tOut.tlsStart + tOut.tlsLength}]`
41
- );
42
- }
43
- if (!buffersEqual(kOut.hashOutput, tOut.hashOutput)) {
44
- throw new AttestorError(
45
- "ERROR_INVALID_CLAIM",
46
- `OPRF MPC hash mismatch at index ${i}: outputs differ between TEE_K and TEE_T`
47
- );
48
- }
49
- const hashOutputHex = Buffer.from(kOut.hashOutput).toString("hex");
50
- const hashOutputBase64 = Buffer.from(kOut.hashOutput).toString("base64");
51
- logger.info(
52
- {
53
- index: i,
54
- position: kOut.tlsStart,
55
- length: kOut.tlsLength,
56
- hashOutputLen: kOut.hashOutput.length,
57
- hashOutputHex: hashOutputHex.substring(0, 32) + "...",
58
- hashOutputBase64Preview: hashOutputBase64.substring(0, 20) + "..."
59
- },
60
- "OPRF MPC output verified"
61
- );
62
- results.push({
63
- position: kOut.tlsStart,
64
- length: kOut.tlsLength,
65
- output: new Uint8Array(kOut.hashOutput),
66
- // Use SHA256(CMAC) as the replacement value
67
- isMPC: true
68
- });
69
- }
70
- logger.info(`Successfully verified ${results.length} OPRF MPC outputs`);
71
- return results;
74
+ logger.info(`Successfully verified ${results.length} OPRF MPC outputs`);
75
+ return results;
72
76
  }
77
+ /**
78
+ * Compare two Uint8Array buffers for equality
79
+ */
73
80
  function buffersEqual(a, b) {
74
- if (a.length !== b.length) {
75
- return false;
76
- }
77
- for (const [i, element] of a.entries()) {
78
- if (element !== b[i]) {
79
- return false;
81
+ if (a.length !== b.length) {
82
+ return false;
83
+ }
84
+ for (const [i, element] of a.entries()) {
85
+ if (element !== b[i]) {
86
+ return false;
87
+ }
80
88
  }
81
- }
82
- return true;
89
+ return true;
83
90
  }
84
- export {
85
- verifyOprfMpcOutputs
86
- };