@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
package/lib/utils/zk.js CHANGED
@@ -1,625 +1,585 @@
1
- import { concatenateUint8Arrays, crypto, generateIV } from "@reclaimprotocol/tls";
2
- import {
3
- ceilToBlockSizeMultiple,
4
- CONFIG as ZK_CONFIG,
5
- generateProof,
6
- getBlockSizeBytes,
7
- makeLocalFileFetch,
8
- makeRemoteFileFetch,
9
- verifyProof
10
- } from "@reclaimprotocol/zk-symmetric-crypto";
11
- import { makeGnarkOPRFOperator, makeGnarkZkOperator } from "@reclaimprotocol/zk-symmetric-crypto/gnark";
12
- import { makeSnarkJsZKOperator } from "@reclaimprotocol/zk-symmetric-crypto/snarkjs";
13
- import { makeStwoZkOperator } from "@reclaimprotocol/zk-symmetric-crypto/stwo";
14
- import PQueue from "p-queue";
15
- import {
16
- DEFAULT_REMOTE_FILE_FETCH_BASE_URL,
17
- DEFAULT_ZK_CONCURRENCY,
18
- TOPRF_DOMAIN_SEPARATOR
19
- } from "../config/index.js";
1
+ import { concatenateUint8Arrays, crypto, generateIV } from '@reclaimprotocol/tls';
2
+ import { ceilToBlockSizeMultiple, CONFIG as ZK_CONFIG, generateProof, getBlockSizeBytes, makeLocalFileFetch, makeRemoteFileFetch, verifyProof } from '@reclaimprotocol/zk-symmetric-crypto';
3
+ import { makeGnarkOPRFOperator, makeGnarkZkOperator, } from '@reclaimprotocol/zk-symmetric-crypto/gnark';
4
+ import { makeSnarkJsZKOperator } from '@reclaimprotocol/zk-symmetric-crypto/snarkjs';
5
+ import { makeStwoZkOperator } from '@reclaimprotocol/zk-symmetric-crypto/stwo';
6
+ import PQueue from 'p-queue';
7
+ import { DEFAULT_REMOTE_FILE_FETCH_BASE_URL, DEFAULT_ZK_CONCURRENCY, TOPRF_DOMAIN_SEPARATOR } from "../config/index.js";
20
8
  import { ZKProofEngine } from "../proto/api.js";
21
- import { detectEnvironment, getEnvVariable } from "../utils/env.js";
22
- import { AttestorError } from "../utils/error.js";
23
- import {
24
- getPureCiphertext,
25
- getRecordIV,
26
- getZkAlgorithmForCipherSuite,
27
- isTls13Suite,
28
- strToUint8Array
29
- } from "../utils/generics.js";
30
- import { logger as LOGGER } from "../utils/logger.js";
31
- import { binaryHashToStr, isFullyRedacted, isRedactionCongruent, REDACTION_CHAR_CODE } from "../utils/redactions.js";
32
- const ZK_CONCURRENCY = +(getEnvVariable("ZK_CONCURRENCY") || DEFAULT_ZK_CONCURRENCY);
33
- async function makeZkProofGenerator({
34
- zkOperators,
35
- oprfOperators,
36
- logger = LOGGER,
37
- zkProofConcurrency = ZK_CONCURRENCY,
38
- cipherSuite,
39
- zkEngine = "snarkjs"
40
- }) {
41
- const zkQueue = new PQueue({ concurrency: zkProofConcurrency, autoStart: true });
42
- const packetsToProve = [];
43
- logger = logger.child({ module: "zk", zkEngine });
44
- let zkProofsToGen = 0;
45
- return {
46
- /**
47
- * Adds the given packet to the list of packets to
48
- * generate ZK proofs for.
49
- *
50
- * Call `generateProofs()` to finally generate the proofs
51
- */
52
- async addPacketToProve(packet, {
53
- redactedPlaintext,
54
- toprfs = [],
55
- overshotToprfFromPrevBlock
56
- }, onGeneratedProofs, getNextPacket) {
57
- if (packet.type === "plaintext") {
58
- throw new Error("Cannot generate proof for plaintext");
59
- }
60
- const alg = getZkAlgorithmForCipherSuite(cipherSuite);
61
- const chunkSizeBytes = getChunkSizeBytes(alg);
62
- const key = await crypto.exportKey(packet.encKey);
63
- const iv = packet.iv;
64
- const ciphertext = getPureCiphertext(packet.ciphertext, cipherSuite);
65
- if (overshotToprfFromPrevBlock) {
66
- redactedPlaintext.set(
67
- new Uint8Array(overshotToprfFromPrevBlock.length).fill(REDACTION_CHAR_CODE)
68
- );
69
- }
70
- const trueCiphertextLength = isTls13Suite(cipherSuite) ? ciphertext.length - 1 : ciphertext.length;
71
- const packetToProve = {
72
- onGeneratedProofs,
73
- algorithm: alg,
74
- proofsToGenerate: [],
75
- toprfsToGenerate: [],
76
- iv: packet.fixedIv
77
- };
78
- for (const toprf of toprfs) {
79
- const toprfDistFromEnd = trueCiphertextLength - (toprf.dataLocation.fromIndex + toprf.dataLocation.length);
80
- if (toprfDistFromEnd < 0) {
81
- const nextPacket = getNextPacket();
82
- if (nextPacket?.type !== "ciphertext") {
83
- throw new AttestorError(
84
- "ERROR_INTERNAL",
85
- "TOPRF data overshoots ciphertext length, but no next ciphertext packet found"
86
- );
87
- }
88
- if (nextPacket.encKey !== packet.encKey) {
89
- throw new AttestorError(
90
- "ERROR_INTERNAL",
91
- "TOPRF data overshoots ciphertext length, but next packet has different encryption key"
92
- );
93
- }
94
- const nextCiphertext = nextPacket.ciphertext.slice(0, Math.abs(toprfDistFromEnd));
95
- const iv2 = nextPacket.iv;
96
- toprf.overshoot = {
97
- ciphertext: nextCiphertext,
98
- iv: iv2,
99
- recordNumber: nextPacket.recordNumber
100
- };
101
- }
102
- const fromIndex = getIdealOffsetForToprfBlock(alg, toprf);
103
- const toIndex = Math.min(fromIndex + chunkSizeBytes, ciphertext.length);
104
- const slice = { fromIndex, toIndex };
105
- packetToProve.toprfsToGenerate.push(getTOPRFProofGenerationParamsForSlice({
106
- key,
107
- iv,
108
- ciphertext,
109
- slice,
110
- toprf: {
111
- ...toprf,
112
- dataLocation: {
113
- ...toprf.dataLocation,
114
- fromIndex: toprf.dataLocation.fromIndex - fromIndex
9
+ import { detectEnvironment, getEnvVariable } from "./env.js";
10
+ import { AttestorError } from "./error.js";
11
+ import { getPureCiphertext, getRecordIV, getZkAlgorithmForCipherSuite, isTls13Suite, strToUint8Array } from "./generics.js";
12
+ import { logger as LOGGER } from "./logger.js";
13
+ import { binaryHashToStr, isFullyRedacted, isRedactionCongruent, REDACTION_CHAR_CODE } from "./redactions.js";
14
+ const ZK_CONCURRENCY = +(getEnvVariable('ZK_CONCURRENCY') || DEFAULT_ZK_CONCURRENCY);
15
+ export async function makeZkProofGenerator({ zkOperators, oprfOperators, logger = LOGGER, zkProofConcurrency = ZK_CONCURRENCY, cipherSuite, zkEngine = 'snarkjs' }) {
16
+ const zkQueue = new PQueue({ concurrency: zkProofConcurrency, autoStart: true });
17
+ const packetsToProve = [];
18
+ logger = logger.child({ module: 'zk', zkEngine });
19
+ let zkProofsToGen = 0;
20
+ return {
21
+ /**
22
+ * Adds the given packet to the list of packets to
23
+ * generate ZK proofs for.
24
+ *
25
+ * Call `generateProofs()` to finally generate the proofs
26
+ */
27
+ async addPacketToProve(packet, { redactedPlaintext, toprfs = [], overshotToprfFromPrevBlock }, onGeneratedProofs, getNextPacket) {
28
+ if (packet.type === 'plaintext') {
29
+ throw new Error('Cannot generate proof for plaintext');
115
30
  }
116
- }
117
- }));
118
- zkProofsToGen += 1;
119
- const pktToIndex = Math.min(
120
- trueCiphertextLength,
121
- toprf.dataLocation.fromIndex + toprf.dataLocation.length
122
- );
123
- const pktFromIndex = toprf.dataLocation.fromIndex;
124
- for (let i = pktFromIndex; i < pktToIndex; i++) {
125
- redactedPlaintext[i] = REDACTION_CHAR_CODE;
126
- }
127
- }
128
- for (let i = 0; i < ciphertext.length; i += chunkSizeBytes) {
129
- const slice = {
130
- fromIndex: i,
131
- toIndex: Math.min(i + chunkSizeBytes, ciphertext.length)
132
- };
133
- const proofParams = getProofGenerationParamsForSlice(
134
- { key, iv, ciphertext, redactedPlaintext, slice }
135
- );
136
- if (!proofParams) {
137
- continue;
138
- }
139
- packetToProve.proofsToGenerate.push(proofParams);
140
- zkProofsToGen += 1;
141
- }
142
- packetsToProve.push(packetToProve);
143
- },
144
- getTotalChunksToProve() {
145
- return zkProofsToGen;
146
- },
147
- async generateProofs(onChunkDone) {
148
- if (!packetsToProve.length) {
149
- return;
150
- }
151
- const start = Date.now();
152
- const tasks = [];
153
- for (const {
154
- onGeneratedProofs,
155
- algorithm,
156
- proofsToGenerate,
157
- toprfsToGenerate
158
- } of packetsToProve) {
159
- const proofs = [];
160
- const toprfs = [];
161
- let proofsLeft = proofsToGenerate.length + toprfsToGenerate.length;
162
- for (const proofToGen of proofsToGenerate) {
163
- tasks.push(zkQueue.add(async () => {
164
- const proof = await generateZkProofForChunk(algorithm, proofToGen);
165
- onChunkDone?.();
166
- proofs.push(proof);
167
- proofsLeft -= 1;
168
- if (proofsLeft === 0) {
169
- onGeneratedProofs(proofs, toprfs);
31
+ const alg = getZkAlgorithmForCipherSuite(cipherSuite);
32
+ const chunkSizeBytes = getChunkSizeBytes(alg);
33
+ const key = await crypto.exportKey(packet.encKey);
34
+ const iv = packet.iv;
35
+ const ciphertext = getPureCiphertext(packet.ciphertext, cipherSuite);
36
+ // if the packet starts with TOPRF overflow from previous packet,
37
+ // we can just redact that part of the ciphertext as it's not required
38
+ // to be proven. Decrypting the raw ciphertext of this part would also
39
+ // reveal the raw underlying text, which we don't want.
40
+ if (overshotToprfFromPrevBlock) {
41
+ redactedPlaintext.set(new Uint8Array(overshotToprfFromPrevBlock.length)
42
+ .fill(REDACTION_CHAR_CODE));
170
43
  }
171
- }, { throwOnTimeout: true }));
172
- }
173
- for (const toprfToGen of toprfsToGenerate) {
174
- tasks.push(zkQueue.add(async () => {
175
- const toprf = await generateOprfProofForChunk(algorithm, toprfToGen);
176
- onChunkDone?.();
177
- toprfs.push(toprf);
178
- proofsLeft -= 1;
179
- if (proofsLeft === 0) {
180
- onGeneratedProofs(proofs, toprfs);
44
+ const trueCiphertextLength = isTls13Suite(cipherSuite)
45
+ ? ciphertext.length - 1 // remove content type byte
46
+ : ciphertext.length;
47
+ const packetToProve = {
48
+ onGeneratedProofs,
49
+ algorithm: alg,
50
+ proofsToGenerate: [],
51
+ toprfsToGenerate: [],
52
+ iv: packet.fixedIv,
53
+ };
54
+ // first we'll handle all TOPRF blocks
55
+ // we do these first, because they can span multiple chunks
56
+ // & we need to be able to span the right chunks
57
+ for (const toprf of toprfs) {
58
+ // if the TOPRF data overshoots the ciphertext length,
59
+ // then it means that the OPRF data is spread across multiple
60
+ // TLS records & we need to include the next record's ciphertext
61
+ // in our proof.
62
+ // At most we support the OPRF data being spread across 2 records
63
+ const toprfDistFromEnd = trueCiphertextLength
64
+ - (toprf.dataLocation.fromIndex + toprf.dataLocation.length);
65
+ if (toprfDistFromEnd < 0) {
66
+ const nextPacket = getNextPacket();
67
+ if (nextPacket?.type !== 'ciphertext') {
68
+ throw new AttestorError('ERROR_INTERNAL', 'TOPRF data overshoots ciphertext length, '
69
+ + 'but no next ciphertext packet found');
70
+ }
71
+ if (nextPacket.encKey !== packet.encKey) {
72
+ throw new AttestorError('ERROR_INTERNAL', 'TOPRF data overshoots ciphertext length, '
73
+ + 'but next packet has different encryption key');
74
+ }
75
+ const nextCiphertext = nextPacket.ciphertext
76
+ .slice(0, Math.abs(toprfDistFromEnd));
77
+ const iv = nextPacket.iv;
78
+ toprf.overshoot = {
79
+ ciphertext: nextCiphertext,
80
+ iv,
81
+ recordNumber: nextPacket.recordNumber,
82
+ };
83
+ }
84
+ const fromIndex = getIdealOffsetForToprfBlock(alg, toprf);
85
+ const toIndex = Math
86
+ .min(fromIndex + chunkSizeBytes, ciphertext.length);
87
+ // ensure this OPRF block doesn't overlap with any other OPRF block
88
+ const slice = { fromIndex, toIndex };
89
+ packetToProve.toprfsToGenerate.push(getTOPRFProofGenerationParamsForSlice({
90
+ key,
91
+ iv,
92
+ ciphertext,
93
+ slice,
94
+ toprf: {
95
+ ...toprf,
96
+ dataLocation: {
97
+ ...toprf.dataLocation,
98
+ fromIndex: toprf.dataLocation.fromIndex - fromIndex
99
+ }
100
+ }
101
+ }));
102
+ zkProofsToGen += 1;
103
+ // we'll redact the OPRF part of the plaintext to not reveal
104
+ // the actual plaintext to the attestor
105
+ const pktToIndex = Math.min(trueCiphertextLength, toprf.dataLocation.fromIndex + toprf.dataLocation.length);
106
+ const pktFromIndex = toprf.dataLocation.fromIndex;
107
+ for (let i = pktFromIndex; i < pktToIndex; i++) {
108
+ redactedPlaintext[i] = REDACTION_CHAR_CODE;
109
+ }
181
110
  }
182
- }, { throwOnTimeout: true }));
183
- }
184
- }
185
- await Promise.all(tasks);
186
- logger?.info(
187
- { durationMs: Date.now() - start, zkProofsToGen },
188
- "generated ZK proofs"
189
- );
190
- packetsToProve.splice(0, packetsToProve.length);
191
- zkProofsToGen = 0;
192
- const alg = getZkAlgorithmForCipherSuite(cipherSuite);
193
- const zkOperator = await getZkOperatorForAlgorithm(alg);
194
- zkOperator.release?.();
195
- }
196
- };
197
- async function generateZkProofForChunk(algorithm, {
198
- startIdx,
199
- redactedPlaintext,
200
- privateInput,
201
- publicInput
202
- }) {
203
- const operator = getZkOperatorForAlgorithm(algorithm);
204
- const proof = await generateProof(
205
- { algorithm, privateInput, publicInput, operator, logger }
206
- );
207
- logger?.debug({ startIdx }, "generated proof for chunk");
208
- return {
209
- proofData: typeof proof.proofData === "string" ? strToUint8Array(proof.proofData) : proof.proofData,
210
- decryptedRedactedCiphertext: proof.plaintext || new Uint8Array(),
211
- redactedPlaintext,
212
- startIdx
213
- };
214
- }
215
- async function generateOprfProofForChunk(algorithm, { startIdx, privateInput, publicInput, toprf }) {
216
- const operator = getOprfOperatorForAlgorithm(algorithm);
217
- const toprfLocations = [];
218
- if (toprf?.overshoot) {
219
- const { dataLocation, overshoot: { ciphertext } } = toprf;
220
- toprfLocations.push(
221
- {
222
- pos: dataLocation.fromIndex,
223
- len: dataLocation.length - ciphertext.length
111
+ for (let i = 0; i < ciphertext.length; i += chunkSizeBytes) {
112
+ const slice = {
113
+ fromIndex: i,
114
+ toIndex: Math.min(i + chunkSizeBytes, ciphertext.length)
115
+ };
116
+ const proofParams = getProofGenerationParamsForSlice({ key, iv, ciphertext, redactedPlaintext, slice });
117
+ if (!proofParams) {
118
+ continue;
119
+ }
120
+ packetToProve.proofsToGenerate.push(proofParams);
121
+ zkProofsToGen += 1;
122
+ }
123
+ packetsToProve.push(packetToProve);
124
+ },
125
+ getTotalChunksToProve() {
126
+ return zkProofsToGen;
127
+ },
128
+ async generateProofs(onChunkDone) {
129
+ if (!packetsToProve.length) {
130
+ return;
131
+ }
132
+ const start = Date.now();
133
+ const tasks = [];
134
+ for (const { onGeneratedProofs, algorithm, proofsToGenerate, toprfsToGenerate } of packetsToProve) {
135
+ const proofs = [];
136
+ const toprfs = [];
137
+ let proofsLeft = proofsToGenerate.length
138
+ + toprfsToGenerate.length;
139
+ for (const proofToGen of proofsToGenerate) {
140
+ tasks.push(zkQueue.add(async () => {
141
+ const proof = await generateZkProofForChunk(algorithm, proofToGen);
142
+ onChunkDone?.();
143
+ proofs.push(proof);
144
+ proofsLeft -= 1;
145
+ if (proofsLeft === 0) {
146
+ onGeneratedProofs(proofs, toprfs);
147
+ }
148
+ }, { throwOnTimeout: true }));
149
+ }
150
+ for (const toprfToGen of toprfsToGenerate) {
151
+ tasks.push(zkQueue.add(async () => {
152
+ const toprf = await generateOprfProofForChunk(algorithm, toprfToGen);
153
+ onChunkDone?.();
154
+ toprfs.push(toprf);
155
+ proofsLeft -= 1;
156
+ if (proofsLeft === 0) {
157
+ onGeneratedProofs(proofs, toprfs);
158
+ }
159
+ }, { throwOnTimeout: true }));
160
+ }
161
+ }
162
+ await Promise.all(tasks);
163
+ logger?.info({ durationMs: Date.now() - start, zkProofsToGen }, 'generated ZK proofs');
164
+ // reset the packets to prove
165
+ packetsToProve.splice(0, packetsToProve.length);
166
+ zkProofsToGen = 0;
167
+ // release ZK resources to free up memory
168
+ const alg = getZkAlgorithmForCipherSuite(cipherSuite);
169
+ const zkOperator = await getZkOperatorForAlgorithm(alg);
170
+ zkOperator.release?.();
224
171
  },
225
- {
226
- pos: ceilToBlockSizeMultiple(
227
- dataLocation.fromIndex + dataLocation.length,
228
- algorithm
229
- ),
230
- len: ciphertext.length
231
- }
232
- );
233
- } else if (toprf) {
234
- toprfLocations.push({
235
- pos: toprf.dataLocation.fromIndex,
236
- len: toprf.dataLocation.length
237
- });
238
- }
239
- const proof = await generateProof(
240
- {
241
- algorithm,
242
- privateInput,
243
- publicInput,
244
- operator,
245
- logger,
246
- ...toprf ? {
247
- toprf: {
248
- locations: toprfLocations,
249
- output: toprf.nullifier,
250
- responses: toprf.responses,
251
- domainSeparator: TOPRF_DOMAIN_SEPARATOR
252
- },
253
- mask: toprf.mask
254
- } : {}
255
- }
256
- );
257
- logger?.debug({ toprfLocations }, "generated TOPRF proof for chunk");
258
- return {
259
- startIdx,
260
- proofData: typeof proof.proofData === "string" ? strToUint8Array(proof.proofData) : proof.proofData,
261
- payload: toprf
262
172
  };
263
- }
264
- function getZkOperatorForAlgorithm(algorithm) {
265
- return zkOperators?.[algorithm] || makeDefaultZkOperator(algorithm, zkEngine, logger);
266
- }
267
- function getOprfOperatorForAlgorithm(algorithm) {
268
- return oprfOperators?.[algorithm] || makeDefaultOPRFOperator(algorithm, zkEngine, logger);
269
- }
270
- }
271
- async function verifyZkPacket({
272
- cipherSuite,
273
- ciphertext,
274
- zkReveal,
275
- zkOperators,
276
- oprfOperators,
277
- logger = LOGGER,
278
- zkEngine = "snarkjs",
279
- iv,
280
- recordNumber,
281
- toprfOvershotNullifier,
282
- getNextPacket
283
- }) {
284
- const { proofs, toprfs, oprfRawMarkers } = zkReveal;
285
- const algorithm = getZkAlgorithmForCipherSuite(cipherSuite);
286
- const recordIV = getRecordIV(ciphertext, cipherSuite);
287
- ciphertext = new Uint8Array(getPureCiphertext(ciphertext, cipherSuite));
288
- const realRedactedPlaintext = new Uint8Array(ciphertext.length).fill(REDACTION_CHAR_CODE);
289
- const replacements = await Promise.all(toprfs.map(async (toprf, i) => {
290
- try {
291
- return await verifyToprfProofPacket(toprf);
292
- } catch (e) {
293
- e.message += ` (TOPRF proof ${i}, from ${toprf.payload?.dataLocation?.fromIndex}, record ${recordNumber})`;
294
- throw e;
295
- }
296
- }));
297
- await Promise.all(proofs.map(async (proof, i) => {
298
- try {
299
- await verifyZkProofPacket(proof);
300
- } catch (e) {
301
- e.message += ` (ZK proof ${i}, startIdx ${proof.startIdx}, record ${recordNumber})`;
302
- throw e;
173
+ async function generateZkProofForChunk(algorithm, { startIdx, redactedPlaintext, privateInput, publicInput }) {
174
+ const operator = getZkOperatorForAlgorithm(algorithm);
175
+ const proof = await generateProof({ algorithm, privateInput, publicInput, operator, logger });
176
+ logger?.debug({ startIdx }, 'generated proof for chunk');
177
+ return {
178
+ proofData: typeof proof.proofData === 'string'
179
+ ? strToUint8Array(proof.proofData)
180
+ : proof.proofData,
181
+ decryptedRedactedCiphertext: proof.plaintext || new Uint8Array(),
182
+ redactedPlaintext,
183
+ startIdx
184
+ };
303
185
  }
304
- }));
305
- for (const { set, startIdx } of replacements) {
306
- realRedactedPlaintext.set(set, startIdx);
307
- }
308
- if (toprfOvershotNullifier) {
309
- realRedactedPlaintext.set(toprfOvershotNullifier);
310
- }
311
- return { redactedPlaintext: realRedactedPlaintext, oprfRawMarkers };
312
- async function verifyZkProofPacket({
313
- proofData,
314
- decryptedRedactedCiphertext,
315
- redactedPlaintext,
316
- startIdx
317
- }) {
318
- const ciphertextChunkEnd = startIdx + redactedPlaintext.length;
319
- const ciphertextChunk = ciphertext.slice(startIdx, ciphertextChunkEnd);
320
- for (let i = 0; i < ciphertextChunk.length; i++) {
321
- if (redactedPlaintext[i] === REDACTION_CHAR_CODE) {
322
- ciphertextChunk[i] = REDACTION_CHAR_CODE;
323
- }
186
+ async function generateOprfProofForChunk(algorithm, { startIdx, privateInput, publicInput, toprf }) {
187
+ const operator = getOprfOperatorForAlgorithm(algorithm);
188
+ const toprfLocations = [];
189
+ if (toprf?.overshoot) {
190
+ const { dataLocation, overshoot: { ciphertext } } = toprf;
191
+ toprfLocations.push({
192
+ pos: dataLocation.fromIndex,
193
+ len: dataLocation.length - ciphertext.length
194
+ }, {
195
+ pos: ceilToBlockSizeMultiple(dataLocation.fromIndex + dataLocation.length, algorithm),
196
+ len: ciphertext.length
197
+ });
198
+ }
199
+ else if (toprf) {
200
+ toprfLocations.push({
201
+ pos: toprf.dataLocation.fromIndex,
202
+ len: toprf.dataLocation.length,
203
+ });
204
+ }
205
+ const proof = await generateProof({
206
+ algorithm,
207
+ privateInput,
208
+ publicInput,
209
+ operator,
210
+ logger,
211
+ ...(toprf
212
+ ? {
213
+ toprf: {
214
+ locations: toprfLocations,
215
+ output: toprf.nullifier,
216
+ responses: toprf.responses,
217
+ domainSeparator: TOPRF_DOMAIN_SEPARATOR
218
+ },
219
+ mask: toprf.mask,
220
+ }
221
+ : {})
222
+ });
223
+ logger?.debug({ toprfLocations }, 'generated TOPRF proof for chunk');
224
+ return {
225
+ startIdx,
226
+ proofData: typeof proof.proofData === 'string'
227
+ ? strToUint8Array(proof.proofData)
228
+ : proof.proofData,
229
+ payload: toprf,
230
+ };
324
231
  }
325
- let nonce = concatenateUint8Arrays([iv, recordIV]);
326
- if (!recordIV.length) {
327
- nonce = generateIV(nonce, recordNumber);
232
+ function getZkOperatorForAlgorithm(algorithm) {
233
+ return zkOperators?.[algorithm]
234
+ || makeDefaultZkOperator(algorithm, zkEngine, logger);
328
235
  }
329
- const ciphertextInput = {
330
- ciphertext: ciphertextChunk,
331
- iv: nonce,
332
- offsetBytes: startIdx
333
- };
334
- if (!isRedactionCongruent(redactedPlaintext, decryptedRedactedCiphertext)) {
335
- throw new Error("redacted ciphertext not congruent");
236
+ function getOprfOperatorForAlgorithm(algorithm) {
237
+ return oprfOperators?.[algorithm]
238
+ || makeDefaultOPRFOperator(algorithm, zkEngine, logger);
336
239
  }
337
- await verifyProof(
338
- {
339
- proof: {
340
- algorithm,
341
- proofData,
342
- plaintext: decryptedRedactedCiphertext
343
- },
344
- publicInput: ciphertextInput,
345
- logger,
346
- operator: getZkOperator()
347
- }
348
- );
349
- logger?.debug(
350
- { startIdx, endIdx: startIdx + redactedPlaintext.length },
351
- "verified proof"
352
- );
353
- realRedactedPlaintext.set(redactedPlaintext, startIdx);
354
- }
355
- async function verifyToprfProofPacket({ startIdx, proofData, payload: toprf }) {
356
- if (!toprf?.dataLocation || !toprf.responses || !toprf.nullifier) {
357
- throw new Error("invalid TOPRF proof payload");
240
+ }
241
+ /**
242
+ * Verify the given ZK proof
243
+ */
244
+ export async function verifyZkPacket({ cipherSuite, ciphertext, zkReveal, zkOperators, oprfOperators, logger = LOGGER, zkEngine = 'snarkjs', iv, recordNumber, toprfOvershotNullifier, getNextPacket }) {
245
+ const { proofs, toprfs, oprfRawMarkers } = zkReveal;
246
+ const algorithm = getZkAlgorithmForCipherSuite(cipherSuite);
247
+ const recordIV = getRecordIV(ciphertext, cipherSuite);
248
+ ciphertext = new Uint8Array(getPureCiphertext(ciphertext, cipherSuite));
249
+ const realRedactedPlaintext = new Uint8Array(ciphertext.length).fill(REDACTION_CHAR_CODE);
250
+ const replacements = await Promise.all(toprfs.map(async (toprf, i) => {
251
+ try {
252
+ return await verifyToprfProofPacket(toprf);
253
+ }
254
+ catch (e) {
255
+ e.message += ` (TOPRF proof ${i}, `
256
+ + `from ${toprf.payload?.dataLocation?.fromIndex}, `
257
+ + `record ${recordNumber})`;
258
+ throw e;
259
+ }
260
+ }));
261
+ await Promise.all(proofs.map(async (proof, i) => {
262
+ try {
263
+ await verifyZkProofPacket(proof);
264
+ }
265
+ catch (e) {
266
+ e.message +=
267
+ ` (ZK proof ${i}, startIdx ${proof.startIdx}, record ${recordNumber})`;
268
+ throw e;
269
+ }
270
+ }));
271
+ for (const { set, startIdx } of replacements) {
272
+ realRedactedPlaintext.set(set, startIdx);
358
273
  }
359
- const { dataLocation, nullifier } = toprf;
360
- const ciphertextChunkEnd = Math.min(ciphertext.length, getChunkSizeBytes(algorithm) + startIdx);
361
- const isLastChunk = ciphertextChunkEnd >= ciphertext.length;
362
- const ciphertextChunk = ciphertext.slice(startIdx, ciphertextChunkEnd);
363
- let nonce = concatenateUint8Arrays([iv, recordIV]);
364
- if (!recordIV.length) {
365
- nonce = generateIV(nonce, recordNumber);
274
+ /**
275
+ * to verify if the user has given us the correct redacted plaintext,
276
+ * and isn't providing plaintext that they haven't proven they have
277
+ * we start with a fully redacted plaintext, and then replace the
278
+ * redacted parts with the plaintext that the user has provided
279
+ * in the proofs
280
+ */
281
+ if (toprfOvershotNullifier) {
282
+ realRedactedPlaintext.set(toprfOvershotNullifier);
366
283
  }
367
- const ciphertextInput = {
368
- ciphertext: ciphertextChunk,
369
- iv: nonce,
370
- offsetBytes: startIdx
371
- };
372
- let pubInput = ciphertextInput;
373
- const nulliferStr = binaryHashToStr(nullifier, dataLocation.length);
374
- const locations = [];
375
- const toprfEndIdx = dataLocation.fromIndex + dataLocation.length;
376
- const trueCiphLen = isLastChunk && isTls13Suite(cipherSuite) ? ciphertextChunk.length - 1 : ciphertextChunk.length;
377
- const overshoot = toprfEndIdx - trueCiphLen;
378
- if (overshoot > 0) {
379
- const nextPkt = getNextPacket(
380
- strToUint8Array(nulliferStr.slice(dataLocation.length - overshoot))
381
- );
382
- if (!nextPkt) {
383
- throw new Error("OPRF data overshot, but no next packet found");
384
- }
385
- const nextRecordIV = getRecordIV(ciphertext, cipherSuite);
386
- let nextNonce = concatenateUint8Arrays([iv, nextRecordIV]);
387
- if (!nextRecordIV.length) {
388
- nextNonce = generateIV(nextNonce, recordNumber + 1);
389
- }
390
- pubInput = [
391
- ciphertextInput,
392
- {
393
- ciphertext: nextPkt.slice(0, overshoot),
394
- iv: nextNonce,
395
- offsetBytes: 0
284
+ return { redactedPlaintext: realRedactedPlaintext, oprfRawMarkers };
285
+ async function verifyZkProofPacket({ proofData, decryptedRedactedCiphertext, redactedPlaintext, startIdx, }) {
286
+ // get the ciphertext chunk we received from the server
287
+ // the ZK library, will verify that the decrypted redacted
288
+ // ciphertext matches the ciphertext received from the server
289
+ const ciphertextChunkEnd = startIdx + redactedPlaintext.length;
290
+ const ciphertextChunk = ciphertext.slice(startIdx, ciphertextChunkEnd);
291
+ // redact ciphertext if plaintext is redacted
292
+ // to prepare for decryption in ZK circuit
293
+ // the ZK circuit will take in the redacted ciphertext,
294
+ // which shall produce the redacted plaintext
295
+ for (let i = 0; i < ciphertextChunk.length; i++) {
296
+ if (redactedPlaintext[i] === REDACTION_CHAR_CODE) {
297
+ ciphertextChunk[i] = REDACTION_CHAR_CODE;
298
+ }
396
299
  }
397
- ];
398
- locations.push(
399
- {
400
- pos: dataLocation.fromIndex,
401
- len: dataLocation.length - overshoot
402
- },
403
- {
404
- pos: ceilToBlockSizeMultiple(
405
- dataLocation.fromIndex + dataLocation.length,
406
- algorithm
407
- ),
408
- len: overshoot
300
+ let nonce = concatenateUint8Arrays([iv, recordIV]);
301
+ if (!recordIV.length) {
302
+ nonce = generateIV(nonce, recordNumber);
409
303
  }
410
- );
411
- } else {
412
- locations.push({
413
- pos: dataLocation.fromIndex,
414
- len: dataLocation.length
415
- });
304
+ const ciphertextInput = {
305
+ ciphertext: ciphertextChunk,
306
+ iv: nonce,
307
+ offsetBytes: startIdx
308
+ };
309
+ if (!isRedactionCongruent(redactedPlaintext, decryptedRedactedCiphertext)) {
310
+ throw new Error('redacted ciphertext not congruent');
311
+ }
312
+ await verifyProof({
313
+ proof: {
314
+ algorithm,
315
+ proofData: proofData,
316
+ plaintext: decryptedRedactedCiphertext,
317
+ },
318
+ publicInput: ciphertextInput,
319
+ logger,
320
+ operator: getZkOperator()
321
+ });
322
+ logger?.debug({ startIdx, endIdx: startIdx + redactedPlaintext.length }, 'verified proof');
323
+ realRedactedPlaintext.set(redactedPlaintext, startIdx);
416
324
  }
417
- await verifyProof(
418
- {
419
- proof: { algorithm, proofData, plaintext: void 0 },
420
- publicInput: pubInput,
421
- logger,
422
- operator: getOprfOperator(),
423
- toprf: {
424
- locations,
425
- domainSeparator: TOPRF_DOMAIN_SEPARATOR,
426
- output: nullifier,
427
- responses: toprf.responses
325
+ async function verifyToprfProofPacket({ startIdx, proofData, payload: toprf }) {
326
+ if (!toprf?.dataLocation || !toprf.responses || !toprf.nullifier) {
327
+ throw new Error('invalid TOPRF proof payload');
428
328
  }
429
- }
430
- );
431
- logger?.debug({ locations }, "verified TOPRF proof");
432
- return {
433
- set: strToUint8Array(
434
- nulliferStr.slice(0, locations[0].len)
435
- ),
436
- startIdx: locations[0].pos + startIdx
437
- };
438
- }
439
- function getZkOperator() {
440
- return zkOperators?.[algorithm] || makeDefaultZkOperator(algorithm, zkEngine, logger);
441
- }
442
- function getOprfOperator() {
443
- return oprfOperators?.[algorithm] || makeDefaultOPRFOperator(algorithm, zkEngine, logger);
444
- }
329
+ const { dataLocation, nullifier } = toprf;
330
+ const ciphertextChunkEnd = Math
331
+ .min(ciphertext.length, getChunkSizeBytes(algorithm) + startIdx);
332
+ const isLastChunk = ciphertextChunkEnd >= ciphertext.length;
333
+ const ciphertextChunk = ciphertext.slice(startIdx, ciphertextChunkEnd);
334
+ let nonce = concatenateUint8Arrays([iv, recordIV]);
335
+ if (!recordIV.length) {
336
+ nonce = generateIV(nonce, recordNumber);
337
+ }
338
+ const ciphertextInput = {
339
+ ciphertext: ciphertextChunk,
340
+ iv: nonce,
341
+ offsetBytes: startIdx
342
+ };
343
+ let pubInput = ciphertextInput;
344
+ const nulliferStr = binaryHashToStr(nullifier, dataLocation.length);
345
+ const locations = [];
346
+ const toprfEndIdx = dataLocation.fromIndex + dataLocation.length;
347
+ const trueCiphLen = isLastChunk && isTls13Suite(cipherSuite)
348
+ ? ciphertextChunk.length - 1
349
+ : ciphertextChunk.length;
350
+ const overshoot = toprfEndIdx - trueCiphLen;
351
+ if (overshoot > 0) {
352
+ // fetch the overshoot part of the nullifier
353
+ const nextPkt = getNextPacket(strToUint8Array(nulliferStr.slice(dataLocation.length - overshoot)));
354
+ if (!nextPkt) {
355
+ throw new Error('OPRF data overshot, but no next packet found');
356
+ }
357
+ const nextRecordIV = getRecordIV(ciphertext, cipherSuite);
358
+ let nextNonce = concatenateUint8Arrays([iv, nextRecordIV]);
359
+ if (!nextRecordIV.length) {
360
+ nextNonce = generateIV(nextNonce, recordNumber + 1);
361
+ }
362
+ pubInput = [
363
+ ciphertextInput,
364
+ {
365
+ ciphertext: nextPkt.slice(0, overshoot),
366
+ iv: nextNonce,
367
+ offsetBytes: 0,
368
+ }
369
+ ];
370
+ locations.push({
371
+ pos: dataLocation.fromIndex,
372
+ len: dataLocation.length - overshoot
373
+ }, {
374
+ pos: ceilToBlockSizeMultiple(dataLocation.fromIndex + dataLocation.length, algorithm),
375
+ len: overshoot
376
+ });
377
+ }
378
+ else {
379
+ locations.push({
380
+ pos: dataLocation.fromIndex,
381
+ len: dataLocation.length,
382
+ });
383
+ }
384
+ await verifyProof({
385
+ proof: { algorithm, proofData: proofData, plaintext: undefined },
386
+ publicInput: pubInput,
387
+ logger,
388
+ operator: getOprfOperator(),
389
+ toprf: {
390
+ locations,
391
+ domainSeparator: TOPRF_DOMAIN_SEPARATOR,
392
+ output: nullifier,
393
+ responses: toprf.responses,
394
+ }
395
+ });
396
+ logger?.debug({ locations }, 'verified TOPRF proof');
397
+ return {
398
+ set: strToUint8Array(nulliferStr.slice(0, locations[0].len)),
399
+ startIdx: locations[0].pos + startIdx,
400
+ };
401
+ }
402
+ function getZkOperator() {
403
+ return zkOperators?.[algorithm]
404
+ || makeDefaultZkOperator(algorithm, zkEngine, logger);
405
+ }
406
+ function getOprfOperator() {
407
+ return oprfOperators?.[algorithm]
408
+ || makeDefaultOPRFOperator(algorithm, zkEngine, logger);
409
+ }
445
410
  }
411
+ // the chunk size of the ZK circuit in bytes
412
+ // this will be >= the block size
446
413
  function getChunkSizeBytes(alg) {
447
- const { chunkSize, bitsPerWord } = ZK_CONFIG[alg];
448
- return chunkSize * bitsPerWord / 8;
414
+ const { chunkSize, bitsPerWord } = ZK_CONFIG[alg];
415
+ return chunkSize * bitsPerWord / 8;
449
416
  }
450
417
  const zkEngines = {};
451
418
  const oprfEngines = {};
452
419
  const operatorMakers = {
453
- "snarkjs": makeSnarkJsZKOperator,
454
- "gnark": makeGnarkZkOperator,
455
- "stwo": makeStwoZkOperator
420
+ 'snarkjs': makeSnarkJsZKOperator,
421
+ 'gnark': makeGnarkZkOperator,
422
+ 'stwo': makeStwoZkOperator,
456
423
  };
457
424
  const OPRF_OPERATOR_MAKERS = {
458
- "gnark": makeGnarkOPRFOperator
425
+ 'gnark': makeGnarkOPRFOperator
459
426
  };
460
- function makeDefaultZkOperator(algorithm, zkEngine, logger) {
461
- let zkOperators = zkEngines[zkEngine];
462
- if (!zkOperators) {
463
- zkEngines[zkEngine] = {};
464
- zkOperators = zkEngines[zkEngine];
465
- }
466
- if (!zkOperators[algorithm]) {
467
- const opType = getOperatorType();
468
- const zkBaseUrl = opType === "remote" ? getZkResourcesBaseUrl() : void 0;
469
- logger?.info({ type: opType, algorithm, zkBaseUrl }, "fetching zk operator");
470
- const fetcher = opType === "local" ? makeLocalFileFetch() : makeRemoteFileFetch({ baseUrl: zkBaseUrl, logger });
471
- const maker = operatorMakers[zkEngine];
472
- if (!maker) {
473
- throw new Error(`No ZK operator maker for ${zkEngine}`);
427
+ export function makeDefaultZkOperator(algorithm, zkEngine, logger) {
428
+ let zkOperators = zkEngines[zkEngine];
429
+ if (!zkOperators) {
430
+ zkEngines[zkEngine] = {};
431
+ zkOperators = zkEngines[zkEngine];
474
432
  }
475
- zkOperators[algorithm] = maker({ algorithm, fetcher });
476
- }
477
- return zkOperators[algorithm];
433
+ if (!zkOperators[algorithm]) {
434
+ const opType = getOperatorType();
435
+ const zkBaseUrl = opType === 'remote' ? getZkResourcesBaseUrl() : undefined;
436
+ logger?.info({ type: opType, algorithm, zkBaseUrl }, 'fetching zk operator');
437
+ const fetcher = opType === 'local'
438
+ ? makeLocalFileFetch()
439
+ : makeRemoteFileFetch({ baseUrl: zkBaseUrl, logger });
440
+ const maker = operatorMakers[zkEngine];
441
+ if (!maker) {
442
+ throw new Error(`No ZK operator maker for ${zkEngine}`);
443
+ }
444
+ zkOperators[algorithm] = maker({ algorithm, fetcher });
445
+ }
446
+ return zkOperators[algorithm];
478
447
  }
479
448
  function getOperatorType() {
480
- const envop = getEnvVariable("ZK_OPERATOR_TYPE");
481
- if (envop === "local" || envop === "remote") {
482
- return envop;
483
- }
484
- return detectEnvironment() === "node" ? "local" : "remote";
449
+ const envop = getEnvVariable('ZK_OPERATOR_TYPE');
450
+ if (envop === 'local' || envop === 'remote') {
451
+ return envop;
452
+ }
453
+ return detectEnvironment() === 'node' ? 'local' : 'remote';
485
454
  }
486
- function makeDefaultOPRFOperator(algorithm, zkEngine, logger) {
487
- let operators = oprfEngines[zkEngine];
488
- if (!operators) {
489
- oprfEngines[zkEngine] = {};
490
- operators = oprfEngines[zkEngine];
491
- }
492
- if (!operators[algorithm]) {
493
- const type = getOperatorType();
494
- const zkBaseUrl = type === "remote" ? getZkResourcesBaseUrl() : void 0;
495
- logger?.info({ type, algorithm, zkBaseUrl }, "fetching oprf operator");
496
- const fetcher = type === "local" ? makeLocalFileFetch() : makeRemoteFileFetch({ baseUrl: zkBaseUrl, logger });
497
- const maker = OPRF_OPERATOR_MAKERS[zkEngine];
498
- if (!maker) {
499
- throw new Error(`No OPRF operator maker for ${zkEngine}`);
455
+ export function makeDefaultOPRFOperator(algorithm, zkEngine, logger) {
456
+ let operators = oprfEngines[zkEngine];
457
+ if (!operators) {
458
+ oprfEngines[zkEngine] = {};
459
+ operators = oprfEngines[zkEngine];
460
+ }
461
+ if (!operators[algorithm]) {
462
+ const type = getOperatorType();
463
+ const zkBaseUrl = type === 'remote' ? getZkResourcesBaseUrl() : undefined;
464
+ logger?.info({ type, algorithm, zkBaseUrl }, 'fetching oprf operator');
465
+ const fetcher = type === 'local'
466
+ ? makeLocalFileFetch()
467
+ : makeRemoteFileFetch({ baseUrl: zkBaseUrl, logger });
468
+ const maker = OPRF_OPERATOR_MAKERS[zkEngine];
469
+ if (!maker) {
470
+ throw new Error(`No OPRF operator maker for ${zkEngine}`);
471
+ }
472
+ operators[algorithm] = maker({ algorithm, fetcher });
500
473
  }
501
- operators[algorithm] = maker({ algorithm, fetcher });
502
- }
503
- return operators[algorithm];
474
+ return operators[algorithm];
504
475
  }
505
- function getEngineString(engine) {
506
- if (engine === ZKProofEngine.ZK_ENGINE_GNARK) {
507
- return "gnark";
508
- }
509
- if (engine === ZKProofEngine.ZK_ENGINE_SNARKJS) {
510
- return "snarkjs";
511
- }
512
- if (engine === ZKProofEngine.ZK_ENGINE_STWO) {
513
- return "stwo";
514
- }
515
- throw new Error(`Unknown ZK engine: ${engine}`);
476
+ export function getEngineString(engine) {
477
+ if (engine === ZKProofEngine.ZK_ENGINE_GNARK) {
478
+ return 'gnark';
479
+ }
480
+ if (engine === ZKProofEngine.ZK_ENGINE_SNARKJS) {
481
+ return 'snarkjs';
482
+ }
483
+ if (engine === ZKProofEngine.ZK_ENGINE_STWO) {
484
+ return 'stwo';
485
+ }
486
+ throw new Error(`Unknown ZK engine: ${engine}`);
516
487
  }
517
- function getEngineProto(engine) {
518
- if (engine === "gnark") {
519
- return ZKProofEngine.ZK_ENGINE_GNARK;
520
- }
521
- if (engine === "snarkjs") {
522
- return ZKProofEngine.ZK_ENGINE_SNARKJS;
523
- }
524
- if (engine === "stwo") {
525
- return ZKProofEngine.ZK_ENGINE_STWO;
526
- }
527
- throw new Error(`Unknown ZK engine: ${engine}`);
488
+ export function getEngineProto(engine) {
489
+ if (engine === 'gnark') {
490
+ return ZKProofEngine.ZK_ENGINE_GNARK;
491
+ }
492
+ if (engine === 'snarkjs') {
493
+ return ZKProofEngine.ZK_ENGINE_SNARKJS;
494
+ }
495
+ if (engine === 'stwo') {
496
+ return ZKProofEngine.ZK_ENGINE_STWO;
497
+ }
498
+ throw new Error(`Unknown ZK engine: ${engine}`);
528
499
  }
529
- function getProofGenerationParamsForSlice({
530
- key,
531
- iv,
532
- ciphertext,
533
- redactedPlaintext,
534
- slice: { fromIndex, toIndex }
535
- }) {
536
- const ciphertextChunk = ciphertext.slice(fromIndex, toIndex);
537
- const plaintextChunk = redactedPlaintext.slice(fromIndex, toIndex);
538
- if (isFullyRedacted(plaintextChunk)) {
539
- return;
540
- }
541
- for (let i = 0; i < ciphertextChunk.length; i++) {
542
- if (plaintextChunk[i] === REDACTION_CHAR_CODE) {
543
- ciphertextChunk[i] = REDACTION_CHAR_CODE;
500
+ function getProofGenerationParamsForSlice({ key, iv, ciphertext, redactedPlaintext, slice: { fromIndex, toIndex }, }) {
501
+ const ciphertextChunk = ciphertext.slice(fromIndex, toIndex);
502
+ const plaintextChunk = redactedPlaintext.slice(fromIndex, toIndex);
503
+ if (isFullyRedacted(plaintextChunk)) {
504
+ return;
505
+ }
506
+ // redact ciphertext if plaintext is redacted
507
+ // to prepare for decryption in ZK circuit
508
+ // the ZK circuit will take in the redacted ciphertext,
509
+ // which shall produce the redacted plaintext
510
+ for (let i = 0; i < ciphertextChunk.length; i++) {
511
+ if (plaintextChunk[i] === REDACTION_CHAR_CODE) {
512
+ ciphertextChunk[i] = REDACTION_CHAR_CODE;
513
+ }
544
514
  }
545
- }
546
- return {
547
- startIdx: fromIndex,
548
- redactedPlaintext: plaintextChunk,
549
- privateInput: { key },
550
- publicInput: { ciphertext: ciphertextChunk, iv, offsetBytes: fromIndex }
551
- };
515
+ return {
516
+ startIdx: fromIndex,
517
+ redactedPlaintext: plaintextChunk,
518
+ privateInput: { key },
519
+ publicInput: { ciphertext: ciphertextChunk, iv, offsetBytes: fromIndex },
520
+ };
552
521
  }
553
- function getTOPRFProofGenerationParamsForSlice({
554
- key,
555
- iv,
556
- ciphertext,
557
- slice: { fromIndex, toIndex },
558
- toprf
559
- }) {
560
- const ciphertextChunk = ciphertext.slice(fromIndex, toIndex);
561
- if (toprf?.overshoot) {
562
- const {
563
- overshoot: { ciphertext: overshootCiphertext, iv: overshootIv }
564
- } = toprf;
522
+ function getTOPRFProofGenerationParamsForSlice({ key, iv, ciphertext, slice: { fromIndex, toIndex }, toprf, }) {
523
+ const ciphertextChunk = ciphertext.slice(fromIndex, toIndex);
524
+ if (toprf?.overshoot) {
525
+ const { overshoot: { ciphertext: overshootCiphertext, iv: overshootIv } } = toprf;
526
+ return {
527
+ privateInput: { key },
528
+ publicInput: [
529
+ {
530
+ ciphertext: ciphertextChunk,
531
+ iv,
532
+ offsetBytes: fromIndex,
533
+ },
534
+ { ciphertext: overshootCiphertext, iv: overshootIv }
535
+ ],
536
+ toprf,
537
+ startIdx: fromIndex,
538
+ };
539
+ }
565
540
  return {
566
- privateInput: { key },
567
- publicInput: [
568
- {
569
- ciphertext: ciphertextChunk,
570
- iv,
571
- offsetBytes: fromIndex
572
- },
573
- { ciphertext: overshootCiphertext, iv: overshootIv }
574
- ],
575
- toprf,
576
- startIdx: fromIndex
541
+ privateInput: { key },
542
+ publicInput: { ciphertext: ciphertextChunk, iv, offsetBytes: fromIndex },
543
+ toprf,
544
+ startIdx: fromIndex,
577
545
  };
578
- }
579
- return {
580
- privateInput: { key },
581
- publicInput: { ciphertext: ciphertextChunk, iv, offsetBytes: fromIndex },
582
- toprf,
583
- startIdx: fromIndex
584
- };
585
546
  }
547
+ /**
548
+ * Get the ideal location to generate a ZK proof for a TOPRF block.
549
+ * Ideally it should be put into a slice that's a divisor of the chunk size,
550
+ * as that'll minimize the number of proofs that need to be generated.
551
+ * @returns the offset in bytes
552
+ */
586
553
  function getIdealOffsetForToprfBlock(alg, { dataLocation, overshoot }) {
587
- const chunkSizeBytes = getChunkSizeBytes(alg);
588
- const blockSizeBytes = getBlockSizeBytes(alg);
589
- const offsetChunks = Math.floor(dataLocation.fromIndex / chunkSizeBytes);
590
- const endOffsetChunks = Math.floor((dataLocation.fromIndex + dataLocation.length) / chunkSizeBytes);
591
- if (endOffsetChunks === offsetChunks) {
592
- const start = offsetChunks * chunkSizeBytes;
593
- if (overshoot) {
594
- const overshootBlocks = Math.ceil(overshoot.ciphertext.length / blockSizeBytes);
595
- return start + overshootBlocks * blockSizeBytes;
554
+ const chunkSizeBytes = getChunkSizeBytes(alg);
555
+ const blockSizeBytes = getBlockSizeBytes(alg);
556
+ const offsetChunks = Math
557
+ .floor(dataLocation.fromIndex / chunkSizeBytes);
558
+ const endOffsetChunks = Math
559
+ .floor((dataLocation.fromIndex + dataLocation.length) / chunkSizeBytes);
560
+ // happy case -- the OPRF block fits into a single chunk, that's a
561
+ // divisor of the chunk size
562
+ if (endOffsetChunks === offsetChunks) {
563
+ const start = offsetChunks * chunkSizeBytes;
564
+ if (overshoot) {
565
+ const overshootBlocks = Math
566
+ .ceil(overshoot.ciphertext.length / blockSizeBytes);
567
+ return start + (overshootBlocks * blockSizeBytes);
568
+ }
569
+ return start;
570
+ }
571
+ const offsetBytes = Math
572
+ .floor(dataLocation.fromIndex / blockSizeBytes) * blockSizeBytes;
573
+ const endOffsetBytes = Math
574
+ .ceil((dataLocation.fromIndex + dataLocation.length) / blockSizeBytes);
575
+ if (endOffsetBytes - offsetBytes > chunkSizeBytes) {
576
+ throw new AttestorError('ERROR_BAD_REQUEST', 'OPRF data cannot fit into a single chunk');
596
577
  }
597
- return start;
598
- }
599
- const offsetBytes = Math.floor(dataLocation.fromIndex / blockSizeBytes) * blockSizeBytes;
600
- const endOffsetBytes = Math.ceil((dataLocation.fromIndex + dataLocation.length) / blockSizeBytes);
601
- if (endOffsetBytes - offsetBytes > chunkSizeBytes) {
602
- throw new AttestorError(
603
- "ERROR_BAD_REQUEST",
604
- "OPRF data cannot fit into a single chunk"
605
- );
606
- }
607
- return offsetBytes;
578
+ return offsetBytes;
608
579
  }
609
580
  function getZkResourcesBaseUrl() {
610
- if (typeof ATTESTOR_BASE_URL !== "string") {
611
- return DEFAULT_REMOTE_FILE_FETCH_BASE_URL;
612
- }
613
- return new URL(
614
- DEFAULT_REMOTE_FILE_FETCH_BASE_URL,
615
- ATTESTOR_BASE_URL
616
- ).toString();
581
+ if (typeof ATTESTOR_BASE_URL !== 'string') {
582
+ return DEFAULT_REMOTE_FILE_FETCH_BASE_URL;
583
+ }
584
+ return new URL(DEFAULT_REMOTE_FILE_FETCH_BASE_URL, ATTESTOR_BASE_URL).toString();
617
585
  }
618
- export {
619
- getEngineProto,
620
- getEngineString,
621
- makeDefaultOPRFOperator,
622
- makeDefaultZkOperator,
623
- makeZkProofGenerator,
624
- verifyZkPacket
625
- };