diff --git a/circuits.json b/circuits.json index 5bd122d..97ce86e 100644 --- a/circuits.json +++ b/circuits.json @@ -217,6 +217,14 @@ "file": "aes-gcm/nivc/aes-gctr-nivc", "template": "AESGCTRFOLD" }, + "http_nivc": { + "file": "http/nivc/http_nivc", + "template": "HttpNIVC", + "params": [ + 1024, + 10 + ] + }, "nivc_start_line": { "file": "http/nivc/parse_and_lock_start_line", "template": "ParseAndLockStartLine", diff --git a/circuits/http/nivc/http_nivc.circom b/circuits/http/nivc/http_nivc.circom new file mode 100644 index 0000000..b7d652e --- /dev/null +++ b/circuits/http/nivc/http_nivc.circom @@ -0,0 +1,77 @@ +pragma circom 2.1.9; + +include "../parser/machine.circom"; +include "../interpreter.circom"; +include "../../utils/bytes.circom"; + +template HttpNIVC(DATA_BYTES, MAX_NUMBER_OF_HEADERS) { + signal input step_in[1]; + signal output step_out[1]; + + // Authenticate the plaintext we are passing in + signal input data[DATA_BYTES]; + signal data_hash <== DataHasher(DATA_BYTES)(data); + data_hash === step_in[0]; + + signal input start_line_hash; + signal input header_hashes[MAX_NUMBER_OF_HEADERS]; + signal input body_hash; + + // TODO: could just have a parser template and reduce code here + 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; + + 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; + } + + // Get the start line shit + signal start_line[DATA_BYTES]; + signal not_start_line_mask[DATA_BYTES]; + for(var i = 0 ; i < DATA_BYTES ; i++) { + not_start_line_mask[i] <== IsZero()(State[i].parsing_start); + start_line[i] <== data[i] * (1 - not_start_line_mask[i]); + } + signal inner_start_line_hash <== DataHasher(DATA_BYTES)(start_line); + inner_start_line_hash === start_line_hash; + + // Get the header shit + signal header[MAX_NUMBER_OF_HEADERS][DATA_BYTES]; + signal header_masks[MAX_NUMBER_OF_HEADERS][DATA_BYTES]; + for(var i = 0 ; i < MAX_NUMBER_OF_HEADERS ; i++) { + for(var j = 0 ; j < DATA_BYTES ; j++) { + header_masks[i][j] <== IsEqual()([State[j].parsing_header, i + 1]); + header[i][j] <== data[j] * header_masks[i][j]; + } + } + signal inner_header_hashes[MAX_NUMBER_OF_HEADERS]; + signal header_is_unused[MAX_NUMBER_OF_HEADERS]; // If a header hash is passed in as 0, it is not used (no way to compute preimage of 0) + for(var i = 0 ; i < MAX_NUMBER_OF_HEADERS ; i++) { + header_is_unused[i] <== IsZero()(header_hashes[i]); + inner_header_hashes[i] <== DataHasher(DATA_BYTES)(header[i]); + (1 - header_is_unused[i]) * inner_header_hashes[i] === header_hashes[i]; + } + + // Get the body shit + signal body[DATA_BYTES]; + for(var i = 0 ; i < DATA_BYTES ; i++) { + body[i] <== data[i] * State[i].parsing_body; + } + signal inner_body_hash <== DataHasher(DATA_BYTES)(body); + inner_body_hash === body_hash; + step_out[0] <== inner_body_hash; +} diff --git a/circuits/http/nivc/notes.md b/circuits/http/nivc/notes.md new file mode 100644 index 0000000..a0bd064 --- /dev/null +++ b/circuits/http/nivc/notes.md @@ -0,0 +1,5 @@ +# JSON Notes + +Okay what we can do is have a hash chain up the stack and store at most MAX_STACK_HEIGHT hash values, then write to this array at the current depth for each new value we get. We also should hash the stack indicator (array vs. object). + +We then just have to assert that we get an `ArrayEqual` with our given input at some point. We also assert the hash of the value itself is correct (possibly this just happens in a uniform way thinking of it as an object itself? Some details remain.) \ No newline at end of file diff --git a/circuits/json/nivc/json_nivc.circom b/circuits/json/nivc/json_nivc.circom new file mode 100644 index 0000000..44c4e9a --- /dev/null +++ b/circuits/json/nivc/json_nivc.circom @@ -0,0 +1,56 @@ +pragma circom 2.1.9; + +include "../interpreter.circom"; + +template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH, MAX_KEY_SIZE) { + signal input step_in[1]; + signal output step_out[1]; + + // Input a hash of the value we are extracting + signal input value_hash[1]; + + // use codified version of keys to get the json shitttt + signal input keys[MAX_STACK_HEIGHT][MAX_KEY_LENGTH][1]; + // ^^^^^^^^ ^^^^^^^^^^^^^^ ^ + // max keys we could ever use max key size key_type: 0 is null, 1 is string, 2 is array index, 3 is value + + /* + [["data",1], ["items", 1], [0,2], ["profile", 1], ["name", 1], ["Taylor Swift", 3], [0,0]] + + this is like the branch of the "tree" of the JSON that we want to prove exists + */ + + // Authenticate the (potentially further masked) plaintext we are passing in + signal input data[DATA_BYTES]; + signal data_hash <== DataHasher(DATA_BYTES)(data); + data_hash === step_in[0]; + + // Run the JSON parser + component State[DATA_BYTES - MAX_KEY_LENGTH]; + State[0] = StateUpdate(MAX_STACK_HEIGHT); + State[0].byte <== data[0]; + 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; + + for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) { + if(data_idx < DATA_BYTES - MAX_KEY_LENGTH) { + State[data_idx] = StateUpdate(MAX_STACK_HEIGHT); + State[data_idx].byte <== data[data_idx]; + State[data_idx].stack <== State[data_idx - 1].next_stack; + State[data_idx].parsing_string <== State[data_idx - 1].next_parsing_string; + State[data_idx].parsing_number <== State[data_idx - 1].next_parsing_number; + } + + // -------------------------------------------------------------------------------------- // + // Do some real shit here + + + + // -------------------------------------------------------------------------------------- // + // TODO (autoparallel): No idea what to do here yet lol + step_out[0] <== DataHasher(DATA_BYTES)(masked); +} + diff --git a/circuits/test/full/full.test.ts b/circuits/test/full/full.test.ts index 1b5089b..28e3bf0 100644 --- a/circuits/test/full/full.test.ts +++ b/circuits/test/full/full.test.ts @@ -62,11 +62,53 @@ const http_response_ciphertext = [ 14, 217, 109, 147, 130, 61, 214, 237, 143, 77, 14, 14, 70, 56, 94, 97, 207, 214, 106, 249, 37, 7, 186, 95, 174, 146, 203, 148, 173, 172, 13, 113 ]; + +const http_start_line = [ + 72, 84, 84, 80, 47, 49, 46, 49, 32, 50, 48, 48, 32, 79, 75, 13, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +]; + +const http_header_0 = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +const http_header_1 = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 99, 111, 110, 116, 101, 110, 116, 45, 101, 110, 99, 111, 100, 105, 110, 103, 58, 32, 103, 122, + 105, 112, 13, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; const http_body = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 123, 13, 10, 32, 32, 32, 34, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, @@ -86,6 +128,9 @@ const padding = new Array(lengthDiff).fill(0); const padded_http_body = [...padding, ...http_body]; const http_response_hash = DataHasher(http_response_plaintext); +const http_start_line_hash = DataHasher(http_start_line); +const http_header_0_hash = DataHasher(http_header_0); +const http_header_1_hash = DataHasher(http_header_1); const http_body_mask_hash = DataHasher(padded_http_body); @@ -170,26 +215,16 @@ const json_key3_mask_hash = DataHasher(json_key3_mask); describe("NIVC_FULL", async () => { let aesCircuit: WitnessTester<["key", "iv", "aad", "ctr", "plainText", "cipherText", "step_in"], ["step_out"]>; - let httpParseAndLockStartLineCircuit: WitnessTester<["step_in", "data", "beginning", "beginning_length", "middle", "middle_length", "final", "final_length"], ["step_out"]>; - let lockHeaderCircuit: WitnessTester<["step_in", "data", "header", "headerNameLength", "value", "headerValueLength"], ["step_out"]>; - let bodyMaskCircuit: WitnessTester<["step_in", "data"], ["step_out"]>; + let httpCircuit: WitnessTester<["step_in", "data", "start_line_hash", "header_hashes", "body_hash"], ["step_out"]>; let json_mask_object_circuit: WitnessTester<["step_in", "data", "key", "keyLen"], ["step_out"]>; let json_mask_arr_circuit: WitnessTester<["step_in", "data", "index"], ["step_out"]>; let extract_value_circuit: WitnessTester<["step_in", "data"], ["step_out"]>; + const MAX_NUMBER_OF_HEADERS = 2; + const DATA_BYTES = 320; const MAX_STACK_HEIGHT = 5; - 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 - const MAX_KEY_LENGTH = 8; const MAX_VALUE_LENGTH = 32; @@ -200,26 +235,12 @@ describe("NIVC_FULL", async () => { }); console.log("#constraints (AES-GCTR):", await aesCircuit.getConstraintCount()); - httpParseAndLockStartLineCircuit = await circomkit.WitnessTester(`ParseAndLockStartLine`, { - file: "http/nivc/parse_and_lock_start_line", - template: "ParseAndLockStartLine", - params: [DATA_BYTES, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENGTH, MAX_FINAL_LENGTH], - }); - console.log("#constraints (HTTP-PARSE-AND-LOCK-START-LINE):", await httpParseAndLockStartLineCircuit.getConstraintCount()); - - lockHeaderCircuit = await circomkit.WitnessTester(`LockHeader`, { - file: "http/nivc/lock_header", - template: "LockHeader", - params: [DATA_BYTES, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH], - }); - console.log("#constraints (HTTP-LOCK-HEADER):", await lockHeaderCircuit.getConstraintCount()); - - bodyMaskCircuit = await circomkit.WitnessTester(`BodyMask`, { - file: "http/nivc/body_mask", - template: "HTTPMaskBodyNIVC", - params: [DATA_BYTES], + httpCircuit = await circomkit.WitnessTester(`HttpNIVC`, { + file: "http/nivc/http_nivc", + template: "HttpNIVC", + params: [DATA_BYTES, MAX_NUMBER_OF_HEADERS], }); - console.log("#constraints (HTTP-BODY-MASK):", await bodyMaskCircuit.getConstraintCount()); + console.log("#constraints (HttpNIVC):", await httpCircuit.getConstraintCount()); json_mask_object_circuit = await circomkit.WitnessTester(`JsonMaskObjectNIVC`, { file: "json/nivc/masker", @@ -243,15 +264,6 @@ describe("NIVC_FULL", async () => { console.log("#constraints (JSON-MASK-EXTRACT-FINAL):", await extract_value_circuit.getConstraintCount()); }); - - 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("NIVC_CHAIN", async () => { // Run AES chain let ctr = [0x00, 0x00, 0x00, 0x01]; @@ -271,19 +283,8 @@ describe("NIVC_FULL", async () => { } assert.deepEqual(http_response_hash, aes_gcm.step_out); - // Lock the start line - let parseAndLockStartLine = await httpParseAndLockStartLineCircuit.compute({ step_in: aes_gcm.step_out, data: http_response_plaintext, beginning: beginningPadded, beginning_length: beginning.length, middle: middlePadded, middle_length: middle.length, final: finalPadded, final_length: final.length }, ["step_out"]); - console.log("Start Line `step_out`: ", parseAndLockStartLine.step_out); - - // Lock a header - let lockHeader = await lockHeaderCircuit.compute({ step_in: parseAndLockStartLine.step_out, data: http_response_plaintext, header: headerNamePadded, headerNameLength: headerName.length, value: headerValuePadded, headerValueLength: headerValue.length }, ["step_out"]); - console.log("Lock Header `step_out`: ", lockHeader.step_out); - - // Mask the body - // let bodyMask = await bodyMaskCircuit.compute({ step_in: lockHeader.step_out, data: http_response_plaintext }, ["step_out"]); - let bodyMask = await bodyMaskCircuit.compute({ step_in: http_response_hash, data: http_response_plaintext }, ["step_out"]); - console.log("Body Mask `step_out`: ", bodyMask.step_out); - assert.deepEqual(bodyMask.step_out, http_body_mask_hash); + let http = await httpCircuit.compute({ step_in: aes_gcm.step_out, data: http_response_plaintext, start_line_hash: http_start_line_hash, header_hashes: [http_header_0_hash, http_header_1_hash], body_hash: http_body_mask_hash }, ["step_out"]); + console.log("HttpNIVC `step_out`:", http.step_out); let key0 = [100, 97, 116, 97, 0, 0, 0, 0]; // "data" let key0Len = 4; @@ -294,9 +295,7 @@ describe("NIVC_FULL", async () => { let key3 = [110, 97, 109, 101, 0, 0, 0, 0]; // "name" let key3Len = 4; - // let value = toByte("\"Taylor Swift\""); - - let json_extract_key0 = await json_mask_object_circuit.compute({ step_in: bodyMask.step_out, data: http_body, key: key0, keyLen: key0Len }, ["step_out"]); + let json_extract_key0 = await json_mask_object_circuit.compute({ step_in: http.step_out, data: http_body, key: key0, keyLen: key0Len }, ["step_out"]); console.log("JSON Extract key0 `step_out`:", json_extract_key0.step_out); assert.deepEqual(json_extract_key0.step_out, json_key0_mask_hash); diff --git a/circuits/test/http/nivc/http_nivc.test.ts b/circuits/test/http/nivc/http_nivc.test.ts new file mode 100644 index 0000000..295c787 --- /dev/null +++ b/circuits/test/http/nivc/http_nivc.test.ts @@ -0,0 +1,167 @@ +import { circomkit, WitnessTester, toByte } from "../../common"; +import { assert } from "chai"; +import { DataHasher } from "../../common/poseidon"; + +// 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 TEST_HTTP = [ + 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]; + +const TEST_HTTP_START_LINE = [ + 72, 84, 84, 80, 47, 49, 46, 49, 32, 50, 48, 48, 32, 79, 75, 13, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, +]; + +const TEST_HTTP_HEADER_0 = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +const TEST_HTTP_HEADER_1 = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 99, 111, 110, 116, 101, 110, 116, 45, 101, 110, 99, 111, 100, 105, 110, 103, 58, 32, 103, 122, + 105, 112, 13, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +const TEST_HTTP_BODY = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, +]; + +const DATA_BYTES = 320; +const TOTAL_BYTES_ACROSS_NIVC = 1; + +describe("HTTP_NIVC", async () => { + let dataHasher: WitnessTester<["in"], ["out"]>; + let httpNivc: WitnessTester<["step_in", "data", "start_line_hash", "header_hashes", "body_hash"], ["step_out"]>; + before(async () => { + dataHasher = await circomkit.WitnessTester(`DataHasher`, { + file: "utils/hash", + template: "DataHasher", + params: [320], + }); + + httpNivc = await circomkit.WitnessTester("http_nivc", { + file: "http/nivc/http_nivc", + template: "HttpNIVC", + params: [320, 2] + }); + }); + + it("witness: TEST_HTTP, single header", async () => { + // Get all the hashes we need + // Get the data hash + let data_hash = await dataHasher.compute({ in: TEST_HTTP }, ["out"]); + // Get the start line hash + let start_line_hash = await dataHasher.compute({ in: TEST_HTTP_START_LINE }, ["out"]) + // Get the header hash + let header_hash = await dataHasher.compute({ in: TEST_HTTP_HEADER_0 }, ["out"]); + // Get the body hash + let body_hash = await dataHasher.compute({ in: TEST_HTTP_BODY }, ["out"]); + + // Run the HTTP circuit + // POTENTIAL BUG: I didn't get this to work with `expectPass` as it didn't compute `step_out` that way??? + let http_nivc_compute = await httpNivc.compute({ + step_in: data_hash.out, + data: TEST_HTTP, + start_line_hash: start_line_hash.out, + header_hashes: [header_hash.out, 0], + body_hash: body_hash.out, + }, ["step_out"]); + + assert.deepEqual(http_nivc_compute.step_out, body_hash.out); + }); + + it("witness: TEST_HTTP, two headers", async () => { + // Get all the hashes we need + // Get the data hash + let data_hash = await dataHasher.compute({ in: TEST_HTTP }, ["out"]); + // Get the start line hash + let start_line_hash = await dataHasher.compute({ in: TEST_HTTP_START_LINE }, ["out"]) + // Get the header hashes + let header_0_hash = await dataHasher.compute({ in: TEST_HTTP_HEADER_0 }, ["out"]); + let header_1_hash = await dataHasher.compute({ in: TEST_HTTP_HEADER_1 }, ["out"]); + // Get the body hash + let body_hash = await dataHasher.compute({ in: TEST_HTTP_BODY }, ["out"]); + + // Run the HTTP circuit + // POTENTIAL BUG: I didn't get this to work with `expectPass` as it didn't compute `step_out` that way??? + let http_nivc_compute = await httpNivc.compute({ + step_in: data_hash.out, + data: TEST_HTTP, + start_line_hash: start_line_hash.out, + header_hashes: [header_0_hash.out, header_1_hash.out], + body_hash: body_hash.out, + }, ["step_out"]); + + assert.deepEqual(http_nivc_compute.step_out, body_hash.out); + }); +}); \ No newline at end of file diff --git a/circuits/test/utils/hash.test.ts b/circuits/test/utils/hash.test.ts index 95e2d80..28c7bde 100644 --- a/circuits/test/utils/hash.test.ts +++ b/circuits/test/utils/hash.test.ts @@ -109,6 +109,42 @@ describe("hash", () => { { out: hash } ); }); + }); + + const TEST_HTTP_BYTES = [ + 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("DataHasherHTTP", () => { + let circuit: WitnessTester<["in"], ["out"]>; + + before(async () => { + circuit = await circomkit.WitnessTester(`DataHasher`, { + file: "utils/hash", + template: "DataHasher", + params: [320], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("witness: TEST HTTP BYTES", async () => { + + await circuit.expectPass({ in: TEST_HTTP_BYTES }, { out: "2195365663909569734943279727560535141179588918483111718403427949138562480675" }); + }); + }); }); \ No newline at end of file diff --git a/package.json b/package.json index 0e493ef..d5e5c58 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "web-prover-circuits", "description": "ZK Circuits for WebProofs", - "version": "0.4.1", + "version": "0.5.0", "license": "Apache-2.0", "repository": { "type": "git",