From 34c23ef69b48a5304b7f441324afa83556180d41 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Fri, 6 Dec 2024 05:33:44 -0700 Subject: [PATCH] release: v0.6.0 (#65) * init: v0.6.0 staging - adds CHANGELOG.md - bumps version - deletes extra builds - updates main.rs - updates circuits.json * chore: remove unused circuit material (#72) * remove: extra HTTP material * remove: aes * fix: tests * remove: old `circuits.json` * cleanup: JSON * Delete json_nivc.circom * Delete extractor.circom * remove: unneeded utils * remove: more utils * consolidate bytes * fix: build * feat: `DataHasher` takes -1 as padding (#76) * feat: authentication of ciphertext hash (#77) * fix: HTTP formatting constraints (#78) * fix: http headers max * fix: verify HTTP machine state at end * use --O1 in build * use `--O2` flag * Update Makefile * delete out-of-sync docs * delete unused example files * delete rogue logs * add rust cache * remove deprecated docs * add git for private repos * remove circuit warnings * place rust cache at correct step --------- Co-authored-by: lonerapier --- .github/workflows/artifacts.yaml | 31 +- .gitignore | 5 +- CHANGELOG.md | 73 ++ Makefile | 2 +- README.md | 35 +- .../target_1024b/aes_gctr_nivc_1024b.circom | 5 - builds/target_1024b/http_nivc_1024b.circom | 5 - .../http_verification_1024b.circom | 5 + ... => plaintext_authentication_1024b.circom} | 0 builds/target_512b/aes_gctr_nivc_512b.circom | 5 - builds/target_512b/chacha20_nivc_512b.circom | 5 - builds/target_512b/http_nivc_512b.circom | 5 - .../json_extract_value_512b.circom | 5 - .../json_mask_array_index_512b.circom | 5 - .../target_512b/json_mask_object_512b.circom | 5 - circomkit.json | 3 +- circuits.json | 265 +----- circuits/aes-gcm/aes-gcm.circom | 145 ---- circuits/aes-gcm/aes/cipher.circom | 159 ---- circuits/aes-gcm/aes/key_expansion.circom | 179 ---- circuits/aes-gcm/aes/mix_columns.circom | 209 ----- circuits/aes-gcm/aes/sbox.circom | 298 ------- circuits/aes-gcm/gctr.circom | 129 --- circuits/aes-gcm/ghash/ghash.circom | 69 -- circuits/aes-gcm/ghash/gmul.circom | 133 --- circuits/aes-gcm/nivc/aes-gctr-nivc.circom | 87 -- circuits/aes-gcm/nivc/gctr-nivc.circom | 76 -- circuits/chacha20/chacha-qr.circom | 2 +- circuits/chacha20/chacha-round.circom | 2 +- circuits/chacha20/chacha20.circom | 2 +- circuits/chacha20/nivc/chacha20_nivc.circom | 28 +- circuits/http/interpreter.circom | 107 --- circuits/http/locker.circom | 181 ---- circuits/http/{parser => }/machine.circom | 7 +- circuits/http/nivc/body_mask.circom | 50 -- circuits/http/nivc/lock_header.circom | 131 --- .../nivc/parse_and_lock_start_line.circom | 99 --- circuits/http/parser/language.circom | 27 - circuits/http/parser/parser.circom | 51 -- .../http_nivc.circom => verification.circom} | 32 +- circuits/json/extractor.circom | 164 ---- circuits/json/interpreter.circom | 7 +- circuits/json/nivc/json_nivc.circom | 56 -- circuits/json/nivc/masker.circom | 6 +- circuits/json/parser/language.circom | 10 +- circuits/json/parser/machine.circom | 18 +- circuits/json/parser/parser.circom | 32 +- circuits/test/aes-gcm/aes-gcm.test.ts | 67 -- circuits/test/aes-gcm/aes/cipher.test.ts | 193 ----- .../test/aes-gcm/aes/key_expansion.test.ts | 106 --- circuits/test/aes-gcm/aes/sbox.test.ts | 125 --- .../test/aes-gcm/aes/transformations.test.ts | 74 -- circuits/test/aes-gcm/gctr.test.ts | 31 - circuits/test/aes-gcm/ghash/ghash.test.ts | 30 - circuits/test/aes-gcm/ghash/gmul.test.ts | 117 --- .../test/aes-gcm/nivc/aes-gctr-nivc.test.ts | 124 --- circuits/test/chacha20/chacha20-nivc.test.ts | 55 +- circuits/test/common/poseidon.ts | 27 +- circuits/test/full/full.test.ts | 259 +----- circuits/test/http/interpreter.test.ts | 111 --- circuits/test/http/locker.test.ts | 136 --- circuits/test/http/nivc/body_mask.test.ts | 127 --- circuits/test/http/nivc/lock_header.test.ts | 100 --- .../nivc/parse_and_lock_start_line.test.ts | 86 -- ...http_nivc.test.ts => verification.test.ts} | 14 +- circuits/test/utils/array.test.ts | 582 +++---------- circuits/test/utils/bits.test.ts | 25 + circuits/test/utils/bytes.test.ts | 170 ---- circuits/test/utils/hash.test.ts | 42 +- circuits/test/utils/search.test.ts | 249 +----- circuits/utils/array.circom | 281 ------ .../{generics-bits.circom => bits.circom} | 24 +- circuits/utils/bytes.circom | 258 ------ circuits/utils/hash.circom | 13 +- circuits/utils/search.circom | 254 ------ docs/http.md | 3 + docs/pabuild.md | 202 ----- examples/http/lockfile/request.lock.json | 9 - examples/http/lockfile/response.lock.json | 7 - examples/http/lockfile/spotify.lock.json | 7 - .../http/lockfile/spotify_extended.lock.json | 19 - examples/json/lockfile/spotify.json | 10 - examples/json/lockfile/two_keys.json | 6 - .../json/lockfile/value_array_nested.json | 8 - .../json/lockfile/value_array_number.json | 7 - .../json/lockfile/value_array_object.json | 9 - .../json/lockfile/value_array_string.json | 7 - examples/json/lockfile/value_number.json | 6 - examples/json/lockfile/value_object.json | 7 - examples/json/lockfile/value_string.json | 6 - examples/json/test/example.json | 24 - examples/json/test/spotify.json | 3 - examples/json/test/two_keys.json | 4 - examples/json/test/value_array_nested.json | 1 - .../json/test/value_array_object_array.json | 1 - examples/json/test/value_number.json | 1 - examples/json/test/value_string.json | 1 - inputs/search/witness.json | 803 ------------------ package.json | 2 +- src/main.rs | 4 +- 100 files changed, 527 insertions(+), 7265 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 builds/target_1024b/aes_gctr_nivc_1024b.circom delete mode 100644 builds/target_1024b/http_nivc_1024b.circom create mode 100644 builds/target_1024b/http_verification_1024b.circom rename builds/target_1024b/{chacha20_nivc_1024.circom => plaintext_authentication_1024b.circom} (100%) delete mode 100644 builds/target_512b/aes_gctr_nivc_512b.circom delete mode 100644 builds/target_512b/chacha20_nivc_512b.circom delete mode 100644 builds/target_512b/http_nivc_512b.circom delete mode 100644 builds/target_512b/json_extract_value_512b.circom delete mode 100644 builds/target_512b/json_mask_array_index_512b.circom delete mode 100644 builds/target_512b/json_mask_object_512b.circom delete mode 100644 circuits/aes-gcm/aes-gcm.circom delete mode 100644 circuits/aes-gcm/aes/cipher.circom delete mode 100644 circuits/aes-gcm/aes/key_expansion.circom delete mode 100644 circuits/aes-gcm/aes/mix_columns.circom delete mode 100644 circuits/aes-gcm/aes/sbox.circom delete mode 100644 circuits/aes-gcm/gctr.circom delete mode 100644 circuits/aes-gcm/ghash/ghash.circom delete mode 100644 circuits/aes-gcm/ghash/gmul.circom delete mode 100644 circuits/aes-gcm/nivc/aes-gctr-nivc.circom delete mode 100644 circuits/aes-gcm/nivc/gctr-nivc.circom delete mode 100644 circuits/http/interpreter.circom delete mode 100644 circuits/http/locker.circom rename circuits/http/{parser => }/machine.circom (96%) delete mode 100644 circuits/http/nivc/body_mask.circom delete mode 100644 circuits/http/nivc/lock_header.circom delete mode 100644 circuits/http/nivc/parse_and_lock_start_line.circom delete mode 100644 circuits/http/parser/language.circom delete mode 100644 circuits/http/parser/parser.circom rename circuits/http/{nivc/http_nivc.circom => verification.circom} (74%) delete mode 100644 circuits/json/extractor.circom delete mode 100644 circuits/json/nivc/json_nivc.circom delete mode 100644 circuits/test/aes-gcm/aes-gcm.test.ts delete mode 100644 circuits/test/aes-gcm/aes/cipher.test.ts delete mode 100644 circuits/test/aes-gcm/aes/key_expansion.test.ts delete mode 100644 circuits/test/aes-gcm/aes/sbox.test.ts delete mode 100644 circuits/test/aes-gcm/aes/transformations.test.ts delete mode 100644 circuits/test/aes-gcm/gctr.test.ts delete mode 100644 circuits/test/aes-gcm/ghash/ghash.test.ts delete mode 100644 circuits/test/aes-gcm/ghash/gmul.test.ts delete mode 100644 circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts delete mode 100644 circuits/test/http/interpreter.test.ts delete mode 100644 circuits/test/http/locker.test.ts delete mode 100644 circuits/test/http/nivc/body_mask.test.ts delete mode 100644 circuits/test/http/nivc/lock_header.test.ts delete mode 100644 circuits/test/http/nivc/parse_and_lock_start_line.test.ts rename circuits/test/http/{nivc/http_nivc.test.ts => verification.test.ts} (96%) create mode 100644 circuits/test/utils/bits.test.ts delete mode 100644 circuits/test/utils/bytes.test.ts rename circuits/utils/{generics-bits.circom => bits.circom} (80%) delete mode 100644 circuits/utils/bytes.circom delete mode 100644 docs/pabuild.md delete mode 100644 examples/http/lockfile/request.lock.json delete mode 100644 examples/http/lockfile/response.lock.json delete mode 100644 examples/http/lockfile/spotify.lock.json delete mode 100644 examples/http/lockfile/spotify_extended.lock.json delete mode 100644 examples/json/lockfile/spotify.json delete mode 100644 examples/json/lockfile/two_keys.json delete mode 100644 examples/json/lockfile/value_array_nested.json delete mode 100644 examples/json/lockfile/value_array_number.json delete mode 100644 examples/json/lockfile/value_array_object.json delete mode 100644 examples/json/lockfile/value_array_string.json delete mode 100644 examples/json/lockfile/value_number.json delete mode 100644 examples/json/lockfile/value_object.json delete mode 100644 examples/json/lockfile/value_string.json delete mode 100644 examples/json/test/example.json delete mode 100644 examples/json/test/spotify.json delete mode 100644 examples/json/test/two_keys.json delete mode 100644 examples/json/test/value_array_nested.json delete mode 100644 examples/json/test/value_array_object_array.json delete mode 100644 examples/json/test/value_number.json delete mode 100644 examples/json/test/value_string.json delete mode 100644 inputs/search/witness.json diff --git a/.github/workflows/artifacts.yaml b/.github/workflows/artifacts.yaml index 9ee91b0..c2bf376 100644 --- a/.github/workflows/artifacts.yaml +++ b/.github/workflows/artifacts.yaml @@ -52,11 +52,20 @@ jobs: sudo apt-get update sudo apt-get install -y protobuf-compiler libprotobuf-dev + - name: Configure Git for Private Repos + run: | + git config --global url."https://${{ secrets.PAT }}@github.com/".insteadOf "https://github.com/" + - name: Install Rust uses: dtolnay/rust-toolchain@master with: toolchain: nightly-2024-10-28 + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + - name: Install Circom run: | CIRCOM_VERSION=2.1.9 @@ -87,33 +96,29 @@ jobs: make debug # Show what will be processed make build # Build the circuits - - name: Configure Git for Private Repos - run: | - git config --global url."https://${{ secrets.PAT }}@github.com/".insteadOf "https://github.com/" - - name: Build and run parameter generator run: | rustup install nightly # Build the parameter generator cargo build --release - + # Generate parameters using makefile target make params - + # Verify parameter files were created for target_dir in builds/target_*b; do size=$(basename "$target_dir" | sed 's/target_//') # Calculate ROM length the same way as in Makefile rom_length=$(echo "${size%b} / 16 + 16" | bc) - + # List of expected files files=( "aux_params_${size}_rom_length_${rom_length}.bin" "prover_key_${size}_rom_length_${rom_length}.bin" "verifier_key_${size}_rom_length_${rom_length}.bin" ) - + for file in "${files[@]}"; do if [ ! -f "$target_dir/artifacts/$file" ]; then echo "Error: File not found: $file in $target_dir/artifacts" @@ -122,7 +127,7 @@ jobs: echo "Successfully verified: $file" fi done - + echo "Successfully generated all parameter files for ${size}" done @@ -133,14 +138,14 @@ jobs: size=$(basename "$target_dir" | sed 's/target_//') # Calculate ROM length the same way as in Makefile rom_length=$(echo "${size%b} / 16 + 16" | bc) - + # List of expected files files=( "aux_params_${size}_rom_length_${rom_length}.bin" "prover_key_${size}_rom_length_${rom_length}.bin" "verifier_key_${size}_rom_length_${rom_length}.bin" ) - + for file in "${files[@]}"; do if [ ! -f "$target_dir/artifacts/$file" ]; then echo "Error: File not found: $file in $target_dir/artifacts" @@ -149,9 +154,9 @@ jobs: echo "Successfully verified: $file" fi done - + echo "Successfully generated all parameter files for ${size}" - + # Create zip archive for this target size if [ -d "$target_dir/artifacts" ]; then echo "Creating archive for $size" diff --git a/.gitignore b/.gitignore index eb8c40e..fef1713 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,7 @@ ir_log/* log_input_signals.txt *.bin *.r1cs -builds/**/artifacts/ \ No newline at end of file +builds/**/artifacts/ + +# MacOS folks +**/.DS_Store \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7ce1a66 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,73 @@ + +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + + +## [UNRELEASED] [0.6.0] - 2024-12-3 + +### Added + +### Changed +#### Circuit Builds +- Removed `512b` build path +- Removed `aes_gctr_nivc_*b.circom` from build + +#### Artifacts +- Adjusted circuit names: + - `aes_gctr_nivc` and `chacha20-nivc` replaced with a more suitable name: `plaintext_authentication` + - Runs with `512b` per fold + - `http_nivc` replaced with more suitable name: `http_verification` + +### Fixed +- TODO + +### Notes +- **Total circuits:** 5 +- **Circuit sizes:** + - `plaintext_authentication_1024b` + - non-linear constraints: `365,484` + - linear-constraints: `40,463` + - Theoretical storage size: `(40,463 + 365,484) * 3 * 32 bytes = 38,971,912 bytes ≈ 39 MB` + - R1CS file: `121.3MB` + - Graph file: `13.1MB` + - **WARNINGS:** Yes. Run `circomkit compile plaintext_authentication_1024b` + - `http_verification_1024b`: + - non-linear constaints: `546,895` **(WARNING: greater than `2^19 == 524,288`)** + - linear-constraints: `543,804` + - Theoretical storage size: `(546,895 + 543,804) * 3 * 32 bytes = 104,707,104 bytes ≈ 105 MB` + - R1CS file: `246.4MB` + - Graph file: `16.5MB` + - **WARNINGS:** Yes. Run `circomkit compile http_verification_1024b` + - `json_mask_object_1024b`: + - non-linear constraints: `550,001` **(WARNING: greater than `2^20 == 524,288`)** + - linear-constraints: `316,205` + - Theoretical storage size: `(550,001 + 316,205) * 3 * 32 bytes = 83,155,776 bytes ≈ 83 MB` + - R1CS file: `109MB` + - Graph file: `9.3MB` + - **WARNINGS:** Yes. Run `circomkit compile json_mask_object_1024b` + - `json_mask_array_index_1024b`: + - non-linear constraints: `295,146` + - linear-constraints: `194,082` + - Theoretical storage size: `(295,146 + 194,082) * 3 * 32 bytes = 46,966,080 bytes ≈ 47 MB` + - R1CS file: `67.4MB` + - Graph file: `7.4MB` + - **WARNINGS:** Yes. Run `circomkit compile json_mask_array_index_1024b` + - `json_extract_value_1024b`: + - non-linear constraints == `32,039` + - linear-constraints: `18,644` + - Theoretical storage size: `(32,039 + 18,644) * 3 * 32 bytes = 4,865,568 bytes ≈ 4.8 MB` + - R1CS file: `11.1MB` + - Graph file: `949KB` +- **Estimated expanded R1CS base memory requirements:** `2^{20} * 32 * 5 ~ 168MB`$ +- **Circuit param file sizes (SNARK):** + - `aux_params`: `115.1MB` + - `prover_key`: `100.7MB` + - `verifier_key`: `780.3MB` +- **Circuit param file sizes (ppSNARK):** + - `aux_params`: `836MB` **(WARNING: THIS IS LARGE)** + - `prover_key`: `5.86GB` **(WARNING: THIS IS EXTREMELY LARGE!!!)** + - `verifier_key`: `16.8MB` + diff --git a/Makefile b/Makefile index 5a142bd..8a638b1 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ build: @set -e; @for circuit in $(CIRCOM_FILES); do \ echo "Processing $${circuit}..."; \ - circom "$${circuit}" --r1cs --wasm -o "$$(dirname $${circuit})/artifacts" -l node_modules; \ + circom "$${circuit}" --r1cs --wasm --O2 -o "$$(dirname $${circuit})/artifacts" -l node_modules; \ build-circuit "$${circuit}" "$$(dirname $${circuit})/artifacts/$$(basename $${circuit} .circom).bin" -l node_modules; \ echo "====================xxxxxxxxxx===================="; \ done diff --git a/README.md b/README.md index a872eb4..1c96613 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,33 @@

- Parser Attestor + Web-Prover Circuits

- + Contributors - + Tests - - Lint -
## Overview -`parser-attestor` is a project focused on implementing parsers and extractors/selective-disclosure for various data formats inside of zero-knowledge circuits. +`web-prover-circuits` is a project focused on implementing parsers and extractors/selective-disclosure for various data formats inside zero-knowledge circuits. ## Repository Structure - `circuits/`: Current implementation of circuits + - `chacha`: ChaCha encryption circuit - `http`: HTTP parser and extractor - `json`: JSON parser and extractor - `json` has its own documentation [here](docs/json.md) - `utils`: Utility circuits - `test`: Circuit tests -- `src/`: Rust `pabuild` binary - - `pabuild` has its own documentation [here](docs/pabuild.md) +- `src/`: Rust public-params creation binary - `examples/`: Reference examples for JSON and HTTP parsers Documentation, in general, can be found in the `docs` directory. -We will add to this over time to make working with `parser-attestor` easier. ## Getting Started @@ -92,9 +88,9 @@ npx circomkit help `circomkit` can essentially do everything you would want to do with these Circuits, though we can't guarantee all commands work properly. **Example:** -For example, to compile the `json-parser`, you can run the following from the repository root: +For example, to compile the `plaintext_authentication`, you can run the following from the repository root: ``` -npx circomkit compile json-parser +npx circomkit compile plaintext_authentication_1024b ``` which implicitly checks the `circuits.json` for an object that points to the circuit's code itself. @@ -114,21 +110,6 @@ npx mocha will run every circuit test. To filter tests, you can use the `-g` flag (very helpful!). - -### Install `pabuild` -From the root of this repository, run: -```sh -cargo install --path . -``` -to install the `pabuild` binary. -You can see a help menu with the subcommands by: -```sh -pabuild --help -``` -This is our local Rust command line application. -Please see the [documentation](docs/pabuild.md) for how to use this alongside the other tools. - - ## License Licensed under the Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) diff --git a/builds/target_1024b/aes_gctr_nivc_1024b.circom b/builds/target_1024b/aes_gctr_nivc_1024b.circom deleted file mode 100644 index 24a2834..0000000 --- a/builds/target_1024b/aes_gctr_nivc_1024b.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../../circuits/aes-gcm/nivc/aes-gctr-nivc.circom"; - -component main { public [step_in] } = AESGCTRFOLD(1); \ No newline at end of file diff --git a/builds/target_1024b/http_nivc_1024b.circom b/builds/target_1024b/http_nivc_1024b.circom deleted file mode 100644 index 68ed7dd..0000000 --- a/builds/target_1024b/http_nivc_1024b.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../../circuits/http/nivc/http_nivc.circom"; - -component main { public [step_in] } = HttpNIVC(1024, 25); diff --git a/builds/target_1024b/http_verification_1024b.circom b/builds/target_1024b/http_verification_1024b.circom new file mode 100644 index 0000000..c2e8855 --- /dev/null +++ b/builds/target_1024b/http_verification_1024b.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../../circuits/http/verification.circom"; + +component main { public [step_in] } = HTTPVerification(1024, 25); diff --git a/builds/target_1024b/chacha20_nivc_1024.circom b/builds/target_1024b/plaintext_authentication_1024b.circom similarity index 100% rename from builds/target_1024b/chacha20_nivc_1024.circom rename to builds/target_1024b/plaintext_authentication_1024b.circom diff --git a/builds/target_512b/aes_gctr_nivc_512b.circom b/builds/target_512b/aes_gctr_nivc_512b.circom deleted file mode 100644 index 24a2834..0000000 --- a/builds/target_512b/aes_gctr_nivc_512b.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../../circuits/aes-gcm/nivc/aes-gctr-nivc.circom"; - -component main { public [step_in] } = AESGCTRFOLD(1); \ No newline at end of file diff --git a/builds/target_512b/chacha20_nivc_512b.circom b/builds/target_512b/chacha20_nivc_512b.circom deleted file mode 100644 index f0c99ff..0000000 --- a/builds/target_512b/chacha20_nivc_512b.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../../circuits/chacha20/nivc/chacha20_nivc.circom"; - -component main { public [step_in] } = ChaCha20_NIVC(512); \ No newline at end of file diff --git a/builds/target_512b/http_nivc_512b.circom b/builds/target_512b/http_nivc_512b.circom deleted file mode 100644 index a202b7d..0000000 --- a/builds/target_512b/http_nivc_512b.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../../circuits/http/nivc/http_nivc.circom"; - -component main { public [step_in] } = HttpNIVC(512, 10); diff --git a/builds/target_512b/json_extract_value_512b.circom b/builds/target_512b/json_extract_value_512b.circom deleted file mode 100644 index e7ad1f8..0000000 --- a/builds/target_512b/json_extract_value_512b.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../../circuits/json/nivc/extractor.circom"; - -component main { public [step_in] } = MaskExtractFinal(512, 48); \ No newline at end of file diff --git a/builds/target_512b/json_mask_array_index_512b.circom b/builds/target_512b/json_mask_array_index_512b.circom deleted file mode 100644 index ec72dc7..0000000 --- a/builds/target_512b/json_mask_array_index_512b.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../../circuits/json/nivc/masker.circom"; - -component main { public [step_in] } = JsonMaskArrayIndexNIVC(512, 10); \ No newline at end of file diff --git a/builds/target_512b/json_mask_object_512b.circom b/builds/target_512b/json_mask_object_512b.circom deleted file mode 100644 index 3bd0e31..0000000 --- a/builds/target_512b/json_mask_object_512b.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../../circuits/json/nivc/masker.circom"; - -component main { public [step_in] } = JsonMaskObjectNIVC(512, 10, 10); diff --git a/circomkit.json b/circomkit.json index 85615f8..924866d 100644 --- a/circomkit.json +++ b/circomkit.json @@ -4,5 +4,6 @@ "curve": "bn128", "includes": [ "node_modules" - ] + ], + "optimization": 2 } \ No newline at end of file diff --git a/circuits.json b/circuits.json index 97ce86e..8ed4b53 100644 --- a/circuits.json +++ b/circuits.json @@ -1,257 +1,20 @@ { - "value_number_test": { - "file": "main/json_value_number_test", - "template": "ExtractNumValue", + "plaintext_authentication_1024b": { + "file": "chacha20/nivc/chacha20_nivc", + "template": "ChaCha20_NIVC", "params": [ - 12, - 1, - 1, - 0, - 2 - ] - }, - "json-parser": { - "file": "json/parser/parser", - "template": "Parser", - "params": [ - 157, - 13 - ] - }, - "value_string_test": { - "file": "main/json_value_string_test", - "template": "ExtractStringValue", - "params": [ - 12, - 1, - 1, - 0, - 1 - ] - }, - "spotify_test": { - "file": "main/json_spotify_test", - "template": "ExtractStringValue", - "params": [ - 85, - 5, - 4, - 0, - 5, - 1, - 0, - 2, - 7, - 3, - 4, - 4, - 12 - ] - }, - "http-parser": { - "file": "http/parser/parser", - "template": "Parser", - "params": [ - 60 - ] - }, - "spotify_top_artists_test": { - "file": "main/http_spotify_top_artists_test", - "template": "LockHTTPResponse", - "params": [ - 203, - 85, - 8, - 3, - 2, - 12, - 31 - ] - }, - "value_array_number_test": { - "file": "main/json_value_array_number_test", - "template": "ExtractNumValue", - "params": [ - 73, - 2, - 1, - 0, - 2, - 1, - 4 - ] - }, - "value_array_object_test": { - "file": "main/json_value_array_object_test", - "template": "ExtractNumValue", - "params": [ - 29, - 4, - 1, - 0, - 0, - 1, - 1, - 2, - 0, - 3, - 1 - ] - }, - "value_string": { - "file": "main/json_value_string", - "template": "ExtractStringValue", - "params": [ - 12, - 1, - 1, - 0, - 1 - ] - }, - "two_keys_test": { - "file": "main/json_two_keys_test", - "template": "ExtractStringValue", - "params": [ - 40, - 1, - 4, - 0, - 3 - ] - }, - "get_request_test": { - "file": "main/http_get_request_test", - "template": "LockHTTPRequest", - "params": [ - 60, - 3, - 4, - 8, - 6, - 16, - 4, - 9 - ] - }, - "value_array_nested_test": { - "file": "main/json_value_array_nested_test", - "template": "ExtractNumValue", - "params": [ - 24, - 3, - 1, - 0, - 0, - 1, - 0, - 2, - 1 - ] - }, - "spotify_top_artists": { - "file": "main/extended_spotify_top_artists", - "template": "HttpJson", - "params": [ - 203, - 85, - 8, - 3, - 2, - 12, - 31, - 5, - 4, - 0, - 5, - 1, - 0, - 2, - 7, - 3, - 4, - 4, - 12 - ] - }, - "value_object_test": { - "file": "main/json_value_object_test", - "template": "ExtractStringValue", - "params": [ - 134, - 3, - 1, - 0, - 1, - 1, - 1 - ] - }, - "value_array_string_test": { - "file": "main/json_value_array_string_test", - "template": "ExtractStringValue", - "params": [ - 73, - 2, - 1, - 0, - 1, - 1, - 2 - ] - }, - "get_response_test": { - "file": "main/http_get_response_test", - "template": "LockHTTPResponse", - "params": [ - 89, - 18, - 8, - 3, - 2, - 12, - 16 - ] - }, - "nivc_aes": { - "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", - "params": [ - 1024, - 50, - 200, - 50 + 1024 ] }, - "nivc_lock_header": { - "file": "http/nivc/lock_header", - "template": "LockHeader", + "http_verification_1024b": { + "file": "http/verification", + "template": "HTTPVerification", "params": [ 1024, - 50, - 100 - ] - }, - "nivc_body_mask": { - "file": "http/nivc/body_mask", - "template": "HTTPMaskBodyNIVC", - "params": [ - 1024 + 25 ] }, - "nivc_json_object": { + "json_mask_object_1024b": { "file": "json/nivc/masker", "template": "JsonMaskObjectNIVC", "params": [ @@ -260,12 +23,20 @@ 10 ] }, - "nivc_json_array": { + "json_mask_array_index_1024b": { "file": "json/nivc/masker", "template": "JsonMaskArrayIndexNIVC", "params": [ 1024, 10 ] + }, + "json_extract_value_1024b": { + "file": "json/nivc/extractor", + "template": "MaskExtractFinal", + "params": [ + 1024, + 50 + ] } } \ No newline at end of file diff --git a/circuits/aes-gcm/aes-gcm.circom b/circuits/aes-gcm/aes-gcm.circom deleted file mode 100644 index ef9373b..0000000 --- a/circuits/aes-gcm/aes-gcm.circom +++ /dev/null @@ -1,145 +0,0 @@ -pragma circom 2.1.9; - -include "ghash/ghash.circom"; -include "aes/cipher.circom"; -include "../utils/array.circom"; -include "gctr.circom"; - - -/// AES-GCM with 128 bit key authenticated encryption according to: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf -/// -/// Parameters: -/// l: length of the plaintext -/// -/// Inputs: -/// key: 128-bit key -/// iv: initialization vector -/// plainText: plaintext to be encrypted -/// aad: additional data to be authenticated -/// -/// Outputs: -/// cipherText: encrypted ciphertext -/// authTag: authentication tag -/// -template AESGCM(l) { - // Inputs - signal input key[16]; // 128-bit key - signal input iv[12]; // IV length is 96 bits (12 bytes) - signal input plainText[l]; - signal input aad[16]; // AAD length is 128 bits (16 bytes) - - // Outputs - signal output cipherText[l]; - signal output authTag[16]; // Authentication tag length is 128 bits (16 bytes) - - component zeroBlock = ToBlocks(16); - for (var i = 0; i < 16; i++) { - zeroBlock.stream[i] <== 0; - } - - // Step 1: Let H = aes(key, zeroBlock) - component cipherH = Cipher(); - cipherH.key <== key; - cipherH.block <== zeroBlock.blocks[0]; - - // Step 2: Define a block, J0 with 96 bits of iv and 32 bits of 0s - component J0builder = ToBlocks(16); - for (var i = 0; i < 12; i++) { - J0builder.stream[i] <== iv[i]; - } - for (var i = 12; i < 16; i++) { - J0builder.stream[i] <== 0; - } - component J0WordIncrementer = IncrementWord(); - J0WordIncrementer.in <== J0builder.blocks[0][3]; - - component J0WordIncrementer2 = IncrementWord(); - J0WordIncrementer2.in <== J0WordIncrementer.out; - - signal J0[4][4]; - for (var i = 0; i < 3; i++) { - J0[i] <== J0builder.blocks[0][i]; - } - J0[3] <== J0WordIncrementer2.out; - - // Step 3: Let C = GCTRK(inc32(J0), P) - component gctr = GCTR(l); - gctr.key <== key; - gctr.initialCounterBlock <== J0; - gctr.plainText <== plainText; - - - // Step 4: Let u and v (v is always zero with out key size and aad length) - var blockCount = l\16; - if(l%16 > 0){ - blockCount = blockCount + 1; - } - // so the reason there is a plus two is because - // the first block is the aad - // the second is the ciphertext - // the last is the length of the aad and ciphertext - // i.e. S = GHASHH (A || C || [len(A)] || [len(C)]). <- which is always 48 bytes: 3 blocks - var ghashblocks = blockCount + 2; - signal ghashMessage[ghashblocks][4][4]; - - // set aad as first block - for (var i=0; i < 4; i++) { - for (var j=0; j < 4; j++) { - ghashMessage[0][i][j] <== aad[i*4+j]; - } - } - // set cipher text block padded - component ciphertextBlocks = ToBlocks(l); - ciphertextBlocks.stream <== gctr.cipherText; - - for (var i=0; i> i*8+j) & 1; - } - ghashMessage[ghashblocks-1][i\4+2][i%4] <== byte_value; - } - - // Step 5: Define a block, S - // needs to take in the number of blocks - component ghash = GHASH(ghashblocks); - component hashKeyToStream = ToStream(1, 16); - hashKeyToStream.blocks[0] <== cipherH.cipher; - ghash.HashKey <== hashKeyToStream.stream; - // S = GHASHH (A || 0^v || C || 0^u || [len(A)] || [len(C)]). - component selectedBlocksToStream[ghashblocks]; - for (var i = 0 ; i 0 && round <= 10); - - var rcon[10][4] = [ - [0x01, 0x00, 0x00, 0x00], - [0x02, 0x00, 0x00, 0x00], - [0x04, 0x00, 0x00, 0x00], - [0x08, 0x00, 0x00, 0x00], - [0x10, 0x00, 0x00, 0x00], - [0x20, 0x00, 0x00, 0x00], - [0x40, 0x00, 0x00, 0x00], - [0x80, 0x00, 0x00, 0x00], - [0x1b, 0x00, 0x00, 0x00], - [0x36, 0x00, 0x00, 0x00] - ]; - - out <== rcon[round-1]; -} - -// Rotates an array of bytes to the left by a specified rotation -template Rotate(rotation, length) { - assert(rotation < length); - signal input bytes[length]; - signal output rotated[length]; - - for(var i = 0; i < length - rotation; i++) { - rotated[i] <== bytes[i + rotation]; - } - - for(var i = length - rotation; i < length; i++) { - rotated[i] <== bytes[i - length + rotation]; - } -} - -// Substitutes each byte in a word using the S-Box -template SubstituteWord() { - signal input bytes[4]; - signal output substituted[4]; - - component sbox[4]; - - for(var i = 0; i < 4; i++) { - sbox[i] = SBox128(); - sbox[i].in <== bytes[i]; - substituted[i] <== sbox[i].out; - } -} \ No newline at end of file diff --git a/circuits/aes-gcm/aes/mix_columns.circom b/circuits/aes-gcm/aes/mix_columns.circom deleted file mode 100644 index f52a40a..0000000 --- a/circuits/aes-gcm/aes/mix_columns.circom +++ /dev/null @@ -1,209 +0,0 @@ -// from: https://github.com/crema-labs/aes-circom/tree/main/circuits -pragma circom 2.1.9; - -include "key_expansion.circom"; -include "circomlib/circuits/bitify.circom"; -include "../../utils/bytes.circom"; - -// MixColumns: Applies the equation for each column: -// [s'0,c] [2 3 1 1][s0,c] -// [s'1,c] = [1 2 3 1][s1,c] -// [s'2,c] [1 1 2 3][s2,c] -// [s'3,c] [3 1 1 2][s3,c] -// Where c is the column number, s are input bytes, s' are output bytes -template MixColumns(){ - signal input state[4][4]; - signal output out[4][4]; - - component s0[4]; - component s1[4]; - component s2[4]; - component s3[4]; - - for (var i = 0; i < 4; i++) { - s0[i] = S0(); - s1[i] = S1(); - s2[i] = S2(); - s3[i] = S3(); - - for(var j = 0; j < 4; j++) { - s0[i].in[j] <== state[j][i]; - s1[i].in[j] <== state[j][i]; - s2[i].in[j] <== state[j][i]; - s3[i].in[j] <== state[j][i]; - } - - out[0][i] <== s0[i].out; - out[1][i] <== s1[i].out; - out[2][i] <== s2[i].out; - out[3][i] <== s3[i].out; - } -} - -// S0: Implements the equation -// out = (2 • in[0]) ⊕ (3 • in[1]) ⊕ in[2] ⊕ in[3] -template S0(){ - signal input in[4]; - signal output out; - component num2bits[4]; - component xor[3]; - - for (var i = 2; i < 4; i++) { - num2bits[i] = Num2Bits(8); - num2bits[i].in <== in[i]; - } - - num2bits[0] = Num2Bits(8); - num2bits[0].in <== TBox(0)(in[0]); - - num2bits[1] = Num2Bits(8); - num2bits[1].in <== TBox(1)(in[1]); - - xor[0] = BitwiseXor(8); - xor[0].a <== num2bits[0].out; - xor[0].b <== num2bits[1].out; - - xor[1] = BitwiseXor(8); - xor[1].a <== xor[0].out; - xor[1].b <== num2bits[2].out; - - xor[2] = BitwiseXor(8); - xor[2].a <== xor[1].out; - xor[2].b <== num2bits[3].out; - - component b2n = Bits2Num(8); - for (var i = 0; i < 8; i++) { - b2n.in[i] <== xor[2].out[i]; - } - - out <== b2n.out; -} - -// S1: Implements the equation -// out = in[0] ⊕ (2 • in[1]) ⊕ (3 • in[2]) ⊕ in[3] -template S1(){ - signal input in[4]; - signal output out; - component num2bits[4]; - component xor[3]; - - num2bits[0] = Num2Bits(8); - num2bits[0].in <== in[0]; - - num2bits[1] = Num2Bits(8); - num2bits[1].in <== TBox(0)(in[1]); - - num2bits[2] = Num2Bits(8); - num2bits[2].in <== TBox(1)(in[2]); - - num2bits[3] = Num2Bits(8); - num2bits[3].in <== in[3]; - - xor[0] = BitwiseXor(8); - xor[0].a <== num2bits[0].out; - xor[0].b <== num2bits[1].out; - - xor[1] = BitwiseXor(8); - xor[1].a <== xor[0].out; - xor[1].b <== num2bits[2].out; - - xor[2] = BitwiseXor(8); - xor[2].a <== xor[1].out; - xor[2].b <== num2bits[3].out; - - component b2n = Bits2Num(8); - for (var i = 0; i < 8; i++) { - b2n.in[i] <== xor[2].out[i]; - } - - out <== b2n.out; -} - -// S2: Implements the equation -// out = in[0] ⊕ in[1] ⊕ (2 • in[2]) ⊕ (3 • in[3]) -template S2() { - signal input in[4]; - signal output out; - component num2bits[4]; - component xor[3]; - - for (var i = 0; i < 2; i++) { - num2bits[i] = Num2Bits(8); - num2bits[i].in <== in[i]; - } - - num2bits[2] = Num2Bits(8); - num2bits[2].in <== TBox(0)(in[2]); - - num2bits[3] = Num2Bits(8); - num2bits[3].in <== TBox(1)(in[3]); - - xor[0] = BitwiseXor(8); - xor[0].a <== num2bits[0].out; - xor[0].b <== num2bits[1].out; - - xor[1] = BitwiseXor(8); - xor[1].a <== xor[0].out; - xor[1].b <== num2bits[2].out; - - xor[2] = BitwiseXor(8); - xor[2].a <== xor[1].out; - xor[2].b <== num2bits[3].out; - - component b2n = Bits2Num(8); - for (var i = 0; i < 8; i++) { - b2n.in[i] <== xor[2].out[i]; - } - - out <== b2n.out; -} - -// S3: Implements the equation -// out = (3 • in[0]) ⊕ in[1] ⊕ in[2] ⊕ (2 • in[3]) -template S3() { - signal input in[4]; - signal output out; - component num2bits[4]; - component xor[3]; - - for (var i = 1; i < 3; i++) { - num2bits[i] = Num2Bits(8); - num2bits[i].in <== in[i]; - } - - num2bits[0] = Num2Bits(8); - num2bits[0].in <== TBox(1)(in[0]); - - num2bits[3] = Num2Bits(8); - num2bits[3].in <== TBox(0)(in[3]); - - xor[0] = BitwiseXor(8); - xor[0].a <== num2bits[0].out; - xor[0].b <== num2bits[1].out; - - xor[1] = BitwiseXor(8); - xor[1].a <== xor[0].out; - xor[1].b <== num2bits[2].out; - - xor[2] = BitwiseXor(8); - xor[2].a <== num2bits[3].out; - xor[2].b <== xor[1].out; - - component b2n = Bits2Num(8); - for (var i = 0; i < 8; i++) { - b2n.in[i] <== xor[2].out[i]; - } - - out <== b2n.out; -} - -template TBox(index) { - signal input subindex; - signal output out; - - if (index == 0) { - out <== FieldMul2()(subindex); - } else if (index == 1) { - out <== FieldMul3()(subindex); - } -} \ No newline at end of file diff --git a/circuits/aes-gcm/aes/sbox.circom b/circuits/aes-gcm/aes/sbox.circom deleted file mode 100644 index bbea049..0000000 --- a/circuits/aes-gcm/aes/sbox.circom +++ /dev/null @@ -1,298 +0,0 @@ -// from: https://github.com/crema-labs/aes-circom/tree/main/circuits -pragma circom 2.1.9; - -include "circomlib/circuits/comparators.circom"; -include "circomlib/circuits/bitify.circom"; -include "circomlib/circuits/gates.circom"; -include "../../utils/array.circom"; -include "../../utils/bytes.circom"; - -template SBox128() { - signal input in; - signal output out; - - signal inv <== FieldInv()(in); - signal invBits[8] <== Num2Bits(8)(inv); - signal outBits[8] <== AffineTransform()(invBits); - out <== Bits2Num(8)(outBits); -} - - - -// All this is for a properly constrained sbox since we don't have lookup arguments -// Finite field addition, the signal variable plus a compile-time constant -template FieldAddConst(c) { - signal input in[8]; - // control bit, if 0, then do not perform addition - signal input control; - signal output out[8]; - - for (var i=0; i<8; i++) { - if(c & (1<>1 if LSB1(Vi) = 0; - // Vi+1 ⎨ - // ⎩ (Vi >>1) ⊕ R if LSB1(Vi) =1. - // - component bit[16]; - component z_i_update[128]; - component mulx[128]; - component bytesToBits = BytesToBits(16); - bytesToBits.in <== X; - signal bitsX[16*8]; - bitsX <== bytesToBits.out; - for (var i = 0; i < 128; i++) { - // z_i_update - z_i_update[i] = Z_UPDATE(16); - z_i_update[i].Z <== Z[i]; - z_i_update[i].V <== V[i]; - z_i_update[i].bit_val <== bitsX[i]; - Z[i + 1] <== z_i_update[i].Z_new; - - // mulx to update V - mulx[i] = Mulx(16); - mulx[i].in <== V[i]; - V[i + 1] <== mulx[i].out; - } - // 4. Return Z128. - out <== Z[128]; -} - - -// if bit value is 0, then Z_new = Z -// if bit value is 1, then Z_new = Z xor V -template Z_UPDATE(n_bytes) { - signal input Z[n_bytes]; // this is Zero block in first itteration - signal input V[n_bytes]; // this is Y in first itteration - signal input bit_val; - signal output Z_new[n_bytes]; - - component mux = ArrayMux(n_bytes); - mux.sel <== bit_val; - mux.a <== Z; - component xorBlock = XORBLOCK(n_bytes); - xorBlock.a <== Z; - xorBlock.b <== V; - mux.b <== xorBlock.out; - Z_new <== mux.out; -} - - - - -// right shift by one bit. If msb is 1: -// then we xor the first byte with 0xE1 (11100001: 1 + X + X^2 + X^7) -// this is the irreducible polynomial used in AES-GCM -template Mulx(n_bytes) { - signal input in[n_bytes]; - signal output out[n_bytes]; - - signal intermediate[n_bytes]; - - component blockRightShift = BlockRightShift(n_bytes); - blockRightShift.in <== in; - intermediate <== blockRightShift.out; - - component xorByte = XorByte(); - xorByte.a <== intermediate[0]; - xorByte.b <== 0xE1; // 11100001 - - // if msb is 1, then we xor the first byte with R[0] - component mux = Mux1(); - mux.s <== blockRightShift.msb; - mux.c[0] <== intermediate[0]; - mux.c[1] <== xorByte.out; - - for (var i = 1; i < n_bytes; i++) { - out[i] <== intermediate[i]; - } - out[0] <== mux.out; -} - -// right shifts 16 bytes by one bit and returns the msb before the shift -template BlockRightShift(n_bytes) { - signal input in[n_bytes]; - signal output out[n_bytes]; - signal output msb; - - signal shiftedbits[n_bytes*8]; - component bytesToBits = BytesToBits(n_bytes); - for (var i = 0; i < n_bytes; i++) { - bytesToBits.in[i] <== in[i]; - } - msb <== bytesToBits.out[n_bytes*8 - 1]; - - component BitwiseRightShift = BitwiseRightShift(n_bytes*8, 1); - BitwiseRightShift.in <== bytesToBits.out; - shiftedbits <== BitwiseRightShift.out; - - component bitsToBytes = BitsToBytes(n_bytes); - bitsToBytes.in <== shiftedbits; - out <== bitsToBytes.out; -} \ No newline at end of file diff --git a/circuits/aes-gcm/nivc/aes-gctr-nivc.circom b/circuits/aes-gcm/nivc/aes-gctr-nivc.circom deleted file mode 100644 index c8ce9dd..0000000 --- a/circuits/aes-gcm/nivc/aes-gctr-nivc.circom +++ /dev/null @@ -1,87 +0,0 @@ -pragma circom 2.1.9; - -include "gctr-nivc.circom"; -include "../../utils/array.circom"; -include "../../utils/hash.circom"; - -// Compute AES-GCTR -template AESGCTRFOLD(NUM_CHUNKS) { - signal input key[16]; - signal input iv[12]; - signal input aad[16]; - - signal input ctr[4]; - - signal input plainText[NUM_CHUNKS][16]; - signal input cipherText[NUM_CHUNKS][16]; - - signal input step_in[1]; - signal output step_out[1]; - - component aes[NUM_CHUNKS]; - for(var i = 0 ; i < NUM_CHUNKS ; i++) { - aes[i] = AESGCTRFOLDABLE(); - if( i == 0) { - aes[i].plainText <== plainText[i]; - aes[i].lastCounter <== ctr; - } else { - aes[i].plainText <== plainText[i]; - aes[i].lastCounter <== aes[i - 1].counter; - } - aes[i].key <== key; - aes[i].iv <== iv; - aes[i].aad <== aad; - } - - // Regroup the plaintext and ciphertext into byte packed form - var computedCipherText[NUM_CHUNKS][16]; - for(var i = 0 ; i < NUM_CHUNKS ; i++) { - computedCipherText[i] = aes[i].cipherText; - } - - // verify that ciphertext supplied as private input match the computed one - signal matchedCiphertext[NUM_CHUNKS]; - for (var i = 0 ; i < NUM_CHUNKS ; i++) { - matchedCiphertext[i] <== IsEqualArray(16)([cipherText[i], aes[i].cipherText]); - matchedCiphertext[i] === 1; - } - - signal packedCiphertext[NUM_CHUNKS] <== GenericBytePackArray(NUM_CHUNKS, 16)(cipherText); - signal packedComputedCiphertext[NUM_CHUNKS] <== GenericBytePackArray(NUM_CHUNKS, 16)(computedCipherText); - signal packedPlaintext[NUM_CHUNKS] <== GenericBytePackArray(NUM_CHUNKS, 16)(plainText); - - signal plaintext_input_was_zero_chunk[NUM_CHUNKS]; - signal ciphertext_input_was_zero_chunk[NUM_CHUNKS]; - signal both_input_chunks_were_zero[NUM_CHUNKS]; - signal ciphertext_option[NUM_CHUNKS]; - signal ciphertext_equal_check[NUM_CHUNKS]; - for(var i = 0 ; i < NUM_CHUNKS; i++) { - plaintext_input_was_zero_chunk[i] <== IsZero()(packedPlaintext[i]); - ciphertext_input_was_zero_chunk[i] <== IsZero()(packedCiphertext[i]); - both_input_chunks_were_zero[i] <== plaintext_input_was_zero_chunk[i] * ciphertext_input_was_zero_chunk[i]; - ciphertext_option[i] <== (1 - both_input_chunks_were_zero[i]) * packedComputedCiphertext[i]; - ciphertext_equal_check[i] <== IsEqual()([packedCiphertext[i], ciphertext_option[i]]); - ciphertext_equal_check[i] === 1; - } - step_out[0] <== AESHasher(NUM_CHUNKS)(packedPlaintext, step_in[0]); -} - -// TODO (autoparallel): Could probably just have datahasher take in an initial hash as an input, but this was quicker to try first. -template AESHasher(NUM_CHUNKS) { - // TODO: add this assert back after witnesscalc supports - // assert(DATA_BYTES % 16 == 0); - signal input in[NUM_CHUNKS]; - signal input initial_hash; - signal output out; - - signal not_to_hash[NUM_CHUNKS]; - signal option_hash[NUM_CHUNKS]; - signal hashes[NUM_CHUNKS + 1]; - hashes[0] <== initial_hash; - for(var i = 0 ; i < NUM_CHUNKS ; i++) { - not_to_hash[i] <== IsZero()(in[i]); - option_hash[i] <== PoseidonChainer()([hashes[i],in[i]]); - hashes[i+1] <== not_to_hash[i] * (hashes[i] - option_hash[i]) + option_hash[i]; // same as: (1 - not_to_hash[i]) * option_hash[i] + not_to_hash[i] * hash[i]; - } - out <== hashes[NUM_CHUNKS]; -} \ No newline at end of file diff --git a/circuits/aes-gcm/nivc/gctr-nivc.circom b/circuits/aes-gcm/nivc/gctr-nivc.circom deleted file mode 100644 index bde7fc9..0000000 --- a/circuits/aes-gcm/nivc/gctr-nivc.circom +++ /dev/null @@ -1,76 +0,0 @@ -pragma circom 2.1.9; - -include "../aes/cipher.circom"; -include "../../utils/array.circom"; -include "../gctr.circom"; - -/// AES-GCTR with 128 bit key encryption according to: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf -/// -/// Parameters: -/// l: length of the plaintext -/// -/// Inputs: -/// key: 128-bit key -/// iv: initialization vector -/// plainText: plaintext to be encrypted -/// aad: additional data to be authenticated -/// -/// Outputs: -/// cipherText: encrypted ciphertext -/// -/// This folds a single block without authentication via ghash. -template AESGCTRFOLDABLE() { - // Inputs - signal input key[16]; // 128-bit key - signal input iv[12]; // IV length is 96 bits (12 bytes) - signal input plainText[16]; // only fold 16 bytes at a time. - signal input aad[16]; // AAD length is 128 bits (16 bytes) - - // Fold inputs - signal input lastCounter[4]; // Always start at one, then bring forward last counter. - - // Fold outputs - signal output counter[4]; - signal output cipherText[16]; - - component zeroBlock = ToBlocks(16); - for (var i = 0; i < 16; i++) { - zeroBlock.stream[i] <== 0; - } - - // Step 1: Let HashKey = aes(key, zeroBlock) - component cipherH = Cipher(); - cipherH.key <== key; - cipherH.block <== zeroBlock.blocks[0]; - - // Step 2: Define a block, J0 with 96 bits of iv and 32 bits of 0s - component J0builder = ToBlocks(16); - for (var i = 0; i < 12; i++) { - J0builder.stream[i] <== iv[i]; - } - // Use the fold counter as input. - for (var i = 12; i < 16; i++) { - J0builder.stream[i] <== lastCounter[i%4]; // initialize to 0001. - } - - component J0WordIncrementer = IncrementWord(); - J0WordIncrementer.in <== J0builder.blocks[0][3]; - - signal J0[4][4]; - for (var i = 0; i < 3; i++) { - J0[i] <== J0builder.blocks[0][i]; - } - J0[3] <== J0WordIncrementer.out; - - // Step 3: Let C = GCTRK(inc32(J0), P) - component gctr = GCTR(16); - gctr.key <== key; - gctr.initialCounterBlock <== J0; - gctr.plainText <== plainText; - cipherText <== gctr.cipherText; - - // extract the counter column wise. - for (var i = 0; i < 4; i++) { - counter[i] <== J0[i][3]; - } -} \ No newline at end of file diff --git a/circuits/chacha20/chacha-qr.circom b/circuits/chacha20/chacha-qr.circom index 7ce1487..7cc7466 100644 --- a/circuits/chacha20/chacha-qr.circom +++ b/circuits/chacha20/chacha-qr.circom @@ -2,7 +2,7 @@ // modified for our needs pragma circom 2.1.9; -include "../utils/generics-bits.circom"; +include "../utils/bits.circom"; /** * Perform ChaCha Quarter Round diff --git a/circuits/chacha20/chacha-round.circom b/circuits/chacha20/chacha-round.circom index 95e4340..0f5feda 100644 --- a/circuits/chacha20/chacha-round.circom +++ b/circuits/chacha20/chacha-round.circom @@ -3,7 +3,7 @@ pragma circom 2.1.9; include "./chacha-qr.circom"; -include "../utils/generics-bits.circom"; +include "../utils/bits.circom"; template Round() { // in => 16 32-bit words diff --git a/circuits/chacha20/chacha20.circom b/circuits/chacha20/chacha20.circom index b9290a8..ea85ffe 100644 --- a/circuits/chacha20/chacha20.circom +++ b/circuits/chacha20/chacha20.circom @@ -4,7 +4,7 @@ pragma circom 2.1.9; include "./chacha-round.circom"; include "./chacha-qr.circom"; -include "../utils/generics-bits.circom"; +include "../utils/bits.circom"; /** ChaCha20 in counter mode */ // Chacha20 opperates a 4x4 matrix of 32-bit words where the first 4 words are constants: C diff --git a/circuits/chacha20/nivc/chacha20_nivc.circom b/circuits/chacha20/nivc/chacha20_nivc.circom index 9752d4b..e969b9d 100644 --- a/circuits/chacha20/nivc/chacha20_nivc.circom +++ b/circuits/chacha20/nivc/chacha20_nivc.circom @@ -4,7 +4,7 @@ pragma circom 2.1.9; include "../chacha-round.circom"; include "../chacha-qr.circom"; -include "../../utils/generics-bits.circom"; +include "../../utils/bits.circom"; include "../../utils/hash.circom"; include "../../utils/array.circom"; @@ -34,18 +34,21 @@ template ChaCha20_NIVC(DATA_BYTES) { // the below can be both ciphertext or plaintext depending on the direction // in => N 32-bit words => N 4 byte words signal input plainText[DATA_BYTES]; - // out => N 32-bit words => N 4 byte words - signal input cipherText[DATA_BYTES]; + // step_in should be the ciphertext digest signal input step_in[1]; + + // step_out should be the plaintext digest signal output step_out[1]; + signal isPadding[DATA_BYTES]; signal plaintextBits[DATA_BYTES / 4][32]; component toBits[DATA_BYTES / 4]; for (var i = 0 ; i < DATA_BYTES / 4 ; i++) { toBits[i] = fromWords32ToLittleEndian(); for (var j = 0 ; j < 4 ; j++) { - toBits[i].words[j] <== plainText[i*4 + j]; + isPadding[i * 4 + j] <== IsEqual()([plainText[i * 4 + j], -1]); + toBits[i].words[j] <== (1 - isPadding[i * 4 + j]) * plainText[i*4 + j]; } plaintextBits[i] <== toBits[i].data; } @@ -102,7 +105,7 @@ template ChaCha20_NIVC(DATA_BYTES) { component xors[DATA_BYTES]; component counter_adder[DATA_BYTES / 64 - 1]; - signal computedCipherText[DATA_BYTES / 4][32]; + signal cipherText[DATA_BYTES / 4][32]; for(i = 0; i < DATA_BYTES / 64; i++) { rounds[i] = Round(); @@ -112,7 +115,7 @@ template ChaCha20_NIVC(DATA_BYTES) { xors[i*16 + j] = XorBits(32); xors[i*16 + j].a <== plaintextBits[i*16 + j]; xors[i*16 + j].b <== rounds[i].out[j]; - computedCipherText[i*16 + j] <== xors[i*16 + j].out; + cipherText[i*16 + j] <== xors[i*16 + j].out; } if(i < DATA_BYTES / 64 - 1) { @@ -127,19 +130,20 @@ template ChaCha20_NIVC(DATA_BYTES) { component toCiphertextBytes[DATA_BYTES / 4]; signal bigEndianCiphertext[DATA_BYTES]; + for (var i = 0 ; i < DATA_BYTES / 4 ; i++) { toCiphertextBytes[i] = fromLittleEndianToWords32(); for (var j = 0 ; j < 32 ; j++) { - toCiphertextBytes[i].data[j] <== computedCipherText[i][j]; + toCiphertextBytes[i].data[j] <== cipherText[i][j]; } for (var j = 0 ; j < 4 ; j++) { - bigEndianCiphertext[i*4 + j] <== toCiphertextBytes[i].words[j]; + bigEndianCiphertext[i*4 + j] <== isPadding[i * 4 + j] * (-1 - toCiphertextBytes[i].words[j]) + toCiphertextBytes[i].words[j]; // equal to: (isPadding[i * 4 + j] * (-1)) + (1 - isPadding[i * 4 + j]) * toCiphertextBytes[i].words[j]; } } - signal paddedCiphertextCheck <== IsEqualArrayPaddedLHS(DATA_BYTES)([cipherText, bigEndianCiphertext]); - paddedCiphertextCheck === 1; + signal ciphertext_hash <== DataHasher(DATA_BYTES)(bigEndianCiphertext); + step_in[0] === ciphertext_hash; - signal data_hash <== DataHasher(DATA_BYTES)(plainText); - step_out[0] <== data_hash; + signal plaintext_hash <== DataHasher(DATA_BYTES)(plainText); + step_out[0] <== plaintext_hash; } \ No newline at end of file diff --git a/circuits/http/interpreter.circom b/circuits/http/interpreter.circom deleted file mode 100644 index 79e6278..0000000 --- a/circuits/http/interpreter.circom +++ /dev/null @@ -1,107 +0,0 @@ -pragma circom 2.1.9; - -include "parser/language.circom"; -include "../utils/search.circom"; -include "../utils/array.circom"; - -template inStartLine() { - signal input parsing_start; - signal output out; - - signal isBeginning <== IsEqual()([parsing_start, 1]); - signal isMiddle <== IsEqual()([parsing_start, 2]); - signal isEnd <== IsEqual()([parsing_start, 3]); - - out <== isBeginning + isMiddle + isEnd; -} - -template inStartMiddle() { - signal input parsing_start; - signal output out; - - out <== IsEqual()([parsing_start, 2]); -} - -template inStartEnd() { - signal input parsing_start; - signal output out; - - out <== IsEqual()([parsing_start, 3]); -} - -// TODO: This likely isn't really an "Intepreter" thing -template MethodMatch(dataLen, methodLen) { - signal input data[dataLen]; - signal input method[methodLen]; - - signal input index; - - signal isMatch <== SubstringMatchWithIndex(dataLen, methodLen)(data, method, index); - isMatch === 1; -} - -// https://www.rfc-editor.org/rfc/rfc9112.html#name-field-syntax -template HeaderFieldNameValueMatch(dataLen, nameLen, valueLen) { - signal input data[dataLen]; - signal input headerName[nameLen]; - signal input headerValue[valueLen]; - signal input index; - - // is name matches - signal headerNameMatch <== SubstringMatchWithIndex(dataLen, nameLen)(data, headerName, index); - - // next byte to name should be COLON - signal endOfHeaderName <== IndexSelector(dataLen)(data, index + nameLen); - signal isNextByteColon <== IsEqual()([endOfHeaderName, 58]); - - signal headerNameMatchAndNextByteColon <== headerNameMatch * isNextByteColon; - - // field-name: SP field-value - signal headerValueMatch <== SubstringMatchWithIndex(dataLen, valueLen)(data, headerValue, index + nameLen + 2); - - // header name matches + header value matches - signal output out <== headerNameMatchAndNextByteColon * headerValueMatch; -} - -// https://www.rfc-editor.org/rfc/rfc9112.html#name-field-syntax -template HeaderFieldNameValueMatchPadded(dataLen, maxNameLen, maxValueLen) { - signal input data[dataLen]; - signal input headerName[maxNameLen]; - signal input nameLen; - signal input headerValue[maxValueLen]; - signal input valueLen; - signal input index; - - // is name matchesnameLen - signal headerNameMatch <== SubstringMatchWithIndexPadded(dataLen, maxNameLen)(data, headerName, nameLen, index); - - // next byte to name should be COLON - signal endOfHeaderName <== IndexSelector(dataLen)(data, index + nameLen); - signal isNextByteColon <== IsEqual()([endOfHeaderName, 58]); - - signal headerNameMatchAndNextByteColon <== headerNameMatch * isNextByteColon; - - // field-name: SP field-value - signal headerValueMatch <== SubstringMatchWithIndexPadded(dataLen, maxValueLen)(data, headerValue, valueLen, index + nameLen + 2); - - // header name matches + header value matches - signal output out <== headerNameMatchAndNextByteColon * headerValueMatch; -} - -// https://www.rfc-editor.org/rfc/rfc9112.html#name-field-syntax -template HeaderFieldNameMatch(dataLen, nameLen) { - signal input data[dataLen]; - signal input headerName[nameLen]; - signal input index; - - // is name matches - signal headerNameMatch <== SubstringMatchWithIndex(dataLen, nameLen)(data, headerName, index); - - // next byte to name should be COLON - signal endOfHeaderName <== IndexSelector(dataLen)(data, index + nameLen); - signal isNextByteColon <== IsEqual()([endOfHeaderName, 58]); - - // header name matches - signal output out; - out <== headerNameMatch * isNextByteColon; -} \ No newline at end of file diff --git a/circuits/http/locker.circom b/circuits/http/locker.circom deleted file mode 100644 index cd8c439..0000000 --- a/circuits/http/locker.circom +++ /dev/null @@ -1,181 +0,0 @@ -pragma circom 2.1.9; - -include "interpreter.circom"; -include "parser/machine.circom"; -include "../utils/bytes.circom"; -include "../utils/search.circom"; -include "circomlib/circuits/gates.circom"; -include "@zk-email/circuits/utils/array.circom"; - -template LockStartLine(DATA_BYTES, beginningLen, middleLen, finalLen) { - signal input data[DATA_BYTES]; - signal input beginning[beginningLen]; - signal input middle[middleLen]; - signal input final[finalLen]; - - //--------------------------------------------------------------------------------------------// - //-CONSTRAINTS--------------------------------------------------------------------------------// - //--------------------------------------------------------------------------------------------// - component dataASCII = ASCII(DATA_BYTES); - dataASCII.in <== data; - //--------------------------------------------------------------------------------------------// - - // 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; - } - - // Middle - 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); - middle_start_counter += startLineMask[data_idx] - middleMask[data_idx] - finalMask[data_idx]; - // The end of middle is the start of the final - middle_end_counter += startLineMask[data_idx] - finalMask[data_idx]; - final_end_counter += startLineMask[data_idx]; - - // Debugging - log("State[", data_idx, "].parsing_start = ", State[data_idx].parsing_start); - log("State[", data_idx, "].parsing_header = ", State[data_idx].parsing_header); - log("State[", data_idx, "].parsing_field_name = ", State[data_idx].parsing_field_name); - log("State[", data_idx, "].parsing_field_value = ", State[data_idx].parsing_field_value); - log("State[", data_idx, "].parsing_body = ", State[data_idx].parsing_body); - log("State[", data_idx, "].line_status = ", State[data_idx].line_status); - log("------------------------------------------------"); - log("middle_start_counter = ", middle_start_counter); - log("middle_end_counter = ", middle_end_counter); - log("final_end_counter = ", final_end_counter); - log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - } - - // Debugging - log("State[", DATA_BYTES, "].parsing_start ", "= ", State[DATA_BYTES-1].next_parsing_start); - log("State[", DATA_BYTES, "].parsing_header ", "= ", State[DATA_BYTES-1].next_parsing_header); - log("State[", DATA_BYTES, "].parsing_field_name ", "= ", State[DATA_BYTES-1].parsing_field_name); - log("State[", DATA_BYTES, "].parsing_field_value", "= ", State[DATA_BYTES-1].parsing_field_value); - log("State[", DATA_BYTES, "].parsing_body ", "= ", State[DATA_BYTES-1].next_parsing_body); - log("State[", DATA_BYTES, "].line_status ", "= ", State[DATA_BYTES-1].next_line_status); - log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - // 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; -} - -template LockHeader(DATA_BYTES, headerNameLen, headerValueLen) { - signal input data[DATA_BYTES]; - signal input header[headerNameLen]; - signal input value[headerValueLen]; - - //--------------------------------------------------------------------------------------------// - //-CONSTRAINTS--------------------------------------------------------------------------------// - //--------------------------------------------------------------------------------------------// - component dataASCII = ASCII(DATA_BYTES); - dataASCII.in <== data; - //--------------------------------------------------------------------------------------------// - - // 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; - - component headerFieldNameValueMatch[DATA_BYTES]; - signal isHeaderFieldNameValueMatch[DATA_BYTES]; - - isHeaderFieldNameValueMatch[0] <== 0; - var hasMatched = 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; - - headerFieldNameValueMatch[data_idx] = HeaderFieldNameValueMatch(DATA_BYTES, headerNameLen, headerValueLen); - headerFieldNameValueMatch[data_idx].data <== data; - headerFieldNameValueMatch[data_idx].headerName <== header; - headerFieldNameValueMatch[data_idx].headerValue <== value; - headerFieldNameValueMatch[data_idx].index <== data_idx; - isHeaderFieldNameValueMatch[data_idx] <== isHeaderFieldNameValueMatch[data_idx-1] + headerFieldNameValueMatch[data_idx].out; - - // Debugging - log("State[", data_idx, "].parsing_start ", "= ", State[data_idx].parsing_start); - log("State[", data_idx, "].parsing_header ", "= ", State[data_idx].parsing_header); - log("State[", data_idx, "].parsing_field_name ", "= ", State[data_idx].parsing_field_name); - log("State[", data_idx, "].parsing_field_value", "= ", State[data_idx].parsing_field_value); - log("State[", data_idx, "].parsing_body ", "= ", State[data_idx].parsing_body); - log("State[", data_idx, "].line_status ", "= ", State[data_idx].line_status); - log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - } - - // Debugging - log("State[", DATA_BYTES, "].parsing_start ", "= ", State[DATA_BYTES-1].next_parsing_start); - log("State[", DATA_BYTES, "].parsing_header ", "= ", State[DATA_BYTES-1].next_parsing_header); - log("State[", DATA_BYTES, "].parsing_field_name ", "= ", State[DATA_BYTES-1].parsing_field_name); - log("State[", DATA_BYTES, "].parsing_field_value", "= ", State[DATA_BYTES-1].parsing_field_value); - log("State[", DATA_BYTES, "].parsing_body ", "= ", State[DATA_BYTES-1].next_parsing_body); - log("State[", DATA_BYTES, "].line_status ", "= ", State[DATA_BYTES-1].next_line_status); - log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - - isHeaderFieldNameValueMatch[DATA_BYTES - 1] === 1; -} \ No newline at end of file diff --git a/circuits/http/parser/machine.circom b/circuits/http/machine.circom similarity index 96% rename from circuits/http/parser/machine.circom rename to circuits/http/machine.circom index 438a178..5f372d5 100644 --- a/circuits/http/parser/machine.circom +++ b/circuits/http/machine.circom @@ -1,7 +1,6 @@ pragma circom 2.1.9; -include "language.circom"; -include "../../utils/array.circom"; +include "../utils/array.circom"; template HttpStateUpdate() { signal input parsing_start; // flag that counts up to 3 for each value in the start line @@ -91,8 +90,8 @@ template StateChange() { // enable parsing header on reading CRLF signal enableParsingHeader <== readCRLF * isParsingStart; // check if we are parsing header - // TODO: correct this 3 (it means we can parse max 2^3 headers) - signal isParsingHeader <== GreaterEqThan(3)([state[1], 1]); + // Allows for max headers to be 2^5 = 32 + signal isParsingHeader <== GreaterEqThan(5)([state[1], 1]); // increment parsing header counter on CRLF and parsing header signal incrementParsingHeader <== readCRLF * isParsingHeader; // disable parsing header on reading CRLF-CRLF diff --git a/circuits/http/nivc/body_mask.circom b/circuits/http/nivc/body_mask.circom deleted file mode 100644 index d8956ed..0000000 --- a/circuits/http/nivc/body_mask.circom +++ /dev/null @@ -1,50 +0,0 @@ -pragma circom 2.1.9; - -include "../parser/machine.circom"; -include "../../utils/hash.circom"; - -template HTTPMaskBodyNIVC(DATA_BYTES) { - 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]; - - // ------------------------------------------------------------------------------------------------------------------ // - // PARSE - // 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; - - 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; - } - // ------------------------------------------------------------------------------------------------------------------ // - - // ------------------------------------------------------------------------------------------------------------------ // - // Mask out just the JSON body - signal bodyMasked[DATA_BYTES]; - for (var i = 0 ; i < DATA_BYTES ; i++) { - bodyMasked[i] <== data[i] * State[i].next_parsing_body; - } - - // Hash the new data so this can now be used in the chain later - step_out[0] <== DataHasher(DATA_BYTES)(bodyMasked); -} - diff --git a/circuits/http/nivc/lock_header.circom b/circuits/http/nivc/lock_header.circom deleted file mode 100644 index 2cb109e..0000000 --- a/circuits/http/nivc/lock_header.circom +++ /dev/null @@ -1,131 +0,0 @@ -pragma circom 2.1.9; - -include "../parser/machine.circom"; -include "../interpreter.circom"; -include "../../utils/array.circom"; -include "circomlib/circuits/comparators.circom"; -include "@zk-email/circuits/utils/array.circom"; - -// TODO: should use a MAX_HEADER_NAME_LENGTH and a MAX_HEADER_VALUE_LENGTH -template LockHeader(DATA_BYTES, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH) { - - assert(DATA_BYTES >= MAX_HEADER_NAME_LENGTH + MAX_HEADER_VALUE_LENGTH); - - 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]; - step_out[0] <== step_in[0]; - - // ------------------------------------------------------------------------------------------------------------------ // - // PARSE - // 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; - - // TODO (autoparallel): Redundant as fuck, but I'm doing this quickly sorry. I don't think this actually adds constraints - signal httpParserState[DATA_BYTES * 5]; - - - 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; - } - // ------------------------------------------------------------------------------------------------------------------ // - - - // TODO (autoparallel): again bad - // Get those redundant variables - for(var i = 0 ; i < DATA_BYTES ; i++) { - httpParserState[i * 5] <== State[i].next_parsing_start; - httpParserState[i * 5 + 1] <== State[i].next_parsing_header; - httpParserState[i * 5 + 2] <== State[i].next_parsing_field_name; - httpParserState[i * 5 + 3] <== State[i].next_parsing_field_value; - httpParserState[i * 5 + 4] <== State[i].next_parsing_body; - } - - - // TODO: Better naming for these variables - signal input header[MAX_HEADER_NAME_LENGTH]; - signal input headerNameLength; - signal input value[MAX_HEADER_VALUE_LENGTH]; - signal input headerValueLength; - - // find header location - signal headerNameLocation <== FirstStringMatch(DATA_BYTES, MAX_HEADER_NAME_LENGTH)(data, header); - - // TODO (autoparallel): This could probably be optimized by selecting a subarray of length `MAX_HEADER_NAME_LENGTH + MAX_HEADER_VALUE_LENGTH` at `headerNameLocation` - // This is the assertion that we have locked down the correct header - - // signal dataSubArray[MAX_HEADER_NAME_LENGTH + MAX_HEADER_VALUE_LENGTH] <== SelectSubArray(DATA_BYTES, MAX_HEADER_NAME_LENGTH + MAX_HEADER_VALUE_LENGTH)(data, headerNameLocation, MAX_HEADER_NAME_LENGTH + MAX_HEADER_VALUE_LENGTH); - // signal headerFieldNameValueMatch <== HeaderFieldNameValueMatchPadded(MAX_HEADER_NAME_LENGTH + MAX_HEADER_VALUE_LENGTH, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH)(dataSubArray, header, headerNameLength, value, headerValueLength, headerNameLocation); - signal headerFieldNameValueMatch <== HeaderFieldNameValueMatchPadded(DATA_BYTES, MAX_HEADER_NAME_LENGTH, MAX_HEADER_VALUE_LENGTH)(data, header, headerNameLength, value, headerValueLength, headerNameLocation); - headerFieldNameValueMatch === 1; - - // parser state should be parsing header upto 2^10 max headers - signal isParsingHeader <== IndexSelector(DATA_BYTES * 5)(httpParserState, headerNameLocation * 5 + 1); - signal parsingHeader <== GreaterThan(10)([isParsingHeader, 0]); - parsingHeader === 1; -} - -// TODO: Handrolled template that I haven't tested YOLO. -template FirstStringMatch(dataLen, maxKeyLen) { - signal input data[dataLen]; - signal input key[maxKeyLen]; - signal output position; - - signal paddedData[dataLen + maxKeyLen]; - for (var i = 0 ; i < dataLen ; i++) { - paddedData[i] <== data[i]; - } - for (var i = 0 ; i < maxKeyLen ; i++) { - paddedData[dataLen + i] <== 0; - } - - signal isMatched[dataLen+1]; - isMatched[0] <== 0; - - var counter = 0; - component stringMatch[dataLen]; - component hasMatched[dataLen]; - signal isKeyOutOfBounds[maxKeyLen]; - signal isFirstMatchAndInsideBound[dataLen * maxKeyLen]; - for (var i = 0 ; i < maxKeyLen ; i++) { - isKeyOutOfBounds[i] <== IsZero()(key[i]); - } - - for (var idx = 0 ; idx < dataLen ; idx++) { - stringMatch[idx] = IsEqualArray(maxKeyLen); - stringMatch[idx].in[0] <== key; - for (var key_idx = 0 ; key_idx < maxKeyLen ; key_idx++) { - isFirstMatchAndInsideBound[idx * maxKeyLen + key_idx] <== (1 - isMatched[idx]) * (1 - isKeyOutOfBounds[key_idx]); - stringMatch[idx].in[1][key_idx] <== paddedData[idx + key_idx] * isFirstMatchAndInsideBound[idx * maxKeyLen + key_idx]; - } - hasMatched[idx] = IsEqual(); - hasMatched[idx].in <== [stringMatch[idx].out, 1]; - isMatched[idx+1] <== isMatched[idx] + hasMatched[idx].out; - counter += (1 - isMatched[idx+1]); // TODO: Off by one? Move before? - } - position <== counter; -} - - diff --git a/circuits/http/nivc/parse_and_lock_start_line.circom b/circuits/http/nivc/parse_and_lock_start_line.circom deleted file mode 100644 index 32cc4ec..0000000 --- a/circuits/http/nivc/parse_and_lock_start_line.circom +++ /dev/null @@ -1,99 +0,0 @@ -pragma circom 2.1.9; - -include "../parser/machine.circom"; -include "../interpreter.circom"; -include "../../utils/bytes.circom"; - -template ParseAndLockStartLine(DATA_BYTES, MAX_BEGINNING_LENGTH, MAX_MIDDLE_LENGTH, MAX_FINAL_LENGTH) { - var MINIMUM_PARSE_LENGTH = MAX_BEGINNING_LENGTH + MAX_MIDDLE_LENGTH + MAX_FINAL_LENGTH; - assert(DATA_BYTES >= MINIMUM_PARSE_LENGTH); - - 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]; - step_out[0] <== step_in[0]; - - signal dataToParse[MINIMUM_PARSE_LENGTH]; - for(var i = 0 ; i < MINIMUM_PARSE_LENGTH ; i++) { - dataToParse[i] <== data[i]; - } - - 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; - - // Initialze the parser, note that we only need to parse as much as the `MINIMUM_PARSE_LENGTH` - // since the start line could not possibly go past this point, or else this would fail anyway - component State[MINIMUM_PARSE_LENGTH]; - State[0] = HttpStateUpdate(); - State[0].byte <== dataToParse[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 `BEGINNING_LENGTH` bytes - of the data ASCII against the beginning ASCII itself. - */ - - // Setup to check middle bytes - signal startLineMask[MINIMUM_PARSE_LENGTH]; - signal middleMask[MINIMUM_PARSE_LENGTH]; - signal finalMask[MINIMUM_PARSE_LENGTH]; - startLineMask[0] <== inStartLine()(State[0].parsing_start); - middleMask[0] <== inStartMiddle()(State[0].parsing_start); - finalMask[0] <== inStartEnd()(State[0].parsing_start); - - - var middle_start_counter = 1; - var middle_end_counter = 1; - var final_end_counter = 1; - for(var data_idx = 1; data_idx < MINIMUM_PARSE_LENGTH; data_idx++) { - State[data_idx] = HttpStateUpdate(); - State[data_idx].byte <== dataToParse[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; - - // 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 - beginning_length === middle_start_counter - 1; - - signal beginningMatch <== SubstringMatchWithIndexPadded(MINIMUM_PARSE_LENGTH, MAX_BEGINNING_LENGTH)(dataToParse, beginning, beginning_length, 0); - - // Check middle is correct by substring match and length check - signal middleMatch <== SubstringMatchWithIndexPadded(MINIMUM_PARSE_LENGTH, MAX_MIDDLE_LENGTH)(dataToParse, middle, middle_length, middle_start_counter); - middleMatch === 1; - middle_length === middle_end_counter - middle_start_counter - 1; - - // Check final is correct by substring match and length check - signal finalMatch <== SubstringMatchWithIndexPadded(MINIMUM_PARSE_LENGTH, MAX_FINAL_LENGTH)(dataToParse, final, final_length, middle_end_counter); - finalMatch === 1; - // -2 here for the CRLF - final_length === final_end_counter - middle_end_counter - 2; -} - diff --git a/circuits/http/parser/language.circom b/circuits/http/parser/language.circom deleted file mode 100644 index 742cf90..0000000 --- a/circuits/http/parser/language.circom +++ /dev/null @@ -1,27 +0,0 @@ -pragma circom 2.1.9; - -// All the possible request methods: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods - -template HttpSyntax() { - //-Delimeters---------------------------------------------------------------------------------// - // - ASCII char `:` - signal output COLON <== 58; - // - ASCII char `;` - signal output SEMICOLON <== 59; - // - ASCII char `,` - signal output COMMA <== 44; - // - ASCII char `"` - signal output QUOTE <== 34; - //-White_space--------------------------------------------------------------------------------// - // https://www.rfc-editor.org/rfc/rfc2616#section-2.2 - // https://www.rfc-editor.org/rfc/rfc7230#section-3.5 - // - ASCII char `\r` (carriage return) - signal output CR <== 13; - // - ASCII char `\n` (line feed) - signal output LF <== 10; - // - ASCII char: ` ` - signal output SPACE <== 32; - //-Escape-------------------------------------------------------------------------------------// - // - ASCII char: `\` - signal output ESCAPE <== 92; -} \ No newline at end of file diff --git a/circuits/http/parser/parser.circom b/circuits/http/parser/parser.circom deleted file mode 100644 index dc00454..0000000 --- a/circuits/http/parser/parser.circom +++ /dev/null @@ -1,51 +0,0 @@ -pragma circom 2.1.9; - -include "../../utils/bytes.circom"; -include "machine.circom"; - - -template Parser(DATA_BYTES) { - signal input data[DATA_BYTES]; - - signal output Method; - - //--------------------------------------------------------------------------------------------// - //-CONSTRAINTS--------------------------------------------------------------------------------// - //--------------------------------------------------------------------------------------------// - component dataASCII = ASCII(DATA_BYTES); - dataASCII.in <== data; - //--------------------------------------------------------------------------------------------// - - // 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_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_body <== State[data_idx - 1].next_parsing_body; - State[data_idx].line_status <== State[data_idx - 1].next_line_status; - - // Debugging - log("State[", data_idx, "].parsing_start ", "= ", State[data_idx].parsing_start); - log("State[", data_idx, "].parsing_header", "= ", State[data_idx].parsing_header); - log("State[", data_idx, "].parsing_body ", "= ", State[data_idx].parsing_body); - log("State[", data_idx, "].line_status ", "= ", State[data_idx].line_status); - log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - } - - // Debugging - log("State[", DATA_BYTES, "].parsing_start ", "= ", State[DATA_BYTES-1].next_parsing_start); - log("State[", DATA_BYTES, "].parsing_header", "= ", State[DATA_BYTES-1].next_parsing_header); - log("State[", DATA_BYTES, "].parsing_body ", "= ", State[DATA_BYTES-1].next_parsing_body); - log("State[", DATA_BYTES, "].line_status ", "= ", State[DATA_BYTES-1].next_line_status); - log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); - -} \ No newline at end of file diff --git a/circuits/http/nivc/http_nivc.circom b/circuits/http/verification.circom similarity index 74% rename from circuits/http/nivc/http_nivc.circom rename to circuits/http/verification.circom index 8187c1b..a88b480 100644 --- a/circuits/http/nivc/http_nivc.circom +++ b/circuits/http/verification.circom @@ -1,10 +1,9 @@ pragma circom 2.1.9; -include "../parser/machine.circom"; -include "../interpreter.circom"; -include "../../utils/bytes.circom"; +include "machine.circom"; +include "../utils/hash.circom"; -template HttpNIVC(DATA_BYTES, MAX_NUMBER_OF_HEADERS) { +template HTTPVerification(DATA_BYTES, MAX_NUMBER_OF_HEADERS) { signal input step_in[1]; signal output step_out[1]; @@ -44,11 +43,11 @@ template HttpNIVC(DATA_BYTES, MAX_NUMBER_OF_HEADERS) { 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]); + start_line[i] <== data[i] * (1 - not_start_line_mask[i]); } - signal inner_start_line_hash <== DataHasher(DATA_BYTES)(start_line); + signal inner_start_line_hash <== DataHasher(DATA_BYTES)(start_line); signal start_line_hash_equal_check <== IsEqual()([inner_start_line_hash, start_line_hash]); - start_line_hash_equal_check === 1; + start_line_hash_equal_check === 1; // Get the header shit signal header[MAX_NUMBER_OF_HEADERS][DATA_BYTES]; @@ -63,8 +62,8 @@ template HttpNIVC(DATA_BYTES, 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) signal header_hashes_equal_check[MAX_NUMBER_OF_HEADERS]; 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]); + header_is_unused[i] <== IsZero()(header_hashes[i]); + inner_header_hashes[i] <== DataHasher(DATA_BYTES)(header[i]); header_hashes_equal_check[i] <== IsEqual()([(1 - header_is_unused[i]) * inner_header_hashes[i], header_hashes[i]]); header_hashes_equal_check[i] === 1; } @@ -72,12 +71,19 @@ template HttpNIVC(DATA_BYTES, MAX_NUMBER_OF_HEADERS) { // Get the body shit signal body[DATA_BYTES]; for(var i = 0 ; i < DATA_BYTES ; i++) { - body[i] <== data[i] * State[i].parsing_body; + body[i] <== data[i] * State[i].parsing_body; } - signal inner_body_hash <== DataHasher(DATA_BYTES)(body); + signal inner_body_hash <== DataHasher(DATA_BYTES)(body); signal body_hash_equal_check <== IsEqual()([inner_body_hash, body_hash]); - body_hash_equal_check === 1; - + body_hash_equal_check === 1; step_out[0] <== inner_body_hash; + + // Verify machine ends in a valid state + State[DATA_BYTES - 1].next_parsing_start === 0; + State[DATA_BYTES - 1].next_parsing_header === 0; + State[DATA_BYTES - 1].next_parsing_field_name === 0; + State[DATA_BYTES - 1].next_parsing_field_value === 0; + State[DATA_BYTES - 1].next_parsing_body === 1; + State[DATA_BYTES - 1].next_line_status === 0; } diff --git a/circuits/json/extractor.circom b/circuits/json/extractor.circom deleted file mode 100644 index b58da5c..0000000 --- a/circuits/json/extractor.circom +++ /dev/null @@ -1,164 +0,0 @@ -pragma circom 2.1.9; - -include "interpreter.circom"; - -template ObjectExtractor(DATA_BYTES, MAX_STACK_HEIGHT, maxKeyLen, maxValueLen) { - assert(MAX_STACK_HEIGHT >= 2); - - // Declaration of signals. - signal input data[DATA_BYTES]; - signal input key[maxKeyLen]; - signal input keyLen; - - signal output value[maxValueLen]; - - // Constraints. - signal value_starting_index[DATA_BYTES - maxKeyLen]; - // flag determining whether this byte is matched value - signal is_value_match[DATA_BYTES - maxKeyLen]; - // final mask - signal mask[DATA_BYTES - maxKeyLen]; - - component State[DATA_BYTES - maxKeyLen]; - 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; - - signal parsing_key[DATA_BYTES - maxKeyLen]; - signal parsing_value[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]; - - // initialise first iteration - - // check inside key or value - parsing_key[0] <== InsideKey()(State[0].next_stack[0], State[0].next_parsing_string, State[0].next_parsing_number); - parsing_value[0] <== InsideValueObject()(State[0].next_stack[0], State[0].next_stack[1], State[0].next_parsing_string, State[0].next_parsing_number); - - is_key_match[0] <== 0; - is_next_pair_at_depth[0] <== NextKVPairAtDepth(MAX_STACK_HEIGHT)(State[0].next_stack, data[0], 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++) { - 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; - - // - parsing key - // - parsing value (different for string/numbers and array) - // - key match (key 1, key 2) - // - is next pair - // - is key match for value - // - value_mask - // - mask - - // check if inside key or not - parsing_key[data_idx] <== InsideKey()(State[data_idx].next_stack[0], State[data_idx].next_parsing_string, State[data_idx].next_parsing_number); - // check if inside value - parsing_value[data_idx] <== InsideValueObject()(State[data_idx].next_stack[0], State[data_idx].next_stack[1], State[data_idx].next_parsing_string, State[data_idx].next_parsing_number); - - // 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)(State[data_idx].next_stack, data[data_idx], 0); - 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]; - } - - // find starting index of value in data by matching mask - signal is_zero_mask[DATA_BYTES]; - signal is_prev_starting_index[DATA_BYTES]; - value_starting_index[0] <== 0; - is_prev_starting_index[0] <== 0; - is_zero_mask[0] <== IsZero()(mask[0]); - for (var i=1 ; i= 2); - - signal input data[DATA_BYTES]; - signal input index; - - signal output value[maxValueLen]; - - // value starting index in `data` - signal value_starting_index[DATA_BYTES]; - // final mask - signal mask[DATA_BYTES]; - - component State[DATA_BYTES]; - 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; - - signal parsing_array[DATA_BYTES]; - signal or[DATA_BYTES]; - - parsing_array[0] <== InsideArrayIndexObject()(State[0].next_stack[0], State[0].next_stack[1], State[0].next_parsing_string, State[0].next_parsing_number, index); - mask[0] <== data[0] * parsing_array[0]; - - for(var data_idx = 1; data_idx < DATA_BYTES; data_idx++) { - 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; - - parsing_array[data_idx] <== InsideArrayIndexObject()(State[data_idx].next_stack[0], State[data_idx].next_stack[1], State[data_idx].next_parsing_string, State[data_idx].next_parsing_number, index); - - or[data_idx] <== OR()(parsing_array[data_idx], parsing_array[data_idx - 1]); - mask[data_idx] <== data[data_idx] * or[data_idx]; - } - - signal is_zero_mask[DATA_BYTES]; - signal is_prev_starting_index[DATA_BYTES]; - value_starting_index[0] <== 0; - is_prev_starting_index[0] <== 0; - is_zero_mask[0] <== IsZero()(mask[0]); - for (var i=1 ; i= 2); // TODO (autoparallel): idk if we need this now - signal input step_in[1]; signal input key[MAX_KEY_LENGTH]; signal input keyLen; @@ -50,7 +49,6 @@ template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) { signal masked[DATA_BYTES]; masked[0] <== data[0] * is_value_match[0]; - // TODO (autoparallel): it might be dumb to do this with the max key length but fuck it 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); @@ -97,8 +95,6 @@ template JsonMaskObjectNIVC(DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH) { } template JsonMaskArrayIndexNIVC(DATA_BYTES, MAX_STACK_HEIGHT) { - assert(MAX_STACK_HEIGHT >= 2); // TODO (autoparallel): idk if we need this now - signal input step_in[1]; signal input index; diff --git a/circuits/json/parser/language.circom b/circuits/json/parser/language.circom index 8063503..0566d74 100644 --- a/circuits/json/parser/language.circom +++ b/circuits/json/parser/language.circom @@ -18,14 +18,16 @@ template Syntax() { signal output COMMA <== 44; //-White_space--------------------------------------------------------------------------------// // - ASCII char: `\n` - signal output NEWLINE <== 10; + // signal output NEWLINE <== 10; // - ASCII char: ` ` - signal output SPACE <== 32; + // signal output SPACE <== 32; //-Escape-------------------------------------------------------------------------------------// // - ASCII char: `\` - signal output ESCAPE <== 92; + // signal output ESCAPE <== 92; //-Number_Remapping---------------------------------------------------------------------------// - signal output NUMBER <== 256; // past a u8 -- reserved for ANY numerical ASCII (48 - 57) + signal output NUMBER_START <== 48; + signal output NUMBER_END <== 57; + } template Command() { diff --git a/circuits/json/parser/machine.circom b/circuits/json/parser/machine.circom index 21c8a87..ff82988 100644 --- a/circuits/json/parser/machine.circom +++ b/circuits/json/parser/machine.circom @@ -24,7 +24,6 @@ Tests for this module are located in the files: `circuits/test/parser/*.test.ts pragma circom 2.1.9; include "../../utils/array.circom"; -include "../../utils/bytes.circom"; include "../../utils/operators.circom"; include "language.circom"; @@ -57,37 +56,38 @@ template StateUpdate(MAX_STACK_HEIGHT) { signal output next_parsing_number; component Command = Command(); + component Syntax = Syntax(); //--------------------------------------------------------------------------------------------// // Break down what was read // * read in a start brace `{` * component readStartBrace = IsEqual(); - readStartBrace.in <== [byte, 123]; + readStartBrace.in <== [byte, Syntax.START_BRACE]; // * read in an end brace `}` * component readEndBrace = IsEqual(); - readEndBrace.in <== [byte, 125]; + readEndBrace.in <== [byte, Syntax.END_BRACE]; // * read in a start bracket `[` * component readStartBracket = IsEqual(); - readStartBracket.in <== [byte, 91]; + readStartBracket.in <== [byte, Syntax.START_BRACKET]; // * read in an end bracket `]` * component readEndBracket = IsEqual(); - readEndBracket.in <== [byte, 93]; + readEndBracket.in <== [byte, Syntax.END_BRACKET]; // * read in a colon `:` * component readColon = IsEqual(); - readColon.in <== [byte, 58]; + readColon.in <== [byte, Syntax.COLON]; // * read in a comma `,` * component readComma = IsEqual(); - readComma.in <== [byte, 44]; + readComma.in <== [byte, Syntax.COMMA]; // * read in some delimeter * signal readDelimeter <== readStartBrace.out + readEndBrace.out + readStartBracket.out + readEndBracket.out + readColon.out + readComma.out; // * read in some number * component readNumber = InRange(8); readNumber.in <== byte; - readNumber.range <== [48, 57]; // This is the range where ASCII digits are + readNumber.range <== [Syntax.NUMBER_START, Syntax.NUMBER_END]; // This is the range where ASCII digits are // * read in a quote `"` * component readQuote = IsEqual(); - readQuote.in <== [byte, 34]; + readQuote.in <== [byte, Syntax.QUOTE]; component readOther = IsZero(); readOther.in <== readDelimeter + readNumber.out + readQuote.out; //--------------------------------------------------------------------------------------------// diff --git a/circuits/json/parser/parser.circom b/circuits/json/parser/parser.circom index 8a435ca..f8f153a 100644 --- a/circuits/json/parser/parser.circom +++ b/circuits/json/parser/parser.circom @@ -1,18 +1,10 @@ pragma circom 2.1.9; -include "../../utils/bytes.circom"; include "machine.circom"; template Parser(DATA_BYTES, MAX_STACK_HEIGHT) { signal input data[DATA_BYTES]; - // TODO: Add assertions on the inputs here! - - //--------------------------------------------------------------------------------------------// - //-CONSTRAINTS--------------------------------------------------------------------------------// - //--------------------------------------------------------------------------------------------// - component dataASCII = ASCII(DATA_BYTES); - dataASCII.in <== data; //--------------------------------------------------------------------------------------------// // Initialze the parser component State[DATA_BYTES]; @@ -30,24 +22,12 @@ template Parser(DATA_BYTES, MAX_STACK_HEIGHT) { 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; - - // Debugging - for(var i = 0; i { - let circuit: WitnessTester<["key", "iv", "plainText", "aad"], ["cipherText", "tag"]>; - - before(async () => { - circuit = await circomkit.WitnessTester(`aes-gcm`, { - file: "aes-gcm/aes-gcm", - template: "AESGCM", - params: [16], - }); - }); - - it("should have correct output", async () => { - let key = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let plainText = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let iv = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let aad = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let expected_output = [0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78]; - - const witness = await circuit.compute({ key: key, iv: iv, plainText: plainText, aad: aad }, ["cipherText", "authTag"]) - - assert.deepEqual(witness.cipherText, hexBytesToBigInt(expected_output)) - }); - - it("should work for self generated test case", async () => { - let circuit_one_block: WitnessTester<["key", "iv", "plainText", "aad"], ["cipherText", "tag"]>; - circuit_one_block = await circomkit.WitnessTester(`aes-gcm`, { - file: "aes-gcm/aes-gcm", - template: "AESGCM", - params: [16], - }); - - const key = hexToBytes('31313131313131313131313131313131'); - const iv = hexToBytes('313131313131313131313131'); - const msg = hexToBytes('7465737468656c6c6f30303030303030'); - const aad = hexToBytes('00000000000000000000000000000000') - const ct = hexToBytes('2929d2bb1ae94804402b8e776e0d3356'); - const auth_tag = hexToBytes('0cab39e1a491b092185965f7b554aea0'); - - const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: msg, aad: aad }, ["cipherText", "authTag"]) - - assert.deepEqual(witness.cipherText, hexBytesToBigInt(ct)) - }); - - it("should work for multiple blocks", async () => { - let circuit_one_block: WitnessTester<["key", "iv", "plainText", "aad"], ["cipherText", "tag"]>; - circuit_one_block = await circomkit.WitnessTester(`aes-gcm`, { - file: "aes-gcm/aes-gcm", - template: "AESGCM", - params: [32], - }); - - const key = hexToBytes('31313131313131313131313131313131'); - const iv = hexToBytes('313131313131313131313131'); - const msg = hexToBytes('7465737468656c6c6f303030303030307465737468656c6c6f30303030303030'); - const aad = hexToBytes('00000000000000000000000000000000') - const ct = hexToBytes('2929d2bb1ae94804402b8e776e0d335626756530713e4c065af1d3c4f56e0204'); - const auth_tag = hexToBytes('438542d7f387568c84d23df60b223ecb'); - - const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: msg, aad: aad }, ["cipherText", "authTag"]) - - assert.deepEqual(witness.cipherText, hexBytesToBigInt(ct)) - }); -}); \ No newline at end of file diff --git a/circuits/test/aes-gcm/aes/cipher.test.ts b/circuits/test/aes-gcm/aes/cipher.test.ts deleted file mode 100644 index f54d90d..0000000 --- a/circuits/test/aes-gcm/aes/cipher.test.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit } from "../../common"; - -describe("Cipher", () => { - let circuit: WitnessTester<["block", "key"], ["cipher"]>; - it("should perform Cipher#1", async () => { - circuit = await circomkit.WitnessTester(`Cipher`, { - file: "aes-gcm/aes/cipher", - template: "Cipher", - }); - - await circuit.expectPass( - { - block: [ - [0x32, 0x88, 0x31, 0xe0], - [0x43, 0x5a, 0x31, 0x37], - [0xf6, 0x30, 0x98, 0x07], - [0xa8, 0x8d, 0xa2, 0x34], - ], - key: [ - 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, - 0x88, 0x09, 0xcf, 0x4f, 0x3c, - ], - }, - { - cipher: [ - [0x39, 0x02, 0xdc, 0x19], - [0x25, 0xdc, 0x11, 0x6a], - [0x84, 0x09, 0x85, 0x0b], - [0x1d, 0xfb, 0x97, 0x32], - ], - } - ); - }); - - // in : f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff - // out : ec8cdf7398607cb0f2d21675ea9ea1e4 - // key : 2b7e151628aed2a6abf7158809cf4f3c - it("should perform Cipher#2", async () => { - circuit = await circomkit.WitnessTester(`Cipher`, { - file: "aes-gcm/aes/cipher", - template: "Cipher", - }); - - await circuit.expectPass( - { - block: [ - [0xf0, 0xf4, 0xf8, 0xfc], - [0xf1, 0xf5, 0xf9, 0xfd], - [0xf2, 0xf6, 0xfa, 0xfe], - [0xf3, 0xf7, 0xfb, 0xff], - ], - key: [ - 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, - 0x88, 0x09, 0xcf, 0x4f, 0x3c, - ], - }, - { - cipher: [ - [0xec, 0x98, 0xf2, 0xea], - [0x8c, 0x60, 0xd2, 0x9e], - [0xdf, 0x7c, 0x16, 0xa1], - [0x73, 0xb0, 0x75, 0xe4], - ], - } - ); - }); - describe("AddRoundKey", () => { - let circuit: WitnessTester<["state", "roundKey"], ["newState"]>; - it("should perform AddRoundKey", async () => { - circuit = await circomkit.WitnessTester(`AddRoundKey`, { - file: "aes-gcm/aes/cipher", - template: "AddRoundKey", - }); - - // 0x57 . 2 = 0xae - await circuit.expectPass( - { - state: [ - [4, 224, 72, 40], - [102, 203, 248, 6], - [129, 25, 211, 38], - [229, 154, 122, 76], - ], - roundKey: [ - [160, 250, 254, 23], - [136, 84, 44, 177], - [35, 163, 57, 57], - [42, 108, 118, 5], - ], - }, - { - newState: [ - [164, 104, 107, 2], - [156, 159, 91, 106], - [127, 53, 234, 80], - [242, 43, 67, 73], - ], - } - ); - }); - }); - - describe("NextRound", () => { - async function generatePassCase( - round: number, - key: number[][], - expectedKey: number[][] - ) { - circuit = await circomkit.WitnessTester(`NextRound_${4}_${4}`, { - file: "aes-gcm/aes/cipher", - template: "NextRound", - }); - } - - it("should compute correctly", async () => { - const key = [ - [0x2b, 0x7e, 0x15, 0x16], - [0x28, 0xae, 0xd2, 0xa6], - [0xab, 0xf7, 0x15, 0x88], - [0x09, 0xcf, 0x4f, 0x3c], - ]; - - const expectedNextKey = [ - [0xa0, 0xfa, 0xfe, 0x17], - [0x88, 0x54, 0x2c, 0xb1], - [0x23, 0xa3, 0x39, 0x39], - [0x2a, 0x6c, 0x76, 0x05], - ]; - - await generatePassCase(1, key, expectedNextKey); - }); - }); - - describe("SubBlock", () => { - let circuit: WitnessTester<["state"], ["newState"]>; - it("should perform SubBlock", async () => { - circuit = await circomkit.WitnessTester(`SubBlock`, { - file: "aes-gcm/aes/cipher", - template: "SubBlock", - }); - // 0x57 . 2 = 0xae - await circuit.expectPass( - { - state: [ - [25, 160, 154, 233], - [61, 244, 198, 248], - [227, 226, 141, 72], - [190, 43, 42, 8], - ], - }, - { - newState: [ - [212, 224, 184, 30], - [39, 191, 180, 65], - [17, 152, 93, 82], - [174, 241, 229, 48], - ], - } - ); - }); - }); - - describe("ShiftRows", () => { - let circuit: WitnessTester<["state"], ["newState"]>; - it("should perform ShiftRows", async () => { - circuit = await circomkit.WitnessTester(`ShiftRows`, { - file: "aes-gcm/aes/cipher", - template: "ShiftRows", - params: [], - }); - // 0x57 . 2 = 0xae - await circuit.expectPass( - { - state: [ - [212, 224, 184, 30], - [39, 191, 180, 65], - [17, 152, 93, 82], - [174, 241, 229, 48], - ], - }, - { - newState: [ - [212, 224, 184, 30], - [191, 180, 65, 39], - [93, 82, 17, 152], - [48, 174, 241, 229], - ], - } - ); - }); - }); -}); diff --git a/circuits/test/aes-gcm/aes/key_expansion.test.ts b/circuits/test/aes-gcm/aes/key_expansion.test.ts deleted file mode 100644 index 906fc44..0000000 --- a/circuits/test/aes-gcm/aes/key_expansion.test.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit } from "../../common"; - -describe("KeyExpansion", () => { - it("should compute correctly for aes128", async () => { - const circuit: WitnessTester<["key"], ["keyExpanded"]> = await circomkit.WitnessTester(`SubBytes`, { - file: "aes-gcm/aes/key_expansion", - template: "KeyExpansion", - params: [], - }); - const key = [0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c]; - const keyExpanded = [ - [0x2b, 0x7e, 0x15, 0x16], - [0x28, 0xae, 0xd2, 0xa6], - [0xab, 0xf7, 0x15, 0x88], - [0x09, 0xcf, 0x4f, 0x3c], - [0xa0, 0xfa, 0xfe, 0x17], - [0x88, 0x54, 0x2c, 0xb1], - [0x23, 0xa3, 0x39, 0x39], - [0x2a, 0x6c, 0x76, 0x05], - [0xf2, 0xc2, 0x95, 0xf2], - [0x7a, 0x96, 0xb9, 0x43], - [0x59, 0x35, 0x80, 0x7a], - [0x73, 0x59, 0xf6, 0x7f], - [0x3d, 0x80, 0x47, 0x7d], - [0x47, 0x16, 0xfe, 0x3e], - [0x1e, 0x23, 0x7e, 0x44], - [0x6d, 0x7a, 0x88, 0x3b], - [0xef, 0x44, 0xa5, 0x41], - [0xa8, 0x52, 0x5b, 0x7f], - [0xb6, 0x71, 0x25, 0x3b], - [0xdb, 0x0b, 0xad, 0x00], - [0xd4, 0xd1, 0xc6, 0xf8], - [0x7c, 0x83, 0x9d, 0x87], - [0xca, 0xf2, 0xb8, 0xbc], - [0x11, 0xf9, 0x15, 0xbc], - [0x6d, 0x88, 0xa3, 0x7a], - [0x11, 0x0b, 0x3e, 0xfd], - [0xdb, 0xf9, 0x86, 0x41], - [0xca, 0x00, 0x93, 0xfd], - [0x4e, 0x54, 0xf7, 0x0e], - [0x5f, 0x5f, 0xc9, 0xf3], - [0x84, 0xa6, 0x4f, 0xb2], - [0x4e, 0xa6, 0xdc, 0x4f], - [0xea, 0xd2, 0x73, 0x21], - [0xb5, 0x8d, 0xba, 0xd2], - [0x31, 0x2b, 0xf5, 0x60], - [0x7f, 0x8d, 0x29, 0x2f], - [0xac, 0x77, 0x66, 0xf3], - [0x19, 0xfa, 0xdc, 0x21], - [0x28, 0xd1, 0x29, 0x41], - [0x57, 0x5c, 0x00, 0x6e], - [0xd0, 0x14, 0xf9, 0xa8], - [0xc9, 0xee, 0x25, 0x89], - [0xe1, 0x3f, 0x0c, 0xc8], - [0xb6, 0x63, 0x0c, 0xa6], - ]; - await circuit.expectPass({ key }, { keyExpanded }); - }); - - describe("Rotate", () => { - let circuit: WitnessTester<["bytes"], ["rotated"]>; - - it("should rotate correctly", async () => { - circuit = await circomkit.WitnessTester(`Rotate`, { - file: "aes-gcm/aes/key_expansion", - template: "Rotate", - params: [1, 4], - }); - await circuit.expectPass({ bytes: [0x01, 0x12, 0x02, 0x30] }, { rotated: [0x12, 0x02, 0x30, 0x01] }); - }); - }); - - describe("SubstituteWord", () => { - let circuit: WitnessTester<["bytes"], ["substituted"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`SubstituteWord`, { - file: "aes-gcm/aes/key_expansion", - template: "SubstituteWord", - }); - }); - - it("should substitute correctly", async () => { - await circuit.expectPass({ bytes: [0x00, 0x10, 0x20, 0x30] }, { substituted: [0x63, 0xca, 0xb7, 0x04] }); - }); - }); - - describe("RCon", () => { - let circuit: WitnessTester<[], ["out"]>; - - async function generatePassCase(round: number, out: number[]) { - circuit = await circomkit.WitnessTester(`RCon`, { - file: "aes-gcm/aes/key_expansion", - template: "RCon", - params: [round] - }); - await circuit.expectPass({}, { out: out }); - } - - it("should compute round constant correctly", async () => { - await generatePassCase(1, [0x01, 0x00, 0x00, 0x00]); - await generatePassCase(2, [0x02, 0x00, 0x00, 0x00]); - await generatePassCase(10, [0x36, 0x00, 0x00, 0x00]); - }); - }); -}); diff --git a/circuits/test/aes-gcm/aes/sbox.test.ts b/circuits/test/aes-gcm/aes/sbox.test.ts deleted file mode 100644 index 13b344a..0000000 --- a/circuits/test/aes-gcm/aes/sbox.test.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit } from "../../common"; - -describe("SBox", () => { - let circuit: WitnessTester<["in"], ["out"]>; - - describe("SubBox", () => { - before(async () => { - circuit = await circomkit.WitnessTester(`SubBytes`, { - file: "aes-gcm/aes/sbox", - template: "SBox128", - }); - }); - - it("should compute correctly", async () => { - await circuit.expectPass({ in: 0x53 }, { out: 0xed }); - await circuit.expectPass({ in: 0x00 }, { out: 0x63 }); - }); - }); -}); - -describe("Field", () => { - let circuit: WitnessTester<["in"], ["out"]>; - - before(async () => { - circuit = await circomkit.WitnessTester(`FieldInv`, { - file: "aes-gcm/aes/sbox", - template: "FieldInv", - }); - }); - - let inverses = [0x00, 0x01, 0x8d, 0xf6, 0xcb, 0x52, 0x7b, 0xd1, 0xe8, 0x4f, 0x29, 0xc0, 0xb0, 0xe1, 0xe5, 0xc7, - 0x74, 0xb4, 0xaa, 0x4b, 0x99, 0x2b, 0x60, 0x5f, 0x58, 0x3f, 0xfd, 0xcc, 0xff, 0x40, 0xee, 0xb2, - 0x3a, 0x6e, 0x5a, 0xf1, 0x55, 0x4d, 0xa8, 0xc9, 0xc1, 0x0a, 0x98, 0x15, 0x30, 0x44, 0xa2, 0xc2, - 0x2c, 0x45, 0x92, 0x6c, 0xf3, 0x39, 0x66, 0x42, 0xf2, 0x35, 0x20, 0x6f, 0x77, 0xbb, 0x59, 0x19, - 0x1d, 0xfe, 0x37, 0x67, 0x2d, 0x31, 0xf5, 0x69, 0xa7, 0x64, 0xab, 0x13, 0x54, 0x25, 0xe9, 0x09, - 0xed, 0x5c, 0x05, 0xca, 0x4c, 0x24, 0x87, 0xbf, 0x18, 0x3e, 0x22, 0xf0, 0x51, 0xec, 0x61, 0x17, - 0x16, 0x5e, 0xaf, 0xd3, 0x49, 0xa6, 0x36, 0x43, 0xf4, 0x47, 0x91, 0xdf, 0x33, 0x93, 0x21, 0x3b, - 0x79, 0xb7, 0x97, 0x85, 0x10, 0xb5, 0xba, 0x3c, 0xb6, 0x70, 0xd0, 0x06, 0xa1, 0xfa, 0x81, 0x82, - 0x83, 0x7e, 0x7f, 0x80, 0x96, 0x73, 0xbe, 0x56, 0x9b, 0x9e, 0x95, 0xd9, 0xf7, 0x02, 0xb9, 0xa4, - 0xde, 0x6a, 0x32, 0x6d, 0xd8, 0x8a, 0x84, 0x72, 0x2a, 0x14, 0x9f, 0x88, 0xf9, 0xdc, 0x89, 0x9a, - 0xfb, 0x7c, 0x2e, 0xc3, 0x8f, 0xb8, 0x65, 0x48, 0x26, 0xc8, 0x12, 0x4a, 0xce, 0xe7, 0xd2, 0x62, - 0x0c, 0xe0, 0x1f, 0xef, 0x11, 0x75, 0x78, 0x71, 0xa5, 0x8e, 0x76, 0x3d, 0xbd, 0xbc, 0x86, 0x57, - 0x0b, 0x28, 0x2f, 0xa3, 0xda, 0xd4, 0xe4, 0x0f, 0xa9, 0x27, 0x53, 0x04, 0x1b, 0xfc, 0xac, 0xe6, - 0x7a, 0x07, 0xae, 0x63, 0xc5, 0xdb, 0xe2, 0xea, 0x94, 0x8b, 0xc4, 0xd5, 0x9d, 0xf8, 0x90, 0x6b, - 0xb1, 0x0d, 0xd6, 0xeb, 0xc6, 0x0e, 0xcf, 0xad, 0x08, 0x4e, 0xd7, 0xe3, 0x5d, 0x50, 0x1e, 0xb3, - 0x5b, 0x23, 0x38, 0x34, 0x68, 0x46, 0x03, 0x8c, 0xdd, 0x9c, 0x7d, 0xa0, 0xcd, 0x1a, 0x41, 0x1c]; - - it("should compute correctly", async () => { - for (let i = 0; i < 256; i++) { - await circuit.expectPass({ in: i }, { out: inverses[i] }); - } - }); - - describe("XTimes1 with XTimes", () => { - let circuit: WitnessTester<["in"], ["out"]>; - it("should perform 1 times with XTERMS", async () => { - circuit = await circomkit.WitnessTester(`XTimes`, { - file: "aes-gcm/aes/sbox", - template: "XTimes", - params: [0x1], - }); - // 0x57 . 2 = 0xae - await circuit.expectPass({ in: [1, 1, 1, 0, 1, 0, 1, 0] }, { out: [1, 1, 1, 0, 1, 0, 1, 0] }); - // 0x54 . 2 = 0xa8 - await circuit.expectPass({ in: [0, 0, 1, 0, 1, 0, 1, 0] }, { out: [0, 0, 1, 0, 1, 0, 1, 0] }); - // 0xae . 2 = 0x47 - await circuit.expectPass({ in: [0, 1, 1, 1, 0, 1, 0, 1] }, { out: [0, 1, 1, 1, 0, 1, 0, 1] }); - // 0x47 . 2 = 0x8e - await circuit.expectPass({ in: [1, 1, 1, 0, 1, 0, 1, 0] }, { out: [1, 1, 1, 0, 1, 0, 1, 0] }); - }); - }); - - describe("XTimes2", () => { - let circuit: WitnessTester<["in"], ["out"]>; - it("should perform 2 times", async () => { - circuit = await circomkit.WitnessTester(`XTimes2`, { - file: "aes-gcm/aes/sbox", - template: "XTimes2", - }); - - // 0x57 . 2 = 0xae - await circuit.expectPass({ in: [1, 1, 1, 0, 1, 0, 1, 0] }, { out: [0, 1, 1, 1, 0, 1, 0, 1] }); - // 0x54 . 2 = 0xa8 - await circuit.expectPass({ in: [0, 0, 1, 0, 1, 0, 1, 0] }, { out: [0, 0, 0, 1, 0, 1, 0, 1] }); - // 0xae . 2 = 0x47 - await circuit.expectPass({ in: [0, 1, 1, 1, 0, 1, 0, 1] }, { out: [1, 1, 1, 0, 0, 0, 1, 0] }); - // 0x47 . 2 = 0x8e - await circuit.expectPass({ in: [1, 1, 1, 0, 0, 0, 1, 0] }, { out: [0, 1, 1, 1, 0, 0, 0, 1] }); - }); - }); - - describe("XTimes", () => { - let circuit: WitnessTester<["in"], ["out"]>; - it("should perform xtimes", async () => { - circuit = await circomkit.WitnessTester(`XTimes`, { - file: "aes-gcm/aes/sbox", - template: "XTimes", - params: [0x13], - }); - // 0x57 . 0x13 = 0xfe - await circuit.expectPass({ in: [1, 1, 1, 0, 1, 0, 1, 0] }, { out: [0, 1, 1, 1, 1, 1, 1, 1] }); - }); - }); - - describe("XTimes2 with XTimes", () => { - let circuit: WitnessTester<["in"], ["out"]>; - it("should perform 2 times with XTERMS", async () => { - circuit = await circomkit.WitnessTester(`XTimes`, { - file: "aes-gcm/aes/sbox", - template: "XTimes", - params: [0x2], - }); - // 0x57 . 2 = 0xae - await circuit.expectPass({ in: [1, 1, 1, 0, 1, 0, 1, 0] }, { out: [0, 1, 1, 1, 0, 1, 0, 1] }); - // 0x54 . 2 = 0xa8 - await circuit.expectPass({ in: [0, 0, 1, 0, 1, 0, 1, 0] }, { out: [0, 0, 0, 1, 0, 1, 0, 1] }); - // 0xae . 2 = 0x47 - await circuit.expectPass({ in: [0, 1, 1, 1, 0, 1, 0, 1] }, { out: [1, 1, 1, 0, 0, 0, 1, 0] }); - // 0x47 . 2 = 0x8e - await circuit.expectPass({ in: [1, 1, 1, 0, 0, 0, 1, 0] }, { out: [0, 1, 1, 1, 0, 0, 0, 1] }); - }); - }); -}); - diff --git a/circuits/test/aes-gcm/aes/transformations.test.ts b/circuits/test/aes-gcm/aes/transformations.test.ts deleted file mode 100644 index 79f11f8..0000000 --- a/circuits/test/aes-gcm/aes/transformations.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit } from "../../common"; - -describe("MixColumns", () => { - it("s0 should compute correctly", async () => { - let circuit: WitnessTester<["in"], ["out"]>; - circuit = await circomkit.WitnessTester(`s0`, { - file: "aes-gcm/aes/mix_columns", - template: "S0", - params: [], - }); - - await circuit.expectPass({ in: [0xd4, 0xbf, 0x5d, 0x30] }, { out: 0x04 }); - }); - - it("s1 should compute correctly", async () => { - let circuit: WitnessTester<["in"], ["out"]>; - circuit = await circomkit.WitnessTester(`s1`, { - file: "aes-gcm/aes/mix_columns", - template: "S1", - params: [], - }); - - await circuit.expectPass({ in: [0xd4, 0xbf, 0x5d, 0x30] }, { out: 0x66 }); - }); - - it("s2 should compute correctly", async () => { - let circuit: WitnessTester<["in"], ["out"]>; - circuit = await circomkit.WitnessTester(`s2`, { - file: "aes-gcm/aes/mix_columns", - template: "S2", - params: [], - }); - - await circuit.expectPass({ in: [0xd4, 0xbf, 0x5d, 0x30] }, { out: 0x81 }); - }); - - it("s3 should compute correctly", async () => { - let circuit: WitnessTester<["in"], ["out"]>; - circuit = await circomkit.WitnessTester(`s3`, { - file: "aes-gcm/aes/mix_columns", - template: "S3", - params: [], - }); - - await circuit.expectPass({ in: [0xd4, 0xbf, 0x5d, 0x30] }, { out: 0xe5 }); - }); - - it("s4 should compute correctly", async () => { - let circuit: WitnessTester<["state"], ["out"]>; - circuit = await circomkit.WitnessTester(`MixColumns`, { - file: "aes-gcm/aes/mix_columns", - template: "MixColumns", - params: [], - }); - const state = [ - [0xd4, 0xe0, 0xb8, 0x1e], - [0xbf, 0xb4, 0x41, 0x27], - [0x5d, 0x52, 0x11, 0x98], - [0x30, 0xae, 0xf1, 0xe5], - ]; - - const out = [ - [0x04, 0xe0, 0x48, 0x28], - [0x66, 0xcb, 0xf8, 0x06], - [0x81, 0x19, 0xd3, 0x26], - [0xe5, 0x9a, 0x7a, 0x4c], - ]; - - await circuit.expectPass({ state }, { out }); - }); -}); - - diff --git a/circuits/test/aes-gcm/gctr.test.ts b/circuits/test/aes-gcm/gctr.test.ts deleted file mode 100644 index 4af80f7..0000000 --- a/circuits/test/aes-gcm/gctr.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit } from "../common"; -import { assert } from "chai"; - -describe("GCTR", () => { - let circuit: WitnessTester<["plainText", "initialCounterBlock", "key"], ["cipherText"]>; - it("should encrypt the plaintext", async () => { - circuit = await circomkit.WitnessTester(`GCTR`, { - file: "aes-gcm/gctr", - template: "GCTR", - params: [16], - }); - - // GOOD TEST CASE. - const key = [0xca, 0xaa, 0x3f, 0x6f, 0xd3, 0x18, 0x22, 0xed, 0x2d, 0x21, 0x25, 0xf2, 0x25, 0xb0, 0x16, 0x9f]; - const column_wise_icb = [0x7f, 0x48, 0x12, 0x00, 0x6d, 0x3e, 0xfa, 0x00, 0x90, 0x8c, 0x55, 0x00, 0x41, 0x14, 0x2a, 0x02]; - const pt = [0x84, 0xc9, 0x07, 0xb1, 0x1a, 0xe3, 0xb7, 0x9f, 0xc4, 0x45, 0x1d, 0x1b, 0xf1, 0x7f, 0x4a, 0x99]; - const ct = [0xfd, 0xb4, 0xaa, 0xfa, 0x35, 0x19, 0xd3, 0xc0, 0x55, 0xbe, 0x8b, 0x34, 0x77, 0x64, 0xea, 0x33]; - - const witness = await circuit.compute({ key: key, initialCounterBlock: column_wise_icb, plainText: pt }, ["cipherText"]) - - assert.deepEqual(witness.cipherText, hexBytesToBigInt(ct)) - }); -}); - -function hexBytesToBigInt(hexBytes: number[]): any[] { - return hexBytes.map(byte => { - let n = BigInt(byte); - return n; - }); - } \ No newline at end of file diff --git a/circuits/test/aes-gcm/ghash/ghash.test.ts b/circuits/test/aes-gcm/ghash/ghash.test.ts deleted file mode 100644 index bddebca..0000000 --- a/circuits/test/aes-gcm/ghash/ghash.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit } from "../../common"; - - - -describe("GHASH", () => { - let circuit: WitnessTester<["HashKey", "msg"], ["tag"]>; - - before(async () => { - circuit = await circomkit.WitnessTester(`ghash`, { - file: "aes-gcm/ghash/ghash", - template: "GHASH", - params: [2], - }); - }); - - it("test ghash", async () => { - // https://datatracker.ietf.org/doc/html/rfc8452#appendix-A - const H = [0x25, 0x62, 0x93, 0x47, 0x58, 0x92, 0x42, 0x76, 0x1d, 0x31, 0xf8, 0x26, 0xba, 0x4b, 0x75, 0x7b]; - const X1 = [0x4f, 0x4f, 0x95, 0x66, 0x8c, 0x83, 0xdf, 0xb6, 0x40, 0x17, 0x62, 0xbb, 0x2d, 0x01, 0xa2, 0x62]; - const X2 = [0xd1, 0xa2, 0x4d, 0xdd, 0x27, 0x21, 0xd0, 0x06, 0xbb, 0xe4, 0x5f, 0x20, 0xd3, 0xc9, 0xf3, 0x62]; - const M = X1.concat(X2); - const EXPECT = [0xbd, 0x9b, 0x39, 0x97, 0x04, 0x67, 0x31, 0xfb, 0x96, 0x25, 0x1b, 0x91, 0xf9, 0xc9, 0x9d, 0x7a]; - await circuit.expectPass({ HashKey: H, msg: M }, { tag: EXPECT }); - }); -}); - - - - diff --git a/circuits/test/aes-gcm/ghash/gmul.test.ts b/circuits/test/aes-gcm/ghash/gmul.test.ts deleted file mode 100644 index b0b65ba..0000000 --- a/circuits/test/aes-gcm/ghash/gmul.test.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit } from "../../common"; - - -describe("GhashMul", () => { - let circuit: WitnessTester<["X", "Y"], ["out"]>; - - before(async () => { - circuit = await circomkit.WitnessTester("ghash_gmul", { - file: "aes-gcm/ghash/gmul", - template: "GhashMul", - }); - }); - - it("Should Compute GhashMul Correctly", async () => { - - let X = [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let Y = [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - - const expected = [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - await circuit.expectPass({ X: X, Y: Y }, { out: expected }); - }); - - it("Should Compute NistGMulByte of LSB=1 Correctly", async () => { - - let X = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; - let Y = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; - - const expected = [0xe6, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03]; - await circuit.expectPass({ X: X, Y: Y }, { out: expected }); - }); - it("Should Compute NistGMulByte of LSB=1 Correctly", async () => { - - // x = "aae06992acbf52a3e8f4a96ec9300bd7" - // y = "98e7247c07f0fe411c267e4384b0f600" - // expected = "90e87315fb7d4e1b4092ec0cbfda5d7d" - let X = [0xaa, 0xe0, 0x69, 0x92, 0xac, 0xbf, 0x52, 0xa3, 0xe8, 0xf4, 0xa9, 0x6e, 0xc9, 0x30, 0x0b, 0xd7]; - let Y = [0x98, 0xe7, 0x24, 0x7c, 0x07, 0xf0, 0xfe, 0x41, 0x1c, 0x26, 0x7e, 0x43, 0x84, 0xb0, 0xf6, 0x00]; - - const expected = [0x90, 0xe8, 0x73, 0x15, 0xfb, 0x7d, 0x4e, 0x1b, 0x40, 0x92, 0xec, 0x0c, 0xbf, 0xda, 0x5d, 0x7d]; - await circuit.expectPass({ X: X, Y: Y }, { out: expected }); - }); - - describe("BlockRightShift", () => { - let circuit: WitnessTester<["in"], ["out", "msb"]>; - - before(async () => { - circuit = await circomkit.WitnessTester("BlockRightShift", { - file: "aes-gcm/ghash/gmul", - template: "BlockRightShift", - params: [16] - }); - }); - - it("Should Compute BlockRightShift Correctly", async () => { - let input = [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - const expected = [0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - await circuit.expectPass({ in: input }, { out: expected, msb: 0 }); - }); - it("Should Compute BlockRightShift Correctly", async () => { - let input = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; - const expected = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - await circuit.expectPass({ in: input }, { out: expected, msb: 1 }); - }); - }); - - describe("Mulx", () => { - let circuit: WitnessTester<["in"], ["out"]>; - - before(async () => { - circuit = await circomkit.WitnessTester("Mulx", { - file: "aes-gcm/ghash/gmul", - template: "Mulx", - params: [16] - }); - }); - // msb is 1 so we xor the first byte with 0xE1 - it("Should Compute Mulx Correctly", async () => { - let input = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; - const expected = [0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - await circuit.expectPass({ in: input }, { out: expected }); - }); - }); - - describe("Z_UPDATE", () => { - let circuit: WitnessTester<["Z", "V", "bit_val"], ["Z_new"]>; - - before(async () => { - circuit = await circomkit.WitnessTester("XORBLOCK", { - file: "aes-gcm/ghash/gmul", - template: "Z_UPDATE", - params: [16] - }); - }); - // msb is 1 so we xor the first byte with 0xE1 - it("Should Compute block XOR Correctly", async () => { - let inputZ = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let inputV = [0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; - let inputc = 0x00; - let expected = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - await circuit.expectPass({ Z: inputZ, V: inputV, bit_val: inputc }, { Z_new: expected }); - }); - - it("Should Compute block XOR Correctly", async () => { - let inputa = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let inputb = [0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; - let inputc = 0x01; - const expected = [0xE1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]; - await circuit.expectPass({ Z: inputa, V: inputb, bit_val: inputc }, { Z_new: expected }); - }); - }); - - -}); - - - diff --git a/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts b/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts deleted file mode 100644 index d44afa4..0000000 --- a/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { assert } from "chai"; -import { WitnessTester } from "circomkit"; -import { circomkit, bytesToBigInt } from "../../common"; -import { PoseidonModular } from "../../common/poseidon"; - -describe("aes-gctr-nivc", () => { - let circuit_one_block: WitnessTester<["key", "iv", "plainText", "aad", "ctr", "cipherText", "step_in"], ["step_out"]>; - - - it("all correct for self generated single zero pt block case", async () => { - circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/nivc/aes-gctr-nivc", - template: "AESGCTRFOLD", - params: [1] - }); - - let key = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let plainText = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let iv = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let aad = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let ct = [0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78]; - - const ctr = [0x00, 0x00, 0x00, 0x01]; - const step_in = 0; - - const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText, aad: aad, ctr: ctr, cipherText: ct, step_in: step_in }, ["step_out"]) - console.log(witness.step_out); - assert.deepEqual(witness.step_out, BigInt(0)); - }); - - it("all correct for self generated single non zero pt block", async () => { - circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/nivc/aes-gctr-nivc", - template: "AESGCTRFOLD", - params: [1] - }); - - let key = [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]; - let plainText = [0x74, 0x65, 0x73, 0x74, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]; - let iv = [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]; - let aad = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let ct = [0x29, 0x29, 0xd2, 0xbb, 0x1a, 0xe9, 0x48, 0x04, 0x40, 0x2b, 0x8e, 0x77, 0x6e, 0x0d, 0x33, 0x56]; - - const ctr = [0x00, 0x00, 0x00, 0x01]; - const step_in = 0; - - const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText, aad: aad, ctr: ctr, cipherText: ct, step_in: step_in }, ["step_out"]) - assert.deepEqual(witness.step_out, PoseidonModular([step_in, bytesToBigInt(plainText)])); - }); - - let key = [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]; - let plainText1 = [0x74, 0x65, 0x73, 0x74, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]; - let plainText2 = [0x74, 0x65, 0x73, 0x74, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30]; - let iv = [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]; - let aad = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let ct_part1 = [0x29, 0x29, 0xd2, 0xbb, 0x1a, 0xe9, 0x48, 0x04, 0x40, 0x2b, 0x8e, 0x77, 0x6e, 0x0d, 0x33, 0x56]; - let ct_part2 = [0x26, 0x75, 0x65, 0x30, 0x71, 0x3e, 0x4c, 0x06, 0x5a, 0xf1, 0xd3, 0xc4, 0xf5, 0x6e, 0x02, 0x04]; - // 2929d2bb1ae94804402b8e776e0d3356 - // 52101644195b206a35c1e3f4c55e3234 - // be0fe3e07b837d0103332436f8f0bbd - - it("all correct for self generated two block case first fold", async () => { - circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/nivc/aes-gctr-nivc", - template: "AESGCTRFOLD", - params: [1] - }); - - const ctr = [0x00, 0x00, 0x00, 0x01]; - const step_in = 0; - - const witness = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText1, aad: aad, ctr: ctr, cipherText: ct_part1, step_in: step_in }, ["step_out"]) - assert.deepEqual(witness.step_out, PoseidonModular([step_in, bytesToBigInt(plainText1)])); - }); - - it("all correct for self generated two block case second fold", async () => { - circuit_one_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/nivc/aes-gctr-nivc", - template: "AESGCTRFOLD", - params: [1] - }); - - const ctr_0 = [0x00, 0x00, 0x00, 0x01]; - const ctr_1 = [0x00, 0x00, 0x00, 0x02]; - const step_in_0 = 0; - - const witness_0 = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText1, aad: aad, ctr: ctr_0, cipherText: ct_part1, step_in: step_in_0 }, ["step_out"]) - const witness_1 = await circuit_one_block.compute({ key: key, iv: iv, plainText: plainText2, aad: aad, ctr: ctr_1, cipherText: ct_part2, step_in: witness_0.step_out }, ["step_out"]) - assert.deepEqual(witness_1.step_out, PoseidonModular([BigInt(witness_0.step_out.toString()), bytesToBigInt(plainText2)])); - }); - - let circuit_two_block: WitnessTester<["key", "iv", "plainText", "aad", "ctr", "cipherText", "step_in"], ["step_out"]>; - it("all correct for two folds at once", async () => { - circuit_two_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/nivc/aes-gctr-nivc", - template: "AESGCTRFOLD", - params: [2] - }); - - const ctr_0 = [0x00, 0x00, 0x00, 0x01]; - const step_in_0 = 0; - - const witness = await circuit_two_block.compute({ key: key, iv: iv, aad: aad, ctr: ctr_0, plainText: [plainText1, plainText2], cipherText: [ct_part1, ct_part2], step_in: step_in_0 }, ["step_out"]) - let hash_0 = PoseidonModular([step_in_0, bytesToBigInt(plainText1)]); - assert.deepEqual(witness.step_out, PoseidonModular([hash_0, bytesToBigInt(plainText2)])); - }); - - it("all correct for two folds at once one zero chunk", async () => { - circuit_two_block = await circomkit.WitnessTester("aes-gcm-fold", { - file: "aes-gcm/nivc/aes-gctr-nivc", - template: "AESGCTRFOLD", - params: [2] - }); - - const ctr_0 = [0x00, 0x00, 0x00, 0x01]; - const step_in_0 = 0; - let zero_chunk = Array(16).fill(0); - let zero_ct = [0x52, 0x10, 0x16, 0x44, 0x19, 0x5b, 0x20, 0x6a, 0x35, 0xc1, 0xe3, 0xf4, 0xc5, 0x5e, 0x32, 0x34]; - - const witness = await circuit_two_block.compute({ key: key, iv: iv, aad: aad, ctr: ctr_0, plainText: [plainText1, zero_chunk], cipherText: [ct_part1, zero_ct], step_in: step_in_0 }, ["step_out"]) - let hash_0 = PoseidonModular([step_in_0, bytesToBigInt(plainText1)]); - assert.deepEqual(witness.step_out, hash_0); - }); -}); \ No newline at end of file diff --git a/circuits/test/chacha20/chacha20-nivc.test.ts b/circuits/test/chacha20/chacha20-nivc.test.ts index a452418..70a743c 100644 --- a/circuits/test/chacha20/chacha20-nivc.test.ts +++ b/circuits/test/chacha20/chacha20-nivc.test.ts @@ -5,7 +5,7 @@ import { assert } from "chai"; describe("chacha20-nivc", () => { - let circuit: WitnessTester<["key", "nonce", "counter", "plainText", "cipherText", "step_in"], ["step_out"]>; + let circuit: WitnessTester<["key", "nonce", "counter", "plainText", "step_in"], ["step_out"]>; describe("16 block test", () => { it("should perform encryption", async () => { circuit = await circomkit.WitnessTester(`ChaCha20`, { @@ -54,9 +54,8 @@ describe("chacha20-nivc", () => { key: toInput(Buffer.from(keyBytes)), nonce: toInput(Buffer.from(nonceBytes)), counter: counterBits, - cipherText: ciphertextBytes, plainText: plaintextBytes, - step_in: 0 + step_in: DataHasher(ciphertextBytes) }, (["step_out"])); assert.deepEqual(w.step_out, DataHasher(plaintextBytes)); }); @@ -103,20 +102,62 @@ describe("chacha20-nivc", () => { 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08 ]; let totalLength = 128; - let paddedPlaintextBytes = plaintextBytes.concat(Array(totalLength - plaintextBytes.length).fill(0)); - let paddedCiphertextBytes = ciphertextBytes.concat(Array(totalLength - ciphertextBytes.length).fill(0)); + let paddedPlaintextBytes = plaintextBytes.concat(Array(totalLength - plaintextBytes.length).fill(-1)); const counterBits = uintArray32ToBits([1])[0] let w = await circuit.compute({ key: toInput(Buffer.from(keyBytes)), nonce: toInput(Buffer.from(nonceBytes)), counter: counterBits, - cipherText: paddedCiphertextBytes, plainText: paddedPlaintextBytes, - step_in: 0 + step_in: DataHasher(ciphertextBytes) }, (["step_out"])); assert.deepEqual(w.step_out, DataHasher(paddedPlaintextBytes)); }); }); + + describe("wrong ciphertext hash", () => { + it("should fail", async () => { + circuit = await circomkit.WitnessTester(`ChaCha20`, { + file: "chacha20/nivc/chacha20_nivc", + template: "ChaCha20_NIVC", + params: [128] // number of bytes in plaintext + }); + // Test case from RCF https://www.rfc-editor.org/rfc/rfc7539.html#section-2.4.2 + // the input encoding here is not the most intuitive. inputs are serialized as little endian. + // i.e. "e4e7f110" is serialized as "10 f1 e7 e4". So the way i am reading in inputs is + // to ensure that every 32 bit word is byte reversed before being turned into bits. + // i think this should be easy when we compute witness in rust. + let keyBytes = [ + 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f + ]; + + let nonceBytes = + [ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4a, + 0x00, 0x00, 0x00, 0x00 + ]; + let plaintextBytes = + toByte("Ladies and Gentlemen of the class of '99: If I could offer you only one tip "); + let totalLength = 128; + let paddedPlaintextBytes = plaintextBytes.concat(Array(totalLength - plaintextBytes.length).fill(-1)); + const counterBits = uintArray32ToBits([1])[0] + await circuit.expectFail({ + key: toInput(Buffer.from(keyBytes)), + nonce: toInput(Buffer.from(nonceBytes)), + counter: counterBits, + plainText: paddedPlaintextBytes, + step_in: 0 + }); + }); + }); }); diff --git a/circuits/test/common/poseidon.ts b/circuits/test/common/poseidon.ts index 0c2a12d..ab18924 100644 --- a/circuits/test/common/poseidon.ts +++ b/circuits/test/common/poseidon.ts @@ -74,22 +74,29 @@ export function PoseidonModular(input: Array): bigint } export function DataHasher(input: number[]): bigint { - if (input.length % 16 !== 0) { - throw new Error("DATA_BYTES must be divisible by 16"); - } - let hashes: bigint[] = [BigInt(0)]; // Initialize first hash as 0 - for (let i = 0; i < Math.floor(input.length / 16); i++) { + for (let i = 0; i < Math.ceil(input.length / 16); i++) { let packedInput = BigInt(0); + let isPaddedChunk = 0; + // Allow for using unpadded input: + let innerLoopLength = 16; + let lengthRemaining = input.length - 16 * i; + if (lengthRemaining < 16) { + innerLoopLength = lengthRemaining; + } // Pack 16 bytes into a single number - for (let j = 0; j < 16; j++) { - packedInput += BigInt(input[16 * i + j]) * BigInt(2 ** (8 * j)); + for (let j = 0; j < innerLoopLength; j++) { + if (input[16 * i + j] != -1) { + packedInput += BigInt(input[16 * i + j]) * BigInt(2 ** (8 * j)); + } else { + isPaddedChunk += 1; + } } - // Compute next hash using previous hash and packed input, but if packed input is zero, don't hash it. - if (packedInput == BigInt(0)) { + // Compute next hash using previous hash and packed input, but if the whole block was padding, don't do it + if (isPaddedChunk == innerLoopLength) { hashes.push(hashes[i]); } else { hashes.push(PoseidonModular([hashes[i], packedInput])); @@ -97,5 +104,5 @@ export function DataHasher(input: number[]): bigint { } // Return the last hash - return hashes[Math.floor(input.length / 16)]; + return hashes[Math.ceil(input.length / 16)]; } \ No newline at end of file diff --git a/circuits/test/full/full.test.ts b/circuits/test/full/full.test.ts index dd6567a..507fa1f 100644 --- a/circuits/test/full/full.test.ts +++ b/circuits/test/full/full.test.ts @@ -54,29 +54,6 @@ const chacha20_http_response_ciphertext = [ 131, 230, 161, 217, 29, 229, 251, 33, 220, 230, 102, 131, 245, 27, 141, 220, 67, 16, 26 ]; -const aes_http_response_ciphertext = [ - 75, 220, 142, 158, 79, 135, 141, 163, 211, 26, 242, 137, 81, 253, 181, 117, - 253, 246, 197, 197, 61, 46, 55, 87, 218, 137, 240, 143, 241, 177, 225, 129, - 80, 114, 125, 72, 45, 18, 224, 179, 79, 231, 153, 198, 163, 252, 197, 219, - 233, 46, 202, 120, 99, 253, 76, 9, 70, 11, 200, 218, 228, 251, 133, 248, - 233, 177, 19, 241, 205, 128, 65, 76, 10, 31, 71, 198, 177, 78, 108, 246, - 175, 152, 42, 97, 255, 182, 157, 245, 123, 95, 130, 101, 129, 138, 236, 146, - 47, 22, 22, 13, 125, 1, 109, 158, 189, 131, 44, 43, 203, 118, 79, 181, - 86, 33, 235, 186, 75, 20, 7, 147, 102, 75, 90, 222, 255, 140, 94, 52, - 191, 145, 192, 71, 239, 245, 247, 175, 117, 136, 173, 235, 250, 189, 74, 155, - 103, 25, 164, 187, 22, 26, 39, 37, 113, 248, 170, 146, 73, 75, 45, 208, - 125, 49, 101, 11, 120, 215, 93, 160, 14, 147, 129, 181, 150, 59, 167, 197, - 230, 122, 77, 245, 247, 215, 136, 98, 1, 180, 213, 30, 214, 88, 83, 42, - 33, 112, 61, 4, 197, 75, 134, 149, 22, 228, 24, 95, 131, 35, 44, 181, - 135, 31, 173, 36, 23, 192, 177, 127, 156, 199, 167, 212, 66, 235, 194, 102, - 61, 144, 121, 59, 187, 179, 212, 34, 117, 47, 96, 3, 169, 73, 204, 88, - 36, 48, 158, 220, 237, 198, 180, 105, 7, 188, 109, 24, 201, 217, 186, 191, - 232, 63, 93, 153, 118, 214, 157, 167, 15, 216, 191, 152, 41, 106, 24, 127, - 8, 144, 78, 218, 133, 125, 89, 97, 10, 246, 8, 244, 112, 169, 190, 206, - 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, @@ -227,118 +204,8 @@ const json_key3_mask = [ ]; const json_key3_mask_hash = DataHasher(json_key3_mask); -describe("NIVC_FULL_AES", async () => { - let aesCircuit: WitnessTester<["key", "iv", "aad", "ctr", "plainText", "cipherText", "step_in"], ["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_KEY_LENGTH = 8; - const MAX_VALUE_LENGTH = 32; - - before(async () => { - aesCircuit = await circomkit.WitnessTester("AESGCTRFOLD", { - file: "aes-gcm/nivc/aes-gctr-nivc", - template: "AESGCTRFOLD", - params: [1] - }); - console.log("#constraints (AES):", await aesCircuit.getConstraintCount()); - - httpCircuit = await circomkit.WitnessTester(`HttpNIVC`, { - file: "http/nivc/http_nivc", - template: "HttpNIVC", - params: [DATA_BYTES, MAX_NUMBER_OF_HEADERS], - }); - console.log("#constraints (HttpNIVC):", await httpCircuit.getConstraintCount()); - - json_mask_object_circuit = await circomkit.WitnessTester(`JsonMaskObjectNIVC`, { - file: "json/nivc/masker", - template: "JsonMaskObjectNIVC", - params: [DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH], - }); - console.log("#constraints (JSON-MASK-OBJECT):", await json_mask_object_circuit.getConstraintCount()); - - json_mask_arr_circuit = await circomkit.WitnessTester(`JsonMaskArrayIndexNIVC`, { - file: "json/nivc/masker", - template: "JsonMaskArrayIndexNIVC", - params: [DATA_BYTES, MAX_STACK_HEIGHT], - }); - console.log("#constraints (JSON-MASK-ARRAY-INDEX):", await json_mask_arr_circuit.getConstraintCount()); - - extract_value_circuit = await circomkit.WitnessTester(`JsonMaskExtractFinal`, { - file: "json/nivc/extractor", - template: "MaskExtractFinal", - params: [DATA_BYTES, MAX_VALUE_LENGTH], - }); - console.log("#constraints (JSON-MASK-EXTRACT-FINAL):", await extract_value_circuit.getConstraintCount()); - }); - - it("NIVC_CHAIN", async () => { - const init_nivc_input = 0; - // Run AES chain - let ctr = [0x00, 0x00, 0x00, 0x01]; - let pt = http_response_plaintext.slice(0, 16); - let ct = aes_http_response_ciphertext.slice(0, 16); - let aes_gcm = await aesCircuit.compute({ key: Array(16).fill(0), iv: Array(12).fill(0), ctr: ctr, plainText: pt, aad: Array(16).fill(0), cipherText: ct, step_in: init_nivc_input }, ["step_out"]); - let i = 0; - console.log("AES `step_out[", i, "]`: ", aes_gcm.step_out); - for (i = 1; i < (DATA_BYTES / 16); i++) { - ctr[3] += 1; // This will work since we don't run a test that overlows a byte - let pt = http_response_plaintext.slice(i * 16, i * 16 + 16); - let ct = aes_http_response_ciphertext.slice(i * 16, i * 16 + 16); - aes_gcm = await aesCircuit.compute({ key: Array(16).fill(0), iv: Array(12).fill(0), ctr: ctr, plainText: pt, aad: Array(16).fill(0), cipherText: ct, step_in: aes_gcm.step_out }, ["step_out"]); - console.log("AES `step_out[", i, "]`: ", aes_gcm.step_out); - } - assert.deepEqual(http_response_hash, aes_gcm.step_out); - - 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; - let key1 = [105, 116, 101, 109, 115, 0, 0, 0]; // "items" - let key1Len = 5; - let key2 = [112, 114, 111, 102, 105, 108, 101, 0]; // "profile" - let key2Len = 7; - let key3 = [110, 97, 109, 101, 0, 0, 0, 0]; // "name" - let key3Len = 4; - - 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); - - let json_extract_key1 = await json_mask_object_circuit.compute({ step_in: json_extract_key0.step_out, data: json_key0_mask, key: key1, keyLen: key1Len }, ["step_out"]); - assert.deepEqual(json_extract_key1.step_out, json_key1_mask_hash); - console.log("JSON Extract key1 `step_out`:", json_extract_key1.step_out); - - let json_extract_arr = await json_mask_arr_circuit.compute({ step_in: json_extract_key1.step_out, data: json_key1_mask, index: 0 }, ["step_out"]); - assert.deepEqual(json_extract_arr.step_out, json_arr_mask_hash); - console.log("JSON Extract arr `step_out`:", json_extract_arr.step_out); - - let json_extract_key2 = await json_mask_object_circuit.compute({ step_in: json_extract_arr.step_out, data: json_arr_mask, key: key2, keyLen: key2Len }, ["step_out"]); - assert.deepEqual(json_extract_key2.step_out, json_key2_mask_hash); - console.log("JSON Extract key2 `step_out`:", json_extract_key2.step_out); - - let json_extract_key3 = await json_mask_object_circuit.compute({ step_in: json_extract_key2.step_out, data: json_key2_mask, key: key3, keyLen: key3Len }, ["step_out"]); - assert.deepEqual(json_extract_key3.step_out, json_key3_mask_hash); - console.log("JSON Extract key3 `step_out`:", json_extract_key3.step_out); - - // TODO (autoparallel): we need to rethink extraction here. - let finalOutput = toByte("\"Taylor Swift\""); - let finalOutputPadded = finalOutput.concat(Array(Math.max(0, MAX_VALUE_LENGTH - finalOutput.length)).fill(0)); - let final_value_hash = DataHasher(finalOutputPadded); - let extractValue = await extract_value_circuit.compute({ step_in: json_extract_key3.step_out, data: json_key3_mask }, ["step_out"]); - console.log("finalValue", extractValue.step_out); - assert.deepEqual(extractValue.step_out, final_value_hash); - }); -}); - -describe("NIVC_FULL_CHACHA", async () => { - let chacha20Circuit: WitnessTester<["key", "nonce", "counter", "plainText", "cipherText", "step_in"], ["step_out"]>; +describe("NIVC_FULL", async () => { + let chacha20Circuit: WitnessTester<["key", "nonce", "counter", "plainText", "step_in"], ["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"]>; @@ -359,11 +226,11 @@ describe("NIVC_FULL_CHACHA", async () => { console.log("#constraints (CHACHA20):", await chacha20Circuit.getConstraintCount()); httpCircuit = await circomkit.WitnessTester(`HttpNIVC`, { - file: "http/nivc/http_nivc", - template: "HttpNIVC", + file: "http/verification", + template: "HTTPVerification", params: [DATA_BYTES, MAX_NUMBER_OF_HEADERS], }); - console.log("#constraints (HttpNIVC):", await httpCircuit.getConstraintCount()); + console.log("#constraints (HTTPVerification):", await httpCircuit.getConstraintCount()); json_mask_object_circuit = await circomkit.WitnessTester(`JsonMaskObjectNIVC`, { file: "json/nivc/masker", @@ -388,12 +255,12 @@ describe("NIVC_FULL_CHACHA", async () => { }); it("NIVC_CHAIN", async () => { - const init_nivc_input = 0; + const init_nivc_input = DataHasher(chacha20_http_response_ciphertext); // Run ChaCha20 const counterBits = uintArray32ToBits([1])[0] const keyIn = toInput(Buffer.from(Array(32).fill(0))); const nonceIn = toInput(Buffer.from([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00])); - let chacha20 = await chacha20Circuit.compute({ key: keyIn, nonce: nonceIn, counter: counterBits, plainText: http_response_plaintext, cipherText: chacha20_http_response_ciphertext, step_in: init_nivc_input }, ["step_out"]); + let chacha20 = await chacha20Circuit.compute({ key: keyIn, nonce: nonceIn, counter: counterBits, plainText: http_response_plaintext, step_in: init_nivc_input }, ["step_out"]); console.log("ChaCha20 `step_out`:", chacha20.step_out); assert.deepEqual(http_response_hash, chacha20.step_out); @@ -438,115 +305,3 @@ describe("NIVC_FULL_CHACHA", async () => { assert.deepEqual(extractValue.step_out, final_value_hash); }); }); - - -describe("NIVC_FULL_2_AES", async () => { - let aesCircuit: WitnessTester<["key", "iv", "aad", "ctr", "plainText", "cipherText", "step_in"], ["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_KEY_LENGTH = 8; - const MAX_VALUE_LENGTH = 32; - - before(async () => { - aesCircuit = await circomkit.WitnessTester("AESGCTRFOLD", { - file: "aes-gcm/nivc/aes-gctr-nivc", - template: "AESGCTRFOLD", - params: [2] - }); - console.log("#constraints (AES):", await aesCircuit.getConstraintCount()); - - httpCircuit = await circomkit.WitnessTester(`HttpNIVC`, { - file: "http/nivc/http_nivc", - template: "HttpNIVC", - params: [DATA_BYTES, MAX_NUMBER_OF_HEADERS], - }); - console.log("#constraints (HttpNIVC):", await httpCircuit.getConstraintCount()); - - json_mask_object_circuit = await circomkit.WitnessTester(`JsonMaskObjectNIVC`, { - file: "json/nivc/masker", - template: "JsonMaskObjectNIVC", - params: [DATA_BYTES, MAX_STACK_HEIGHT, MAX_KEY_LENGTH], - }); - console.log("#constraints (JSON-MASK-OBJECT):", await json_mask_object_circuit.getConstraintCount()); - - json_mask_arr_circuit = await circomkit.WitnessTester(`JsonMaskArrayIndexNIVC`, { - file: "json/nivc/masker", - template: "JsonMaskArrayIndexNIVC", - params: [DATA_BYTES, MAX_STACK_HEIGHT], - }); - console.log("#constraints (JSON-MASK-ARRAY-INDEX):", await json_mask_arr_circuit.getConstraintCount()); - - extract_value_circuit = await circomkit.WitnessTester(`JsonMaskExtractFinal`, { - file: "json/nivc/extractor", - template: "MaskExtractFinal", - params: [DATA_BYTES, MAX_VALUE_LENGTH], - }); - console.log("#constraints (JSON-MASK-EXTRACT-FINAL):", await extract_value_circuit.getConstraintCount()); - }); - - it("NIVC_CHAIN_2", async () => { - const init_nivc_input = 0; - - // Run AES chain - let ctr = [0x00, 0x00, 0x00, 0x01]; - let pt = [http_response_plaintext.slice(0, 16), http_response_plaintext.slice(16, 32)]; - let ct = [aes_http_response_ciphertext.slice(0, 16), aes_http_response_ciphertext.slice(16, 32)]; - let aes_gcm = await aesCircuit.compute({ key: Array(16).fill(0), iv: Array(12).fill(0), ctr: ctr, plainText: pt, aad: Array(16).fill(0), cipherText: ct, step_in: init_nivc_input }, ["step_out"]); - let i = 0; - console.log("AES `step_out[", i, "]`: ", aes_gcm.step_out); - for (i = 1; i < (DATA_BYTES / (16 * 2)); i++) { - ctr[3] += 2; // This will work since we don't run a test that overlows a byte - let pt = [http_response_plaintext.slice(i * 32, i * 32 + 16), http_response_plaintext.slice(i * 32 + 16, i * 32 + 32)]; - let ct = [aes_http_response_ciphertext.slice(i * 32, i * 32 + 16), aes_http_response_ciphertext.slice(i * 32 + 16, i * 32 + 32)]; - aes_gcm = await aesCircuit.compute({ key: Array(16).fill(0), iv: Array(12).fill(0), ctr: ctr, plainText: pt, aad: Array(16).fill(0), cipherText: ct, step_in: aes_gcm.step_out }, ["step_out"]); - console.log("AES `step_out[", i, "]`: ", aes_gcm.step_out); - } - assert.deepEqual(http_response_hash, aes_gcm.step_out); - - 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; - let key1 = [105, 116, 101, 109, 115, 0, 0, 0]; // "items" - let key1Len = 5; - let key2 = [112, 114, 111, 102, 105, 108, 101, 0]; // "profile" - let key2Len = 7; - let key3 = [110, 97, 109, 101, 0, 0, 0, 0]; // "name" - let key3Len = 4; - - 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); - - let json_extract_key1 = await json_mask_object_circuit.compute({ step_in: json_extract_key0.step_out, data: json_key0_mask, key: key1, keyLen: key1Len }, ["step_out"]); - assert.deepEqual(json_extract_key1.step_out, json_key1_mask_hash); - console.log("JSON Extract key1 `step_out`:", json_extract_key1.step_out); - - let json_extract_arr = await json_mask_arr_circuit.compute({ step_in: json_extract_key1.step_out, data: json_key1_mask, index: 0 }, ["step_out"]); - assert.deepEqual(json_extract_arr.step_out, json_arr_mask_hash); - console.log("JSON Extract arr `step_out`:", json_extract_arr.step_out); - - let json_extract_key2 = await json_mask_object_circuit.compute({ step_in: json_extract_arr.step_out, data: json_arr_mask, key: key2, keyLen: key2Len }, ["step_out"]); - assert.deepEqual(json_extract_key2.step_out, json_key2_mask_hash); - console.log("JSON Extract key2 `step_out`:", json_extract_key2.step_out); - - let json_extract_key3 = await json_mask_object_circuit.compute({ step_in: json_extract_key2.step_out, data: json_key2_mask, key: key3, keyLen: key3Len }, ["step_out"]); - assert.deepEqual(json_extract_key3.step_out, json_key3_mask_hash); - console.log("JSON Extract key3 `step_out`:", json_extract_key3.step_out); - - // TODO (autoparallel): we need to rethink extraction here. - let finalOutput = toByte("\"Taylor Swift\""); - let finalOutputPadded = finalOutput.concat(Array(Math.max(0, MAX_VALUE_LENGTH - finalOutput.length)).fill(0)); - let final_value_hash = DataHasher(finalOutputPadded); - let extractValue = await extract_value_circuit.compute({ step_in: json_extract_key3.step_out, data: json_key3_mask }, ["step_out"]); - console.log("finalValue", extractValue.step_out); - assert.deepEqual(extractValue.step_out, final_value_hash); - }); -}); \ No newline at end of file diff --git a/circuits/test/http/interpreter.test.ts b/circuits/test/http/interpreter.test.ts deleted file mode 100644 index 5bc6968..0000000 --- a/circuits/test/http/interpreter.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { circomkit, WitnessTester, generateDescription, toByte } from "../common"; -import { readHTTPInputFile } from "../common/http"; - -describe("HTTP :: Interpreter", async () => { - describe("MethodMatch", async () => { - let circuit: WitnessTester<["data", "method", "index"], []>; - - function generatePassCase(input: number[], method: number[], index: number, desc: string) { - const description = generateDescription(input); - - it(`(valid) witness: ${description} ${desc}`, async () => { - circuit = await circomkit.WitnessTester(`LockRequestLineData`, { - file: "http/interpreter", - template: "MethodMatch", - params: [input.length, method.length], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - - await circuit.expectPass({ data: input, method: method, index: index }, {}); - }); - } - - function generateFailCase(input: number[], method: number[], index: number, desc: string) { - const description = generateDescription(input); - - it(`(invalid) witness: ${description} ${desc}`, async () => { - circuit = await circomkit.WitnessTester(`LockRequestLineData`, { - file: "http/interpreter", - template: "MethodMatch", - params: [input.length, method.length], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - - await circuit.expectFail({ data: input, method: method, index: index }); - }); - } - - let parsedHttp = readHTTPInputFile("get_request.http"); - generatePassCase(parsedHttp.input, toByte("GET"), 0, ""); - generateFailCase(parsedHttp.input, toByte("POST"), 0, ""); - }); -}); - -// 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("HeaderFieldNameValueMatchPadded", async () => { - let circuit: WitnessTester<["data", "headerName", "nameLen", "headerValue", "valueLen", "index"], ["out"]>; - - let DATA_BYTES = 320; - let MAX_NAME_LENGTH = 20; - let MAX_VALUE_LENGTH = 35; - - before(async () => { - circuit = await circomkit.WitnessTester(`HeaderFieldNameValueMatchPadded`, { - file: "http/interpreter", - template: "HeaderFieldNameValueMatchPadded", - params: [DATA_BYTES, MAX_NAME_LENGTH, MAX_VALUE_LENGTH], - }); - }); - - function generatePassCase(input: any, expected: any, desc: string) { - const description = generateDescription(input); - input["headerName"] = input["headerName"].concat(Array(MAX_NAME_LENGTH - input["headerName"].length).fill(0)); - input["headerValue"] = input["headerValue"].concat(Array(MAX_VALUE_LENGTH - input["headerValue"].length).fill(0)); - - it(`(valid) witness: ${desc}`, async () => { - // console.log(JSON.stringify(await circuit.compute(input, ["step_out"]))) - await circuit.expectPass(input, expected); - }); - - } - - let header_name = toByte("content-type"); - let header_value = toByte("application/json; charset=utf-8"); - - let input = { - data: http_response_plaintext, - headerName: header_name, - nameLen: header_name.length, - headerValue: header_value, - valueLen: header_value.length, - index: 17, - } - generatePassCase(input, { out: 1 }, "header name and value matches"); - - let input2 = { - data: http_response_plaintext, - headerName: header_name, - nameLen: header_name.length, - headerValue: header_value, - valueLen: header_value.length, - index: 16, - } - generatePassCase(input2, { out: 0 }, "incorrect index"); -}); \ No newline at end of file diff --git a/circuits/test/http/locker.test.ts b/circuits/test/http/locker.test.ts deleted file mode 100644 index 34f32be..0000000 --- a/circuits/test/http/locker.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -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 diff --git a/circuits/test/http/nivc/body_mask.test.ts b/circuits/test/http/nivc/body_mask.test.ts deleted file mode 100644 index 04067cb..0000000 --- a/circuits/test/http/nivc/body_mask.test.ts +++ /dev/null @@ -1,127 +0,0 @@ -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 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]; - -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, - 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 lengthDiff = http_response_plaintext.length - http_body.length; - -// Create an array of zeros with the length difference -const padding = new Array(lengthDiff).fill(0); - -// Concatenate the padding with http_body -const padded_http_body = [...padding, ...http_body]; - -const http_response_hash = DataHasher(http_response_plaintext); -const http_body_mask_hash = DataHasher(padded_http_body); - -describe("NIVC_HTTP", async () => { - 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"]>; - - const DATA_BYTES = 320; - const TOTAL_BYTES_ACROSS_NIVC = 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 () => { - 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:", 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:", await lockHeaderCircuit.getConstraintCount()); - - bodyMaskCircuit = await circomkit.WitnessTester(`BodyMask`, { - file: "http/nivc/body_mask", - template: "HTTPMaskBodyNIVC", - params: [DATA_BYTES], - }); - 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: http_response_hash, data: http_response_plaintext, 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, data: http_response_plaintext, header: headerNamePadded, headerNameLength: headerName.length, value: headerValuePadded, headerValueLength: headerValue.length }, ["step_out"]); - let bodyMask = await bodyMaskCircuit.compute({ step_in: lockHeader.step_out, data: http_response_plaintext }, ["step_out"]); - console.log("GOT TRHOUGH THIRD CIRCUIT"); - - assert.deepEqual(bodyMask.step_out, http_body_mask_hash); - }); -}); \ No newline at end of file diff --git a/circuits/test/http/nivc/lock_header.test.ts b/circuits/test/http/nivc/lock_header.test.ts deleted file mode 100644 index 14cc914..0000000 --- a/circuits/test/http/nivc/lock_header.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { circomkit, WitnessTester, toByte } from "../../common"; -import { readHTTPInputFile } from "../../common/http"; -import { DataHasher } from "../../common/poseidon"; - -describe("HTTPLockHeader", async () => { - 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"]>; - - const DATA_BYTES = 336; - - const MAX_BEGINNING_LENGTH = 10; - const MAX_MIDDLE_LENGTH = 50; - const MAX_FINAL_LENGTH = 10; - const MAX_HEADER_NAME_LENGTH = 20; - const MAX_HEADER_VALUE_LENGTH = 35; - - before(async () => { - 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:", 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:", await lockHeaderCircuit.getConstraintCount()); - }); - - function generatePassCase(input: number[], beginning: number[], middle: number[], final: number[], headerName: number[], headerValue: number[], desc: string) { - it(`should pass: \"${headerName}: ${headerValue}\", ${desc}`, async () => { - let extendedInput = input.concat(Array(Math.max(0, DATA_BYTES - input.length)).fill(0)); - const http_response_hash = DataHasher(extendedInput); - - 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)); - - 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 parseAndLockStartLine = await httpParseAndLockStartLineCircuit.compute({ step_in: http_response_hash, data: extendedInput, beginning: beginningPadded, beginning_length: beginning.length, middle: middlePadded, middle_length: middle.length, final: finalPadded, final_length: final.length }, ["step_out"]); - - await lockHeaderCircuit.expectPass({ step_in: parseAndLockStartLine.step_out, data: extendedInput, header: headerNamePadded, headerNameLength: headerName.length, value: headerValuePadded, headerValueLength: headerValue.length }); - }); - } - - function generateFailCase(input: number[], beginning: number[], middle: number[], final: number[], headerName: number[], headerValue: number[], desc: string) { - it(`should fail: ${desc}`, async () => { - let extendedInput = input.concat(Array(Math.max(0, DATA_BYTES - input.length)).fill(0)); - const http_response_hash = DataHasher(extendedInput); - - 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)); - - 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 parseAndLockStartLine = await httpParseAndLockStartLineCircuit.compute({ step_in: http_response_hash, data: extendedInput, beginning: beginningPadded, beginning_length: beginning.length, middle: middlePadded, middle_length: middle.length, final: finalPadded, final_length: final.length }, ["step_out"]); - - await lockHeaderCircuit.expectFail({ step_in: parseAndLockStartLine.step_out, data: extendedInput, header: headerNamePadded, headerNameLength: headerName.length, value: headerValuePadded, headerValueLength: headerValue.length }); - }); - } - - describe("request", async () => { - let { input, headers } = readHTTPInputFile("post_request.http"); - - let beginning = toByte("POST"); - let middle = toByte("/contact_form.php"); - let final = toByte("HTTP/1.1"); - - let headerName = toByte("Host"); - let headerValue = toByte("developer.mozilla.org"); - - for (const [key, value] of Object.entries(headers)) { - generatePassCase(input, beginning, middle, final, toByte(key), toByte(value), "request"); - } - let incorrectHeaderValue = toByte("application/json"); - generateFailCase(input, beginning, middle, final, headerName, incorrectHeaderValue, "incorrect header value"); - }); - - describe("response", async () => { - let { input, headers } = readHTTPInputFile("spotify_top_artists_response.http"); - let beginning = toByte("HTTP/1.1"); - let middle = toByte("200"); - let final = toByte("OK"); - - for (const [key, value] of Object.entries(headers)) { - generatePassCase(input, beginning, middle, final, toByte(key), toByte(value), "response"); - } - - let headerName = toByte("content-encoding"); - let invalidHeaderValue = toByte("chunked"); - generateFailCase(input, beginning, middle, final, headerName, invalidHeaderValue, "should fail: invalid header value"); - }); -}); \ No newline at end of file diff --git a/circuits/test/http/nivc/parse_and_lock_start_line.test.ts b/circuits/test/http/nivc/parse_and_lock_start_line.test.ts deleted file mode 100644 index 202650c..0000000 --- a/circuits/test/http/nivc/parse_and_lock_start_line.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { circomkit, WitnessTester, toByte } from "../../common"; -import { readHTTPInputFile } from "../../common/http"; -import { DataHasher } from "../../common/poseidon"; - -describe("HTTPParseAndLockStartLine", async () => { - let httpParseAndLockStartLineCircuit: WitnessTester<["step_in", "data", "beginning", "beginning_length", "middle", "middle_length", "final", "final_length"], ["step_out"]>; - - const DATA_BYTES = 336; - - const MAX_BEGINNING_LENGTH = 10; - const MAX_MIDDLE_LENGTH = 50; - const MAX_FINAL_LENGTH = 10; - - before(async () => { - 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:", await httpParseAndLockStartLineCircuit.getConstraintCount()); - }); - - function generatePassCase(input: number[], beginning: number[], middle: number[], final: number[], desc: string) { - it(`(valid) witness: ${desc}`, async () => { - let extendedInput = input.concat(Array(Math.max(0, DATA_BYTES - input.length)).fill(0)); - const http_response_hash = DataHasher(extendedInput); - - 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)); - - await httpParseAndLockStartLineCircuit.expectPass({ step_in: [http_response_hash], data: extendedInput, beginning: beginningPadded, beginning_length: beginning.length, middle: middlePadded, middle_length: middle.length, final: finalPadded, final_length: final.length }); - }); - } - - function generateFailCase(input: number[], beginning: number[], middle: number[], final: number[], desc: string) { - it(`(valid) witness: ${desc}`, async () => { - let extendedInput = input.concat(Array(Math.max(0, DATA_BYTES - input.length)).fill(0)); - const http_response_hash = DataHasher(extendedInput); - - - 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)); - - await httpParseAndLockStartLineCircuit.expectFail({ step_in: [http_response_hash], data: extendedInput, beginning: beginningPadded, beginning_length: beginning.length, middle: middlePadded, middle_length: middle.length, final: finalPadded, final_length: final.length }); - }); - } - - describe("request", async () => { - let { input, } = readHTTPInputFile("spotify_top_artists_request.http"); - - let beginning = toByte("GET"); - let middle = toByte("/v1/me/top/artists?time_range=medium_term&limit=1"); - let final = toByte("HTTP/1.1"); - - generatePassCase(input, beginning, middle, final, "should pass request"); - - let incorrectBeginning = toByte("DELETE"); - generateFailCase(input, incorrectBeginning, middle, final, "should fail: incorrect BEGINNING"); - - let incorrectMiddle = toByte("/contact_form.php"); - generateFailCase(input, beginning, incorrectMiddle, final, "should fail: incorrect MIDDLE"); - - let incorrectFinal = toByte("HTTP/2"); - generateFailCase(input, beginning, middle, incorrectFinal, "should fail: incorrect FINAL"); - }) - - describe("response", async () => { - let { input, } = readHTTPInputFile("spotify_top_artists_response.http"); - let beginning = toByte("HTTP/1.1"); - let middle = toByte("200"); - let final = toByte("OK"); - - generatePassCase(input, beginning, middle, final, "should pass response"); it - - let incorrectBeginning = toByte("HTTP/2"); - generateFailCase(input, incorrectBeginning, middle, final, "should fail: incorrect BEGINNING"); - - let incorrectMiddle = toByte("2000"); - generateFailCase(input, beginning, incorrectMiddle, final, "should fail: incorrect MIDDLE"); - - let incorrectFinal = toByte("INVALID"); - generateFailCase(input, beginning, middle, incorrectFinal, "should fail: incorrect FINAL"); - }); -}); \ No newline at end of file diff --git a/circuits/test/http/nivc/http_nivc.test.ts b/circuits/test/http/verification.test.ts similarity index 96% rename from circuits/test/http/nivc/http_nivc.test.ts rename to circuits/test/http/verification.test.ts index 295c787..065ab51 100644 --- a/circuits/test/http/nivc/http_nivc.test.ts +++ b/circuits/test/http/verification.test.ts @@ -1,6 +1,6 @@ -import { circomkit, WitnessTester, toByte } from "../../common"; +import { circomkit, WitnessTester, toByte } from "../common"; import { assert } from "chai"; -import { DataHasher } from "../../common/poseidon"; +import { DataHasher } from "../common/poseidon"; // HTTP/1.1 200 OK // content-type: application/json; charset=utf-8 @@ -97,9 +97,9 @@ const TEST_HTTP_BODY = [ ]; const DATA_BYTES = 320; -const TOTAL_BYTES_ACROSS_NIVC = 1; +const MAX_NUMBER_OF_HEADERS = 2; -describe("HTTP_NIVC", async () => { +describe("HTTP Verfication", async () => { let dataHasher: WitnessTester<["in"], ["out"]>; let httpNivc: WitnessTester<["step_in", "data", "start_line_hash", "header_hashes", "body_hash"], ["step_out"]>; before(async () => { @@ -110,9 +110,9 @@ describe("HTTP_NIVC", async () => { }); httpNivc = await circomkit.WitnessTester("http_nivc", { - file: "http/nivc/http_nivc", - template: "HttpNIVC", - params: [320, 2] + file: "http/verification", + template: "HTTPVerification", + params: [DATA_BYTES, MAX_NUMBER_OF_HEADERS] }); }); diff --git a/circuits/test/utils/array.test.ts b/circuits/test/utils/array.test.ts index e95512c..1202a47 100644 --- a/circuits/test/utils/array.test.ts +++ b/circuits/test/utils/array.test.ts @@ -1,476 +1,166 @@ import { circomkit, WitnessTester } from "../common"; describe("IsEqualArray", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`IsEqualArray`, { - file: "utils/array", - template: "IsEqualArray", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); + let circuit: WitnessTester<["in"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`IsEqualArray`, { + file: "utils/array", + template: "IsEqualArray", + params: [3], }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("witness: [[0,0,0],[0,0,0]]", async () => { - await circuit.expectPass( - { in: [[0, 0, 0], [0, 0, 0]] }, - { out: 1 } - ); - }); + it("witness: [[0,0,0],[0,0,0]]", async () => { + await circuit.expectPass( + { in: [[0, 0, 0], [0, 0, 0]] }, + { out: 1 } + ); + }); - it("witness: [[1,420,69],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[1, 420, 69], [1, 420, 69]] }, - { out: 1 }, - ); - }); + it("witness: [[1,420,69],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[1, 420, 69], [1, 420, 69]] }, + { out: 1 }, + ); + }); - it("witness: [[0,0,0],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[0, 0, 0], [1, 420, 69]] }, - { out: 0 }, - ); - }); + it("witness: [[0,0,0],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[0, 0, 0], [1, 420, 69]] }, + { out: 0 }, + ); + }); - it("witness: [[1,420,0],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[1, 420, 0], [1, 420, 69]] }, - { out: 0 }, - ); - }); + it("witness: [[1,420,0],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[1, 420, 0], [1, 420, 69]] }, + { out: 0 }, + ); + }); - it("witness: [[1,0,69],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[1, 0, 69], [1, 420, 69]] }, - { out: 0 }, - ); - }); + it("witness: [[1,0,69],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[1, 0, 69], [1, 420, 69]] }, + { out: 0 }, + ); + }); - it("witness: [[0,420,69],[1,420,69]]", async () => { - await circuit.expectPass( - { in: [[0, 420, 69], [1, 420, 69]] }, - { out: 0 }, - ); - }); + it("witness: [[0,420,69],[1,420,69]]", async () => { + await circuit.expectPass( + { in: [[0, 420, 69], [1, 420, 69]] }, + { out: 0 }, + ); + }); }); describe("Contains", () => { - let circuit: WitnessTester<["in", "array"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`Contains`, { - file: "utils/array", - template: "Contains", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); + let circuit: WitnessTester<["in", "array"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`Contains`, { + file: "utils/array", + template: "Contains", + params: [3], }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("witness: in = 0, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 0, array: [0, 1, 2] }, - { out: 1 } - ); - }); + it("witness: in = 0, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 0, array: [0, 1, 2] }, + { out: 1 } + ); + }); - it("witness: in = 1, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 1, array: [0, 1, 2] }, - { out: 1 } - ); - }); + it("witness: in = 1, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 1, array: [0, 1, 2] }, + { out: 1 } + ); + }); - it("witness: in = 2, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 2, array: [0, 1, 2] }, - { out: 1 } - ); - }); + it("witness: in = 2, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 2, array: [0, 1, 2] }, + { out: 1 } + ); + }); - it("witness: in = 42069, array = [0,1,2]", async () => { - await circuit.expectPass( - { in: 42069, array: [0, 1, 2] }, - { out: 0 } - ); - }); + it("witness: in = 42069, array = [0,1,2]", async () => { + await circuit.expectPass( + { in: 42069, array: [0, 1, 2] }, + { out: 0 } + ); + }); }); describe("ArrayAdd", () => { - let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`ArrayAdd`, { - file: "utils/array", - template: "ArrayAdd", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); + let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ArrayAdd`, { + file: "utils/array", + template: "ArrayAdd", + params: [3], }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("witness: lhs = [0,1,2], rhs = [3,5,7]", async () => { - await circuit.expectPass( - { lhs: [0, 1, 2], rhs: [3, 5, 7] }, - { out: [3, 6, 9] } - ); - }); + it("witness: lhs = [0,1,2], rhs = [3,5,7]", async () => { + await circuit.expectPass( + { lhs: [0, 1, 2], rhs: [3, 5, 7] }, + { out: [3, 6, 9] } + ); + }); }); describe("ArrayMul", () => { - let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`ArrayMul`, { - file: "utils/array", - template: "ArrayMul", - params: [3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); + let circuit: WitnessTester<["lhs", "rhs"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ArrayMul`, { + file: "utils/array", + template: "ArrayMul", + params: [3], }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); - it("witness: lhs = [0,1,2], rhs = [3,5,7]", async () => { - await circuit.expectPass( - { lhs: [0, 1, 2], rhs: [3, 5, 7] }, - { out: [0, 5, 14] } - ); - }); + it("witness: lhs = [0,1,2], rhs = [3,5,7]", async () => { + await circuit.expectPass( + { lhs: [0, 1, 2], rhs: [3, 5, 7] }, + { out: [0, 5, 14] } + ); + }); }); describe("GenericArrayAdd", () => { - let circuit: WitnessTester<["arrays"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`ArrayAdd`, { - file: "utils/array", - template: "GenericArrayAdd", - params: [3, 2], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: arrays = [[0,1,2],[3,5,7]]", async () => { - await circuit.expectPass( - { arrays: [[0, 1, 2], [3, 5, 7]] }, - { out: [3, 6, 9] } - ); - }); - -}); - -describe("array_builder", () => { - it("test array builder", async () => { - let circuit: WitnessTester<["array_to_write_to", "array_to_write_at_index", "index"], ["out"]>; - circuit = await circomkit.WitnessTester(`ArrayBuilder`, { - file: "utils/array", - template: "WriteToIndex", - params: [160, 16], - }); - - let array_to_write_to = new Array(160).fill(0x00); - let array_to_write_at_index = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]; - let expected = array_to_write_at_index.concat(new Array(160 - array_to_write_at_index.length).fill(0x00)); - let index = 0; - - await circuit.expectPass( - { - array_to_write_to: array_to_write_to, - array_to_write_at_index: array_to_write_at_index, - index: index - }, - { - out: expected - } - ); - }); - it("test array builder", async () => { - let circuit: WitnessTester<["array_to_write_to", "array_to_write_at_index", "index"], ["out"]>; - circuit = await circomkit.WitnessTester(`ArrayBuilder`, { - file: "utils/array", - template: "WriteToIndex", - params: [160, 16], - }); - - let array_to_write_to = new Array(160).fill(0x00); - let array_to_write_at_index = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]; - let expected = [0x00].concat(array_to_write_at_index).concat(new Array(160 - array_to_write_at_index.length - 1).fill(0x00)); - let index = 1; - - await circuit.expectPass( - { - array_to_write_to: array_to_write_to, - array_to_write_at_index: array_to_write_at_index, - index: index - }, - { - out: expected - } - ); - }); - it("test array builder", async () => { - let circuit: WitnessTester<["array_to_write_to", "array_to_write_at_index", "index"], ["out"]>; - circuit = await circomkit.WitnessTester(`ArrayBuilder`, { - file: "utils/array", - template: "WriteToIndex", - params: [160, 16], - }); - - let array_to_write_to = new Array(160).fill(0x00); - let array_to_write_at_index = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]; - let expected = [0x00, 0x00].concat(array_to_write_at_index).concat(new Array(160 - array_to_write_at_index.length - 2).fill(0x00)); - let index = 2; - - await circuit.expectPass( - { - array_to_write_to: array_to_write_to, - array_to_write_at_index: array_to_write_at_index, - index: index - }, - { - out: expected - } - ); - }); - it("test array builder with index = n", async () => { - let circuit: WitnessTester<["array_to_write_to", "array_to_write_at_index", "index"], ["out"]>; - circuit = await circomkit.WitnessTester(`ArrayBuilder`, { - file: "utils/array", - template: "WriteToIndex", - params: [37, 16], - }); - - let array_to_write_to = new Array(37).fill(0x00); - let array_to_write_at_index = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]; - let expected = new Array(16).fill(0x00).concat(array_to_write_at_index).concat(new Array(37 - array_to_write_at_index.length - 16).fill(0x00)); - let index = 16; - - await circuit.expectPass( - { - array_to_write_to: array_to_write_to, - array_to_write_at_index: array_to_write_at_index, - index: index - }, - { - out: expected - } - ); - }); - - it("test array builder with index > n", async () => { - let circuit: WitnessTester<["array_to_write_to", "array_to_write_at_index", "index"], ["out"]>; - circuit = await circomkit.WitnessTester(`ArrayBuilder`, { - file: "utils/array", - template: "WriteToIndex", - params: [37, 4], - }); - - let array_to_write_to = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x88, 0xDA, 0xCE, 0x60, 0xB6, 0xA3, 0x92, 0xF3, 0x28, 0xC2, 0xB9, 0x71, 0xB2, 0xFE, 0x78, - 0x00, 0x00, 0x00, 0x00, 0x00 - ]; - let array_to_write_at_index = [0x00, 0x00, 0x00, 0x01]; - let expected = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x88, 0xDA, 0xCE, 0x60, 0xB6, 0xA3, 0x92, 0xF3, 0x28, 0xC2, 0xB9, 0x71, 0xB2, 0xFE, 0x78, - 0x00, 0x00, 0x00, 0x01, 0x00 - ]; - let index = 32; - - await circuit.expectPass( - { - array_to_write_to: array_to_write_to, - array_to_write_at_index: array_to_write_at_index, - index: index - }, - { - out: expected - } - ); - }); -}); - -describe("ToBlocks", () => { - let circuit: WitnessTester<["stream"], ["blocks"]>; - it("should convert stream to block", async () => { - circuit = await circomkit.WitnessTester(`ToBlocks`, { - file: "utils/array", - template: "ToBlocks", - params: [16], - }); - await circuit.expectPass( - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x34], - }, - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x34], - ], - ], - } - ); - }); - it("should pad 1 in block", async () => { - circuit = await circomkit.WitnessTester(`ToBlocks`, { - file: "utils/array", - template: "ToBlocks", - params: [15], - }); - await circuit.expectPass( - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2], - }, - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x01], - ], - ], - } - ); - }); - it("should pad 0's in block", async () => { - circuit = await circomkit.WitnessTester(`ToBlocks`, { - file: "utils/array", - template: "ToBlocks", - params: [14], - }); - await circuit.expectPass( - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d], - }, - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0x01], - [0xe0, 0x37, 0x07, 0x00], - ], - ], - } - ); - }); - it("should generate enough blocks", async () => { - circuit = await circomkit.WitnessTester(`ToBlocks`, { - file: "utils/array", - template: "ToBlocks", - params: [17], - }); - await circuit.expectPass( - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x42, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x34, 0x12], - }, - { - blocks: [ - [ - [0x32, 0x42, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x34], - ], - [ - [0x12, 0x00, 0x00, 0x00], - [0x01, 0x00, 0x00, 0x00], - [0x00, 0x00, 0x00, 0x00], - [0x00, 0x00, 0x00, 0x00], - ], - ], - } - ); + let circuit: WitnessTester<["arrays"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ArrayAdd`, { + file: "utils/array", + template: "GenericArrayAdd", + params: [3, 2], }); + console.log("#constraints:", await circuit.getConstraintCount()); }); - - - describe("ToStream", () => { - let circuit: WitnessTester<["blocks"], ["stream"]>; - it("should convert blocks to stream#1", async () => { - circuit = await circomkit.WitnessTester(`ToStream`, { - file: "utils/array", - template: "ToStream", - params: [1, 16], - }); - await circuit.expectPass( - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x34], - ], - ], - }, - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x34], - } - ); - }); - it("should convert blocks to stream#2", async () => { - circuit = await circomkit.WitnessTester(`ToStream`, { - file: "utils/array", - template: "ToStream", - params: [1, 15], - }); - await circuit.expectPass( - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x01], - ], - ], - }, - { - stream: [0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2], - } - ); - }); - it("should convert multiple blocks to stream", async () => { - circuit = await circomkit.WitnessTester(`ToStream`, { - file: "utils/array", - template: "ToStream", - params: [2, 18], - }); - await circuit.expectPass( - { - blocks: [ - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x01], - ], - [ - [0x32, 0x43, 0xf6, 0xa8], - [0x88, 0x5a, 0x30, 0x8d], - [0x31, 0x31, 0x98, 0xa2], - [0xe0, 0x37, 0x07, 0x01], - ], - ], - }, - { - stream: [ - 0x32, 0x88, 0x31, 0xe0, 0x43, 0x5a, 0x31, 0x37, 0xf6, 0x30, 0x98, 0x07, 0xa8, 0x8d, 0xa2, 0x01, 0x32, 0x88, - ], - } - ); - }); + + it("witness: arrays = [[0,1,2],[3,5,7]]", async () => { + await circuit.expectPass( + { arrays: [[0, 1, 2], [3, 5, 7]] }, + { out: [3, 6, 9] } + ); }); +}); describe("fromLittleEndianToWords32", () => { let circuit: WitnessTester<["data"], ["words"]>; - it("fromLittleEndianToWords32", async () => { - circuit = await circomkit.WitnessTester(`fromLittleEndianToWords32`, { - file: "utils/array", - template: "fromLittleEndianToWords32", - }); + it("fromLittleEndianToWords32", async () => { + circuit = await circomkit.WitnessTester(`fromLittleEndianToWords32`, { + file: "utils/array", + template: "fromLittleEndianToWords32", + }); console.log("#constraints:", await circuit.getConstraintCount()); let input = [ @@ -479,26 +169,28 @@ describe("fromLittleEndianToWords32", () => { 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0 ]; - await circuit.expectPass({data: input}, {words: [72, 84, 84, 80]}) + await circuit.expectPass({ data: input }, { words: [72, 84, 84, 80] }) }); }); describe("fromWords32ToLittleEndian", () => { let circuit: WitnessTester<["words"], ["data"]>; - it("fromWords32ToLittleEndian", async () => { - circuit = await circomkit.WitnessTester(`fromWords32ToLittleEndian`, { - file: "utils/array", - template: "fromWords32ToLittleEndian", - }); + it("fromWords32ToLittleEndian", async () => { + circuit = await circomkit.WitnessTester(`fromWords32ToLittleEndian`, { + file: "utils/array", + template: "fromWords32ToLittleEndian", + }); console.log("#constraints:", await circuit.getConstraintCount()); let input = [72, 84, 84, 80]; - await circuit.expectPass({words: input}, {data: [ - 0, 1, 0, 1, 0, 0, 0, 0, 0, - 1, 0, 1, 0, 1, 0, 0, 0, 1, - 0, 1, 0, 1, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0 - ]}) + await circuit.expectPass({ words: input }, { + data: [ + 0, 1, 0, 1, 0, 0, 0, 0, 0, + 1, 0, 1, 0, 1, 0, 0, 0, 1, + 0, 1, 0, 1, 0, 0, 0, 1, 0, + 0, 1, 0, 0, 0 + ] + }) }); }); diff --git a/circuits/test/utils/bits.test.ts b/circuits/test/utils/bits.test.ts new file mode 100644 index 0000000..30dfdc4 --- /dev/null +++ b/circuits/test/utils/bits.test.ts @@ -0,0 +1,25 @@ +import { circomkit, WitnessTester } from "../common"; + +describe("ASCII", () => { + let circuit: WitnessTester<["in"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`ASCII`, { + file: "utils/bits", + template: "ASCII", + params: [13], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("(valid) witness: in = b\"Hello, world!\"", async () => { + await circuit.expectPass( + { in: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] }, + ); + }); + + it("(invalid) witness: in = [256, ...]", async () => { + await circuit.expectFail( + { in: [256, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] } + ); + }); +}); \ No newline at end of file diff --git a/circuits/test/utils/bytes.test.ts b/circuits/test/utils/bytes.test.ts deleted file mode 100644 index dfb11db..0000000 --- a/circuits/test/utils/bytes.test.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { circomkit, WitnessTester } from "../common"; - -describe("ASCII", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`ASCII`, { - file: "utils/bytes", - template: "ASCII", - params: [13], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("(valid) witness: in = b\"Hello, world!\"", async () => { - await circuit.expectPass( - { in: [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] }, - ); - }); - - it("(invalid) witness: in = [256, ...]", async () => { - await circuit.expectFail( - { in: [256, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33] } - ); - }); -}); - -describe("BytePack", () => { - let circuit: WitnessTester<["lower", "upper"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`DoubleBytePackArray`, { - file: "utils/bytes", - template: "DoubleBytePackArray", - params: [1], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: lower = 0, upper = 1", async () => { - await circuit.expectPass( - { lower: [0], upper: [1] }, { out: [256] } - ); - }); - - it("witness: lower = 1, upper = 1", async () => { - await circuit.expectPass( - { lower: [1], upper: [1] }, { out: [257] } - ); - }); - - it("witness: lower = 1, upper = 0", async () => { - await circuit.expectPass( - { lower: [1], upper: [0] }, { out: [1] } - ); - }); -}); - -describe("BytePack2", () => { - let circuit: WitnessTester<["lower", "upper"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`DoubleBytePackArray`, { - file: "utils/bytes", - template: "DoubleBytePackArray", - params: [2], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - it("witness: lower = [1,0], upper = [0,1]", async () => { - await circuit.expectPass( - { lower: [1, 0], upper: [0, 1] }, { out: [1, 256] } - ); - }); -}); - -describe("ByteUnpack", () => { - let circuit: WitnessTester<["in"], ["lower", "upper"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`UnpackDoubleByteArray`, { - file: "utils/bytes", - template: "UnpackDoubleByteArray", - params: [1], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: in = 256", async () => { - await circuit.expectPass( - { in: [256] }, { lower: [0], upper: [1] } - ); - }); - - it("witness: in = 257", async () => { - await circuit.expectPass( - { in: [257] }, { lower: [1], upper: [1] } - ); - }); - - it("witness: in = 1", async () => { - await circuit.expectPass( - { in: [1] }, { lower: [1], upper: [] } - ); - }); -}); - -describe("ByteUnpack2", () => { - let circuit: WitnessTester<["in"], ["lower", "upper"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`UnpackDoubleByteArray`, { - file: "utils/bytes", - template: "UnpackDoubleByteArray", - params: [2], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: in = [1,256]", async () => { - await circuit.expectPass( - { in: [1, 256] }, { lower: [1, 0], upper: [0, 1] } - ); - }); - -}); - - - -// Generic version -describe("GenericBytePack2", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`GenericBytePackArray`, { - file: "utils/bytes", - template: "GenericBytePackArray", - params: [2, 3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - it("witness: lower = [1,0,0], upper = [0,1,0]", async () => { - await circuit.expectPass( - { in: [[1, 0, 0], [0, 1, 0]] }, { out: [1, 256] } - ); - }); - it("witness: lower = [1,0,0], upper = [0,0,1]", async () => { - await circuit.expectPass( - { in: [[1, 0, 0], [0, 0, 1]] }, { out: [1, 65536] } - ); - }); -}); - -describe("GenericByteUnpack2", () => { - let circuit: WitnessTester<["in"], ["out"]>; - before(async () => { - circuit = await circomkit.WitnessTester(`GenericByteUnpackArray`, { - file: "utils/bytes", - template: "GenericByteUnpackArray", - params: [2, 3], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("witness: in = [1,256]", async () => { - await circuit.expectPass( - { in: [1, 256] }, { out: [[1, 0, 0], [0, 1, 0]] } - ); - }); - - it("witness: in = [1,256]", async () => { - await circuit.expectPass( - { in: [1, 65536] }, { out: [[1, 0, 0], [0, 0, 1]] } - ); - }); -}); diff --git a/circuits/test/utils/hash.test.ts b/circuits/test/utils/hash.test.ts index 9d7ac37..c62b969 100644 --- a/circuits/test/utils/hash.test.ts +++ b/circuits/test/utils/hash.test.ts @@ -82,13 +82,26 @@ describe("hash", () => { console.log("#constraints:", await circuit.getConstraintCount()); }); + let all_zero_hash = BigInt("14744269619966411208579211824598458697587494354926760081771325075741142829156"); it("witness: in = [0,...x16]", async () => { const input = Array(16).fill(0); + await circuit.expectPass( + { in: input }, + { out: all_zero_hash } + ); + }); + // Check that TS version of DataHasher also is correct + assert.deepEqual(DataHasher(Array(16).fill(0)), all_zero_hash); + + it("witness: in = [-1,...x16]", async () => { + const input = Array(16).fill(-1); await circuit.expectPass( { in: input }, { out: 0 } ); }); + // Check that TS version of DataHasher also is correct + assert.deepEqual(DataHasher(Array(16).fill(-1)), 0); it("witness: in = [1,0,...x15]", async () => { let input = Array(16).fill(0); @@ -100,6 +113,7 @@ describe("hash", () => { ); }); + it("witness: in = [0,0,...x15,1]", async () => { let input = Array(16).fill(0); input[15] = 1; @@ -128,19 +142,8 @@ describe("hash", () => { 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 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_start_line = [72, 84, 84, 80, 47, 49, 46, 49, 32, 50, 48, 48, 32, 79, 75, 13, 10]; + const padded_http_start_line = http_start_line.concat(Array(320 - http_start_line.length).fill(-1)); describe("DataHasherHTTP", () => { let circuit: WitnessTester<["in"], ["out"]>; @@ -162,19 +165,20 @@ describe("hash", () => { console.log("#constraints:", await circuit.getConstraintCount()); }); - it("witness: TEST HTTP BYTES", async () => { + it("witness: HTTP bytes", async () => { let hash = DataHasher(TEST_HTTP_BYTES); assert.deepEqual(String(hash), "2195365663909569734943279727560535141179588918483111718403427949138562480675"); await circuit.expectPass({ in: TEST_HTTP_BYTES }, { out: "2195365663909569734943279727560535141179588918483111718403427949138562480675" }); }); - let hash = DataHasher(http_start_line); - it("witness: TEST HTTP START LINE MASK", async () => { - await circuit.expectPass({ in: http_start_line }, { out: hash }); + let padded_hash = DataHasher(padded_http_start_line); + it("witness: padded HTTP start line", async () => { + await circuit.expectPass({ in: padded_http_start_line }, { out: padded_hash }); }); - it("witness: TEST HTTP START LINE MASK TRUNCATED", async () => { - await circuit_small.expectPass({ in: http_start_line.slice(0, 32) }, { out: hash }); + let hash = DataHasher(http_start_line); + it("witness: unpadded HTTP start line", async () => { + await circuit_small.expectPass({ in: http_start_line.concat(Array(32 - http_start_line.length).fill(-1)) }, { out: hash }); }); }); }); diff --git a/circuits/test/utils/search.test.ts b/circuits/test/utils/search.test.ts index f172fa1..d05b948 100644 --- a/circuits/test/utils/search.test.ts +++ b/circuits/test/utils/search.test.ts @@ -1,228 +1,39 @@ -import { circomkit, WitnessTester } from "../common"; +import { circomkit, toByte, WitnessTester } from "../common"; -import witness from "../../../inputs/search/witness.json"; -import { PoseidonModular } from "../common/poseidon"; +const data = toByte("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum"); +const key = toByte("Ipsum"); -describe("search", () => { - describe("SubstringSearch", () => { - let circuit: WitnessTester<["data", "key", "random_num"], ["position"]>; +describe("SubstringMatchWithIndex", () => { + let circuit: WitnessTester<["data", "key", "start"], ["out"]>; - it("key at first position", async () => { - const data = [10, 8, 9, 4, 11, 9, 1, 2]; - const key = [10, 8, 9, 4]; - const concatenatedInput = key.concat(data); - const hashResult = PoseidonModular(concatenatedInput); - - circuit = await circomkit.WitnessTester(`SubstringSearch`, { - file: "utils/search", - template: "SubstringSearch", - params: [data.length, key.length], - }); - - await circuit.expectPass( - { data: data, key: key, random_num: hashResult }, - { position: 0 }, - ); - }); - - it("key at last position", async () => { - const data = [11, 9, 1, 2, 10, 8, 9, 4]; - const key = [10, 8, 9, 4]; - const concatenatedInput = key.concat(data); - const hashResult = PoseidonModular(concatenatedInput); - - circuit = await circomkit.WitnessTester(`SubstringSearch`, { - file: "utils/search", - template: "SubstringSearch", - params: [data.length, key.length], - }); - - await circuit.expectPass( - { data: data, key: key, random_num: hashResult }, - { position: 4 }, - ); - }); - - /// highlights the importance of appropriate calculation of random number for linear matching. - /// `1` as used here leads to passing constraints because [1, 0] matches with [0, 1] - /// because both have equal linear combination sum. - it("(INVALID `r=1` value) random_num input passes for different position, correct key position: 2", async () => { - const data = [0, 0, 1, 0, 0]; - const key = [1, 0]; - - circuit = await circomkit.WitnessTester(`SubstringSearch`, { - file: "utils/search", - template: "SubstringSearch", - params: [data.length, key.length], - }); - - await circuit.expectPass( - { data: data, key: key, random_num: 1 }, - { position: 1 }, - ); - }); - - it("data = witness.json:data, key = witness.json:key, r = hash(data+key)", async () => { - const concatenatedInput = witness["key"].concat(witness["data"]); - const hashResult = PoseidonModular(concatenatedInput); - - circuit = await circomkit.WitnessTester(`SubstringSearch`, { - file: "utils/search", - template: "SubstringSearch", - params: [witness["data"].length, witness["key"].length], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - - await circuit.expectPass( - { data: witness["data"], key: witness["key"], random_num: hashResult }, - { position: 6 } - ); - }); - }); - - describe("SubstringMatchWithHasher", () => { - let circuit: WitnessTester<["data", "key", "r", "start"], ["out"]>; - - before(async () => { - circuit = await circomkit.WitnessTester(`SubstringSearch`, { - file: "utils/search", - template: "SubstringMatchWithHasher", - params: [787, 10], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("data = witness.json:data, key = witness.json:key, r = hash(key+data)", async () => { - await circuit.expectPass( - { - data: witness["data"], - key: witness["key"], - r: PoseidonModular(witness["key"].concat(witness["data"])), - start: 6 - }, - { out: 1 }, - ); - }); - - it("data = witness.json:data, key = witness.json:key, r = hash(key+data), output false", async () => { - await circuit.expectPass( - { - data: witness["data"], - key: witness["key"], - r: PoseidonModular(witness["key"].concat(witness["data"])), - start: 98 - }, - { out: 0 } - ); + before(async () => { + circuit = await circomkit.WitnessTester(`SubstringSearch`, { + file: "utils/search", + template: "SubstringMatchWithIndex", + params: [data.length, key.length], }); + console.log("#constraints:", await circuit.getConstraintCount()); }); - describe("SubstringMatchWithIndex", () => { - let circuit: WitnessTester<["data", "key", "start"], ["out"]>; - - before(async () => { - circuit = await circomkit.WitnessTester(`SubstringSearch`, { - file: "utils/search", - template: "SubstringMatchWithIndex", - params: [787, 10], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("data = witness.json:data, key = witness.json:key, r = hash(key+data)", async () => { - await circuit.expectPass( - { - data: witness["data"], - key: witness["key"], - start: 6 - }, - { out: 1 }, - ); - }); - - it("data = witness.json:data, key = witness.json:key, r = hash(key+data), output false", async () => { - await circuit.expectPass( - { - data: witness["data"], - key: witness["key"], - start: 98 - }, - { out: 0 } - ); - }); - }); - - describe("SubstringMatchWithIndexPadded", () => { - let circuit: WitnessTester<["data", "key", "keyLen", "start"], ["out"]>; - let maxKeyLen = 30; - - before(async () => { - circuit = await circomkit.WitnessTester(`SubstringSearch`, { - file: "utils/search", - template: "SubstringMatchWithIndexPadded", - params: [787, maxKeyLen], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("data = witness.json:data, key = witness.json:key, r = hash(key+data)", async () => { - let key = witness["key"]; - let pad_key = key.concat(Array(maxKeyLen - key.length).fill(0)); - await circuit.expectPass( - { - data: witness["data"], - key: pad_key, - keyLen: witness["key"].length, - start: 6 - }, - { out: 1 }, - ); - }); - - it("data = witness.json:data, key = witness.json:key, r = hash(key+data), output false", async () => { - let key = witness["key"]; - let pad_key = key.concat(Array(maxKeyLen - key.length).fill(0)); - await circuit.expectPass( - { - data: witness["data"], - key: pad_key, - keyLen: witness["key"].length, - start: 98 - }, - { out: 0 } - ); - }); + it("data = witness.json:data, key = witness.json:key, r = hash(key+data)", async () => { + await circuit.expectPass( + { + data: data, + key: key, + start: 6 + }, + { out: 1 }, + ); }); - describe("SubstringMatch", () => { - let circuit: WitnessTester<["data", "key"], ["position"]>; - - before(async () => { - circuit = await circomkit.WitnessTester(`SubstringSearch`, { - file: "utils/search", - template: "SubstringMatch", - params: [787, 10], - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("data = witness.json:data, key = witness.json:key", async () => { - await circuit.expectPass( - { data: witness["data"], key: witness["key"] }, - { position: 6 }, - ); - }); - - it("data = witness.json:data, key = invalid key byte", async () => { - await circuit.expectFail( - { data: witness["data"], key: witness["key"].concat(257) }, - ); - }); - - it("data = witness.json:data, key = wrong key", async () => { - await circuit.expectFail( - { data: witness["data"], key: witness["key"].concat(0) }, - ); - }); + it("data = witness.json:data, key = witness.json:key, r = hash(key+data), output false", async () => { + await circuit.expectPass( + { + data: data, + key: key, + start: 98 + }, + { out: 0 } + ); }); -}); \ No newline at end of file +}); diff --git a/circuits/utils/array.circom b/circuits/utils/array.circom index f03530b..87baf4b 100644 --- a/circuits/utils/array.circom +++ b/circuits/utils/array.circom @@ -38,28 +38,6 @@ template IsEqualArray(n) { out <== totalEqual.out; } -template IsEqualArrayPaddedLHS(n) { - signal input in[2][n]; - signal output out; - - var accum = 0; - component equalComponent[n]; - component isPaddedElement[n]; - - for(var i = 0; i < n; i++) { - isPaddedElement[i] = IsZero(); - isPaddedElement[i].in <== in[0][i]; - equalComponent[i] = IsEqual(); - equalComponent[i].in[0] <== in[0][i]; - equalComponent[i].in[1] <== in[1][i] * (1-isPaddedElement[i].out); - accum += equalComponent[i].out; - } - - component totalEqual = IsEqual(); - totalEqual.in[0] <== n; - totalEqual.in[1] <== accum; - out <== totalEqual.out; -} // TODO: There should be a way to have the below assertion come from the field itself. /* @@ -254,265 +232,6 @@ template IndexSelector(total) { out <== calcTotal.sum; } -/* -This template selects an array in a mxn matrix -# Params: - - `m`: the row dimensions - - `n`: the column dimensions - -# Inputs: - - `index`: the index to select - -# Outputs: - - `out`: the array at index -*/ -template ArraySelector(m, n) { - signal input in[m][n]; - signal input index; - signal output out[n]; - assert(index >= 0 && index < m); - - signal selector[m]; - component Equal[m]; - for (var i = 0; i < m; i++) { - selector[i] <== IsEqual()([index, i]); - } - - var sum = 0; - for (var i = 0; i < m; i++) { - sum += selector[i]; - } - sum === 1; - - signal sums[n][m+1]; - for (var j = 0; j < n; j++) { - sums[j][0] <== 0; - for (var i = 0; i < m; i++) { - sums[j][i+1] <== sums[j][i] + in[i][j] * selector[i]; - } - out[j] <== sums[j][m]; - } -} - -/* -This template is multiplexer for two arrays of length n -# Params: - - `n`: the array length - -# Inputs: - - `a`: the first array - - `b`: the second array - - `sel`: the selector (1 or 0) - -# Outputs: - - `out`: the array selected -*/ -template ArrayMux(n) { - signal input a[n]; - signal input b[n]; - signal input sel; - signal output out[n]; - - for (var i = 0; i < n; i++) { - out[i] <== (b[i] - a[i]) * sel + a[i]; - } -} - -/* -This template writes one array to a larger array of fixed size starting at an index -E.g., given an array of m=160, we want to write at `index` to the n=16 bytes at that index. -This is used to write to nivc signals that are incrementally written to on each fold. -# Params: - - `m`: the length of the array writing to - - `n`: the array be written - -# Inputs: - - `array_to_write_to`: the array we of length m we are writing to - - `array_to_write_at_index`: the array of length n we are writing to the array of length m - - `index`: the index we are writing to `array_to_write_to` - -# Outputs: - - `out`: the new array -*/ -template WriteToIndex(m, n) { - signal input array_to_write_to[m]; - signal input array_to_write_at_index[n]; - signal input index; - - signal output out[m]; - - assert(m >= n); - - // Note: this is underconstrained, we need to constrain that index + n <= m - // Need to constrain that index + n <= m -- can't be an assertion, because uses a signal - // ------------------------- // - - // Here, we get an array of ALL zeros, except at the `index` AND `index + n` - // beginning-------^^^^^ end---^^^^^^^^^ - signal indexMatched[m]; - component indexBegining[m]; - component indexEnding[m]; - for(var i = 0 ; i < m ; i++) { - indexBegining[i] = IsZero(); - indexBegining[i].in <== i - index; - indexEnding[i] = IsZero(); - indexEnding[i].in <== i - (index + n); - indexMatched[i] <== indexBegining[i].out + indexEnding[i].out; - } - - // E.g., index == 31, m == 160, n == 16 - // => indexMatch[31] == 1; - // => indexMatch[47] == 1; - // => otherwise, all 0. - - signal accum[m]; - accum[0] <== indexMatched[0]; - - component writeAt = IsZero(); - writeAt.in <== accum[0] - 1; - - component or = OR(); - or.a <== (writeAt.out * array_to_write_at_index[0]); - or.b <== (1 - writeAt.out) * array_to_write_to[0]; - out[0] <== or.out; - // IF accum == 1 then { array_to_write_at } ELSE IF accum != 1 then { array to write_to } - var accum_index = accum[0]; - - component writeSelector[m - 1]; - component indexSelector[m - 1]; - component ors[m-1]; - for(var i = 1 ; i < m ; i++) { - // accum will be 1 at all indices where we want to write the new array - accum[i] <== accum[i-1] + indexMatched[i]; - writeSelector[i-1] = IsZero(); - writeSelector[i-1].in <== accum[i] - 1; - // IsZero(accum[i] - 1); --> tells us we are in the range where we want to write the new array - - indexSelector[i-1] = IndexSelector(n); - indexSelector[i-1].index <== accum_index; - indexSelector[i-1].in <== array_to_write_at_index; - // When accum is not zero, out is array_to_write_at_index, otherwise it is array_to_write_to - - ors[i-1] = OR(); - ors[i-1].a <== (writeSelector[i-1].out * indexSelector[i-1].out); - ors[i-1].b <== (1 - writeSelector[i-1].out) * array_to_write_to[i]; - out[i] <== ors[i-1].out; - accum_index += writeSelector[i-1].out; - } -} - - -/* -Convert stream of plain text to blocks of 16 bytes -# Params: - - `l`: the length of the byte stream - -# Inputs: - - `stream`: the stream of bytes of length l - -# Outputs: - - `out`: n 4x4 blocks representing 16 bytes -*/ -template ToBlocks(l){ - signal input stream[l]; - - var n = l\16; - if(l%16 > 0){ - n = n + 1; - } - signal output blocks[n][4][4]; - - var i, j, k; - - for (var idx = 0; idx < l; idx++) { - blocks[i][k][j] <== stream[idx]; - k = k + 1; - if (k == 4){ - k = 0; - j = j + 1; - if (j == 4){ - j = 0; - i = i + 1; - } - } - } - - if (l%16 > 0){ - blocks[i][k][j] <== 1; - k = k + 1; - } -} - - -/* -convert blocks of 16 bytes to stream of bytes -# Params: - - `l`: the length of the byte stream - - `n`: the number of blocks - -# Inputs: - - `blocks`: n 4x4 blocks representing 16 bytes - -# Outputs: - - `out`: the stream of bytes of length l -*/ -template ToStream(n,l){ - signal input blocks[n][4][4]; - - signal output stream[l]; - - var i, j, k; - - while(i*16 + j*4 + k < l){ - stream[i*16 + j*4 + k] <== blocks[i][k][j]; - k = k + 1; - if (k == 4){ - k = 0; - j = j + 1; - if (j == 4){ - j = 0; - i = i + 1; - } - } - } -} - -/* -Increment a 32-bit word, represented as a 4-byte array -# Inputs: - - `in`: a 4 byte word - -# Outputs: - - `out`: an incremented 4 byte word -*/ -template IncrementWord() { - signal input in[4]; - signal output out[4]; - signal carry[4]; - carry[3] <== 1; - - component IsGreaterThan[4]; - component mux[4]; - for (var i = 3; i >= 0; i--) { - // check to carry overflow - IsGreaterThan[i] = GreaterThan(8); - IsGreaterThan[i].in[0] <== in[i] + carry[i]; - IsGreaterThan[i].in[1] <== 0xFF; - - // multiplexer to select the output - mux[i] = Mux1(); - mux[i].c[0] <== in[i] + carry[i]; - mux[i].c[1] <== 0x00; - mux[i].s <== IsGreaterThan[i].out; - out[i] <== mux[i].out; - - // propagate the carry to the next bit - if (i > 0) { - carry[i - 1] <== IsGreaterThan[i].out; - } - } -} - // // from little endian to 32 bit words // // example: // 0, 1, 0, 1, 0, 0, 0, 0, => 80 diff --git a/circuits/utils/generics-bits.circom b/circuits/utils/bits.circom similarity index 80% rename from circuits/utils/generics-bits.circom rename to circuits/utils/bits.circom index b1311e9..08f2daf 100644 --- a/circuits/utils/generics-bits.circom +++ b/circuits/utils/bits.circom @@ -1,6 +1,28 @@ +pragma circom 2.1.9; + +include "circomlib/circuits/bitify.circom"; + +/* +This template passes if a given array contains only valid ASCII values (e.g., u8 vals). + +# Params: + - `n`: the length of the array + +# Inputs: + - `in[n]`: array to check +*/ +template ASCII(n) { + signal input in[n]; + + component Byte[n]; + for(var i = 0; i < n; i++) { + Byte[i] = Num2Bits(8); + Byte[i].in <== in[i]; + } +} + // initially from https://github.com/reclaimprotocol/zk-symmetric-crypto // modified for our needs -pragma circom 2.1.9; /** * Add N bit numbers together diff --git a/circuits/utils/bytes.circom b/circuits/utils/bytes.circom deleted file mode 100644 index 93a523d..0000000 --- a/circuits/utils/bytes.circom +++ /dev/null @@ -1,258 +0,0 @@ -pragma circom 2.1.9; - -include "circomlib/circuits/bitify.circom"; -include "circomlib/circuits/gates.circom"; - -/* -This template passes if a given array contains only valid ASCII values (e.g., u8 vals). - -# Params: - - `n`: the length of the array - -# Inputs: - - `in[n]`: array to check -*/ -template ASCII(n) { - signal input in[n]; - - component Byte[n]; - for(var i = 0; i < n; i++) { - Byte[i] = Num2Bits(8); - Byte[i].in <== in[i]; - } -} - -/* -This template converts bytes to bits. -# Params: - - `n`: the number of bytes - -# Inputs: - - `in[n]`: array of bytes of length n -# Outputs: - - `out`: an array of bits of length n * 8 -*/ -template BytesToBits(n_bytes) { - signal input in[n_bytes]; - signal output out[n_bytes*8]; - component num2bits[n_bytes]; - for (var i = 0; i < n_bytes; i++) { - num2bits[i] = Num2Bits(8); - num2bits[i].in <== in[i]; - for (var j = 7; j >=0; j--) { - out[i*8 + j] <== num2bits[i].out[7 -j]; - } - } -} - -/* -This template converts bits to bytes. -# Params: - - `n`: the number of bytes you want out -# Inputs: - - `in[n]`: array of bits of length n * 8 -# Outputs: - - `out`: an array of bytes of length n -*/ -template BitsToBytes(n) { - signal input in[n*8]; - signal output out[n]; - component bits2num[n]; - for (var i = 0; i < n; i++) { - bits2num[i] = Bits2Num(8); - for (var j = 0; j < 8; j++) { - bits2num[i].in[7 - j] <== in[i*8 + j]; - } - out[i] <== bits2num[i].out; - } -} - -/* -This template XORs two bytes. -# Inputs: - - `a`: a single byte - - `b`: a single byte -# Outputs: - - `out`: a XOR b -*/ -template XorByte(){ - signal input a; - signal input b; - signal output out; - - component abits = Num2Bits(8); - abits.in <== a; - - component bbits = Num2Bits(8); - bbits.in <== b; - - component XorBits = BitwiseXor(8); - XorBits.a <== abits.out; - XorBits.b <== bbits.out; - - component num = Bits2Num(8); - num.in <== XorBits.out; - - out <== num.out; -} - -/* -This template XORs n bytes. -# Inputs: - - `a`: an array of bytes of length n - - `b`: an array of bytes of length n -# Outputs: - - `out`: a XOR b -*/ -template XORBLOCK(n_bytes){ - signal input a[n_bytes]; - signal input b[n_bytes]; - signal output out[n_bytes]; - - component xorByte[n_bytes]; - for (var i = 0; i < n_bytes; i++) { - xorByte[i] = XorByte(); - xorByte[i].a <== a[i]; - xorByte[i].b <== b[i]; - out[i] <== xorByte[i].out; - } -} - -/* -This template right shifts an n bit array by r. -# Params: - - `n`: length of bits to right shift - - `r`: number of bits to right shift by -# Inputs: - - `in`: an array of bits of length n -# Outputs: - - `out`: the bit array right shifted by r -*/ -template BitwiseRightShift(n, r) { - signal input in[n]; - signal output out[n]; - for (var i=0; i 0); - assert(keyLen > 0); - assert(dataLen >= keyLen); - - // position accumulator - signal pos[dataLen-keyLen+2]; - pos[0] <== 0; - - // total matches found so far - signal num_matches[dataLen-keyLen+2]; - num_matches[0] <== 0; - - // calculate powers of r - signal r_powers[dataLen]; - r_powers[0] <== random_num; - for (var i=1 ; i [!WARNING] Deprecated docs +> These docs are out-of-date with current architecture which uses hash-based approach. Will be updated later. More details can be found in [circuits](../circuits/http/verification.circom) + HTTP is a more strict and well-defined specification that JSON, and thus, it's parser is a lot easier than JSON. Proof generation for HTTP extractor is broken into: diff --git a/docs/pabuild.md b/docs/pabuild.md deleted file mode 100644 index 0782a68..0000000 --- a/docs/pabuild.md +++ /dev/null @@ -1,202 +0,0 @@ -# `pabuild` CLI Tool -This repository contains a small Rust CLI tool called `pabuild`. - -## Install `pabuild` -From the root of this repository, run: -```sh -cargo install --path . -``` -to install the `pabuild` binary. -You can see a help menu with the subcommands by: -```sh -pabuild --help -``` - -## Witnessgen -To get the basic idea, run -```sh -pabuild witness --help -``` -It can process and generate input JSON files to be used for parser/extractor circuits. - -> [!NOTE] -> `circuit-name` need to be **same** for witness generator and codegen. - -### Examples -**JSON Parsing:** -If we have a given JSON file we want to parse such as [`examples/json/test/example.json`](../examples/json/test/example.json) for the `json-parser` circuit (see [`circuits.json`](../circuits.json)), then we can: - -```sh -pabuild witness parser json --input-file examples/json/test/example.json --circuit-name json-parser -``` - -Afterwards, you can run `npx circomkit compile json-parser` then `circomkit witness json-parser input`. - -**HTTP Parsing:** -If we have a given HTTP request/response (as a file) we want to parse such as [`examples/http/get_request.http`](../examples/http/get_request.http) for the `http-parser` circuit (see `circuits.json`), then we can: - -```sh -pabuild witness parser http --input-file examples/http/get_request.http --circuit-name http-parser -``` - -Afterwards, you can run `npx circomkit compile http-parser` then `circomkit witness http-parser input`. - -**JSON Extractor:** -To extract a value out of a JSON, we need a lockfile that contains keys and value type. - -```sh -pabuild witness extractor json --input-file examples/json/test/value_string.json --lockfile examples/json/lockfile/value_string.json --circuit-name value_string -``` - -**HTTP Extractor:** -To extract reponse from HTTP, a lockfile need to be given with start line (method, status, version) and headers to be matched. Example can be found in [examples/http/lockfile](../examples/http/lockfile/). - -```sh -pabuild witness extractor http --input-file examples/http/get_response.http --lockfile examples/http/lockfile/response.lock.json --circuit-name get-response -``` - -## Codegen -Extractor circuit is generated using rust to handle arbitrary keys and array indices. - -Run: -```sh -pabuild codegen --help -``` -to get options: -``` -Usage: pabuild codegen [OPTIONS] --circuit-name --input-file --lockfile - -Arguments: - [possible values: json, http, extended] - -Options: - --circuit-name Name of the circuit (to be used in circomkit config) - --input-file Path to the JSON/HTTP file - --lockfile Path to the lockfile - -d, --debug Optional circuit debug logs - -h, --help Print help -``` -Takes 3 input arguments: -- `input-file`: input json/http file. Examples are located in [examples/json](../examples/json/test/). -- `lockfile`: keys and value type for extraction. Should contain only two keys: - - `keys`: list of all the keys for the value to be extracted. - - `value_type`: Currently only two value types are supported: `String`,`Number`. -- `circuit-name`: circuit filename to save. Located in [circuits/main](../circuits/main/). Prefixed with `json_` -- `debug`: Optional circuit debug logs. - -### JSON Extraction - -To test an end-to-end JSON extraction proof: -- Run codegen to generate circuits. Replace `value_string` with `circuit-name`. - ```sh - pabuild codegen json --circuit-name value_string --input-file examples/json/test/value_string.json --lockfile examples/json/lockfile/value_string.json -d - ``` - -- codegen adds circuit config to [circuits.json](../circuits.json) for circomkit support. Compile circuits using `npx circomkit compile value_string` - -- Generate witness: - ```sh - node build/value_string/value_string_js/generate_witness.js build/value_string/value_string_js/value_string.wasm inputs/value_string/inputs.json build/value_string/witness.wtns - ``` - or generate using circomkit: - ```bash - npx circomkit witness value_string inputs - ``` - -- create trusted setup, circomkit downloads the required trusted setup file. Download manually, if using `snarkjs`: - ```bash - npx circomkit setup value_string - # OR - snarkjs groth16 setup build/value_string/value_string.r1cs ptau/powersOfTau28_hez_final_14.ptau build/value_string/groth16_pkey.zkey - - snarkjs zkey contribute build/value_string/groth16_pkey.zkey build/value_string/groth16_pkey_1.zkey --name="random" -v - - snarkjs zkey beacon build/value_string/groth16_pkey_1.zkey build/value_string/groth16_pkey_final.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon phase2" - - snarkjs zkey verify build/value_string/value_string.r1cs ptau/powersOfTau28_hez_final_14.ptau build/value_string/groth16_pkey_final.zkey - - snarkjs zkey export verificationkey build/value_string/groth16_pkey_final.zkey build/value_string/groth16_vkey.json - ``` - -- create proof: - ```bash - npx circomkit prove value_string inputs - # OR - snarkjs groth16 prove build/value_string/groth16_pkey_final.zkey build/value_string/witness.wtns build/value_string/groth16_proof.json inputs/value_string/inputs.json - ``` - -- verify proof: - ```bash - npx circomkit verify value_string value_string - # OR - snarkjs groth16 verify build/value_string/groth16_vkey.json inputs/value_string/inputs.json build/value_string/groth16_proof.json - ``` - -### HTTP Locking and Extraction - -To test an end-to-end HTTP response extraction proof: -- Run codegen to generate circuits. Replace `get-response` with `circuit-name`. - ```sh - pabuild codegen http --circuit-name get-response --input-file examples/http/get_response.http --lockfile examples/http/lockfile/response.lock.json -d - ``` - -- codegen adds circuit config to [circuits.json](../circuits.json) for circomkit support. Compile circuits using `npx circomkit compile get-response` - -- Generate witness: - ```sh - node build/get-response/get-response_js/generate_witness.js build/get-response/get-response_js/get-response.wasm inputs/get-response/inputs.json build/get-response/witness.wtns - ``` - or generate using circomkit: - ```bash - npx circomkit witness get-response inputs - ``` - -- create trusted setup, circomkit downloads the required trusted setup file. Download manually, if using `snarkjs`: - ```bash - npx circomkit setup get-response - # OR - snarkjs groth16 setup build/get-response/get-response.r1cs ptau/powersOfTau28_hez_final_16.ptau build/get-response/groth16_pkey.zkey - - snarkjs zkey contribute build/get-response/groth16_pkey.zkey build/get-response/groth16_pkey_1.zkey --name="random" -v - - snarkjs zkey beacon build/get-response/groth16_pkey_1.zkey build/get-response/groth16_pkey_final.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon phase2" - - snarkjs zkey verify build/get-response/get-response.r1cs ptau/powersOfTau28_hez_final_16.ptau build/get-response/groth16_pkey_final.zkey - - snarkjs zkey export verificationkey build/get-response/groth16_pkey_final.zkey build/get-response/groth16_vkey.json - ``` - -- create proof: - ```bash - npx circomkit prove get-response inputs - # OR - snarkjs groth16 prove build/get-response/groth16_pkey_final.zkey build/get-response/witness.wtns build/get-response/groth16_proof.json inputs/get-response/inputs.json - ``` - -- verify proof: - ```bash - npx circomkit verify value_string value_string - # OR - snarkjs groth16 verify build/get-response/groth16_vkey.json inputs/get-response/inputs.json build/get-response/groth16_proof.json - ``` - -### Extended HTTP + JSON extraction - -`pabuild` allows to create a proof of arbitrary HTTP response. -- Locks start line, and headers for HTTP as specified in [lockfile](../examples/http/lockfile/spotify_extended.lock.json). - - **NOTE**: `Accept-Encoding: identity` header is mandatory as pabuild doesn't support `gzip` encoding. -- extracts response body out -- create a JSON value extractor circuit based on keys in [lockfile](../examples/http/lockfile/spotify_extended.lock.json) -- extract the value out and create a proof - -Steps to run an end-to-end proof is similar to HTTP/JSON extractor: -- Run codegen to generate circuits. Replace `value_string` with `circuit-name`. - ```sh - pabuild codegen extended --circuit-name spotify_top_artists --input-file examples/http/spotify_top_artists.json --lockfile examples/http/lockfile/spotify_extended.lock.json -d - ``` - -- Refer to [HTTP extractor](#http-locking-and-extraction) for following steps: - - generate witness - - create trusted setup - - create proof - - verify proof \ No newline at end of file diff --git a/examples/http/lockfile/request.lock.json b/examples/http/lockfile/request.lock.json deleted file mode 100644 index 7e4935c..0000000 --- a/examples/http/lockfile/request.lock.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "method": "GET", - "target": "/api", - "version": "HTTP/1.1", - "headerName1": "Host", - "headerValue1": "localhost", - "headerName2": "Accept", - "headerValue2": "application/json" -} \ No newline at end of file diff --git a/examples/http/lockfile/response.lock.json b/examples/http/lockfile/response.lock.json deleted file mode 100644 index cf02dd8..0000000 --- a/examples/http/lockfile/response.lock.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "HTTP/1.1", - "status": "200", - "message": "OK", - "headerName1": "Content-Type", - "headerValue1": "application/json" -} \ No newline at end of file diff --git a/examples/http/lockfile/spotify.lock.json b/examples/http/lockfile/spotify.lock.json deleted file mode 100644 index e3ca2da..0000000 --- a/examples/http/lockfile/spotify.lock.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": "HTTP/1.1", - "status": "200", - "message": "OK", - "headerName1": "content-type", - "headerValue1": "application/json; charset=utf-8" -} \ No newline at end of file diff --git a/examples/http/lockfile/spotify_extended.lock.json b/examples/http/lockfile/spotify_extended.lock.json deleted file mode 100644 index 8940987..0000000 --- a/examples/http/lockfile/spotify_extended.lock.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "http": { - "version": "HTTP/1.1", - "status": "200", - "message": "OK", - "headerName1": "content-type", - "headerValue1": "application/json; charset=utf-8" - }, - "json": { - "keys": [ - "data", - "items", - 0, - "profile", - "name" - ], - "value_type": "string" - } -} \ No newline at end of file diff --git a/examples/json/lockfile/spotify.json b/examples/json/lockfile/spotify.json deleted file mode 100644 index 0669a7b..0000000 --- a/examples/json/lockfile/spotify.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "keys": [ - "data", - "items", - 0, - "profile", - "name" - ], - "value_type": "string" -} \ No newline at end of file diff --git a/examples/json/lockfile/two_keys.json b/examples/json/lockfile/two_keys.json deleted file mode 100644 index a819752..0000000 --- a/examples/json/lockfile/two_keys.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "keys": [ - "key2" - ], - "value_type": "string" -} \ No newline at end of file diff --git a/examples/json/lockfile/value_array_nested.json b/examples/json/lockfile/value_array_nested.json deleted file mode 100644 index 01fa95f..0000000 --- a/examples/json/lockfile/value_array_nested.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "keys": [ - "a", - 0, - 0 - ], - "value_type": "number" -} \ No newline at end of file diff --git a/examples/json/lockfile/value_array_number.json b/examples/json/lockfile/value_array_number.json deleted file mode 100644 index c34e4a7..0000000 --- a/examples/json/lockfile/value_array_number.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "keys": [ - "k", - 2 - ], - "value_type": "number" -} \ No newline at end of file diff --git a/examples/json/lockfile/value_array_object.json b/examples/json/lockfile/value_array_object.json deleted file mode 100644 index b7f4b94..0000000 --- a/examples/json/lockfile/value_array_object.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "keys": [ - "a", - 0, - "b", - 0 - ], - "value_type": "number" -} \ No newline at end of file diff --git a/examples/json/lockfile/value_array_string.json b/examples/json/lockfile/value_array_string.json deleted file mode 100644 index ade1526..0000000 --- a/examples/json/lockfile/value_array_string.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "keys": [ - "b", - 1 - ], - "value_type": "string" -} \ No newline at end of file diff --git a/examples/json/lockfile/value_number.json b/examples/json/lockfile/value_number.json deleted file mode 100644 index d36e42f..0000000 --- a/examples/json/lockfile/value_number.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "keys": [ - "k" - ], - "value_type": "number" -} \ No newline at end of file diff --git a/examples/json/lockfile/value_object.json b/examples/json/lockfile/value_object.json deleted file mode 100644 index e5b7245..0000000 --- a/examples/json/lockfile/value_object.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "keys": [ - "a", - "e" - ], - "value_type": "string" -} \ No newline at end of file diff --git a/examples/json/lockfile/value_string.json b/examples/json/lockfile/value_string.json deleted file mode 100644 index c94b050..0000000 --- a/examples/json/lockfile/value_string.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "keys": [ - "k" - ], - "value_type": "string" -} \ No newline at end of file diff --git a/examples/json/test/example.json b/examples/json/test/example.json deleted file mode 100644 index 8664af9..0000000 --- a/examples/json/test/example.json +++ /dev/null @@ -1,24 +0,0 @@ -{ "a": -{ "b": "c", -"d": { -"e": "f", -"g": { -"h": { -"i": "j", -"k": "l", -"m": "n", -"o": "p", -"q": "r", -"s": { -"t": "u", -"v": [ -"w", -"x" -] -}, -"y": "z" -} -} -} -} -} \ No newline at end of file diff --git a/examples/json/test/spotify.json b/examples/json/test/spotify.json deleted file mode 100644 index dcacc81..0000000 --- a/examples/json/test/spotify.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "data": {"items": [{"data": "Artist","profile": {"name": "Taylor Swift"}}]} -} \ No newline at end of file diff --git a/examples/json/test/two_keys.json b/examples/json/test/two_keys.json deleted file mode 100644 index 437a6c5..0000000 --- a/examples/json/test/two_keys.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "key1": "abc", - "key2": "def" -} \ No newline at end of file diff --git a/examples/json/test/value_array_nested.json b/examples/json/test/value_array_nested.json deleted file mode 100644 index 13907e1..0000000 --- a/examples/json/test/value_array_nested.json +++ /dev/null @@ -1 +0,0 @@ -{ "a": [[1,0],[0,1,3]] } \ No newline at end of file diff --git a/examples/json/test/value_array_object_array.json b/examples/json/test/value_array_object_array.json deleted file mode 100644 index 885e4ed..0000000 --- a/examples/json/test/value_array_object_array.json +++ /dev/null @@ -1 +0,0 @@ -{"a":[{"b":5},{"c":[0,1,"a"]}]} \ No newline at end of file diff --git a/examples/json/test/value_number.json b/examples/json/test/value_number.json deleted file mode 100644 index 70ddfbb..0000000 --- a/examples/json/test/value_number.json +++ /dev/null @@ -1 +0,0 @@ -{ "k" : 69 } \ No newline at end of file diff --git a/examples/json/test/value_string.json b/examples/json/test/value_string.json deleted file mode 100644 index 7f1fc65..0000000 --- a/examples/json/test/value_string.json +++ /dev/null @@ -1 +0,0 @@ -{ "k": "v" } \ No newline at end of file diff --git a/inputs/search/witness.json b/inputs/search/witness.json deleted file mode 100644 index c69c0a8..0000000 --- a/inputs/search/witness.json +++ /dev/null @@ -1,803 +0,0 @@ -{ - "key": [ - 34, - 103, - 108, - 111, - 115, - 115, - 97, - 114, - 121, - 34 - ], - "data": [ - 123, - 10, - 32, - 32, - 32, - 32, - 34, - 103, - 108, - 111, - 115, - 115, - 97, - 114, - 121, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 116, - 105, - 116, - 108, - 101, - 34, - 58, - 32, - 34, - 101, - 120, - 97, - 109, - 112, - 108, - 101, - 32, - 103, - 108, - 111, - 115, - 115, - 97, - 114, - 121, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 68, - 105, - 118, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 116, - 105, - 116, - 108, - 101, - 34, - 58, - 32, - 34, - 83, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 76, - 105, - 115, - 116, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 69, - 110, - 116, - 114, - 121, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 73, - 68, - 34, - 58, - 32, - 34, - 83, - 71, - 77, - 76, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 83, - 111, - 114, - 116, - 65, - 115, - 34, - 58, - 32, - 34, - 83, - 71, - 77, - 76, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 84, - 101, - 114, - 109, - 34, - 58, - 32, - 34, - 83, - 116, - 97, - 110, - 100, - 97, - 114, - 100, - 32, - 71, - 101, - 110, - 101, - 114, - 97, - 108, - 105, - 122, - 101, - 100, - 32, - 77, - 97, - 114, - 107, - 117, - 112, - 32, - 76, - 97, - 110, - 103, - 117, - 97, - 103, - 101, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 65, - 99, - 114, - 111, - 110, - 121, - 109, - 34, - 58, - 32, - 34, - 83, - 71, - 77, - 76, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 65, - 98, - 98, - 114, - 101, - 118, - 34, - 58, - 32, - 34, - 73, - 83, - 79, - 32, - 56, - 56, - 55, - 57, - 58, - 49, - 57, - 56, - 54, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 68, - 101, - 102, - 34, - 58, - 32, - 123, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 112, - 97, - 114, - 97, - 34, - 58, - 32, - 34, - 65, - 32, - 109, - 101, - 116, - 97, - 45, - 109, - 97, - 114, - 107, - 117, - 112, - 32, - 108, - 97, - 110, - 103, - 117, - 97, - 103, - 101, - 44, - 32, - 117, - 115, - 101, - 100, - 32, - 116, - 111, - 32, - 99, - 114, - 101, - 97, - 116, - 101, - 32, - 109, - 97, - 114, - 107, - 117, - 112, - 32, - 108, - 97, - 110, - 103, - 117, - 97, - 103, - 101, - 115, - 32, - 115, - 117, - 99, - 104, - 32, - 97, - 115, - 32, - 68, - 111, - 99, - 66, - 111, - 111, - 107, - 46, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 83, - 101, - 101, - 65, - 108, - 115, - 111, - 34, - 58, - 32, - 91, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 77, - 76, - 34, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 88, - 77, - 76, - 34, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 93, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 125, - 44, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 34, - 71, - 108, - 111, - 115, - 115, - 83, - 101, - 101, - 34, - 58, - 32, - 34, - 109, - 97, - 114, - 107, - 117, - 112, - 34, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 125, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 125, - 10, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 32, - 125, - 10, - 32, - 32, - 32, - 32, - 125, - 10, - 125 - ] -} \ No newline at end of file diff --git a/package.json b/package.json index 1cf72bf..051a8ee 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "web-prover-circuits", "description": "ZK Circuits for WebProofs", - "version": "0.5.10", + "version": "0.6.0", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/src/main.rs b/src/main.rs index d9f5b66..b5ecf0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,8 @@ struct CircuitFiles { } const BASE_CIRCUIT_NAMES: &[&str] = &[ - "aes_gctr_nivc", - "http_nivc", + "plaintext_authentication", + "http_verification", "json_mask_object", "json_mask_array_index", "json_extract_value",