diff --git a/Makefile b/Makefile index a3a15cf..9745476 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ all: build build: @for circuit in $(CIRCOM_FILES); do \ echo "Processing $${circuit}..."; \ - circom "$${circuit}" --r1cs -o "$$(dirname $${circuit})/artifacts" -l node_modules; \ + circom "$${circuit}" --r1cs --wasm -o "$$(dirname $${circuit})/artifacts" -l node_modules; \ build-circuit "$${circuit}" "$$(dirname $${circuit})/artifacts/$$(basename $${circuit} .circom).bin" -l node_modules; \ done diff --git a/circuits/aes-gcm/nivc/aes-gctr-nivc.circom b/circuits/aes-gcm/nivc/aes-gctr-nivc.circom index bae0890..d0f97ca 100644 --- a/circuits/aes-gcm/nivc/aes-gctr-nivc.circom +++ b/circuits/aes-gcm/nivc/aes-gctr-nivc.circom @@ -9,8 +9,8 @@ template AESGCTRFOLD(DATA_BYTES) { // ------------------------------------------------------------------------------------------------------------------ // // ~~ Set sizes at compile time ~~ assert(DATA_BYTES % 16 == 0); - // Value for accumulating both plaintext and ciphertext as well as counter - var TOTAL_BYTES_ACROSS_NIVC = 2 * DATA_BYTES + 4; + // Value for accumulating both packed plaintext and ciphertext as well as counter + var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; // ------------------------------------------------------------------------------------------------------------------ // @@ -29,7 +29,7 @@ template AESGCTRFOLD(DATA_BYTES) { // We extract the number from the 4 byte word counter component last_counter_bits = BytesToBits(4); for(var i = 0; i < 4; i ++) { - last_counter_bits.in[i] <== step_in[DATA_BYTES * 2 + i]; + last_counter_bits.in[i] <== step_in[DATA_BYTES + i]; } component last_counter_num = Bits2Num(32); // pass in reverse order @@ -46,124 +46,32 @@ template AESGCTRFOLD(DATA_BYTES) { aes.plainText <== plainText; for(var i = 0; i < 4; i++) { - aes.lastCounter[i] <== step_in[DATA_BYTES * 2 + i]; + aes.lastCounter[i] <== step_in[DATA_BYTES + i]; } - // Write out the plaintext and ciphertext to our accumulation arrays, both at once. - signal prevAccumulatedPlaintext[DATA_BYTES]; - for(var i = 0 ; i < DATA_BYTES ; i++) { - prevAccumulatedPlaintext[i] <== step_in[i]; + signal textToPack[16][2]; + for(var i = 0 ; i < 16 ; i++) { + textToPack[i][0] <== plainText[i]; + textToPack[i][1] <== aes.cipherText[i]; } - signal prevAccumulatedCiphertext[DATA_BYTES]; + signal nextPackedChunk[16] <== GenericBytePackArray(16,2)(textToPack); + + signal prevAccumulatedPackedText[DATA_BYTES]; for(var i = 0 ; i < DATA_BYTES ; i++) { - prevAccumulatedCiphertext[i] <== step_in[DATA_BYTES + i]; + prevAccumulatedPackedText[i] <== step_in[i]; } - component nextTexts = WriteToIndexForTwoArrays(DATA_BYTES, 16); - nextTexts.first_array_to_write_to <== prevAccumulatedPlaintext; - nextTexts.second_array_to_write_to <== prevAccumulatedCiphertext; - nextTexts.first_array_to_write_at_index <== plainText; - nextTexts.second_array_to_write_at_index <== aes.cipherText; - nextTexts.index <== index * 16; - + component nextAccumulatedPackedText = WriteToIndex(DATA_BYTES, 16); + nextAccumulatedPackedText.array_to_write_to <== prevAccumulatedPackedText; + nextAccumulatedPackedText.array_to_write_at_index <== nextPackedChunk; + nextAccumulatedPackedText.index <== index * 16; for(var i = 0 ; i < TOTAL_BYTES_ACROSS_NIVC ; i++) { if(i < DATA_BYTES) { - step_out[i] <== nextTexts.outFirst[i]; - } else if(i < 2 * DATA_BYTES) { - step_out[i] <== nextTexts.outSecond[i - DATA_BYTES]; - } else if(i < 2 * DATA_BYTES + 4) { - step_out[i] <== aes.counter[i - (2 * DATA_BYTES)]; + step_out[i] <== nextAccumulatedPackedText.out[i]; + } else { + step_out[i] <== aes.counter[i - DATA_BYTES]; } } } - - -template WriteToIndexForTwoArrays(m, n) { - signal input first_array_to_write_to[m]; - signal input second_array_to_write_to[m]; - signal input first_array_to_write_at_index[n]; - signal input second_array_to_write_at_index[n]; - signal input index; - - signal output outFirst[m]; - signal output outSecond[m]; - - assert(m >= n); - - // Note: this is underconstrained, we need to constrain that index + n <= m - // Need to constrain that index + n <= m -- can't be an assertion, because uses a signal - // ------------------------- // - - // Here, we get an array of ALL zeros, except at the `index` AND `index + n` - // beginning-------^^^^^ end---^^^^^^^^^ - signal indexMatched[m]; - component indexBegining[m]; - component indexEnding[m]; - for(var i = 0 ; i < m ; i++) { - indexBegining[i] = IsZero(); - indexBegining[i].in <== i - index; - indexEnding[i] = IsZero(); - indexEnding[i].in <== i - (index + n); - indexMatched[i] <== indexBegining[i].out + indexEnding[i].out; - } - - // E.g., index == 31, m == 160, n == 16 - // => indexMatch[31] == 1; - // => indexMatch[47] == 1; - // => otherwise, all 0. - - signal accum[m]; - accum[0] <== indexMatched[0]; - - component writeAt = IsZero(); - writeAt.in <== accum[0] - 1; - - component orFirst = OR(); - orFirst.a <== (writeAt.out * first_array_to_write_at_index[0]); - orFirst.b <== (1 - writeAt.out) * first_array_to_write_to[0]; - outFirst[0] <== orFirst.out; - - component orSecond = OR(); - orSecond.a <== (writeAt.out * second_array_to_write_at_index[0]); - orSecond.b <== (1 - writeAt.out) * second_array_to_write_to[0]; - outSecond[0] <== orSecond.out; - // IF accum == 1 then { array_to_write_at } ELSE IF accum != 1 then { array to write_to } - signal accum_index[m]; - accum_index[0] <== accum[0]; - - component writeSelector[m - 1]; - component indexSelectorFirst[m - 1]; - component indexSelectorSecond[m - 1]; - component orsFirst[m-1]; - component orsSecond[m-1]; - for(var i = 1 ; i < m ; i++) { - // accum will be 1 at all indices where we want to write the new array - accum[i] <== accum[i-1] + indexMatched[i]; - writeSelector[i-1] = IsZero(); - writeSelector[i-1].in <== accum[i] - 1; - // IsZero(accum[i] - 1); --> tells us we are in the range where we want to write the new array - - indexSelectorFirst[i-1] = IndexSelector(n); - indexSelectorFirst[i-1].index <== accum_index[i-1]; - indexSelectorFirst[i-1].in <== first_array_to_write_at_index; - - indexSelectorSecond[i-1] = IndexSelector(n); - indexSelectorSecond[i-1].index <== accum_index[i-1]; - indexSelectorSecond[i-1].in <== second_array_to_write_at_index; - // When accum is not zero, out is array_to_write_at_index, otherwise it is array_to_write_to - - orsFirst[i-1] = OR(); - orsFirst[i-1].a <== (writeSelector[i-1].out * indexSelectorFirst[i-1].out); - orsFirst[i-1].b <== (1 - writeSelector[i-1].out) * first_array_to_write_to[i]; - outFirst[i] <== orsFirst[i-1].out; - - orsSecond[i-1] = OR(); - orsSecond[i-1].a <== (writeSelector[i-1].out * indexSelectorSecond[i-1].out); - orsSecond[i-1].b <== (1 - writeSelector[i-1].out) * second_array_to_write_to[i]; - outSecond[i] <== orsSecond[i-1].out; - - accum_index[i] <== accum_index[i-1] + writeSelector[i-1].out; - } -} diff --git a/circuits/http/nivc/body_mask.circom b/circuits/http/nivc/body_mask.circom index d4b46f2..1e53c34 100644 --- a/circuits/http/nivc/body_mask.circom +++ b/circuits/http/nivc/body_mask.circom @@ -4,7 +4,7 @@ include "../parser/machine.circom"; template HTTPMaskBodyNIVC(DATA_BYTES) { // ------------------------------------------------------------------------------------------------------------------ // - var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; // aes ct/pt + ctr + var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; // aes ct/pt + ctr // ------------------------------------------------------------------------------------------------------------------ // // ------------------------------------------------------------------------------------------------------------------ // diff --git a/circuits/http/nivc/lock_header.circom b/circuits/http/nivc/lock_header.circom index 8b9e430..29c31e5 100644 --- a/circuits/http/nivc/lock_header.circom +++ b/circuits/http/nivc/lock_header.circom @@ -8,7 +8,7 @@ include "circomlib/circuits/comparators.circom"; // TODO: should use a MAX_HEADER_NAME_LENGTH and a MAX_HEADER_VALUE_LENGTH template LockHeader(DATA_BYTES, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH) { // ------------------------------------------------------------------------------------------------------------------ // - var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; // aes pt/ct + ctr + var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; // aes pt/ct + ctr // ------------------------------------------------------------------------------------------------------------------ // // ------------------------------------------------------------------------------------------------------------------ // diff --git a/circuits/http/nivc/parse_and_lock_start_line.circom b/circuits/http/nivc/parse_and_lock_start_line.circom index afa1d3a..5b00255 100644 --- a/circuits/http/nivc/parse_and_lock_start_line.circom +++ b/circuits/http/nivc/parse_and_lock_start_line.circom @@ -9,7 +9,7 @@ include "../../utils/bytes.circom"; template ParseAndLockStartLine(DATA_BYTES, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENGTH, MAX_FINAL_LENGTH) { // ------------------------------------------------------------------------------------------------------------------ // // ~~ Set sizes at compile time ~~ - var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; // AES ct/pt + ctr + var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; // AES ct/pt + ctr // ------------------------------------------------------------------------------------------------------------------ // // ------------------------------------------------------------------------------------------------------------------ // @@ -17,10 +17,13 @@ template ParseAndLockStartLine(DATA_BYTES, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENG signal output step_out[TOTAL_BYTES_ACROSS_NIVC]; // Get the plaintext - signal data[DATA_BYTES]; + signal packedData[DATA_BYTES]; for (var i = 0 ; i < DATA_BYTES ; i++) { - data[i] <== step_in[i]; + packedData[i] <== step_in[i]; } + component unpackData = UnpackDoubleByteArray(DATA_BYTES); + unpackData.in <== packedData; + signal data[DATA_BYTES] <== unpackData.lower; signal input beginning[MAX_BEGINNING_LENGTH]; signal input beginning_length; @@ -100,7 +103,7 @@ template ParseAndLockStartLine(DATA_BYTES, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENG for (var i = 0 ; i < TOTAL_BYTES_ACROSS_NIVC ; i++) { // add plaintext http input to step_out and ignore the ciphertext if(i < DATA_BYTES) { - step_out[i] <== step_in[i]; + step_out[i] <== data[i]; // PASS OUT JUST THE PLAINTEXT DATA } else { step_out[i] <== 0; } diff --git a/circuits/json/nivc/extractor.circom b/circuits/json/nivc/extractor.circom index 32b4b1a..b5ba6b3 100644 --- a/circuits/json/nivc/extractor.circom +++ b/circuits/json/nivc/extractor.circom @@ -5,7 +5,7 @@ include "@zk-email/circuits/utils/array.circom"; template MaskExtractFinal(DATA_BYTES, MAX_VALUE_LENGTH) { // ------------------------------------------------------------------------------------------------------------------ // - var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; // aes pt/ct + ctr + var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; // aes pt/ct + ctr // ------------------------------------------------------------------------------------------------------------------ // signal input step_in[TOTAL_BYTES_ACROSS_NIVC]; signal output step_out[TOTAL_BYTES_ACROSS_NIVC]; diff --git a/circuits/json/nivc/masker.circom b/circuits/json/nivc/masker.circom index b10351f..76d4c08 100644 --- a/circuits/json/nivc/masker.circom +++ b/circuits/json/nivc/masker.circom @@ -5,7 +5,7 @@ include "../interpreter.circom"; template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) { // ------------------------------------------------------------------------------------------------------------------ // assert(MAX_STACK_HEIGHT >= 2); // TODO (autoparallel): idk if we need this now - var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; // aes ct/pt + ctr + var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; // aes ct/pt + ctr // ------------------------------------------------------------------------------------------------------------------ // signal input step_in[TOTAL_BYTES_ACROSS_NIVC]; signal output step_out[TOTAL_BYTES_ACROSS_NIVC]; @@ -87,7 +87,7 @@ template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) { // mask = currently parsing value and all subsequent keys matched step_out[data_idx] <== data[data_idx] * or[data_idx - 1]; } - for(var i = DATA_BYTES - MAX_KEY_LENGTH; i < 2 * DATA_BYTES + 4; i ++) { + for(var i = DATA_BYTES - MAX_KEY_LENGTH; i < DATA_BYTES + 4; i ++) { step_out[i] <== 0; } } @@ -95,7 +95,7 @@ template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) { template JsonMaskArrayIndexNIVC(DATA_BYTES, MAX_STACK_HEIGHT) { // ------------------------------------------------------------------------------------------------------------------ // assert(MAX_STACK_HEIGHT >= 2); // TODO (autoparallel): idk if we need this now - var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; // aes ct/pt + ctr + var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; // aes ct/pt + ctr // ------------------------------------------------------------------------------------------------------------------ // signal input step_in[TOTAL_BYTES_ACROSS_NIVC]; signal output step_out[TOTAL_BYTES_ACROSS_NIVC]; @@ -136,7 +136,7 @@ template JsonMaskArrayIndexNIVC(DATA_BYTES, MAX_STACK_HEIGHT) { or[data_idx - 1] <== OR()(parsing_array[data_idx], parsing_array[data_idx - 1]); step_out[data_idx] <== data[data_idx] * or[data_idx - 1]; } - for(var i = DATA_BYTES ; i < 2 * DATA_BYTES + 4; i++) { + for(var i = DATA_BYTES ; i < TOTAL_BYTES_ACROSS_NIVC; i++) { step_out[i] <== 0; } } diff --git a/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts b/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts index 88e361e..c80fae5 100644 --- a/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts +++ b/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts @@ -7,7 +7,7 @@ describe("aes-gctr-nivc", () => { const DATA_BYTES_0 = 16; - const TOTAL_BYTES_ACROSS_NIVC_0 = 2 * DATA_BYTES_0 + 4; + const TOTAL_BYTES_ACROSS_NIVC_0 = DATA_BYTES_0 + 4; it("all correct for self generated single zero pt block case", async () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { @@ -25,12 +25,13 @@ describe("aes-gctr-nivc", () => { const counter = [0x00, 0x00, 0x00, 0x01]; const step_in = new Array(TOTAL_BYTES_ACROSS_NIVC_0).fill(0x00); counter.forEach((value, index) => { - step_in[2 * DATA_BYTES_0 + index] = value; + step_in[DATA_BYTES_0 + index] = value; }); - let expected = plainText.concat(ct).concat([0x00, 0x00, 0x00, 0x02]); - expected = expected.concat(new Array(TOTAL_BYTES_ACROSS_NIVC_0 - expected.length).fill(0)); const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText, aad: aad, step_in: step_in }, ["step_out"]) + + let packed = plainText.map((x, i) => x + (ct[i] * 256)); + let expected = [...packed, 0x00, 0x00, 0x00, 0x02]; assert.deepEqual(witness.step_out, expected.map(BigInt)); }); @@ -50,18 +51,18 @@ describe("aes-gctr-nivc", () => { const counter = [0x00, 0x00, 0x00, 0x01]; const step_in = new Array(TOTAL_BYTES_ACROSS_NIVC_0).fill(0x00); counter.forEach((value, index) => { - step_in[2 * DATA_BYTES_0 + index] = value; + step_in[DATA_BYTES_0 + index] = value; }); - let expected = plainText.concat(ct).concat([0x00, 0x00, 0x00, 0x02]); - expected = expected.concat(new Array(TOTAL_BYTES_ACROSS_NIVC_0 - expected.length).fill(0)); - const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText, aad: aad, step_in: step_in }, ["step_out"]) + + let packed = plainText.map((x, i) => x + (ct[i] * 256)); + let expected = [...packed, 0x00, 0x00, 0x00, 0x02]; assert.deepEqual(witness.step_out, expected.map(BigInt)); }); const DATA_BYTES_1 = 32; - const TOTAL_BYTES_ACROSS_NIVC_1 = DATA_BYTES_1 * 2 + 4; + const TOTAL_BYTES_ACROSS_NIVC_1 = DATA_BYTES_1 + 4; let zero_block = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; @@ -83,12 +84,13 @@ describe("aes-gctr-nivc", () => { const counter = [0x00, 0x00, 0x00, 0x01]; const step_in = new Array(TOTAL_BYTES_ACROSS_NIVC_1).fill(0x00); counter.forEach((value, index) => { - step_in[2 * DATA_BYTES_1 + index] = value; + step_in[DATA_BYTES_1 + index] = value; }); - let expected = plainText1.concat(zero_block).concat(ct_part1).concat(zero_block).concat([0x00, 0x00, 0x00, 0x02]); - expected = expected.concat(new Array(TOTAL_BYTES_ACROSS_NIVC_1 - expected.length).fill(0)); const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText1, aad: aad, step_in: step_in }, ["step_out"]) + + let packed1 = plainText1.map((x, i) => x + (ct_part1[i] * 256)); + let expected = packed1.concat(zero_block).concat([0x00, 0x00, 0x00, 0x02]); assert.deepEqual(witness.step_out, expected.map(BigInt)); }); @@ -99,12 +101,13 @@ describe("aes-gctr-nivc", () => { params: [DATA_BYTES_1], // input len is 32 bytes }); - const counter = [0x00, 0x00, 0x00, 0x02]; - let step_in = plainText1.concat(zero_block).concat(ct_part1).concat(zero_block).concat(counter); + let packed1 = plainText1.map((x, i) => x + (ct_part1[i] * 256)); + let packed2 = plainText2.map((x, i) => x + (ct_part2[i] * 256)); + let step_in = packed1.concat(zero_block).concat([0x00, 0x00, 0x00, 0x02]); step_in = step_in.concat(new Array(TOTAL_BYTES_ACROSS_NIVC_1 - step_in.length).fill(0)); - let expected = plainText1.concat(plainText2).concat(ct_part1).concat(ct_part2).concat([0x00, 0x00, 0x00, 0x03]); - expected = expected.concat(new Array(TOTAL_BYTES_ACROSS_NIVC_1 - expected.length).fill(0)); + + let expected = packed1.concat(packed2).concat([0x00, 0x00, 0x00, 0x03]); const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText2, aad: aad, step_in: step_in }, ["step_out"]) assert.deepEqual(witness.step_out, expected.map(BigInt)); diff --git a/circuits/test/full/full.test.ts b/circuits/test/full/full.test.ts index 380d862..d657774 100644 --- a/circuits/test/full/full.test.ts +++ b/circuits/test/full/full.test.ts @@ -49,7 +49,7 @@ describe("NIVC_FULL", async () => { const DATA_BYTES = 320; const MAX_STACK_HEIGHT = 5; - const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; + const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; const MAX_HEADER_NAME_LENGTH = 20; const MAX_HEADER_VALUE_LENGTH = 35; @@ -132,7 +132,7 @@ describe("NIVC_FULL", async () => { const counter = [0x00, 0x00, 0x00, 0x01]; const init_nivc_input = new Array(TOTAL_BYTES_ACROSS_NIVC).fill(0x00); counter.forEach((value, index) => { - init_nivc_input[2 * DATA_BYTES + index] = value; + init_nivc_input[DATA_BYTES + index] = value; }); let pt = http_response_plaintext.slice(0, 16); aes_gcm = await aesCircuit.compute({ key: Array(16).fill(0), iv: Array(12).fill(0), plainText: pt, aad: Array(16).fill(0), step_in: init_nivc_input }, ["step_out"]); @@ -154,8 +154,6 @@ describe("NIVC_FULL", async () => { let maskedInput = extendedJsonInput.fill(0, 0, idx); maskedInput = maskedInput.fill(0, 320); - - let key0 = [100, 97, 116, 97, 0, 0, 0, 0]; // "data" let key0Len = 4; let key1 = [105, 116, 101, 109, 115, 0, 0, 0]; // "items" diff --git a/circuits/test/http/nivc/body_mask.test.ts b/circuits/test/http/nivc/body_mask.test.ts index 6c5e5b6..1e45ed4 100644 --- a/circuits/test/http/nivc/body_mask.test.ts +++ b/circuits/test/http/nivc/body_mask.test.ts @@ -42,7 +42,7 @@ describe("NIVC_HTTP", async () => { let bodyMaskCircuit: WitnessTester<["step_in"], ["step_out"]>; const DATA_BYTES = 320; - const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; + const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; const MAX_HEADER_NAME_LENGTH = 20; const MAX_HEADER_VALUE_LENGTH = 35; diff --git a/circuits/test/http/nivc/lock_header.test.ts b/circuits/test/http/nivc/lock_header.test.ts index 06eae58..81d3dd6 100644 --- a/circuits/test/http/nivc/lock_header.test.ts +++ b/circuits/test/http/nivc/lock_header.test.ts @@ -6,7 +6,7 @@ describe("HTTPLockHeader", async () => { let lockHeaderCircuit: WitnessTester<["step_in", "header", "headerNameLength", "value", "headerValueLength"], ["step_out"]>; const DATA_BYTES = 320; - const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; + const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; const MAX_BEGINNING_LENGTH = 10; const MAX_MIDDLE_LENGTH = 50; diff --git a/circuits/test/http/nivc/parse_and_lock_start_line.test.ts b/circuits/test/http/nivc/parse_and_lock_start_line.test.ts index c843c6d..ff0c29b 100644 --- a/circuits/test/http/nivc/parse_and_lock_start_line.test.ts +++ b/circuits/test/http/nivc/parse_and_lock_start_line.test.ts @@ -5,7 +5,7 @@ describe("HTTPParseAndLockStartLine", async () => { let httpParseAndLockStartLineCircuit: WitnessTester<["step_in", "beginning", "beginning_length", "middle", "middle_length", "final", "final_length"], ["step_out"]>; const DATA_BYTES = 320; - const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; + const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; const MAX_BEGINNING_LENGTH = 10; const MAX_MIDDLE_LENGTH = 50; diff --git a/circuits/test/json/nivc/masker_nivc.test.ts b/circuits/test/json/nivc/masker_nivc.test.ts index d3ec35b..455dd89 100644 --- a/circuits/test/json/nivc/masker_nivc.test.ts +++ b/circuits/test/json/nivc/masker_nivc.test.ts @@ -39,7 +39,7 @@ describe("NIVC Extract", async () => { const MAX_STACK_HEIGHT = 5; const MAX_KEY_LENGTH = 8; const MAX_VALUE_LENGTH = 35; - const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; + const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES + 4; before(async () => { json_mask_arr_circuit = await circomkit.WitnessTester(`JsonMaskArrayIndexNIVC`, { diff --git a/circuits/test/utils/bytes.test.ts b/circuits/test/utils/bytes.test.ts index d0690a3..dfb11db 100644 --- a/circuits/test/utils/bytes.test.ts +++ b/circuits/test/utils/bytes.test.ts @@ -22,4 +22,149 @@ describe("ASCII", () => { { in: [256, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] } ); }); -}); \ No newline at end of file +}); + +describe("BytePack", () => { + let circuit: WitnessTester<["lower", "upper"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`DoubleBytePackArray`, { + file: "utils/bytes", + template: "DoubleBytePackArray", + params: [1], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: lower = 0, upper = 1", async () => { + await circuit.expectPass( + { lower: [0], upper: [1] }, { out: [256] } + ); + }); + + it("witness: lower = 1, upper = 1", async () => { + await circuit.expectPass( + { lower: [1], upper: [1] }, { out: [257] } + ); + }); + + it("witness: lower = 1, upper = 0", async () => { + await circuit.expectPass( + { lower: [1], upper: [0] }, { out: [1] } + ); + }); +}); + +describe("BytePack2", () => { + let circuit: WitnessTester<["lower", "upper"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`DoubleBytePackArray`, { + file: "utils/bytes", + template: "DoubleBytePackArray", + params: [2], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + it("witness: lower = [1,0], upper = [0,1]", async () => { + await circuit.expectPass( + { lower: [1, 0], upper: [0, 1] }, { out: [1, 256] } + ); + }); +}); + +describe("ByteUnpack", () => { + let circuit: WitnessTester<["in"], ["lower", "upper"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`UnpackDoubleByteArray`, { + file: "utils/bytes", + template: "UnpackDoubleByteArray", + params: [1], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: in = 256", async () => { + await circuit.expectPass( + { in: [256] }, { lower: [0], upper: [1] } + ); + }); + + it("witness: in = 257", async () => { + await circuit.expectPass( + { in: [257] }, { lower: [1], upper: [1] } + ); + }); + + it("witness: in = 1", async () => { + await circuit.expectPass( + { in: [1] }, { lower: [1], upper: [] } + ); + }); +}); + +describe("ByteUnpack2", () => { + let circuit: WitnessTester<["in"], ["lower", "upper"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`UnpackDoubleByteArray`, { + file: "utils/bytes", + template: "UnpackDoubleByteArray", + params: [2], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: in = [1,256]", async () => { + await circuit.expectPass( + { in: [1, 256] }, { lower: [1, 0], upper: [0, 1] } + ); + }); + +}); + + + +// Generic version +describe("GenericBytePack2", () => { + let circuit: WitnessTester<["in"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`GenericBytePackArray`, { + file: "utils/bytes", + template: "GenericBytePackArray", + params: [2, 3], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + it("witness: lower = [1,0,0], upper = [0,1,0]", async () => { + await circuit.expectPass( + { in: [[1, 0, 0], [0, 1, 0]] }, { out: [1, 256] } + ); + }); + it("witness: lower = [1,0,0], upper = [0,0,1]", async () => { + await circuit.expectPass( + { in: [[1, 0, 0], [0, 0, 1]] }, { out: [1, 65536] } + ); + }); +}); + +describe("GenericByteUnpack2", () => { + let circuit: WitnessTester<["in"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`GenericByteUnpackArray`, { + file: "utils/bytes", + template: "GenericByteUnpackArray", + params: [2, 3], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: in = [1,256]", async () => { + await circuit.expectPass( + { in: [1, 256] }, { out: [[1, 0, 0], [0, 1, 0]] } + ); + }); + + it("witness: in = [1,256]", async () => { + await circuit.expectPass( + { in: [1, 65536] }, { out: [[1, 0, 0], [0, 0, 1]] } + ); + }); +}); diff --git a/circuits/utils/bytes.circom b/circuits/utils/bytes.circom index 10090cc..93a523d 100644 --- a/circuits/utils/bytes.circom +++ b/circuits/utils/bytes.circom @@ -187,4 +187,72 @@ template BitwiseXor(n) { mid[k] <== a[k]*b[k]; out[k] <== a[k] + b[k] - 2*mid[k]; } +} + +template DoubleBytePackArray(n) { + signal input lower[n]; + signal input upper[n]; + signal output out[n]; + + for(var i = 0 ; i < n ; i++) { + out[i] <== lower[i] + 2**8 * upper[i]; + } +} + +template UnpackDoubleByteArray(n) { + signal input in[n]; + signal output lower[n]; + signal output upper[n]; + + signal inBits[n][16]; + var lowerAccum[n]; + var upperAccum[n]; + for(var i = 0 ; i < n ; i ++) { + inBits[i] <== Num2Bits(16)(in[i]); + lowerAccum[i] = 0; + upperAccum[i] = 0; + for(var j = 0 ; j < 16 ; j++) { + var selector = j \ 8; + if(selector == 0) { + lowerAccum[i] += inBits[i][j] * 2**(j%8); + } else if(selector == 1) { + upperAccum[i] += inBits[i][j] * 2**(j%8); + } + } + } + lower <== lowerAccum; + upper <== upperAccum; +} + +template GenericBytePackArray(n,p) { + assert(p<=16); + signal input in[n][p]; + signal output out[n]; + + var accum[n]; + for(var i = 0 ; i < n ; i++) { + for(var j = 0 ; j < p ; j++) { + accum[i] += 2**(8*j)*in[i][j]; + } + } + out <== accum; +} + +template GenericByteUnpackArray(n,p) { + assert(p<=16); + signal input in[n]; + signal output out[n][p]; + + signal inBits[n][8 * p]; + var accum[n][p]; + for(var i = 0 ; i < n ; i++) { + inBits[i] <== Num2Bits(8 * p)(in[i]); + for(var j = 0 ; j < p ; j++) { + accum[i][j] = 0; + } + for(var j = 0 ; j < 8 * p ; j++) { + accum[i][j\8] += inBits[i][j] * 2**(j%8); + } + } + out <== accum; } \ No newline at end of file diff --git a/package.json b/package.json index 13e5752..b993580 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "web-prover-circuits", "description": "ZK Circuits for WebProofs", - "version": "0.3.0", + "version": "0.3.1", "license": "Apache-2.0", "repository": { "type": "git",