From 10c9c0743c8226a18db2ca2bd491ba368df41742 Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 10:22:16 +0700 Subject: [PATCH 01/11] added test/http/locker.test.ts --- circuits/test/http/locker.test.ts | 136 ++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 circuits/test/http/locker.test.ts diff --git a/circuits/test/http/locker.test.ts b/circuits/test/http/locker.test.ts new file mode 100644 index 0000000..34f32be --- /dev/null +++ b/circuits/test/http/locker.test.ts @@ -0,0 +1,136 @@ +import { circomkit, WitnessTester, generateDescription, toByte } from "../common"; +import { readHTTPInputFile } from "../common/http"; + +describe("HTTP :: Locker :: Request Line", async () => { + let circuit: WitnessTester<["data", "beginning", "middle", "final"], []>; + + function generatePassCase(input: number[], beginning: number[], middle: number[], final: number[], desc: string) { + const description = generateDescription(input); + + it(`(valid) witness: ${description} ${desc}`, async () => { + circuit = await circomkit.WitnessTester(`LockStartLine`, { + file: "http/locker", + template: "LockStartLine", + params: [input.length, beginning.length, middle.length, final.length], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + + await circuit.expectPass({ data: input, beginning: beginning, middle: middle, final: final }, {}); + }); + } + + function generateFailCase(input: number[], beginning: number[], middle: number[], final: number[], desc: string) { + const description = generateDescription(input); + + it(`(invalid) witness: ${description} ${desc}`, async () => { + circuit = await circomkit.WitnessTester(`LockStartLine`, { + file: "http/locker", + template: "LockStartLine", + params: [input.length, beginning.length, middle.length, final.length], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + + await circuit.expectFail({ data: input, beginning: beginning, middle: middle, final: final }); + }); + } + + describe("GET", async () => { + let parsedHttp = readHTTPInputFile("get_request.http"); + generatePassCase(parsedHttp.input, toByte("GET"), toByte("/api"), toByte("HTTP/1.1"), ""); + generateFailCase(parsedHttp.input.slice(0), toByte("POST"), toByte("/api"), toByte("HTTP/1.1"), ""); + generateFailCase(parsedHttp.input.slice(0), toByte("GET"), toByte("/"), toByte("HTTP/1.1"), ""); + generateFailCase(parsedHttp.input.slice(0), toByte("GET"), toByte("/api"), toByte("HTTP"), ""); + }); + + describe("POST", async () => { + let parsedHttp = readHTTPInputFile("post_request.http"); + generatePassCase(parsedHttp.input, toByte("POST"), toByte("/contact_form.php"), toByte("HTTP/1.1"), ""); + generateFailCase(parsedHttp.input.slice(0), toByte("GET"), toByte("/contact_form.php"), toByte("HTTP/1.1"), ""); + generateFailCase(parsedHttp.input.slice(0), toByte("POST"), toByte("/"), toByte("HTTP/1.1"), ""); + generateFailCase(parsedHttp.input.slice(0), toByte("POST"), toByte("/contact_form.php"), toByte("HTTP"), ""); + }); +}); + +describe("HTTP :: Locker :: Status Line", async () => { + let circuit: WitnessTester<["data", "beginning", "middle", "final"], []>; + + function generatePassCase(input: number[], beginning: number[], middle: number[], final: number[], desc: string) { + const description = generateDescription(input); + + it(`(valid) witness: ${description} ${desc}`, async () => { + circuit = await circomkit.WitnessTester(`LockStartLine`, { + file: "http/locker", + template: "LockStartLine", + params: [input.length, beginning.length, middle.length, final.length], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + + await circuit.expectPass({ data: input, beginning: beginning, middle: middle, final: final }, {}); + }); + } + + function generateFailCase(input: number[], beginning: number[], middle: number[], final: number[], desc: string) { + const description = generateDescription(input); + + it(`(invalid) witness: ${description} ${desc}`, async () => { + circuit = await circomkit.WitnessTester(`LockStartLine`, { + file: "http/locker", + template: "LockStartLine", + params: [input.length, beginning.length, middle.length, final.length], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + + await circuit.expectFail({ data: input, beginning: beginning, middle: middle, final: final }); + }); + } + + describe("GET", async () => { + let parsedHttp = readHTTPInputFile("get_response.http"); + generatePassCase(parsedHttp.input, toByte("HTTP/1.1"), toByte("200"), toByte("OK"), ""); + generateFailCase(parsedHttp.input, toByte("HTTP"), toByte("200"), toByte("OK"), ""); + generateFailCase(parsedHttp.input, toByte("HTTP/1.1"), toByte("404"), toByte("OK"), ""); + generateFailCase(parsedHttp.input, toByte("HTTP/1.1"), toByte("200"), toByte("Not Found"), ""); + }); +}); + +describe("HTTP :: Locker :: Header", async () => { + let circuit: WitnessTester<["data", "header", "value"], []>; + + function generatePassCase(input: number[], header: number[], value: number[], desc: string) { + const description = generateDescription(input); + + it(`(valid) witness: ${description} ${desc}`, async () => { + circuit = await circomkit.WitnessTester(`LockHeader`, { + file: "http/locker", + template: "LockHeader", + params: [input.length, header.length, value.length], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + + await circuit.expectPass({ data: input, header: header, value: value }, {}); + }); + } + + function generateFailCase(input: number[], header: number[], value: number[], desc: string) { + const description = generateDescription(input); + + it(`(invalid) witness: ${description} ${desc}`, async () => { + circuit = await circomkit.WitnessTester(`LockHeader`, { + file: "http/locker", + template: "LockHeader", + params: [input.length, header.length, value.length], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + + await circuit.expectFail({ data: input, header: header, value: value }); + }); + } + + describe("GET", async () => { + let parsedHttp = readHTTPInputFile("get_request.http"); + generatePassCase(parsedHttp.input, toByte("Host"), toByte("localhost"), ""); + generateFailCase(parsedHttp.input, toByte("Accept"), toByte("localhost"), ""); + generateFailCase(parsedHttp.input, toByte("Host"), toByte("venmo.com"), ""); + generateFailCase(parsedHttp.input, toByte("Connection"), toByte("keep-alive"), ""); + }); +}); \ No newline at end of file From cf2b7f2e91b50daeda3f148e331a730e78abcb0f Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 11:23:54 +0700 Subject: [PATCH 02/11] use updated circuits local paths. All compile --- circuits/aes-gctr-nivc.circom | 1 - circuits/extract_value.circom | 34 +---- circuits/http_body_mask.circom | 34 +---- circuits/http_lock_header.circom | 86 +----------- .../http_parse_and_lock_start_line.circom | 125 +----------------- circuits/json/nivc/extractor.circom | 4 +- circuits/json_mask_array_index.circom | 70 +--------- circuits/json_mask_object.circom | 109 +-------------- circuits/json_parse.circom | 58 +------- package-lock.json | 97 +++++++------- package.json | 6 +- 11 files changed, 68 insertions(+), 556 deletions(-) diff --git a/circuits/aes-gctr-nivc.circom b/circuits/aes-gctr-nivc.circom index 7886f37..2ec7580 100644 --- a/circuits/aes-gctr-nivc.circom +++ b/circuits/aes-gctr-nivc.circom @@ -2,6 +2,5 @@ pragma circom 2.1.9; include "aes-gcm/nivc/aes-gctr-nivc.circom"; -// Note(WJ 2024-10-31): I put this here like this because i have tests i wanted to include for this component // the circomkit tests become unhappy when there is a main. component main { public [step_in] } = AESGCTRFOLD(48); \ No newline at end of file diff --git a/circuits/extract_value.circom b/circuits/extract_value.circom index 3a492ab..8e3906b 100644 --- a/circuits/extract_value.circom +++ b/circuits/extract_value.circom @@ -1,35 +1,5 @@ pragma circom 2.1.9; -include "circomlib/circuits/gates.circom"; -include "@zk-email/circuits/utils/array.circom"; +include "json/nivc/extractor.circom"; -template MaskExtractFinal(TOTAL_BYTES, maxValueLen) { - signal input step_in[TOTAL_BYTES]; - signal output step_out[TOTAL_BYTES]; - - signal is_zero_mask[TOTAL_BYTES]; - signal is_prev_starting_index[TOTAL_BYTES]; - signal value_starting_index[TOTAL_BYTES]; - - value_starting_index[0] <== 0; - is_prev_starting_index[0] <== 0; - is_zero_mask[0] <== IsZero()(step_in[0]); - for (var i=1 ; i var TOTAL_BYTES = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1); // data + parser vars - // ------------------------------------------------------------------------------------------------------------------ // - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Unravel from previous NIVC step ~ - // Read in from previous NIVC step (HttpParseAndLockStartLine or HTTPLockHeader) - signal input step_in[TOTAL_BYTES]; - - signal data[DATA_BYTES]; - signal parsing_body[DATA_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - data[i] <== step_in[i]; - parsing_body[i] <== step_in[DATA_BYTES + i * 5 + 4]; - } - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Write out to next NIVC step - signal output step_out[TOTAL_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - step_out[i] <== data[i] * parsing_body[i]; - } - // Write out padded with zeros - for (var i = DATA_BYTES ; i < TOTAL_BYTES ; i++) { - step_out[i] <== 0; - } -} +include "http/nivc/body_mask.circom"; component main { public [step_in] } = HTTPMaskBodyNIVC(4160, 320); diff --git a/circuits/http_lock_header.circom b/circuits/http_lock_header.circom index 46a55f0..c81aa82 100644 --- a/circuits/http_lock_header.circom +++ b/circuits/http_lock_header.circom @@ -1,87 +1,7 @@ pragma circom 2.1.9; -include "parser-attestor/circuits/http/interpreter.circom"; -include "parser-attestor/circuits/utils/array.circom"; +include "http/nivc/lock_header.circom"; -template LockHeader(TOTAL_BYTES, DATA_BYTES, headerNameLen, headerValueLen) { - // ------------------------------------------------------------------------------------------------------------------ // - // ~~ Set sizes at compile time ~~ - // Total number of variables in the parser for each byte of data - var PER_ITERATION_DATA_LENGTH = 5; - var TOTAL_BYTES_USED = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1); // data + parser vars - // ------------------------------------------------------------------------------------------------------------------ // - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Unravel from previous NIVC step ~ - // Read in from previous NIVC step (HttpParseAndLockStartLine or HTTPLockHeader) - signal input step_in[TOTAL_BYTES]; - - signal data[DATA_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - data[i] <== step_in[i]; - } - - signal input header[headerNameLen]; - signal input value[headerValueLen]; - - component headerNameLocation = FirstStringMatch(DATA_BYTES, headerNameLen); - headerNameLocation.data <== data; - headerNameLocation.key <== header; - - component headerFieldNameValueMatch; - headerFieldNameValueMatch = HeaderFieldNameValueMatch(DATA_BYTES, headerNameLen, headerValueLen); - headerFieldNameValueMatch.data <== data; - headerFieldNameValueMatch.headerName <== header; - headerFieldNameValueMatch.headerValue <== value; - headerFieldNameValueMatch.index <== headerNameLocation.position; - - // TODO: Make this assert we are parsing header!!! - // This is the assertion that we have locked down the correct header - headerFieldNameValueMatch.out === 1; - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Write out to next NIVC step - signal output step_out[TOTAL_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - // add plaintext http input to step_out - step_out[i] <== step_in[i]; - - // add parser state - step_out[DATA_BYTES + i * 5] <== step_in[DATA_BYTES + i * 5]; - step_out[DATA_BYTES + i * 5 + 1] <== step_in[DATA_BYTES + i * 5 + 1]; - step_out[DATA_BYTES + i * 5 + 2] <== step_in[DATA_BYTES + i * 5 + 2]; - step_out[DATA_BYTES + i * 5 + 3] <== step_in[DATA_BYTES + i * 5 + 3]; - step_out[DATA_BYTES + i * 5 + 4] <== step_in[DATA_BYTES + i * 5 + 4]; - } - // Pad remaining with zeros - for (var i = TOTAL_BYTES_USED ; i < TOTAL_BYTES ; i++ ) { - step_out[i] <== 0; - } -} - -// TODO: Handrolled template that I haven't tested YOLO. -template FirstStringMatch(dataLen, keyLen) { - signal input data[dataLen]; - signal input key[keyLen]; - signal output position; - - var matched = 0; - var counter = 0; - component stringMatch[dataLen - keyLen]; - component hasMatched[dataLen - keyLen]; - for (var idx = 0 ; idx < dataLen - keyLen ; idx++) { - stringMatch[idx] = IsEqualArray(keyLen); - stringMatch[idx].in[0] <== key; - for (var key_idx = 0 ; key_idx < keyLen ; key_idx++) { - stringMatch[idx].in[1][key_idx] <== data[idx + key_idx] * (1 - matched); - } - hasMatched[idx] = IsEqual(); - hasMatched[idx].in <== [stringMatch[idx].out, 1]; - matched += hasMatched[idx].out; - counter += (1 - matched); // TODO: Off by one? Move before? - } - position <== counter; -} - -component main { public [step_in] } = LockHeader(4160, 320, 12, 31); +component main { public [step_in] } = LockHeader(48, 16, 12, 16); +// this one took about diff --git a/circuits/http_parse_and_lock_start_line.circom b/circuits/http_parse_and_lock_start_line.circom index d47d50b..f0ba8a5 100644 --- a/circuits/http_parse_and_lock_start_line.circom +++ b/circuits/http_parse_and_lock_start_line.circom @@ -1,126 +1,5 @@ pragma circom 2.1.9; -include "parser-attestor/circuits/http/parser/machine.circom"; -include "parser-attestor/circuits/http/interpreter.circom"; -include "parser-attestor/circuits/utils/bytes.circom"; +include "http/nivc/parse_and_lock_start_line.circom"; -// TODO: Note that TOTAL_BYTES will match what we have for AESGCMFOLD step_out -// I have not gone through to double check the sizes of everything yet. -template LockStartLine(TOTAL_BYTES, DATA_BYTES, beginningLen, middleLen, finalLen) { - // ------------------------------------------------------------------------------------------------------------------ // - // ~~ Set sizes at compile time ~~ - // Total number of variables in the parser for each byte of data - var AES_BYTES = DATA_BYTES + 50; // TODO: Might be wrong, but good enough for now - var PER_ITERATION_DATA_LENGTH = 5; - var TOTAL_BYTES_USED = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1); // data + parser vars - // ------------------------------------------------------------------------------------------------------------------ // - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Unravel from previous NIVC step ~ - // Read in from previous NIVC step (JsonParseNIVC) - signal input step_in[TOTAL_BYTES]; - - signal data[DATA_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - data[i] <== step_in[50 + i]; - } - - // // TODO: check if these needs to here or not - // component dataASCII = ASCII(DATA_BYTES); - // dataASCII.in <== data; - - signal input beginning[beginningLen]; - signal input middle[middleLen]; - signal input final[finalLen]; - - // Initialze the parser - component State[DATA_BYTES]; - State[0] = HttpStateUpdate(); - State[0].byte <== data[0]; - State[0].parsing_start <== 1; - State[0].parsing_header <== 0; - State[0].parsing_field_name <== 0; - State[0].parsing_field_value <== 0; - State[0].parsing_body <== 0; - State[0].line_status <== 0; - - /* - Note, because we know a beginning is the very first thing in a request - we can make this more efficient by just comparing the first `beginningLen` bytes - of the data ASCII against the beginning ASCII itself. - */ - // Check first beginning byte - signal beginningIsEqual[beginningLen]; - beginningIsEqual[0] <== IsEqual()([data[0],beginning[0]]); - beginningIsEqual[0] === 1; - - // Setup to check middle bytes - signal startLineMask[DATA_BYTES]; - signal middleMask[DATA_BYTES]; - signal finalMask[DATA_BYTES]; - - var middle_start_counter = 1; - var middle_end_counter = 1; - var final_end_counter = 1; - for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) { - State[data_idx] = HttpStateUpdate(); - State[data_idx].byte <== data[data_idx]; - State[data_idx].parsing_start <== State[data_idx - 1].next_parsing_start; - State[data_idx].parsing_header <== State[data_idx - 1].next_parsing_header; - State[data_idx].parsing_field_name <== State[data_idx - 1].next_parsing_field_name; - State[data_idx].parsing_field_value <== State[data_idx - 1].next_parsing_field_value; - State[data_idx].parsing_body <== State[data_idx - 1].next_parsing_body; - State[data_idx].line_status <== State[data_idx - 1].next_line_status; - - // Check remaining beginning bytes - if(data_idx < beginningLen) { - beginningIsEqual[data_idx] <== IsEqual()([data[data_idx], beginning[data_idx]]); - beginningIsEqual[data_idx] === 1; - } - - // Set the masks based on parser state - startLineMask[data_idx] <== inStartLine()(State[data_idx].parsing_start); - middleMask[data_idx] <== inStartMiddle()(State[data_idx].parsing_start); - finalMask[data_idx] <== inStartEnd()(State[data_idx].parsing_start); - - // Increment counters based on mask information - middle_start_counter += startLineMask[data_idx] - middleMask[data_idx] - finalMask[data_idx]; - middle_end_counter += startLineMask[data_idx] - finalMask[data_idx]; - final_end_counter += startLineMask[data_idx]; - } - - // Additionally verify beginning had correct length - beginningLen === middle_start_counter - 1; - - // Check middle is correct by substring match and length check - signal middleMatch <== SubstringMatchWithIndex(DATA_BYTES, middleLen)(data, middle, middle_start_counter); - middleMatch === 1; - middleLen === middle_end_counter - middle_start_counter - 1; - - // Check final is correct by substring match and length check - signal finalMatch <== SubstringMatchWithIndex(DATA_BYTES, finalLen)(data, final, middle_end_counter); - finalMatch === 1; - // -2 here for the CRLF - finalLen === final_end_counter - middle_end_counter - 2; - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Write out to next NIVC step (Lock Header) - signal output step_out[TOTAL_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - // add plaintext http input to step_out - step_out[i] <== step_in[50 + i]; - - // add parser state - step_out[DATA_BYTES + i * 5] <== State[i].next_parsing_start; - step_out[DATA_BYTES + i * 5 + 1] <== State[i].next_parsing_header; - step_out[DATA_BYTES + i * 5 + 2] <== State[i].next_parsing_field_name; - step_out[DATA_BYTES + i * 5 + 3] <== State[i].next_parsing_field_value; - step_out[DATA_BYTES + i * 5 + 4] <== State[i].next_parsing_body; - } - // Pad remaining with zeros - for (var i = TOTAL_BYTES_USED ; i < TOTAL_BYTES ; i++ ) { - step_out[i] <== 0; - } -} - -component main { public [step_in] } = LockStartLine(4160, 320, 8, 3, 2); \ No newline at end of file +component main { public [step_in] } = ParseAndLockStartLine(48, 16, 8, 3, 2); \ No newline at end of file diff --git a/circuits/json/nivc/extractor.circom b/circuits/json/nivc/extractor.circom index aadb436..f3d48eb 100644 --- a/circuits/json/nivc/extractor.circom +++ b/circuits/json/nivc/extractor.circom @@ -42,6 +42,4 @@ template MaskExtractFinal(DATA_BYTES, MAX_STACK_HEIGHT, MAX_VALUE_LENGTH) { } // TODO: Do anything with last depth? // step_out[TOTAL_BYTES_ACROSS_NIVC - 1] <== 0; -} - -// component main { public [step_in] } = MaskExtractFinal(4160, 320, 200); \ No newline at end of file +} \ No newline at end of file diff --git a/circuits/json_mask_array_index.circom b/circuits/json_mask_array_index.circom index 5cf36c7..dfe2334 100644 --- a/circuits/json_mask_array_index.circom +++ b/circuits/json_mask_array_index.circom @@ -1,71 +1,5 @@ pragma circom 2.1.9; -include "parser-attestor/circuits/json/interpreter.circom"; +include "json/nivc/masker.circom"; -template JsonMaskArrayIndexNIVC(TOTAL_BYTES, DATA_BYTES, MAX_STACK_HEIGHT) { - // ------------------------------------------------------------------------------------------------------------------ // - // ~~ Set sizes at compile time ~~ - // Total number of variables in the parser for each byte of data - assert(MAX_STACK_HEIGHT >= 2); - var PER_ITERATION_DATA_LENGTH = MAX_STACK_HEIGHT * 2 + 2; - var TOTAL_BYTES_USED = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1); - // ------------------------------------------------------------------------------------------------------------------ // - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Unravel from previous NIVC step ~ - // Read in from previous NIVC step (JsonParseNIVC) - signal input step_in[TOTAL_BYTES]; - - // Grab the raw data bytes from the `step_in` variable - signal data[DATA_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - data[i] <== step_in[i]; - } - - // Decode the encoded data in `step_in` back into parser variables - signal stack[DATA_BYTES][MAX_STACK_HEIGHT][2]; - signal parsingData[DATA_BYTES][2]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - for (var j = 0 ; j < MAX_STACK_HEIGHT ; j++) { - stack[i][j][0] <== step_in[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + j * 2]; - stack[i][j][1] <== step_in[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + j * 2 + 1]; - } - parsingData[i][0] <== step_in[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + MAX_STACK_HEIGHT * 2]; - parsingData[i][1] <== step_in[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + MAX_STACK_HEIGHT * 2 + 1]; - } - // ------------------------------------------------------------------------------------------------------------------ // - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Array index masking ~ - signal input index; - - // value starting index in `data` - signal value_starting_index[DATA_BYTES]; - signal mask[DATA_BYTES]; - - signal parsing_array[DATA_BYTES]; - signal or[DATA_BYTES]; - - parsing_array[0] <== InsideArrayIndexObject()(stack[0][0], stack[0][1], parsingData[0][0], parsingData[0][1], index); - mask[0] <== data[0] * parsing_array[0]; - - for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) { - parsing_array[data_idx] <== InsideArrayIndexObject()(stack[data_idx][0], stack[data_idx][1], parsingData[data_idx][0], parsingData[data_idx][1], index); - - or[data_idx] <== OR()(parsing_array[data_idx], parsing_array[data_idx - 1]); - mask[data_idx] <== data[data_idx] * or[data_idx]; - } - - // Write the `step_out` with masked data - signal output step_out[TOTAL_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - step_out[i] <== mask[i]; - } - // Append the parser state back on `step_out` - for (var i = DATA_BYTES ; i < TOTAL_BYTES ; i++) { - step_out[i] <== step_in[i]; - } - // No need to pad as this is currently when TOTAL_BYTES == TOTAL_BYTES_USED -} - -component main { public [step_in] } = JsonMaskArrayIndexNIVC(4160, 320, 5); \ No newline at end of file +component main { public [step_in] } = JsonMaskArrayIndexNIVC(48, 16); \ No newline at end of file diff --git a/circuits/json_mask_object.circom b/circuits/json_mask_object.circom index 89b7135..5f49944 100644 --- a/circuits/json_mask_object.circom +++ b/circuits/json_mask_object.circom @@ -1,110 +1,5 @@ pragma circom 2.1.9; -include "parser-attestor/circuits/json/interpreter.circom"; +include "json/nivc/masker.circom"; -template JsonMaskObjectNIVC(TOTAL_BYTES, DATA_BYTES, MAX_STACK_HEIGHT, maxKeyLen) { - // ------------------------------------------------------------------------------------------------------------------ // - // ~~ Set sizes at compile time ~~ - // Total number of variables in the parser for each byte of data - assert(MAX_STACK_HEIGHT >= 2); - var PER_ITERATION_DATA_LENGTH = MAX_STACK_HEIGHT * 2 + 2; - var TOTAL_BYTES_USED = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1); // data + parser vars - // ------------------------------------------------------------------------------------------------------------------ // - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Unravel from previous NIVC step ~ - // Read in from previous NIVC step (JsonParseNIVC) - signal input step_in[TOTAL_BYTES]; - - // Grab the raw data bytes from the `step_in` variable - signal data[DATA_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - data[i] <== step_in[i]; - } - - // Decode the encoded data in `step_in` back into parser variables - signal stack[DATA_BYTES][MAX_STACK_HEIGHT][2]; - signal parsingData[DATA_BYTES][2]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - for (var j = 0 ; j < MAX_STACK_HEIGHT ; j++) { - stack[i][j][0] <== step_in[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + j * 2]; - stack[i][j][1] <== step_in[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + j * 2 + 1]; - } - parsingData[i][0] <== step_in[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + MAX_STACK_HEIGHT * 2]; - parsingData[i][1] <== step_in[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + MAX_STACK_HEIGHT * 2 + 1]; - } - // ------------------------------------------------------------------------------------------------------------------ // - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Object masking ~ - // Key data to use to point to which object to extract - signal input key[maxKeyLen]; - signal input keyLen; - - // flag determining whether this byte is matched value - signal is_value_match[DATA_BYTES - maxKeyLen]; - // final mask - signal mask[DATA_BYTES - maxKeyLen]; - - - // signal parsing_object_value[DATA_BYTES - maxKeyLen]; - signal is_key_match[DATA_BYTES - maxKeyLen]; - signal is_key_match_for_value[DATA_BYTES + 1 - maxKeyLen]; - is_key_match_for_value[0] <== 0; - signal is_next_pair_at_depth[DATA_BYTES - maxKeyLen]; - signal or[DATA_BYTES - maxKeyLen]; - - // Signals to detect if we are parsing a key or value with initial setup - signal parsing_key[DATA_BYTES - maxKeyLen]; - signal parsing_value[DATA_BYTES - maxKeyLen]; - // TODO: Can't these just be 0 since the start of object can't be either of these? - // parsing_key[0] <== InsideKey()(stack[0][0], parsingData[0][0], parsingData[0][1]); - // parsing_value[0] <== InsideValueObject()(stack[0][0], stack[0][1], parsingData[0][0], parsingData[0][1]); - - // Initialize values knowing 0th bit of data will never be a key/value - parsing_key[0] <== 0; - parsing_value[0] <== 0; - is_key_match[0] <== 0; - - is_next_pair_at_depth[0] <== NextKVPairAtDepth(MAX_STACK_HEIGHT, 0)(stack[0], data[0]); - is_key_match_for_value[1] <== Mux1()([is_key_match_for_value[0] * (1-is_next_pair_at_depth[0]), is_key_match[0] * (1-is_next_pair_at_depth[0])], is_key_match[0]); - is_value_match[0] <== parsing_value[0] * is_key_match_for_value[1]; - - mask[0] <== data[0] * is_value_match[0]; - - for(var data_idx = 1; data_idx < DATA_BYTES - maxKeyLen; data_idx++) { - parsing_key[data_idx] <== InsideKey()(stack[data_idx][0], parsingData[data_idx][0], parsingData[data_idx][1]); - parsing_value[data_idx] <== InsideValueObject()(stack[data_idx][0], stack[data_idx][1], parsingData[data_idx][0], parsingData[data_idx][1]); - - // to get correct value, check: - // - key matches at current index and depth of key is as specified - // - whether next KV pair starts - // - whether key matched for a value (propogate key match until new KV pair of lower depth starts) - is_key_match[data_idx] <== KeyMatchAtIndex(DATA_BYTES, maxKeyLen, data_idx)(data, key, keyLen, parsing_key[data_idx]); - is_next_pair_at_depth[data_idx] <== NextKVPairAtDepth(MAX_STACK_HEIGHT, 0)(stack[data_idx], data[data_idx]); - is_key_match_for_value[data_idx+1] <== Mux1()([is_key_match_for_value[data_idx] * (1-is_next_pair_at_depth[data_idx]), is_key_match[data_idx] * (1-is_next_pair_at_depth[data_idx])], is_key_match[data_idx]); - is_value_match[data_idx] <== is_key_match_for_value[data_idx+1] * parsing_value[data_idx]; - - or[data_idx] <== OR()(is_value_match[data_idx], is_value_match[data_idx - 1]); - - // mask = currently parsing value and all subsequent keys matched - mask[data_idx] <== data[data_idx] * or[data_idx]; - - } - - // Write the `step_out` with masked data - signal output step_out[TOTAL_BYTES]; - for (var i = 0 ; i < DATA_BYTES - maxKeyLen ; i++) { - step_out[i] <== mask[i]; - } - for (var i = 0 ; i < maxKeyLen ; i++) { - step_out[DATA_BYTES - maxKeyLen + i] <== 0; - } - // Append the parser state back on `step_out` - for (var i = DATA_BYTES ; i < TOTAL_BYTES ; i++) { - step_out[i] <== step_in[i]; - } - // No need to pad as this is currently when TOTAL_BYTES == TOTAL_BYTES_USED -} - -component main { public [step_in] } = JsonMaskObjectNIVC(4160, 320, 5, 10); +component main { public [step_in] } = JsonMaskObjectNIVC(48, 16, 5); diff --git a/circuits/json_parse.circom b/circuits/json_parse.circom index db2e154..3204dff 100644 --- a/circuits/json_parse.circom +++ b/circuits/json_parse.circom @@ -1,60 +1,6 @@ pragma circom 2.1.9; -include "parser-attestor/circuits/json/parser/parser.circom"; +include "json/nivc/parse.circom"; -template JsonParseNIVC(TOTAL_BYTES, DATA_BYTES, MAX_STACK_HEIGHT) { - // ------------------------------------------------------------------------------------------------------------------ // - // ~~ Set sizes at compile time ~~ - // Total number of variables in the parser for each byte of data - var PER_ITERATION_DATA_LENGTH = MAX_STACK_HEIGHT * 2 + 2; - var TOTAL_BYTES_USED = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1); - // ------------------------------------------------------------------------------------------------------------------ // - - // Read in from previous NIVC step (AESNIVC) - signal input step_in[TOTAL_BYTES]; - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Parse JSON ~ - // Initialize the parser - component State[DATA_BYTES]; - State[0] = StateUpdate(MAX_STACK_HEIGHT); - for(var i = 0; i < MAX_STACK_HEIGHT; i++) { - State[0].stack[i] <== [0,0]; - } - State[0].parsing_string <== 0; - State[0].parsing_number <== 0; - State[0].byte <== step_in[0]; - - // Parse all the data to generate the complete parser state - for(var i = 1; i < DATA_BYTES; i++) { - State[i] = StateUpdate(MAX_STACK_HEIGHT); - State[i].byte <== step_in[i]; - State[i].stack <== State[i - 1].next_stack; - State[i].parsing_string <== State[i - 1].next_parsing_string; - State[i].parsing_number <== State[i - 1].next_parsing_number; - } - // ------------------------------------------------------------------------------------------------------------------ // - - // ------------------------------------------------------------------------------------------------------------------ // - // ~ Write to `step_out` for next NIVC step - // Pass the data bytes back out in the first `step_out` signals - signal output step_out[TOTAL_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - step_out[i] <== step_in[i]; - } - - // Decode the parser state into the `step_out` remaining signals - for (var i = 0 ; i < DATA_BYTES ; i++) { - for (var j = 0 ; j < MAX_STACK_HEIGHT ; j++) { - step_out[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + j * 2] <== State[i].next_stack[j][0]; - step_out[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + j * 2 + 1] <== State[i].next_stack[j][1]; - } - step_out[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + MAX_STACK_HEIGHT * 2] <== State[i].next_parsing_string; - step_out[DATA_BYTES + i * PER_ITERATION_DATA_LENGTH + MAX_STACK_HEIGHT * 2 + 1] <== State[i].next_parsing_number; - } - // No need to pad as this is currently when TOTAL_BYTES == TOTAL_BYTES_USED - // ------------------------------------------------------------------------------------------------------------------ // -} - -component main { public [step_in] } = JsonParseNIVC(4160, 320, 5); +component main { public [step_in] } = JsonParseNIVC(48, 16); diff --git a/package-lock.json b/package-lock.json index acf451a..b4e8631 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,16 @@ { "name": "web-prover-circuits", - "version": "0.2.4", + "version": "0.2.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "web-prover-circuits", - "version": "0.2.4", + "version": "0.2.5", "license": "Apache-2.0", "dependencies": { "@zk-email/circuits": "^6.1.1", - "aes-proof": "github:pluto/aes-proof#1d08e13ea4f381649cdff83f56d93444008b6548", - "circomlib": "^2.0.5", - "parser-attestor": "github:pluto/parser-attestor#b9feeeb240ddf867da85198de7d59e73cba4b008" + "circomlib": "^2.0.5" }, "devDependencies": { "@semantic-release/commit-analyzer": "^11.1.0", @@ -91,6 +89,7 @@ "version": "0.0.11", "resolved": "https://registry.npmjs.org/@iden3/binfileutils/-/binfileutils-0.0.11.tgz", "integrity": "sha512-LylnJoZ0CTdgErnKY8OxohvW4K+p6UHD3sxt+3P9AmMyBQjYR4IpoqoYZZ+9aMj89cmCQ21UvdhndAx04er3NA==", + "dev": true, "license": "GPL-3.0", "dependencies": { "fastfile": "0.0.20", @@ -917,6 +916,7 @@ "version": "0.7.8", "resolved": "https://registry.npmjs.org/@types/snarkjs/-/snarkjs-0.7.8.tgz", "integrity": "sha512-x37Jsv1vx6I6RMJdfvYEmDUOLYgzYMecwlk13gniDOcN20xLVe9hy9DlQxWeCPirqpDY/jwugQSqCi2RxehU3g==", + "dev": true, "license": "MIT", "peer": true }, @@ -976,37 +976,6 @@ "node": ">=0.4.0" } }, - "node_modules/aes-proof": { - "version": "0.1.0", - "resolved": "git+ssh://git@github.com/pluto/aes-proof.git#1d08e13ea4f381649cdff83f56d93444008b6548", - "integrity": "sha512-e/wiJJgISYDBE47vddzI3nyM1odtYqvH55AOYHQQWu5otp7KUuwHI+oO6XVsbQuPr992jo1+CQbqs19Jb1I+UQ==", - "license": "Apache-2.0", - "dependencies": { - "circomkit": "^0.2.1", - "circomlib": "^2.0.5" - } - }, - "node_modules/aes-proof/node_modules/circomkit": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/circomkit/-/circomkit-0.2.1.tgz", - "integrity": "sha512-7O8QsOLUq2QvwGMimvWxwdg7OgV33OT7ZBND+81dv3JrVp8ove93yV16jF3TW6XBncSY92/Aka8F4CAi/H9VQw==", - "license": "MIT", - "dependencies": { - "circom_tester": "^0.0.19", - "commander": "^12.0.0", - "loglevel": "^1.8.1" - }, - "bin": { - "circomkit": "dist/cli.js" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/snarkjs": "^0.7.x", - "snarkjs": "^0.7.x" - } - }, "node_modules/agent-base": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", @@ -1138,6 +1107,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -1153,6 +1123,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -1269,6 +1240,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -1311,6 +1283,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, "license": "MIT", "dependencies": { "assertion-error": "^1.1.0", @@ -1355,6 +1328,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, "license": "MIT", "dependencies": { "get-func-name": "^2.0.2" @@ -1373,6 +1347,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==", + "dev": true, "license": "ISC" }, "node_modules/chokidar": { @@ -1427,6 +1402,7 @@ "version": "0.0.19", "resolved": "https://registry.npmjs.org/circom_tester/-/circom_tester-0.0.19.tgz", "integrity": "sha512-SNHaBsGxcBH6XsVWfsRbRPA7NF8m8AMKJI9dtJJCFGUtOTT2+zsoIqAwi50z6XCnO4TtjyXq7AeXa1PLHqT0tw==", + "dev": true, "license": "GPL-3.0", "dependencies": { "chai": "^4.3.6", @@ -1443,6 +1419,7 @@ "version": "0.1.21", "resolved": "https://registry.npmjs.org/circom_runtime/-/circom_runtime-0.1.21.tgz", "integrity": "sha512-qTkud630B/GK8y76hnOaaS1aNuF6prfV0dTrkeRsiJKnlP1ryQbP2FWLgDOPqn6aKyaPlam+Z+DTbBhkEzh8dA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "ffjavascript": "0.2.56" @@ -1455,6 +1432,7 @@ "version": "0.2.56", "resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.2.56.tgz", "integrity": "sha512-em6G5Lrj7ucIqj4TYEgyoHs/j99Urwwqa4+YxEVY2hggnpRimVj+noX5pZQTxI1pvtiekZI4rG65JBf0xraXrg==", + "dev": true, "license": "GPL-3.0", "dependencies": { "wasmbuilder": "0.0.16", @@ -1466,6 +1444,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/snarkjs/-/snarkjs-0.5.0.tgz", "integrity": "sha512-KWz8mZ2Y+6wvn6GGkQo6/ZlKwETdAGohd40Lzpwp5TUZCn6N6O4Az1SuX1rw/qREGL6Im+ycb19suCFE8/xaKA==", + "dev": true, "license": "GPL-3.0", "dependencies": { "@iden3/binfileutils": "0.0.11", @@ -1487,6 +1466,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.2.0.tgz", "integrity": "sha512-3e2rbxdujOwaod657gxgmdhZNn+i1qKdHO3Y/bK+8E7bV8ttV/fu5FO4/WLBACF375cK0QDLOP+65Na63qYuWA==", + "dev": true, "license": "GPL-3.0", "dependencies": { "wasmbuilder": "0.0.16" @@ -1646,6 +1626,7 @@ "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -1894,6 +1875,7 @@ "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, "license": "MIT", "dependencies": { "type-detect": "^4.0.0" @@ -1922,6 +1904,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -2212,6 +2195,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" @@ -2224,6 +2208,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2397,6 +2382,7 @@ "version": "0.2.63", "resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.2.63.tgz", "integrity": "sha512-dBgdsfGks58b66JnUZeZpGxdMIDQ4QsD3VYlRJyFVrKQHb2kJy4R2gufx5oetrTxXPT+aEjg0dOvOLg1N0on4A==", + "dev": true, "license": "GPL-3.0", "dependencies": { "wasmbuilder": "0.0.16", @@ -2520,12 +2506,14 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/fnv-plus/-/fnv-plus-1.3.1.tgz", "integrity": "sha512-Gz1EvfOneuFfk4yG458dJ3TLJ7gV19q3OM/vVvvHf7eT02Hm1DleB4edsia6ahbKgAYxO9gvyQ1ioWZR+a00Yw==", + "dev": true, "license": "MIT" }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.1.3" @@ -2583,6 +2571,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2615,6 +2604,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -2624,6 +2614,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2772,6 +2763,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" @@ -2822,6 +2814,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -2834,6 +2827,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2846,6 +2840,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2858,6 +2853,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -2873,6 +2869,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3075,6 +3072,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -3105,6 +3103,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.2", @@ -3141,6 +3140,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3173,6 +3173,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" @@ -3260,6 +3261,7 @@ "version": "1.1.13", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, "license": "MIT", "dependencies": { "which-typed-array": "^1.1.14" @@ -3588,6 +3590,7 @@ "version": "1.9.2", "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -3607,6 +3610,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, "license": "MIT", "dependencies": { "get-func-name": "^2.0.1" @@ -7442,16 +7446,6 @@ "dev": true, "license": "MIT" }, - "node_modules/parser-attestor": { - "version": "0.1.0", - "resolved": "git+ssh://git@github.com/pluto/parser-attestor.git#b9feeeb240ddf867da85198de7d59e73cba4b008", - "integrity": "sha512-8yldptobgJKXHzSpb1/xdtEQLHqzGb55bqrdyTbapyZ1zR482FKiS4RXw8gXKwKGLQK0WNSumYDbh8BBqlJGgw==", - "license": "Apache-2.0", - "dependencies": { - "@zk-email/circuits": "^6.1.1", - "circomlib": "^2.0.5" - } - }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -7486,6 +7480,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -7546,6 +7541,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7614,6 +7610,7 @@ "version": "0.0.41", "resolved": "https://registry.npmjs.org/r1csfile/-/r1csfile-0.0.41.tgz", "integrity": "sha512-Q1WDF3u1vYeAwjHo4YuddkA8Aq0TulbKjmGm99+Atn13Lf5fTsMZBnBV9T741w8iSyPFG6Uh6sapQby77sREqA==", + "dev": true, "license": "GPL-3.0", "dependencies": { "@iden3/bigarray": "0.0.2", @@ -7626,6 +7623,7 @@ "version": "0.2.56", "resolved": "https://registry.npmjs.org/ffjavascript/-/ffjavascript-0.2.56.tgz", "integrity": "sha512-em6G5Lrj7ucIqj4TYEgyoHs/j99Urwwqa4+YxEVY2hggnpRimVj+noX5pZQTxI1pvtiekZI4rG65JBf0xraXrg==", + "dev": true, "license": "GPL-3.0", "dependencies": { "wasmbuilder": "0.0.16", @@ -7637,6 +7635,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/wasmcurves/-/wasmcurves-0.2.0.tgz", "integrity": "sha512-3e2rbxdujOwaod657gxgmdhZNn+i1qKdHO3Y/bK+8E7bV8ttV/fu5FO4/WLBACF375cK0QDLOP+65Na63qYuWA==", + "dev": true, "license": "GPL-3.0", "dependencies": { "wasmbuilder": "0.0.16" @@ -8448,6 +8447,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -9003,6 +9003,7 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, "license": "MIT", "engines": { "node": ">=14.14" @@ -9012,6 +9013,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, "license": "MIT", "dependencies": { "tmp": "^0.2.0" @@ -9119,6 +9121,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -9248,6 +9251,7 @@ "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -9323,6 +9327,7 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", diff --git a/package.json b/package.json index d2e637d..4f472fc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "web-prover-circuits", "description": "ZK Circuits for WebProofs", - "version": "0.2.4", + "version": "0.2.5", "license": "Apache-2.0", "repository": { "type": "git", @@ -13,9 +13,7 @@ }, "dependencies": { "@zk-email/circuits": "^6.1.1", - "aes-proof": "github:pluto/aes-proof#1d08e13ea4f381649cdff83f56d93444008b6548", - "circomlib": "^2.0.5", - "parser-attestor": "github:pluto/parser-attestor#b9feeeb240ddf867da85198de7d59e73cba4b008" + "circomlib": "^2.0.5" }, "devDependencies": { "@semantic-release/commit-analyzer": "^11.1.0", From 69c6129472e2b5af118c0685dad636d5814c6a54 Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 11:38:07 +0700 Subject: [PATCH 03/11] compose web_proof.circom --- circuits/aes-gctr-nivc.circom | 6 ---- circuits/extract_value.circom | 5 ---- circuits/http_body_mask.circom | 6 ---- circuits/http_lock_header.circom | 7 ----- .../http_parse_and_lock_start_line.circom | 5 ---- circuits/json_mask_array_index.circom | 5 ---- circuits/json_mask_object.circom | 5 ---- circuits/json_parse.circom | 6 ---- circuits/web_proof.circom | 30 +++++++++++++++++++ 9 files changed, 30 insertions(+), 45 deletions(-) delete mode 100644 circuits/aes-gctr-nivc.circom delete mode 100644 circuits/extract_value.circom delete mode 100644 circuits/http_body_mask.circom delete mode 100644 circuits/http_lock_header.circom delete mode 100644 circuits/http_parse_and_lock_start_line.circom delete mode 100644 circuits/json_mask_array_index.circom delete mode 100644 circuits/json_mask_object.circom delete mode 100644 circuits/json_parse.circom create mode 100644 circuits/web_proof.circom diff --git a/circuits/aes-gctr-nivc.circom b/circuits/aes-gctr-nivc.circom deleted file mode 100644 index 2ec7580..0000000 --- a/circuits/aes-gctr-nivc.circom +++ /dev/null @@ -1,6 +0,0 @@ -pragma circom 2.1.9; - -include "aes-gcm/nivc/aes-gctr-nivc.circom"; - -// the circomkit tests become unhappy when there is a main. -component main { public [step_in] } = AESGCTRFOLD(48); \ No newline at end of file diff --git a/circuits/extract_value.circom b/circuits/extract_value.circom deleted file mode 100644 index 8e3906b..0000000 --- a/circuits/extract_value.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "json/nivc/extractor.circom"; - -component main { public [step_in] } = MaskExtractFinal(49, 32, 32); \ No newline at end of file diff --git a/circuits/http_body_mask.circom b/circuits/http_body_mask.circom deleted file mode 100644 index de2a95c..0000000 --- a/circuits/http_body_mask.circom +++ /dev/null @@ -1,6 +0,0 @@ -pragma circom 2.1.9; - -include "http/nivc/body_mask.circom"; - -component main { public [step_in] } = HTTPMaskBodyNIVC(4160, 320); - diff --git a/circuits/http_lock_header.circom b/circuits/http_lock_header.circom deleted file mode 100644 index c81aa82..0000000 --- a/circuits/http_lock_header.circom +++ /dev/null @@ -1,7 +0,0 @@ -pragma circom 2.1.9; - -include "http/nivc/lock_header.circom"; - -component main { public [step_in] } = LockHeader(48, 16, 12, 16); -// this one took about - diff --git a/circuits/http_parse_and_lock_start_line.circom b/circuits/http_parse_and_lock_start_line.circom deleted file mode 100644 index f0ba8a5..0000000 --- a/circuits/http_parse_and_lock_start_line.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "http/nivc/parse_and_lock_start_line.circom"; - -component main { public [step_in] } = ParseAndLockStartLine(48, 16, 8, 3, 2); \ No newline at end of file diff --git a/circuits/json_mask_array_index.circom b/circuits/json_mask_array_index.circom deleted file mode 100644 index dfe2334..0000000 --- a/circuits/json_mask_array_index.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "json/nivc/masker.circom"; - -component main { public [step_in] } = JsonMaskArrayIndexNIVC(48, 16); \ No newline at end of file diff --git a/circuits/json_mask_object.circom b/circuits/json_mask_object.circom deleted file mode 100644 index 5f49944..0000000 --- a/circuits/json_mask_object.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "json/nivc/masker.circom"; - -component main { public [step_in] } = JsonMaskObjectNIVC(48, 16, 5); diff --git a/circuits/json_parse.circom b/circuits/json_parse.circom deleted file mode 100644 index 3204dff..0000000 --- a/circuits/json_parse.circom +++ /dev/null @@ -1,6 +0,0 @@ -pragma circom 2.1.9; - -include "json/nivc/parse.circom"; - -component main { public [step_in] } = JsonParseNIVC(48, 16); - diff --git a/circuits/web_proof.circom b/circuits/web_proof.circom new file mode 100644 index 0000000..177486b --- /dev/null +++ b/circuits/web_proof.circom @@ -0,0 +1,30 @@ +pragma circom 2.1.9; + +include "aes-gcm/nivc/aes-gctr-nivc.circom"; +include "http/nivc/parse_and_lock_start_line.circom"; +include "http/nivc/lock_header.circom"; +include "http/nivc/body_mask.circom"; +include "json/nivc/parse.circom"; +include "json/nivc/masker.circom"; +include "json/nivc/masker.circom"; +include "json/nivc/extractor.circom"; + +// AES -> HTTP Parse -> http lock header -> http body mask -> json parse -> json_mask_object/json_mask_array -> extract value +template WEPPROOF { + + component aes_gctr_nivc = AESGCTRFOLD(48); + component http_parse = ParseAndLockStartLine(48, 16, 8, 3, 2); + component http_lock_header = LockHeader(48, 16, 12, 16); + component http_body_mask = HTTPMaskBodyNIVC(48, 16); + component json_parse = JsonParseNIVC(48, 16); + // need logic to specif which json type + // object or array + component json_mask_object = JsonMaskObjectNIVC(48, 16, 4); + component json_mask_array = JsonMaskArrayIndexNIVC(48, 16); + // extract value + component extract_value = JsonParseNIVC(48, 16); +} + +// = AESGCTRFOLD(48); +component main = WEPPROOF(); + From 784db469888a9b66daec8e3600db7c28d0b72e09 Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 11:43:49 +0700 Subject: [PATCH 04/11] compose web_proof.circom --- circuits/web_proof.circom | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/circuits/web_proof.circom b/circuits/web_proof.circom index 177486b..ecd2c5c 100644 --- a/circuits/web_proof.circom +++ b/circuits/web_proof.circom @@ -12,17 +12,28 @@ include "json/nivc/extractor.circom"; // AES -> HTTP Parse -> http lock header -> http body mask -> json parse -> json_mask_object/json_mask_array -> extract value template WEPPROOF { + // template AESGCTRFOLD(INPUT_LEN) component aes_gctr_nivc = AESGCTRFOLD(48); + + // template ParseAndLockStartLine(DATA_BYTES, MAX_STACK_HEIGHT, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENGTH, MAX_FINAL_LENGTH) component http_parse = ParseAndLockStartLine(48, 16, 8, 3, 2); + + // template LockHeader(DATA_BYTES, MAX_STACK_HEIGHT, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH) component http_lock_header = LockHeader(48, 16, 12, 16); + + // template HTTPMaskBodyNIVC(DATA_BYTES, MAX_STACK_HEIGHT) component http_body_mask = HTTPMaskBodyNIVC(48, 16); + + // JsonParseNIVC(DATA_BYTES, MAX_STACK_HEIGHT) component json_parse = JsonParseNIVC(48, 16); // need logic to specif which json type // object or array + + component json_mask_object = JsonMaskObjectNIVC(48, 16, 4); component json_mask_array = JsonMaskArrayIndexNIVC(48, 16); // extract value - component extract_value = JsonParseNIVC(48, 16); + component extract_value = MaskExtractFinal(49, 32, 32); } // = AESGCTRFOLD(48); From 118d4cb2d12bfb7cc0b30b60dfad9f78c71d8fb7 Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 11:55:33 +0700 Subject: [PATCH 05/11] change aes input to data bytes --- circuits/aes-gcm/nivc/aes-gctr-nivc.circom | 6 ++-- .../test/aes-gcm/nivc/aes-gctr-nivc.test.ts | 8 ++--- circuits/web_proof.circom | 30 +++++++++++-------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/circuits/aes-gcm/nivc/aes-gctr-nivc.circom b/circuits/aes-gcm/nivc/aes-gctr-nivc.circom index 689da97..995fe9e 100644 --- a/circuits/aes-gcm/nivc/aes-gctr-nivc.circom +++ b/circuits/aes-gcm/nivc/aes-gctr-nivc.circom @@ -5,9 +5,11 @@ include "../../utils/array.circom"; // Compute AES-GCTR -template AESGCTRFOLD(INPUT_LEN) { +template AESGCTRFOLD(DATA_BYTES) { + // Length of plaintext + var INPUT_LEN = (DATA_BYTES - 4) / 2; assert(INPUT_LEN % 16 == 0); - var DATA_BYTES = (INPUT_LEN * 2) + 4; + signal input key[16]; signal input iv[12]; signal input aad[16]; 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 4b63918..75de5dd 100644 --- a/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts +++ b/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts @@ -9,7 +9,7 @@ describe("aes-gctr-nivc", () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { file: "aes-gcm/nivc/aes-gctr-nivc", template: "AESGCTRFOLD", - params: [16], // input len is 16 bytes + params: [36], // input len is 16 bytes }); let key = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; @@ -30,7 +30,7 @@ describe("aes-gctr-nivc", () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { file: "aes-gcm/nivc/aes-gctr-nivc", template: "AESGCTRFOLD", - params: [16], // input len is 16 bytes + params: [36], // input len is 16 bytes }); let key = [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]; @@ -54,7 +54,7 @@ describe("aes-gctr-nivc", () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { file: "aes-gcm/nivc/aes-gctr-nivc", template: "AESGCTRFOLD", - params: [32], // input len is 32 bytes + params: [68], // input len is 32 bytes }); let zero_block = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; @@ -78,7 +78,7 @@ describe("aes-gctr-nivc", () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { file: "aes-gcm/nivc/aes-gctr-nivc", template: "AESGCTRFOLD", - params: [32], // input len is 32 bytes + params: [68], // input len is 32 bytes }); let zero_block = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; diff --git a/circuits/web_proof.circom b/circuits/web_proof.circom index ecd2c5c..19ccaf9 100644 --- a/circuits/web_proof.circom +++ b/circuits/web_proof.circom @@ -10,32 +10,36 @@ include "json/nivc/masker.circom"; include "json/nivc/extractor.circom"; // AES -> HTTP Parse -> http lock header -> http body mask -> json parse -> json_mask_object/json_mask_array -> extract value -template WEPPROOF { +// DATA_BYTES = length of block * 2 + 4 +// e.g. 36 = 16 * 2 + 4 for a single block +template WEPPROOF(DATA_BYTES) { - // template AESGCTRFOLD(INPUT_LEN) - component aes_gctr_nivc = AESGCTRFOLD(48); + // template AESGCTRFOLD(DATA) + component aes_gctr_nivc = AESGCTRFOLD(DATA_BYTES); // template ParseAndLockStartLine(DATA_BYTES, MAX_STACK_HEIGHT, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENGTH, MAX_FINAL_LENGTH) - component http_parse = ParseAndLockStartLine(48, 16, 8, 3, 2); + component http_parse = ParseAndLockStartLine(DATA_BYTES, 16, 8, 3, 2); // template LockHeader(DATA_BYTES, MAX_STACK_HEIGHT, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH) - component http_lock_header = LockHeader(48, 16, 12, 16); + component http_lock_header = LockHeader(DATA_BYTES, 16, 12, 16); // template HTTPMaskBodyNIVC(DATA_BYTES, MAX_STACK_HEIGHT) - component http_body_mask = HTTPMaskBodyNIVC(48, 16); + component http_body_mask = HTTPMaskBodyNIVC(DATA_BYTES, 16); // JsonParseNIVC(DATA_BYTES, MAX_STACK_HEIGHT) - component json_parse = JsonParseNIVC(48, 16); + component json_parse = JsonParseNIVC(DATA_BYTES, 16); // need logic to specif which json type // object or array + // template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) + component json_mask_object = JsonMaskObjectNIVC(DATA_BYTES, 16, 4); - component json_mask_object = JsonMaskObjectNIVC(48, 16, 4); - component json_mask_array = JsonMaskArrayIndexNIVC(48, 16); - // extract value - component extract_value = MaskExtractFinal(49, 32, 32); + // template JsonMaskArrayIndexNIVC(DATA_BYTES, MAX_STACK_HEIGHT) + component json_mask_array = JsonMaskArrayIndexNIVC(DATA_BYTES, 16); + + // template MaskExtractFinal(DATA_BYTES, MAX_STACK_HEIGHT, MAX_VALUE_LENGTH) + component extract_value = MaskExtractFinal(DATA_BYTES, 32, 32); } -// = AESGCTRFOLD(48); -component main = WEPPROOF(); +component main = WEPPROOF(36); From a67abaefa6121afffd2b95ffd10808ba9ab2ff3c Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 13:59:11 +0700 Subject: [PATCH 06/11] save --- circuits/web_proof.circom | 41 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/circuits/web_proof.circom b/circuits/web_proof.circom index 19ccaf9..7b37db9 100644 --- a/circuits/web_proof.circom +++ b/circuits/web_proof.circom @@ -14,11 +14,46 @@ include "json/nivc/extractor.circom"; // e.g. 36 = 16 * 2 + 4 for a single block template WEPPROOF(DATA_BYTES) { - // template AESGCTRFOLD(DATA) + // AES inputs + signal input key[16]; + signal input iv[12]; + signal input aad[16]; + signal input plainText[16]; + // step_in[0..INPUT_LEN] => accumulate plaintext blocks + // step_in[INPUT_LEN..INPUT_LEN*2] => accumulate ciphertext blocks + // step_in[INPUT_LEN*2..INPUT_LEN*2+4] => accumulate counter + signal input step_in[DATA_BYTES]; + signal output step_out[DATA_BYTES]; + component aes_gctr_nivc = AESGCTRFOLD(DATA_BYTES); + aes_gctr_nivc.key <== key; + aes_gctr_nivc.iv <== iv; + aes_gctr_nivc.aad <== aad; + aes_gctr_nivc.plainText <== plainText; + aes_gctr_nivc.step_in <== step_in; + + // Parse and lock start line inputs + signal input beginning; + signal input beginning_length; + signal input middle; + signal input middle_length; + signal input final; + signal input final_length; + + // ParseAndLockStartLine(DATA_BYTES, MAX_STACK_HEIGHT, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENGTH, MAX_FINAL_LENGTH) + component http_parse = ParseAndLockStartLine(DATA_BYTES, 16, 10, 3, 2); + + http_parse.step_in <== aes_gctr_nivc.step_out; + + // First three bytes are "GET", then zero's for third parameter - 3 bytes + // in this case 4 so we add one zero byte + http_parse.beginning <== [0x47, 0x45, 0x54, 0x00]; + http_parse.beginning_length <== MAX_BEGINNING_LENGTH; + http_parse.middle[MAX_MIDDLE_LENGTH]; + http_parse.middle_length; + http_parse.final[MAX_FINAL_LENGTH]; + http_parse.final_length; - // template ParseAndLockStartLine(DATA_BYTES, MAX_STACK_HEIGHT, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENGTH, MAX_FINAL_LENGTH) - component http_parse = ParseAndLockStartLine(DATA_BYTES, 16, 8, 3, 2); // template LockHeader(DATA_BYTES, MAX_STACK_HEIGHT, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH) component http_lock_header = LockHeader(DATA_BYTES, 16, 12, 16); From 17231845ea7ae67b2f9b6b9de8f481d878767e9d Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 14:08:53 +0700 Subject: [PATCH 07/11] save --- circuits/aes-gcm/nivc/aes-gctr-nivc.circom | 38 +++++++++--------- .../test/aes-gcm/nivc/aes-gctr-nivc.test.ts | 8 ++-- circuits/web_proof.circom | 39 ++++++++++++------- 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/circuits/aes-gcm/nivc/aes-gctr-nivc.circom b/circuits/aes-gcm/nivc/aes-gctr-nivc.circom index 995fe9e..9a0892a 100644 --- a/circuits/aes-gcm/nivc/aes-gctr-nivc.circom +++ b/circuits/aes-gcm/nivc/aes-gctr-nivc.circom @@ -6,26 +6,26 @@ include "../../utils/array.circom"; // Compute AES-GCTR template AESGCTRFOLD(DATA_BYTES) { - // Length of plaintext - var INPUT_LEN = (DATA_BYTES - 4) / 2; - assert(INPUT_LEN % 16 == 0); - + + assert(DATA_BYTES % 16 == 0); + var TOTAL_BYTES_ACROSS_NIVC = (DATA_BYTES * 2) + 4; + signal input key[16]; signal input iv[12]; signal input aad[16]; signal input plainText[16]; - // step_in[0..INPUT_LEN] => accumulate plaintext blocks - // step_in[INPUT_LEN..INPUT_LEN*2] => accumulate ciphertext blocks - // step_in[INPUT_LEN*2..INPUT_LEN*2+4] => accumulate counter - signal input step_in[DATA_BYTES]; - signal output step_out[DATA_BYTES]; + // step_in[0..DATA_BYTES] => accumulate plaintext blocks + // step_in[DATA_BYTES..DATA_BYTES*2] => accumulate ciphertext blocks + // step_in[DATA_BYTES_LEN*2..DATA_BYTES*2+4] => accumulate counter + signal input step_in[TOTAL_BYTES_ACROSS_NIVC]; + signal output step_out[TOTAL_BYTES_ACROSS_NIVC]; signal counter; // 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[INPUT_LEN*2 + i]; + last_counter_bits.in[i] <== step_in[DATA_BYTES*2 + i]; } component last_counter_num = Bits2Num(32); // pass in reverse order @@ -36,8 +36,8 @@ template AESGCTRFOLD(DATA_BYTES) { counter <== last_counter_num.out - 1; // write new plain text block. - signal plainTextAccumulator[DATA_BYTES]; - component writeToIndex = WriteToIndex(DATA_BYTES, 16); + signal plainTextAccumulator[TOTAL_BYTES_ACROSS_NIVC]; + component writeToIndex = WriteToIndex(TOTAL_BYTES_ACROSS_NIVC, 16); writeToIndex.array_to_write_to <== step_in; writeToIndex.array_to_write_at_index <== plainText; writeToIndex.index <== counter * 16; @@ -51,22 +51,22 @@ template AESGCTRFOLD(DATA_BYTES) { aes.plainText <== plainText; for(var i = 0; i < 4; i++) { - aes.lastCounter[i] <== step_in[INPUT_LEN*2 + i]; + aes.lastCounter[i] <== step_in[DATA_BYTES*2 + i]; } // accumulate cipher text - signal cipherTextAccumulator[DATA_BYTES]; - component writeCipherText = WriteToIndex(DATA_BYTES, 16); + signal cipherTextAccumulator[TOTAL_BYTES_ACROSS_NIVC]; + component writeCipherText = WriteToIndex(TOTAL_BYTES_ACROSS_NIVC, 16); writeCipherText.array_to_write_to <== plainTextAccumulator; writeCipherText.array_to_write_at_index <== aes.cipherText; - writeCipherText.index <== INPUT_LEN + counter * 16; + writeCipherText.index <== DATA_BYTES + counter * 16; writeCipherText.out ==> cipherTextAccumulator; // get counter - signal counterAccumulator[DATA_BYTES]; - component writeCounter = WriteToIndex(DATA_BYTES, 4); + signal counterAccumulator[TOTAL_BYTES_ACROSS_NIVC]; + component writeCounter = WriteToIndex(TOTAL_BYTES_ACROSS_NIVC, 4); writeCounter.array_to_write_to <== cipherTextAccumulator; writeCounter.array_to_write_at_index <== aes.counter; - writeCounter.index <== INPUT_LEN*2; + writeCounter.index <== DATA_BYTES*2; writeCounter.out ==> step_out; } \ No newline at end of file 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 75de5dd..4b63918 100644 --- a/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts +++ b/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts @@ -9,7 +9,7 @@ describe("aes-gctr-nivc", () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { file: "aes-gcm/nivc/aes-gctr-nivc", template: "AESGCTRFOLD", - params: [36], // input len is 16 bytes + params: [16], // input len is 16 bytes }); let key = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; @@ -30,7 +30,7 @@ describe("aes-gctr-nivc", () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { file: "aes-gcm/nivc/aes-gctr-nivc", template: "AESGCTRFOLD", - params: [36], // input len is 16 bytes + params: [16], // input len is 16 bytes }); let key = [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]; @@ -54,7 +54,7 @@ describe("aes-gctr-nivc", () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { file: "aes-gcm/nivc/aes-gctr-nivc", template: "AESGCTRFOLD", - params: [68], // input len is 32 bytes + params: [32], // input len is 32 bytes }); let zero_block = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; @@ -78,7 +78,7 @@ describe("aes-gctr-nivc", () => { circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { file: "aes-gcm/nivc/aes-gctr-nivc", template: "AESGCTRFOLD", - params: [68], // input len is 32 bytes + params: [32], // input len is 32 bytes }); let zero_block = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; diff --git a/circuits/web_proof.circom b/circuits/web_proof.circom index 7b37db9..a637c71 100644 --- a/circuits/web_proof.circom +++ b/circuits/web_proof.circom @@ -44,33 +44,44 @@ template WEPPROOF(DATA_BYTES) { component http_parse = ParseAndLockStartLine(DATA_BYTES, 16, 10, 3, 2); http_parse.step_in <== aes_gctr_nivc.step_out; - - // First three bytes are "GET", then zero's for third parameter - 3 bytes - // in this case 4 so we add one zero byte - http_parse.beginning <== [0x47, 0x45, 0x54, 0x00]; - http_parse.beginning_length <== MAX_BEGINNING_LENGTH; - http_parse.middle[MAX_MIDDLE_LENGTH]; - http_parse.middle_length; - http_parse.final[MAX_FINAL_LENGTH]; - http_parse.final_length; - + http_parse.beginning <== beginning; + http_parse.beginning_length <== beginning_length; + http_parse.middle <== middle; + http_parse.middle_length <== middle_length; + http_parse.final <== final; + http_parse.final_length <== final_length; // template LockHeader(DATA_BYTES, MAX_STACK_HEIGHT, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH) component http_lock_header = LockHeader(DATA_BYTES, 16, 12, 16); + + signal input header; + signal input headerNameLength; + signal input value; + signal input headerValueLength; + + http_lock_header.step_in <== http_parse.step_out; + http_lock_header.header <== header; + http_lock_header.headerNameLength <== headerNameLength; + http_lock_header.value <== value; + http_lock_header.headerValueLength <== headerValueLength; + // template HTTPMaskBodyNIVC(DATA_BYTES, MAX_STACK_HEIGHT) component http_body_mask = HTTPMaskBodyNIVC(DATA_BYTES, 16); + http_body_mask.step_in <== http_lock_header.step_out; + // JsonParseNIVC(DATA_BYTES, MAX_STACK_HEIGHT) component json_parse = JsonParseNIVC(DATA_BYTES, 16); - // need logic to specif which json type - // object or array - // template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) - component json_mask_object = JsonMaskObjectNIVC(DATA_BYTES, 16, 4); + json_parse.step_in <== http_body_mask.step_out; + + // // template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) + // component json_mask_object = JsonMaskObjectNIVC(DATA_BYTES, 16, 4); // template JsonMaskArrayIndexNIVC(DATA_BYTES, MAX_STACK_HEIGHT) component json_mask_array = JsonMaskArrayIndexNIVC(DATA_BYTES, 16); + json_mask_array.step_in <== json_parse.step_out; // template MaskExtractFinal(DATA_BYTES, MAX_STACK_HEIGHT, MAX_VALUE_LENGTH) component extract_value = MaskExtractFinal(DATA_BYTES, 32, 32); From 493eef52c2b7e29dbc04abb11ec495bf2ef811f9 Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 15:18:22 +0700 Subject: [PATCH 08/11] Update web_proof.circom --- circuits/web_proof.circom | 100 +++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 35 deletions(-) diff --git a/circuits/web_proof.circom b/circuits/web_proof.circom index a637c71..ec2ca1b 100644 --- a/circuits/web_proof.circom +++ b/circuits/web_proof.circom @@ -12,19 +12,26 @@ include "json/nivc/extractor.circom"; // AES -> HTTP Parse -> http lock header -> http body mask -> json parse -> json_mask_object/json_mask_array -> extract value // DATA_BYTES = length of block * 2 + 4 // e.g. 36 = 16 * 2 + 4 for a single block -template WEPPROOF(DATA_BYTES) { - - // AES inputs +template WEPPROOF( + DATA_BYTES, + MAX_STACK_HEIGHT, + MAX_BEGINNING_LENGTH, + MAX_MIDDLE_LENGTH, + MAX_FINAL_LENGTH, + MAX_HEADER_NAME_LENGTH, + MAX_HEADER_VALUE_LENGTH, + MAX_KEY_LENGTH, + MAX_VALUE_LENGTH) { + + var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; + signal input step_in[TOTAL_BYTES_ACROSS_NIVC]; + signal output step_out[TOTAL_BYTES_ACROSS_NIVC]; + + // AES signal input key[16]; signal input iv[12]; signal input aad[16]; signal input plainText[16]; - // step_in[0..INPUT_LEN] => accumulate plaintext blocks - // step_in[INPUT_LEN..INPUT_LEN*2] => accumulate ciphertext blocks - // step_in[INPUT_LEN*2..INPUT_LEN*2+4] => accumulate counter - signal input step_in[DATA_BYTES]; - signal output step_out[DATA_BYTES]; - component aes_gctr_nivc = AESGCTRFOLD(DATA_BYTES); aes_gctr_nivc.key <== key; aes_gctr_nivc.iv <== iv; @@ -32,16 +39,19 @@ template WEPPROOF(DATA_BYTES) { aes_gctr_nivc.plainText <== plainText; aes_gctr_nivc.step_in <== step_in; - // Parse and lock start line inputs - signal input beginning; + // Parse and lock + component http_parse = ParseAndLockStartLine(DATA_BYTES, + MAX_STACK_HEIGHT, + MAX_BEGINNING_LENGTH, + MAX_MIDDLE_LENGTH, + MAX_FINAL_LENGTH); + + signal input beginning[MAX_BEGINNING_LENGTH]; signal input beginning_length; - signal input middle; + signal input middle[MAX_MIDDLE_LENGTH]; signal input middle_length; - signal input final; + signal input final[MAX_FINAL_LENGTH]; signal input final_length; - - // ParseAndLockStartLine(DATA_BYTES, MAX_STACK_HEIGHT, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENGTH, MAX_FINAL_LENGTH) - component http_parse = ParseAndLockStartLine(DATA_BYTES, 16, 10, 3, 2); http_parse.step_in <== aes_gctr_nivc.step_out; http_parse.beginning <== beginning; @@ -51,13 +61,15 @@ template WEPPROOF(DATA_BYTES) { http_parse.final <== final; http_parse.final_length <== final_length; - // template LockHeader(DATA_BYTES, MAX_STACK_HEIGHT, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH) - component http_lock_header = LockHeader(DATA_BYTES, 16, 12, 16); + // Lock header + component http_lock_header = LockHeader(DATA_BYTES, + MAX_STACK_HEIGHT, + MAX_HEADER_NAME_LENGTH, + MAX_HEADER_VALUE_LENGTH); - - signal input header; + signal input header[MAX_HEADER_NAME_LENGTH]; signal input headerNameLength; - signal input value; + signal input value[MAX_HEADER_VALUE_LENGTH]; signal input headerValueLength; http_lock_header.step_in <== http_parse.step_out; @@ -66,26 +78,44 @@ template WEPPROOF(DATA_BYTES) { http_lock_header.value <== value; http_lock_header.headerValueLength <== headerValueLength; - // template HTTPMaskBodyNIVC(DATA_BYTES, MAX_STACK_HEIGHT) - component http_body_mask = HTTPMaskBodyNIVC(DATA_BYTES, 16); - + // HTTP body mask + component http_body_mask = HTTPMaskBodyNIVC(DATA_BYTES, MAX_STACK_HEIGHT); http_body_mask.step_in <== http_lock_header.step_out; - // JsonParseNIVC(DATA_BYTES, MAX_STACK_HEIGHT) - component json_parse = JsonParseNIVC(DATA_BYTES, 16); - + // JSON parse + component json_parse = JsonParseNIVC(DATA_BYTES, MAX_STACK_HEIGHT); json_parse.step_in <== http_body_mask.step_out; - // // template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) - // component json_mask_object = JsonMaskObjectNIVC(DATA_BYTES, 16, 4); + // Note: picked Array over object for now + // TODO(WJ 2024-11-01): add conditional logic via a mux + // template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) + // component json_mask_object = JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH); - // template JsonMaskArrayIndexNIVC(DATA_BYTES, MAX_STACK_HEIGHT) - component json_mask_array = JsonMaskArrayIndexNIVC(DATA_BYTES, 16); + // JSON array + component json_mask_array = JsonMaskArrayIndexNIVC(DATA_BYTES, MAX_STACK_HEIGHT); json_mask_array.step_in <== json_parse.step_out; - // template MaskExtractFinal(DATA_BYTES, MAX_STACK_HEIGHT, MAX_VALUE_LENGTH) - component extract_value = MaskExtractFinal(DATA_BYTES, 32, 32); + // Final Extraction + component extract_value = MaskExtractFinal(DATA_BYTES, MAX_STACK_HEIGHT, MAX_VALUE_LENGTH); } - -component main = WEPPROOF(36); +/// Note, DATA_BYTES > MAX_BEGINNING_LENGTH and MAX_MIDDLE_LENGTH and MAX_FINAL_LENGTH +component main = WEPPROOF( + 64, // DATA_BYTES + 5, // MAX_STACK_HEIGHT + 10, // MAX_BEGINNING_LENGTH + 50, // MAX_MIDDLE_LENGTH + 10, // MAX_FINAL_LENGTH + 12, // MAX_HEADER_NAME_LENGTH + 16, // MAX_HEADER_VALUE_LENGTH + 8, // MAX_KEY_LENGTH + 16 // MAX_VALUE_LENGTH + ); + + // const MAX_STACK_HEIGHT = 5; + // const PER_ITERATION_DATA_LENGTH = MAX_STACK_HEIGHT * 2 + 2; + // const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1) + 1; + + // const MAX_BEGINNING_LENGTH = 10; + // const MAX_MIDDLE_LENGTH = 50; + // const MAX_FINAL_LENGTH = 10; \ No newline at end of file From 021b3d7881558d2e496fcceca1209db01fab4515 Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 15:35:02 +0700 Subject: [PATCH 09/11] Revert "compose web_proof.circom" This reverts commit 69c6129472e2b5af118c0685dad636d5814c6a54. --- circuits/aes-gctr-nivc.circom | 6 + circuits/extract_value.circom | 5 + circuits/http_body_mask.circom | 6 + circuits/http_lock_header.circom | 7 + .../http_parse_and_lock_start_line.circom | 5 + circuits/json_mask_array_index.circom | 5 + circuits/json_mask_object.circom | 5 + circuits/json_parse.circom | 6 + circuits/web_proof.circom | 121 ------------------ 9 files changed, 45 insertions(+), 121 deletions(-) create mode 100644 circuits/aes-gctr-nivc.circom create mode 100644 circuits/extract_value.circom create mode 100644 circuits/http_body_mask.circom create mode 100644 circuits/http_lock_header.circom create mode 100644 circuits/http_parse_and_lock_start_line.circom create mode 100644 circuits/json_mask_array_index.circom create mode 100644 circuits/json_mask_object.circom create mode 100644 circuits/json_parse.circom delete mode 100644 circuits/web_proof.circom diff --git a/circuits/aes-gctr-nivc.circom b/circuits/aes-gctr-nivc.circom new file mode 100644 index 0000000..2ec7580 --- /dev/null +++ b/circuits/aes-gctr-nivc.circom @@ -0,0 +1,6 @@ +pragma circom 2.1.9; + +include "aes-gcm/nivc/aes-gctr-nivc.circom"; + +// the circomkit tests become unhappy when there is a main. +component main { public [step_in] } = AESGCTRFOLD(48); \ No newline at end of file diff --git a/circuits/extract_value.circom b/circuits/extract_value.circom new file mode 100644 index 0000000..8e3906b --- /dev/null +++ b/circuits/extract_value.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "json/nivc/extractor.circom"; + +component main { public [step_in] } = MaskExtractFinal(49, 32, 32); \ No newline at end of file diff --git a/circuits/http_body_mask.circom b/circuits/http_body_mask.circom new file mode 100644 index 0000000..de2a95c --- /dev/null +++ b/circuits/http_body_mask.circom @@ -0,0 +1,6 @@ +pragma circom 2.1.9; + +include "http/nivc/body_mask.circom"; + +component main { public [step_in] } = HTTPMaskBodyNIVC(4160, 320); + diff --git a/circuits/http_lock_header.circom b/circuits/http_lock_header.circom new file mode 100644 index 0000000..c81aa82 --- /dev/null +++ b/circuits/http_lock_header.circom @@ -0,0 +1,7 @@ +pragma circom 2.1.9; + +include "http/nivc/lock_header.circom"; + +component main { public [step_in] } = LockHeader(48, 16, 12, 16); +// this one took about + diff --git a/circuits/http_parse_and_lock_start_line.circom b/circuits/http_parse_and_lock_start_line.circom new file mode 100644 index 0000000..f0ba8a5 --- /dev/null +++ b/circuits/http_parse_and_lock_start_line.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "http/nivc/parse_and_lock_start_line.circom"; + +component main { public [step_in] } = ParseAndLockStartLine(48, 16, 8, 3, 2); \ No newline at end of file diff --git a/circuits/json_mask_array_index.circom b/circuits/json_mask_array_index.circom new file mode 100644 index 0000000..dfe2334 --- /dev/null +++ b/circuits/json_mask_array_index.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "json/nivc/masker.circom"; + +component main { public [step_in] } = JsonMaskArrayIndexNIVC(48, 16); \ No newline at end of file diff --git a/circuits/json_mask_object.circom b/circuits/json_mask_object.circom new file mode 100644 index 0000000..5f49944 --- /dev/null +++ b/circuits/json_mask_object.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "json/nivc/masker.circom"; + +component main { public [step_in] } = JsonMaskObjectNIVC(48, 16, 5); diff --git a/circuits/json_parse.circom b/circuits/json_parse.circom new file mode 100644 index 0000000..3204dff --- /dev/null +++ b/circuits/json_parse.circom @@ -0,0 +1,6 @@ +pragma circom 2.1.9; + +include "json/nivc/parse.circom"; + +component main { public [step_in] } = JsonParseNIVC(48, 16); + diff --git a/circuits/web_proof.circom b/circuits/web_proof.circom deleted file mode 100644 index ec2ca1b..0000000 --- a/circuits/web_proof.circom +++ /dev/null @@ -1,121 +0,0 @@ -pragma circom 2.1.9; - -include "aes-gcm/nivc/aes-gctr-nivc.circom"; -include "http/nivc/parse_and_lock_start_line.circom"; -include "http/nivc/lock_header.circom"; -include "http/nivc/body_mask.circom"; -include "json/nivc/parse.circom"; -include "json/nivc/masker.circom"; -include "json/nivc/masker.circom"; -include "json/nivc/extractor.circom"; - -// AES -> HTTP Parse -> http lock header -> http body mask -> json parse -> json_mask_object/json_mask_array -> extract value -// DATA_BYTES = length of block * 2 + 4 -// e.g. 36 = 16 * 2 + 4 for a single block -template WEPPROOF( - DATA_BYTES, - MAX_STACK_HEIGHT, - MAX_BEGINNING_LENGTH, - MAX_MIDDLE_LENGTH, - MAX_FINAL_LENGTH, - MAX_HEADER_NAME_LENGTH, - MAX_HEADER_VALUE_LENGTH, - MAX_KEY_LENGTH, - MAX_VALUE_LENGTH) { - - var TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * 2 + 4; - signal input step_in[TOTAL_BYTES_ACROSS_NIVC]; - signal output step_out[TOTAL_BYTES_ACROSS_NIVC]; - - // AES - signal input key[16]; - signal input iv[12]; - signal input aad[16]; - signal input plainText[16]; - component aes_gctr_nivc = AESGCTRFOLD(DATA_BYTES); - aes_gctr_nivc.key <== key; - aes_gctr_nivc.iv <== iv; - aes_gctr_nivc.aad <== aad; - aes_gctr_nivc.plainText <== plainText; - aes_gctr_nivc.step_in <== step_in; - - // Parse and lock - component http_parse = ParseAndLockStartLine(DATA_BYTES, - MAX_STACK_HEIGHT, - MAX_BEGINNING_LENGTH, - MAX_MIDDLE_LENGTH, - MAX_FINAL_LENGTH); - - signal input beginning[MAX_BEGINNING_LENGTH]; - signal input beginning_length; - signal input middle[MAX_MIDDLE_LENGTH]; - signal input middle_length; - signal input final[MAX_FINAL_LENGTH]; - signal input final_length; - - http_parse.step_in <== aes_gctr_nivc.step_out; - http_parse.beginning <== beginning; - http_parse.beginning_length <== beginning_length; - http_parse.middle <== middle; - http_parse.middle_length <== middle_length; - http_parse.final <== final; - http_parse.final_length <== final_length; - - // Lock header - component http_lock_header = LockHeader(DATA_BYTES, - MAX_STACK_HEIGHT, - MAX_HEADER_NAME_LENGTH, - MAX_HEADER_VALUE_LENGTH); - - signal input header[MAX_HEADER_NAME_LENGTH]; - signal input headerNameLength; - signal input value[MAX_HEADER_VALUE_LENGTH]; - signal input headerValueLength; - - http_lock_header.step_in <== http_parse.step_out; - http_lock_header.header <== header; - http_lock_header.headerNameLength <== headerNameLength; - http_lock_header.value <== value; - http_lock_header.headerValueLength <== headerValueLength; - - // HTTP body mask - component http_body_mask = HTTPMaskBodyNIVC(DATA_BYTES, MAX_STACK_HEIGHT); - http_body_mask.step_in <== http_lock_header.step_out; - - // JSON parse - component json_parse = JsonParseNIVC(DATA_BYTES, MAX_STACK_HEIGHT); - json_parse.step_in <== http_body_mask.step_out; - - // Note: picked Array over object for now - // TODO(WJ 2024-11-01): add conditional logic via a mux - // template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) - // component json_mask_object = JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH); - - // JSON array - component json_mask_array = JsonMaskArrayIndexNIVC(DATA_BYTES, MAX_STACK_HEIGHT); - json_mask_array.step_in <== json_parse.step_out; - - // Final Extraction - component extract_value = MaskExtractFinal(DATA_BYTES, MAX_STACK_HEIGHT, MAX_VALUE_LENGTH); -} - -/// Note, DATA_BYTES > MAX_BEGINNING_LENGTH and MAX_MIDDLE_LENGTH and MAX_FINAL_LENGTH -component main = WEPPROOF( - 64, // DATA_BYTES - 5, // MAX_STACK_HEIGHT - 10, // MAX_BEGINNING_LENGTH - 50, // MAX_MIDDLE_LENGTH - 10, // MAX_FINAL_LENGTH - 12, // MAX_HEADER_NAME_LENGTH - 16, // MAX_HEADER_VALUE_LENGTH - 8, // MAX_KEY_LENGTH - 16 // MAX_VALUE_LENGTH - ); - - // const MAX_STACK_HEIGHT = 5; - // const PER_ITERATION_DATA_LENGTH = MAX_STACK_HEIGHT * 2 + 2; - // const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1) + 1; - - // const MAX_BEGINNING_LENGTH = 10; - // const MAX_MIDDLE_LENGTH = 50; - // const MAX_FINAL_LENGTH = 10; \ No newline at end of file From 6d12a420bdb28068b85b758a522ef208796ab8c2 Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 15:45:18 +0700 Subject: [PATCH 10/11] starting on test --- circuits/http_body_mask.circom | 2 +- circuits/http_lock_header.circom | 4 +- circuits/test/full/full.test.ts | 113 +++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 circuits/test/full/full.test.ts diff --git a/circuits/http_body_mask.circom b/circuits/http_body_mask.circom index de2a95c..fedbe20 100644 --- a/circuits/http_body_mask.circom +++ b/circuits/http_body_mask.circom @@ -2,5 +2,5 @@ pragma circom 2.1.9; include "http/nivc/body_mask.circom"; -component main { public [step_in] } = HTTPMaskBodyNIVC(4160, 320); +component main { public [step_in] } = HTTPMaskBodyNIVC(48, 16); diff --git a/circuits/http_lock_header.circom b/circuits/http_lock_header.circom index c81aa82..0bb436e 100644 --- a/circuits/http_lock_header.circom +++ b/circuits/http_lock_header.circom @@ -2,6 +2,4 @@ pragma circom 2.1.9; include "http/nivc/lock_header.circom"; -component main { public [step_in] } = LockHeader(48, 16, 12, 16); -// this one took about - +component main { public [step_in] } = LockHeader(48, 16, 12, 16); \ No newline at end of file diff --git a/circuits/test/full/full.test.ts b/circuits/test/full/full.test.ts new file mode 100644 index 0000000..1b1bbe7 --- /dev/null +++ b/circuits/test/full/full.test.ts @@ -0,0 +1,113 @@ +import { circomkit, WitnessTester, toByte } from "../common"; + +// HTTP/1.1 200 OK +// content-type: application/json; charset=utf-8 +// content-encoding: gzip +// Transfer-Encoding: chunked +// +// { +// "data": { +// "items": [ +// { +// "data": "Artist", +// "profile": { +// "name": "Taylor Swift" +// } +// } +// ] +// } +// } + +// 320 bytes in the HTTP response +let http_response_plaintext = [ + 72, 84, 84, 80, 47, 49, 46, 49, 32, 50, 48, 48, 32, 79, 75, 13, 10, 99, 111, 110, 116, 101, 110, + 116, 45, 116, 121, 112, 101, 58, 32, 97, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110, 47, 106, + 115, 111, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 13, 10, 99, + 111, 110, 116, 101, 110, 116, 45, 101, 110, 99, 111, 100, 105, 110, 103, 58, 32, 103, 122, 105, + 112, 13, 10, 84, 114, 97, 110, 115, 102, 101, 114, 45, 69, 110, 99, 111, 100, 105, 110, 103, 58, + 32, 99, 104, 117, 110, 107, 101, 100, 13, 10, 13, 10, 123, 13, 10, 32, 32, 32, 34, 100, 97, 116, + 97, 34, 58, 32, 123, 13, 10, 32, 32, 32, 32, 32, 32, 32, 34, 105, 116, 101, 109, 115, 34, 58, 32, + 91, 13, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 123, 13, 10, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 34, 100, 97, 116, 97, 34, 58, 32, 34, 65, 114, 116, 105, 115, + 116, 34, 44, 13, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 112, 114, + 111, 102, 105, 108, 101, 34, 58, 32, 123, 13, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 34, 110, 97, 109, 101, 34, 58, 32, 34, 84, 97, 121, 108, 111, 114, 32, 83, 119, + 105, 102, 116, 34, 13, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 13, + 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 13, 10, 32, 32, 32, 32, 32, 32, 32, 93, 13, + 10, 32, 32, 32, 125, 13, 10, 125]; + +describe("NIVC_HTTP", async () => { + let aesCircuit: WitnessTester<["key", "iv", "plainText", "aad", "step_in"], ["step_out"]>; + let httpParseAndLockStartLineCircuit: WitnessTester<["step_in", "beginning", "beginning_length", "middle", "middle_length", "final", "final_length"], ["step_out"]>; + let lockHeaderCircuit: WitnessTester<["step_in", "header", "headerNameLength", "value", "headerValueLength"], ["step_out"]>; + let bodyMaskCircuit: WitnessTester<["step_in"], ["step_out"]>; + + const DATA_BYTES = 320; + const MAX_STACK_HEIGHT = 5; + const PER_ITERATION_DATA_LENGTH = MAX_STACK_HEIGHT * 2 + 2; + const TOTAL_BYTES_ACROSS_NIVC = DATA_BYTES * (PER_ITERATION_DATA_LENGTH + 1) + 1; + + const MAX_HEADER_NAME_LENGTH = 20; + const MAX_HEADER_VALUE_LENGTH = 35; + const MAX_BEGINNING_LENGTH = 10; + const MAX_MIDDLE_LENGTH = 30; + const MAX_FINAL_LENGTH = 10; + + const beginning = [72, 84, 84, 80, 47, 49, 46, 49]; // HTTP/1.1 + const middle = [50, 48, 48]; // 200 + const final = [79, 75]; // OK + + before(async () => { + aesCircuit = await circomkit.WitnessTester("AESGCTRFOLD", { + file: "aes-gcm/nivc/aes-gctr-nivc", + template: "AESGCTRFOLD", + params: [DATA_BYTES], + }); + httpParseAndLockStartLineCircuit = await circomkit.WitnessTester(`ParseAndLockStartLine`, { + file: "http/nivc/parse_and_lock_start_line", + template: "ParseAndLockStartLine", + params: [DATA_BYTES, MAX_STACK_HEIGHT, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENGTH, MAX_FINAL_LENGTH], + }); + console.log("#constraints:", await httpParseAndLockStartLineCircuit.getConstraintCount()); + + lockHeaderCircuit = await circomkit.WitnessTester(`LockHeader`, { + file: "http/nivc/lock_header", + template: "LockHeader", + params: [DATA_BYTES, MAX_STACK_HEIGHT, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH], + }); + console.log("#constraints:", await lockHeaderCircuit.getConstraintCount()); + + bodyMaskCircuit = await circomkit.WitnessTester(`BodyMask`, { + file: "http/nivc/body_mask", + template: "HTTPMaskBodyNIVC", + params: [DATA_BYTES, MAX_STACK_HEIGHT], + }); + console.log("#constraints:", await bodyMaskCircuit.getConstraintCount()); + }); + + let extendedJsonInput = http_response_plaintext.concat(Array(Math.max(0, TOTAL_BYTES_ACROSS_NIVC - http_response_plaintext.length)).fill(0)); + + let headerName = toByte("content-type"); + let headerValue = toByte("application/json; charset=utf-8"); + + let headerNamePadded = headerName.concat(Array(MAX_HEADER_NAME_LENGTH - headerName.length).fill(0)); + let headerValuePadded = headerValue.concat(Array(MAX_HEADER_VALUE_LENGTH - headerValue.length).fill(0)); + let beginningPadded = beginning.concat(Array(MAX_BEGINNING_LENGTH - beginning.length).fill(0)); + let middlePadded = middle.concat(Array(MAX_MIDDLE_LENGTH - middle.length).fill(0)); + let finalPadded = final.concat(Array(MAX_FINAL_LENGTH - final.length).fill(0)); + it("HTTPParseAndExtract", async () => { + let parseAndLockStartLine = await httpParseAndLockStartLineCircuit.compute({ step_in: extendedJsonInput, beginning: beginningPadded, beginning_length: beginning.length, middle: middlePadded, middle_length: middle.length, final: finalPadded, final_length: final.length }, ["step_out"]); + + let lockHeader = await lockHeaderCircuit.compute({ step_in: parseAndLockStartLine.step_out, header: headerNamePadded, headerNameLength: headerName.length, value: headerValuePadded, headerValueLength: headerValue.length }, ["step_out"]); + + let bodyMask = await bodyMaskCircuit.compute({ step_in: lockHeader.step_out }, ["step_out"]); + + let bodyMaskOut = bodyMask.step_out as number[]; + let idx = bodyMaskOut.indexOf('{'.charCodeAt(0)); + + let maskedInput = extendedJsonInput.fill(0, 0, idx); + maskedInput = maskedInput.fill(0, 320); + + bodyMaskOut === maskedInput; + }); +}); \ No newline at end of file From 757ed4f8978c80c81e7c107754063a77eeb2b349 Mon Sep 17 00:00:00 2001 From: Waylon Jepsen Date: Fri, 1 Nov 2024 22:51:28 +0700 Subject: [PATCH 11/11] fix aes fold --- circuits/test/full/full.test.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/circuits/test/full/full.test.ts b/circuits/test/full/full.test.ts index 1b1bbe7..164dd7a 100644 --- a/circuits/test/full/full.test.ts +++ b/circuits/test/full/full.test.ts @@ -1,3 +1,4 @@ +import { CircuitSignals } from "circomkit"; import { circomkit, WitnessTester, toByte } from "../common"; // HTTP/1.1 200 OK @@ -36,7 +37,7 @@ let http_response_plaintext = [ 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 13, 10, 32, 32, 32, 32, 32, 32, 32, 93, 13, 10, 32, 32, 32, 125, 13, 10, 125]; -describe("NIVC_HTTP", async () => { +describe("NIVC_FULL", async () => { let aesCircuit: WitnessTester<["key", "iv", "plainText", "aad", "step_in"], ["step_out"]>; let httpParseAndLockStartLineCircuit: WitnessTester<["step_in", "beginning", "beginning_length", "middle", "middle_length", "final", "final_length"], ["step_out"]>; let lockHeaderCircuit: WitnessTester<["step_in", "header", "headerNameLength", "value", "headerValueLength"], ["step_out"]>; @@ -85,7 +86,6 @@ describe("NIVC_HTTP", async () => { console.log("#constraints:", await bodyMaskCircuit.getConstraintCount()); }); - let extendedJsonInput = http_response_plaintext.concat(Array(Math.max(0, TOTAL_BYTES_ACROSS_NIVC - http_response_plaintext.length)).fill(0)); let headerName = toByte("content-type"); let headerValue = toByte("application/json; charset=utf-8"); @@ -96,6 +96,18 @@ describe("NIVC_HTTP", async () => { let middlePadded = middle.concat(Array(MAX_MIDDLE_LENGTH - middle.length).fill(0)); let finalPadded = final.concat(Array(MAX_FINAL_LENGTH - final.length).fill(0)); it("HTTPParseAndExtract", async () => { + // fold 16 bytes at a time + let aes_gcm: CircuitSignals = { step_out: [] }; + console.log("DATA_BYTES", DATA_BYTES); + + for (let i = 0; i < (DATA_BYTES / 16); i++) { + // off by one here + let pt = http_response_plaintext.slice(i * 16, i * 16 +16); + console.log("pt", pt); + aes_gcm = await aesCircuit.compute({ key: Array(16).fill(0), iv: Array(12).fill(0), plainText: pt, aad: Array(16).fill(0), step_in: Array(DATA_BYTES * 2 + 4).fill(0) }, ["step_out"]); + } + let out = aes_gcm.step_out as number[]; + let extendedJsonInput = out.slice(0, DATA_BYTES).concat(Array(Math.max(0, TOTAL_BYTES_ACROSS_NIVC - http_response_plaintext.length)).fill(0)); let parseAndLockStartLine = await httpParseAndLockStartLineCircuit.compute({ step_in: extendedJsonInput, beginning: beginningPadded, beginning_length: beginning.length, middle: middlePadded, middle_length: middle.length, final: finalPadded, final_length: final.length }, ["step_out"]); let lockHeader = await lockHeaderCircuit.compute({ step_in: parseAndLockStartLine.step_out, header: headerNamePadded, headerNameLength: headerName.length, value: headerValuePadded, headerValueLength: headerValue.length }, ["step_out"]);