@crisp-e3/zk-inputs 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,17 +7,25 @@ witness data needed for Noir-based vote-validity proofs without duplicating the
7
7
  ## What it generates
8
8
 
9
9
  The WASM module wraps a `ZKInputsGenerator` class that performs BFV encryption and produces the
10
- witness data needed for CRISP's Noir circuits. Two main proof types are supported:
10
+ witness data needed for CRISP's Noir circuits.
11
11
 
12
- - **Vote proof** (`generateInputs`) encrypts a vote under the committee's threshold BFV public key
13
- and produces a witness proving the vote is correctly encrypted and that the voter is eligible
14
- (e.g. holds the required token balance, verified via a Merkle membership proof).
12
+ `generateInputs` is the only entry point, and it covers all three operations: a first vote, a
13
+ re-vote, and a masker contribution under the
14
+ [vote masking](https://blog.theinterfold.com/vote-masking-receipt-freeness-secret-ballots/) scheme
15
+ that provides receipt-freeness.
15
16
 
16
- - **Vote update / mask proof** (`generateInputsForUpdate`) — same structure, but used for revotes or
17
- masker contributions under the
18
- [vote masking](https://blog.theinterfold.com/vote-masking-receipt-freeness-secret-ballots/) scheme
19
- that provides receipt-freeness. Unlike the first-vote path, this preserves the real
20
- `prev_ct_commitment` (rather than zeroing it) to chain updates together.
17
+ ```typescript
18
+ generateInputs(previousCiphertext, publicKey, vote, keepPrevious)
19
+ ```
20
+
21
+ - `previousCiphertext` is the ciphertext currently in the slot, or `undefined` when it is empty.
22
+ - `keepPrevious` is set only for a mask over an occupied slot. It makes the ballot add to that
23
+ ciphertext; everything else replaces it.
24
+
25
+ One function rather than one per operation, deliberately: the encryption, the witness, and the
26
+ published ciphertext have the same shape in every case, so nothing about a submission says which
27
+ operation it was. Splitting the paths would make the three tellable apart, which is the attack
28
+ masking exists to prevent.
21
29
 
22
30
  The generator also exposes `encryptVote` / `decryptVote` for standalone BFV operations and
23
31
  `generateKeys` for key generation.
@@ -38,17 +46,23 @@ The `init` subpackage handles both environments transparently.
38
46
 
39
47
  ```ts
40
48
  // Bad — the raw default loader doesn't work in Node.js contexts
41
- import init, { generateVoteInputs } from '@crisp-e3/zk-inputs'
49
+ import init, { ZKInputsGenerator } from '@crisp-e3/zk-inputs'
42
50
  ```
43
51
 
44
52
  ### ✅ Use the universal subpackage loader
45
53
 
46
54
  ```ts
47
55
  import init from '@crisp-e3/zk-inputs/init'
48
- import { generateVoteInputs } from '@crisp-e3/zk-inputs'
56
+ import { ZKInputsGenerator } from '@crisp-e3/zk-inputs'
49
57
 
50
58
  await init()
51
- const inputs = generateVoteInputs(/* ... */)
59
+ const generator = ZKInputsGenerator.withDefaults()
60
+ const { encryptedVote, inputs } = generator.generateInputs(
61
+ previousCiphertext,
62
+ publicKey,
63
+ vote,
64
+ keepPrevious,
65
+ )
52
66
  ```
53
67
 
54
68
  Call `init()` once before using any other imports from `@crisp-e3/zk-inputs`. In browser
package/dist/index.d.ts CHANGED
@@ -7,6 +7,14 @@
7
7
  export class ZKInputsGenerator {
8
8
  free(): void;
9
9
  [Symbol.dispose](): void;
10
+ /**
11
+ * Computes the SAFE commitment of serialized ciphertext bytes.
12
+ *
13
+ * The witness already carries this value for the ciphertext a ballot publishes. Kept for
14
+ * callers that hold bytes they did not generate, such as a slot ciphertext read back from the
15
+ * server.
16
+ */
17
+ computeCtCommitment(ciphertext: Uint8Array): Uint8Array;
10
18
  /**
11
19
  * Decrypt a vote from JavaScript.
12
20
  */
@@ -16,13 +24,20 @@ export class ZKInputsGenerator {
16
24
  */
17
25
  encryptVote(public_key: Uint8Array, vote: BigInt64Array): Uint8Array;
18
26
  /**
19
- * Generate CRISP ZK inputs from JavaScript.
20
- */
21
- generateInputs(prev_ciphertext: Uint8Array, public_key: Uint8Array, vote: BigInt64Array): any;
22
- /**
23
- * Generate CRISP ZK inputs for a vote update (either from voter or as a masker) from JavaScript.
27
+ * Generate the CRISP ZK inputs for one ballot from JavaScript.
28
+ *
29
+ * One entry point for a first vote, a re-vote, and a mask. Pass the ciphertext currently in
30
+ * the slot, or `undefined` when the slot is empty, and set `keepPrevious` only for a mask over
31
+ * an occupied slot. Everything else is identical between the three, which is what keeps them
32
+ * indistinguishable once published.
33
+ *
34
+ * # Arguments
35
+ * - `previous_ciphertext`: The ciphertext in the slot, or `undefined` when it is empty
36
+ * - `public_key`: Public key bytes for encryption
37
+ * - `vote`: Vote value as a vector of coefficients
38
+ * - `keep_previous`: Whether the ballot adds to the slot rather than replacing it
24
39
  */
25
- generateInputsForUpdate(prev_ciphertext: Uint8Array, public_key: Uint8Array, vote: BigInt64Array): any;
40
+ generateInputs(previous_ciphertext: Uint8Array | null | undefined, public_key: Uint8Array, vote: BigInt64Array, keep_previous: boolean): any;
26
41
  /**
27
42
  * Generate a public/secret key pair from JavaScript.
28
43
  */
@@ -55,10 +70,10 @@ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembl
55
70
  export interface InitOutput {
56
71
  readonly memory: WebAssembly.Memory;
57
72
  readonly __wbg_zkinputsgenerator_free: (a: number, b: number) => void;
73
+ readonly zkinputsgenerator_computeCtCommitment: (a: number, b: number, c: number) => [number, number, number, number];
58
74
  readonly zkinputsgenerator_decryptVote: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
59
75
  readonly zkinputsgenerator_encryptVote: (a: number, b: number, c: number, d: number, e: number) => [number, number, number, number];
60
- readonly zkinputsgenerator_generateInputs: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
61
- readonly zkinputsgenerator_generateInputsForUpdate: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
76
+ readonly zkinputsgenerator_generateInputs: (a: number, b: number, c: number, d: number, e: number, f: number, g: number, h: number) => [number, number, number];
62
77
  readonly zkinputsgenerator_generateKeys: (a: number) => [number, number, number];
63
78
  readonly zkinputsgenerator_getBFVParams: (a: number) => [number, number, number];
64
79
  readonly zkinputsgenerator_new: (a: number, b: bigint, c: number, d: number) => [number, number, number];
package/dist/index.js CHANGED
@@ -20,6 +20,26 @@ export class ZKInputsGenerator {
20
20
  const ptr = this.__destroy_into_raw();
21
21
  wasm.__wbg_zkinputsgenerator_free(ptr, 0);
22
22
  }
23
+ /**
24
+ * Computes the SAFE commitment of serialized ciphertext bytes.
25
+ *
26
+ * The witness already carries this value for the ciphertext a ballot publishes. Kept for
27
+ * callers that hold bytes they did not generate, such as a slot ciphertext read back from the
28
+ * server.
29
+ * @param {Uint8Array} ciphertext
30
+ * @returns {Uint8Array}
31
+ */
32
+ computeCtCommitment(ciphertext) {
33
+ const ptr0 = passArray8ToWasm0(ciphertext, wasm.__wbindgen_malloc);
34
+ const len0 = WASM_VECTOR_LEN;
35
+ const ret = wasm.zkinputsgenerator_computeCtCommitment(this.__wbg_ptr, ptr0, len0);
36
+ if (ret[3]) {
37
+ throw takeFromExternrefTable0(ret[2]);
38
+ }
39
+ var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
40
+ wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
41
+ return v2;
42
+ }
23
43
  /**
24
44
  * Decrypt a vote from JavaScript.
25
45
  * @param {Uint8Array} secret_key
@@ -59,40 +79,32 @@ export class ZKInputsGenerator {
59
79
  return v3;
60
80
  }
61
81
  /**
62
- * Generate CRISP ZK inputs from JavaScript.
63
- * @param {Uint8Array} prev_ciphertext
64
- * @param {Uint8Array} public_key
65
- * @param {BigInt64Array} vote
66
- * @returns {any}
67
- */
68
- generateInputs(prev_ciphertext, public_key, vote) {
69
- const ptr0 = passArray8ToWasm0(prev_ciphertext, wasm.__wbindgen_malloc);
70
- const len0 = WASM_VECTOR_LEN;
71
- const ptr1 = passArray8ToWasm0(public_key, wasm.__wbindgen_malloc);
72
- const len1 = WASM_VECTOR_LEN;
73
- const ptr2 = passArray64ToWasm0(vote, wasm.__wbindgen_malloc);
74
- const len2 = WASM_VECTOR_LEN;
75
- const ret = wasm.zkinputsgenerator_generateInputs(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2);
76
- if (ret[2]) {
77
- throw takeFromExternrefTable0(ret[1]);
78
- }
79
- return takeFromExternrefTable0(ret[0]);
80
- }
81
- /**
82
- * Generate CRISP ZK inputs for a vote update (either from voter or as a masker) from JavaScript.
83
- * @param {Uint8Array} prev_ciphertext
82
+ * Generate the CRISP ZK inputs for one ballot from JavaScript.
83
+ *
84
+ * One entry point for a first vote, a re-vote, and a mask. Pass the ciphertext currently in
85
+ * the slot, or `undefined` when the slot is empty, and set `keepPrevious` only for a mask over
86
+ * an occupied slot. Everything else is identical between the three, which is what keeps them
87
+ * indistinguishable once published.
88
+ *
89
+ * # Arguments
90
+ * - `previous_ciphertext`: The ciphertext in the slot, or `undefined` when it is empty
91
+ * - `public_key`: Public key bytes for encryption
92
+ * - `vote`: Vote value as a vector of coefficients
93
+ * - `keep_previous`: Whether the ballot adds to the slot rather than replacing it
94
+ * @param {Uint8Array | null | undefined} previous_ciphertext
84
95
  * @param {Uint8Array} public_key
85
96
  * @param {BigInt64Array} vote
97
+ * @param {boolean} keep_previous
86
98
  * @returns {any}
87
99
  */
88
- generateInputsForUpdate(prev_ciphertext, public_key, vote) {
89
- const ptr0 = passArray8ToWasm0(prev_ciphertext, wasm.__wbindgen_malloc);
90
- const len0 = WASM_VECTOR_LEN;
100
+ generateInputs(previous_ciphertext, public_key, vote, keep_previous) {
101
+ var ptr0 = isLikeNone(previous_ciphertext) ? 0 : passArray8ToWasm0(previous_ciphertext, wasm.__wbindgen_malloc);
102
+ var len0 = WASM_VECTOR_LEN;
91
103
  const ptr1 = passArray8ToWasm0(public_key, wasm.__wbindgen_malloc);
92
104
  const len1 = WASM_VECTOR_LEN;
93
105
  const ptr2 = passArray64ToWasm0(vote, wasm.__wbindgen_malloc);
94
106
  const len2 = WASM_VECTOR_LEN;
95
- const ret = wasm.zkinputsgenerator_generateInputsForUpdate(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2);
107
+ const ret = wasm.zkinputsgenerator_generateInputs(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2, keep_previous);
96
108
  if (ret[2]) {
97
109
  throw takeFromExternrefTable0(ret[1]);
98
110
  }
@@ -291,6 +303,10 @@ function handleError(f, args) {
291
303
  }
292
304
  }
293
305
 
306
+ function isLikeNone(x) {
307
+ return x === undefined || x === null;
308
+ }
309
+
294
310
  function passArray64ToWasm0(arg, malloc) {
295
311
  const ptr = malloc(arg.length * 8, 8) >>> 0;
296
312
  getBigUint64ArrayMemory0().set(arg, ptr / 8);