From 5e3101a5470eb13fa1db70be392080d5e8a0958a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thor=20=F0=9F=AA=81?= <7041313+thor314@users.noreply.github.com> Date: Tue, 6 Aug 2024 21:56:22 +0100 Subject: [PATCH] chore: clean up the repo (#18) * .github ci * readme updated * justfile * fmt * lints * move aes circuits -> circuits/aes directory * fmt settings * main refactor * refactor: main -> lib. mv Witness -> witness mod * mv witness type aliases to crate root * witness refactor * witness minor refactor notes * refactor proof * just adjust --- .github/settings.yml | 42 ++++ .github/workflows/ci.yml | 53 +++++ .justfile | 47 ++++ .rustfmt.toml | 43 ++++ Cargo.lock | 84 ++----- Cargo.toml | 3 +- README.md | 87 ++++---- circuits/README.md | 1 - circuits/aes-gcm/.gitkeep | 0 circuits/aes/README.md | 10 + circuits/{ => aes}/aes_256_ctr.circom | 0 circuits/{ => aes}/aes_256_ctr_test.circom | 0 circuits/{ => aes}/aes_256_encrypt.circom | 0 .../{ => aes}/aes_256_key_expansion.circom | 0 circuits/{ => aes}/aes_emulation.circom | 0 .../{ => aes}/aes_emulation_tables.circom | 0 .../{ => aes}/dec_aes_256_ctr_try1.circom | 0 circuits/{ => aes}/gcm_siv_dec_2_keys.circom | 0 .../{ => aes}/gcm_siv_dec_2_keys_test.circom | 0 circuits/{ => aes}/gcm_siv_enc_2_keys.circom | 0 circuits/{ => aes}/gfmul_int.circom | 0 circuits/{ => aes}/helper_functions.circom | 0 .../{ => aes}/lib_circuits/aliascheck.circom | 0 circuits/{ => aes}/lib_circuits/binsum.circom | 0 circuits/{ => aes}/lib_circuits/bitify.circom | 0 .../{ => aes}/lib_circuits/comparators.circom | 0 .../lib_circuits/compconstant.circom | 0 circuits/{ => aes}/lib_circuits/gates.circom | 0 circuits/{ => aes}/lib_circuits/mux1.circom | 0 circuits/{ => aes}/lib_circuits/sha256.circom | 0 circuits/{ => aes}/mul.circom | 0 circuits/{ => aes}/polyval.circom | 0 circuits/{ => aes}/tiny.circom | 0 circuits/{ => aes}/vclmul_emulator.circom | 0 src/consts.rs | 40 ++++ src/lib.rs | 66 ++++++ src/main.rs | 105 --------- src/proof.rs | 107 +++++---- src/utils.rs | 66 ++++++ src/witness.rs | 208 ++++++------------ 40 files changed, 545 insertions(+), 417 deletions(-) create mode 100644 .github/settings.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .justfile create mode 100644 .rustfmt.toml delete mode 100644 circuits/README.md create mode 100644 circuits/aes-gcm/.gitkeep create mode 100644 circuits/aes/README.md rename circuits/{ => aes}/aes_256_ctr.circom (100%) rename circuits/{ => aes}/aes_256_ctr_test.circom (100%) rename circuits/{ => aes}/aes_256_encrypt.circom (100%) rename circuits/{ => aes}/aes_256_key_expansion.circom (100%) rename circuits/{ => aes}/aes_emulation.circom (100%) rename circuits/{ => aes}/aes_emulation_tables.circom (100%) rename circuits/{ => aes}/dec_aes_256_ctr_try1.circom (100%) rename circuits/{ => aes}/gcm_siv_dec_2_keys.circom (100%) rename circuits/{ => aes}/gcm_siv_dec_2_keys_test.circom (100%) rename circuits/{ => aes}/gcm_siv_enc_2_keys.circom (100%) rename circuits/{ => aes}/gfmul_int.circom (100%) rename circuits/{ => aes}/helper_functions.circom (100%) rename circuits/{ => aes}/lib_circuits/aliascheck.circom (100%) rename circuits/{ => aes}/lib_circuits/binsum.circom (100%) rename circuits/{ => aes}/lib_circuits/bitify.circom (100%) rename circuits/{ => aes}/lib_circuits/comparators.circom (100%) rename circuits/{ => aes}/lib_circuits/compconstant.circom (100%) rename circuits/{ => aes}/lib_circuits/gates.circom (100%) rename circuits/{ => aes}/lib_circuits/mux1.circom (100%) rename circuits/{ => aes}/lib_circuits/sha256.circom (100%) rename circuits/{ => aes}/mul.circom (100%) rename circuits/{ => aes}/polyval.circom (100%) rename circuits/{ => aes}/tiny.circom (100%) rename circuits/{ => aes}/vclmul_emulator.circom (100%) create mode 100644 src/consts.rs create mode 100644 src/lib.rs delete mode 100644 src/main.rs create mode 100644 src/utils.rs diff --git a/.github/settings.yml b/.github/settings.yml new file mode 100644 index 0000000..1dc050d --- /dev/null +++ b/.github/settings.yml @@ -0,0 +1,42 @@ +# These settings are synced to GitHub by https://probot.github.io/apps/settings/ +# Requires sign up, and allowing Probot to manage your repos. + +repository: + # name: + description: "circom AES-GCM circuits" + private: true + has_issues: true + has_projects: false + has_wiki: false + has_downloads: false + default_branch: main + allow_squash_merge: true + allow_merge_commit: false + allow_rebase_merge: false + +# # Labels: define labels for Issues and Pull Requests +# labels: +# - name: bug +# color: CC0000 +# - name: feature +# color: 336699 +# - name: first-timers-only +# # include the old name to rename and existing label +# oldname: Help Wanted + +# # Collaborators: give specific users access to this repository. +# collaborators: +# - username: bkeepers +# # Note: Only valid on organization-owned repositories. +# # The permission to grant the collaborator. Can be one of: +# # * `pull` - can pull, but not push to or administer this repository. +# # * `push` - can pull and push, but not administer this repository. +# # * `admin` - can pull, push and administer this repository. +# permission: push + +# - username: hubot +# permission: pull +# - username: +# permission: pull + + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6bfbab4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +# Rust template: https://github.com/actions/starter-workflows/blob/main/ci/rust.yml +# Resources: https://docs.github.com/en/actions +# Examples: https://github.com/actions/starter-workflows +# Process: make small changes, push them, check the Actions tab on github +# also see templates https://github.com/rust-github/template/tree/main/.github/workflows +name: Rust +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + CARGO_TERM_COLOR: always # pretty colors + +jobs: + lint: + name: lint project + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: + - nightly + steps: + - uses: actions/checkout@v4 + - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + - run: rustup component add clippy + - run: cargo clippy -- -Dwarnings + # test: + # name: test project + # runs-on: ubuntu-latest + # strategy: + # matrix: + # toolchain: + # - nightly + # steps: + # - uses: actions/checkout@v4 + # - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + # - run: cargo test --all-features --verbose + + fmt: + name: fmt project + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: + - nightly + steps: + - uses: actions/checkout@v4 + - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + - run: rustup component add rustfmt + - run: cargo fmt --all -- --check + diff --git a/.justfile b/.justfile new file mode 100644 index 0000000..bfb46a2 --- /dev/null +++ b/.justfile @@ -0,0 +1,47 @@ +# A configuration file for `just`, a command runner and successor to `make` +# https://github.com/casey/just/tree/master +# +# examples: +# https://github.com/casey/just/blob/master/examples/pre-commit.just +# https://github.com/casey/just/blob/master/examples/kitchen-sink.just + +# ignore comments in the command area +set ignore-comments := true + +# load .env vars +# set dotenv-load := true + +# set custom env vars +export RUST_LOG := "info" +# export RUST_BACKTRACE := "1" + + +@just: + just --list + +build: + cargo build -r + +check: + cargo check --all --tests + cargo fmt --all --check + +format: + cargo fmt --all + +fix: + cargo clippy --all --tests --fix + +lint: + cargo clippy --all --tests -- -D warnings + +run: + cargo run -r + +test: + RUST_MIN_STACK=8388608 cargo test --all -- --nocapture + +@versions: + rustc --version + cargo fmt -- --version + cargo clippy -- --version diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..504cdec --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,43 @@ +# Opinionated whitespace and tabs. The most important of these are imports and width settings. +# Others may want to borrow or change these to their own liking. +# https://rust-lang.github.io/rustfmt + +# version-related +edition = "2021" # redundant, fmt will read Cargo.toml for editor edition year +unstable_features = true +use_try_shorthand = true # replace any `try!` (2015 Rust) with `?` + +# misc formatting +condense_wildcard_suffixes = true # replace: (a,b,_,_)=(1, 2, 3, 4); -> (a,b,..)=(1, 2, 3, 4); +format_code_in_doc_comments = true # format code blocks in doc comments +format_macro_matchers = true # $a: ident -> $a:ident +format_strings = true # break and insert newlines for long string literals +match_block_trailing_comma = true # include comma in match blocks after '}' +normalize_comments = true # convert /*..*/ to //.. where possible +reorder_impl_items = true # move `type` and `const` declarations to top of impl block +struct_field_align_threshold = 20 # align struct arguments' types vertically +use_field_init_shorthand = true # struct initialization short {x: x} -> {x} + +# reduce whitespace +blank_lines_upper_bound = 1 # default: 1. Sometimes useful to change to 0 to condense a file. +brace_style = "PreferSameLine" # prefer starting `{` without inserting extra \n +fn_single_line = true # if it's a short 1-liner, let it be a short 1-liner +match_arm_blocks = false # remove unnecessary {} in match arms +newline_style = "Unix" # not auto, we won the culture war. \n over \r\n +overflow_delimited_expr = true # prefer ]); to ]\n); +where_single_line = true # put where on a single line if possible + +# imports preferences +group_imports = "StdExternalCrate" # create import groupings for std, external libs, and internal deps +imports_granularity = "Crate" # aggressively group imports + +# width settings: everything to 100 +comment_width = 100 # default: 80 +inline_attribute_width = 60 # inlines #[cfg(test)]\nmod test -> #[cfg(test)] mod test +max_width = 100 # default: 100 +use_small_heuristics = "Max" # don't ever newline short of `max_width`. +wrap_comments = true # wrap comments at `comment_width` +# format_strings = true # wrap strings at `max_length` + +# tabs and spaces +hard_tabs = false # (def: false) use spaces over tabs diff --git a/Cargo.lock b/Cargo.lock index 7a51e20..045ee25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,7 @@ dependencies = [ "aes 0.8.4", "aes-gcm", "aes-gcm-siv", + "anyhow", "ark-bn254", "ark-circom", "ark-crypto-primitives", @@ -142,6 +143,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + [[package]] name = "ark-bn254" version = "0.4.0" @@ -1307,7 +1314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1576,7 +1583,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2559,22 +2566,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -2583,46 +2575,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2635,48 +2609,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index c8ab9e2..8e0d0e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,8 @@ ark-groth16 = { version = "=0.4.0", default-features = false, features = ["paral ark-poly = { version = "0.4.1", default-features = false, features = ["parallel"] } ark-relations = { version = "=0.4.0", default-features = false } ark-serialize = { version = "0.4.1", default-features = false } +anyhow = "1.0.86" [profile.release] lto = true -opt-level = "z" \ No newline at end of file +opt-level = "z" diff --git a/README.md b/README.md index 94b8c87..6060878 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,53 @@ -# Demo of AES Proving +# AES-GCM circom circuits +A (WIP) implementation of [AES-GCM](https://web.cs.ucdavis.edu/~rogaway/ocb/gcm.pdf) in Circom. -Go through installation steps below to prepare build artifacts. -Generate various witnesses & an aes proof +## Generate AES witnsess values +Generate witnesses and an AES proof: `cargo run --release` -## TODO -The goal of this repo is to create a proof-of-concept for our AES requests. +## Browser Execution Demo +To prove an AES execution with the witness files generated above: -1. Verify circuits with aes-256-ctr -2. Configure circuits to work for 128 bit -3. Generate TLS specific witness conversion for aes-128-ctr -4. Performance test with snarkjs -5. Probably add a wasm target -6. Potentially rewrite in Halo2 for smaller PK/VK -7. Concerningly, these proofs verify with invalid ciphertext (only output is invalid) +Install node, circom, and set up the directory: +```sh +# install node +# setup js +cd client && npm install +cd client && npm start -## Browser Execution +# add build symlink +cd client/static && ln -s ../../build build -*install node* +# install circom and snarkjs +git clone https://github.com/iden3/circom.git +cargo build --release +cargo install --path circom +npm install -g snarkjs@latest +``` -*setup js* -`cd client && npm install` -`cd client && npm start` +```sh +# compile circuits +mkdir build -*add build symlink* -`cd client/static && ln -s ../../build build` +circom --wasm --sym --r1cs --output ./build ./circuits/aes/gcm_siv_dec_2_keys_test.circom +# generate trusted setup +# NOTE: This is currently unused because the rust zkey parser is horrible. -## Installation -*install circom* +pushd build +curl "https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_10.ptau" --output 'powersOfTau28_hez_final_10.ptau' +# we just did this: +curl "https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_19.ptau" --output 'powersOfTau28_hez_final_19.ptau' +popd -`git clone https://github.com/iden3/circom.git` +SJS_BIN=$(dirname $(npm list -g --depth=0 | head -n 1)); SJS_BIN+="/bin/snarkjs" -`cargo build --release` +node $SJS_BIN groth16 setup ./build/gcm_siv_dec_2_keys_test.r1cs ./build/powersOfTau28_hez_final_19.ptau ./build/test_0000.zkey -`cargo install --path circom` +# test circuit +circom --wasm --sym --r1cs --output ./build ./circuits/aes/tiny.circom -*install snarkjs* - -`npm install -g snarkjs@latest` - -*compile circuits* - -`mkdir build` - -`circom --wasm --sym --r1cs --output ./build ./circuits/gcm_siv_dec_2_keys_test.circom` - -*generate trusted setup* -NOTE: This is currently unused because the rust zkey parser is horrible. - -`cd build && curl "https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_10.ptau" --output './build/powersOfTau28_hez_final_10.ptau' && cd ..` - -`cd build && curl "https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_19.ptau" --output './build/powersOfTau28_hez_final_19.ptau' && cd ..` - -`SJS_BIN=$(dirname $(npm list -g --depth=0 | head -n 1)); SJS_BIN+="/bin/snarkjs"` - -`node $SJS_BIN groth16 setup ./build/gcm_siv_dec_2_keys_test.r1cs ./build/powersOfTau28_hez_final_19.ptau ./build/test_0000.zkey` - -*test circuit* - -`circom --wasm --sym --r1cs --output ./build ./circuits/tiny.circom` - -`snarkjs zkey new ./build/tiny.r1cs ./build/powersOfTau28_hez_final_10.ptau ./build/tiny.zkey` \ No newline at end of file +snarkjs zkey new ./build/tiny.r1cs ./build/powersOfTau28_hez_final_10.ptau ./build/tiny.zkey +``` \ No newline at end of file diff --git a/circuits/README.md b/circuits/README.md deleted file mode 100644 index cef9c19..0000000 --- a/circuits/README.md +++ /dev/null @@ -1 +0,0 @@ -Circom circuits for aes-gcm-siv \ No newline at end of file diff --git a/circuits/aes-gcm/.gitkeep b/circuits/aes-gcm/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/circuits/aes/README.md b/circuits/aes/README.md new file mode 100644 index 0000000..9e43d08 --- /dev/null +++ b/circuits/aes/README.md @@ -0,0 +1,10 @@ +# AES-GCM Implementation in Circom +Circom circuits for aes-gcm-siv, borrowed from [electron-labs/aes-circom](https://github.com/Electron-Labs/aes-circom). + +❗❗❗ Note that these circuits have been [shown](https://github.com/Electron-Labs/aes-circom/issues/25) to be underconstrained. ❗❗❗ + +We intend to work on correctly constraining these circuits. + +This is based on the S-table implementation of [AES GCM SIV](https://datatracker.ietf.org/doc/html/rfc8452) encryption scheme. + +It is heavily inspired by the C implementation of [AES-GCM-SIV](https://github.com/Shay-Gueron/AES-GCM-SIV) \ No newline at end of file diff --git a/circuits/aes_256_ctr.circom b/circuits/aes/aes_256_ctr.circom similarity index 100% rename from circuits/aes_256_ctr.circom rename to circuits/aes/aes_256_ctr.circom diff --git a/circuits/aes_256_ctr_test.circom b/circuits/aes/aes_256_ctr_test.circom similarity index 100% rename from circuits/aes_256_ctr_test.circom rename to circuits/aes/aes_256_ctr_test.circom diff --git a/circuits/aes_256_encrypt.circom b/circuits/aes/aes_256_encrypt.circom similarity index 100% rename from circuits/aes_256_encrypt.circom rename to circuits/aes/aes_256_encrypt.circom diff --git a/circuits/aes_256_key_expansion.circom b/circuits/aes/aes_256_key_expansion.circom similarity index 100% rename from circuits/aes_256_key_expansion.circom rename to circuits/aes/aes_256_key_expansion.circom diff --git a/circuits/aes_emulation.circom b/circuits/aes/aes_emulation.circom similarity index 100% rename from circuits/aes_emulation.circom rename to circuits/aes/aes_emulation.circom diff --git a/circuits/aes_emulation_tables.circom b/circuits/aes/aes_emulation_tables.circom similarity index 100% rename from circuits/aes_emulation_tables.circom rename to circuits/aes/aes_emulation_tables.circom diff --git a/circuits/dec_aes_256_ctr_try1.circom b/circuits/aes/dec_aes_256_ctr_try1.circom similarity index 100% rename from circuits/dec_aes_256_ctr_try1.circom rename to circuits/aes/dec_aes_256_ctr_try1.circom diff --git a/circuits/gcm_siv_dec_2_keys.circom b/circuits/aes/gcm_siv_dec_2_keys.circom similarity index 100% rename from circuits/gcm_siv_dec_2_keys.circom rename to circuits/aes/gcm_siv_dec_2_keys.circom diff --git a/circuits/gcm_siv_dec_2_keys_test.circom b/circuits/aes/gcm_siv_dec_2_keys_test.circom similarity index 100% rename from circuits/gcm_siv_dec_2_keys_test.circom rename to circuits/aes/gcm_siv_dec_2_keys_test.circom diff --git a/circuits/gcm_siv_enc_2_keys.circom b/circuits/aes/gcm_siv_enc_2_keys.circom similarity index 100% rename from circuits/gcm_siv_enc_2_keys.circom rename to circuits/aes/gcm_siv_enc_2_keys.circom diff --git a/circuits/gfmul_int.circom b/circuits/aes/gfmul_int.circom similarity index 100% rename from circuits/gfmul_int.circom rename to circuits/aes/gfmul_int.circom diff --git a/circuits/helper_functions.circom b/circuits/aes/helper_functions.circom similarity index 100% rename from circuits/helper_functions.circom rename to circuits/aes/helper_functions.circom diff --git a/circuits/lib_circuits/aliascheck.circom b/circuits/aes/lib_circuits/aliascheck.circom similarity index 100% rename from circuits/lib_circuits/aliascheck.circom rename to circuits/aes/lib_circuits/aliascheck.circom diff --git a/circuits/lib_circuits/binsum.circom b/circuits/aes/lib_circuits/binsum.circom similarity index 100% rename from circuits/lib_circuits/binsum.circom rename to circuits/aes/lib_circuits/binsum.circom diff --git a/circuits/lib_circuits/bitify.circom b/circuits/aes/lib_circuits/bitify.circom similarity index 100% rename from circuits/lib_circuits/bitify.circom rename to circuits/aes/lib_circuits/bitify.circom diff --git a/circuits/lib_circuits/comparators.circom b/circuits/aes/lib_circuits/comparators.circom similarity index 100% rename from circuits/lib_circuits/comparators.circom rename to circuits/aes/lib_circuits/comparators.circom diff --git a/circuits/lib_circuits/compconstant.circom b/circuits/aes/lib_circuits/compconstant.circom similarity index 100% rename from circuits/lib_circuits/compconstant.circom rename to circuits/aes/lib_circuits/compconstant.circom diff --git a/circuits/lib_circuits/gates.circom b/circuits/aes/lib_circuits/gates.circom similarity index 100% rename from circuits/lib_circuits/gates.circom rename to circuits/aes/lib_circuits/gates.circom diff --git a/circuits/lib_circuits/mux1.circom b/circuits/aes/lib_circuits/mux1.circom similarity index 100% rename from circuits/lib_circuits/mux1.circom rename to circuits/aes/lib_circuits/mux1.circom diff --git a/circuits/lib_circuits/sha256.circom b/circuits/aes/lib_circuits/sha256.circom similarity index 100% rename from circuits/lib_circuits/sha256.circom rename to circuits/aes/lib_circuits/sha256.circom diff --git a/circuits/mul.circom b/circuits/aes/mul.circom similarity index 100% rename from circuits/mul.circom rename to circuits/aes/mul.circom diff --git a/circuits/polyval.circom b/circuits/aes/polyval.circom similarity index 100% rename from circuits/polyval.circom rename to circuits/aes/polyval.circom diff --git a/circuits/tiny.circom b/circuits/aes/tiny.circom similarity index 100% rename from circuits/tiny.circom rename to circuits/aes/tiny.circom diff --git a/circuits/vclmul_emulator.circom b/circuits/aes/vclmul_emulator.circom similarity index 100% rename from circuits/vclmul_emulator.circom rename to circuits/aes/vclmul_emulator.circom diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000..fdcc079 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,40 @@ +// @devloper: document each const origin on call +pub(crate) const KEY_ASCII: &str = "1111111111111111"; // 16 bytes +pub(crate) const IV_ASCII: &str = "111111111111"; // 12 bytes +pub(crate) const MESSAGE: &str = "test000000000000"; +pub(crate) const KEY_BYTES: [u8; 16] = [ + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, +]; +pub(crate) const KEY_BYTES_256: [u8; 32] = [ + 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +]; +// test ascii +pub(crate) const MESSAGE_BYTES: [u8; 16] = [ + 0x74, 0x65, 0x73, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, +]; +pub(crate) const ZERO_MESSAGE_BYTES_256: [u8; 16] = + [0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]; + +// 765697b2244f246112a0d551aba59013a51e2eb57a229b92be46bf4e1e1c2068 +// 85a01b63025ba19b7fd3ddfc033b3e76c9eac6fa700942702e90862383c6c366 + +// The TLS version converts the 12-byte IV into 16 bytes by padding with 0001. +pub(crate) const IV_BYTES: [u8; 16] = [ + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x0, 0x0, 0x0, + 0x01, // GCM fills it out like this (when the IV is 12 bytes) +]; +pub(crate) const IV_BYTES_256: [u8; 16] = [ + 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + // This matches the other impl passed in 12 bytes. + // This is how GCM implements it. + 0x0, 0x0, 0x0, 0x1, +]; + +pub(crate) const IV_BYTES_SHORT: [u8; 12] = + [0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31]; +pub(crate) const IV_BYTES_SHORT_256: [u8; 12] = + [0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]; + +pub(crate) const SIV_AAD: [u8; 16] = + [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7f292aa --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,66 @@ +//! This generates witnesses to test circom artifacts in the `circuits` directory. + +#![feature(trivial_bounds)] +#![allow(unused_imports)] +#![allow(unused_variables)] +#![allow(dead_code)] +#![allow(unreachable_code)] +#![allow(non_snake_case)] +#![allow(clippy::clone_on_copy)] +#![allow(unused_mut)] + +use std::io; + +use aes::{cipher::generic_array::GenericArray, Aes256}; +use ark_circom::CircomBuilder; +use ark_ec::pairing::Pairing; +use cipher::consts::U16; + +mod consts; +mod proof; +mod utils; +mod witness; + +/// Circom compilation artifacts +/// Must compile circom artifacts first if these aren't found. +const SIV_WTNS: &str = "./build/gcm_siv_dec_2_keys_test_js/gcm_siv_dec_2_keys_test.wasm"; +const SIV_R1CS: &str = "./build/gcm_siv_dec_2_keys_test.r1cs"; +const AES_256_CRT_WTNS: &str = "./build/aes_256_ctr_test_js/aes_256_ctr_test.wasm"; +const AES_256_CRT_R1CS: &str = "./build/aes_256_ctr_test.r1cs"; + +pub type AAD = [u8; 5]; +pub type Nonce = [u8; 12]; + +// convenience type aliases for AES-CTR, wrapping type aliases from `ctr` crate +pub(crate) type Ctr32BE = ctr::CtrCore; +pub(crate) type Aes256Ctr32BE = ctr::Ctr32BE; +pub(crate) type Aes128Ctr32BE = ctr::Ctr32BE; // Note: Ctr32BE is used in AES GCM + +/// AES 128-bit block +pub(crate) type Block = GenericArray; + +#[cfg(test)] +mod tests { + use super::*; + + // Test the AES-GCM-SIV circuit (from electron labs) + #[tokio::test] + async fn test_aes_gcm_siv() -> io::Result<()> { + // generate witness + let mut witness = witness::aes_witnesses(witness::CipherMode::GcmSiv).unwrap(); + + // log one of them + println!( + "proof gen: key={:?}, iv={:?}, ct={:?}, pt={:?}", + witness.key, witness.iv, witness.ct, witness.pt + ); + + // TODO(TK 2024-08-06): replace hackz with documented methods + // hackz for 128 bit iv, Ask Tracy about this + witness.iv.extend_from_slice(&[0, 0, 0, 0]); + + // generate proof + proof::gen_proof_aes_gcm_siv(&witness, SIV_WTNS, SIV_R1CS); + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index db293b8..0000000 --- a/src/main.rs +++ /dev/null @@ -1,105 +0,0 @@ -#![feature(trivial_bounds)] - -use std::io; - -use ark_circom::CircomBuilder; -use ark_ec::pairing::Pairing; - -mod proof; -mod witness; - - -// You have to compile circom artifacts first if these aren't found. -// should I commit them? -const SIV_WTNS: &str = "./build/gcm_siv_dec_2_keys_test_js/gcm_siv_dec_2_keys_test.wasm"; -const SIV_R1CS: &str = "./build/gcm_siv_dec_2_keys_test.r1cs"; - -const AES_256_CRT_WTNS: &str = "./build/aes_256_ctr_test_js/aes_256_ctr_test.wasm"; -const AES_256_CRT_R1CS: &str = "./build/aes_256_ctr_test.r1cs"; - - -pub struct Witness { - pub key: Vec, - pub iv: Vec, - pub ct: Vec, - pub pt: Vec, -} - -#[tokio::main] -async fn main() -> io::Result<()> { - // aes_gcm_siv_test - // aes_gcm_siv_test().await?; - - // plain aes_ctr test - aes_256ctr_test().await?; - Ok(()) -} - -async fn aes_gcm_siv_test() -> io::Result<()> { - // generate witness - let mut witness = witness::aes_witnesses(witness::CipherMode::GcmSiv); - - // log one of them - println!( - "proof gen: key={:?}, iv={:?}, ct={:?}, pt={:?}", - witness.key, witness.iv, witness.ct, witness.pt - ); - witness.iv.extend_from_slice(&[0, 0, 0, 0]); // hackz for 128 bit iv, Ask Tracy about this - - // generate a proof - proof::gen_proof_aes_gcm_siv(&witness, SIV_WTNS, SIV_R1CS); - Ok(()) -} - -async fn aes_256ctr_test() -> io::Result<()> { - let mut witness = witness::aes_witnesses(witness::CipherMode::Ctr256); - - // log one of them - println!( - "proof gen: key={:?}, iv={:?}, ct={:?}, pt={:?}", - witness.key, witness.iv, witness.ct, witness.pt - ); - witness.iv.extend_from_slice(&[0, 0, 0, 0]); // hackz for 128 bit iv - - // generate a proof - proof::gen_proof_aes_gcm_siv(&witness, AES_256_CRT_WTNS, AES_256_CRT_R1CS); - - Ok(()) -} - -// Convert bytes to bits (process in big endian order) -fn push_bytes_as_bits( - mut builder: CircomBuilder, - field: &str, - bytes: &[u8], -) -> CircomBuilder { - for byte in bytes { - for i in 0..8 { - let bit = (byte >> (7 - i)) & 1; - builder.push_input(field, bit as u64); - } - } - - builder -} - -pub fn make_nonce(iv: [u8; 12], seq: u64) -> [u8; 12] { - let mut nonce = [0u8; 12]; - nonce[4..].copy_from_slice(&seq.to_be_bytes()); - - nonce.iter_mut().zip(iv.iter()).for_each(|(nonce, iv)| { - *nonce ^= *iv; - }); - - nonce -} - -fn make_tls13_aad(len: usize) -> [u8; 5] { - [ - 0x17, // ContentType::ApplicationData - 0x3, // ProtocolVersion (major) - 0x3, // ProtocolVersion (minor) - (len >> 8) as u8, - len as u8, - ] -} diff --git a/src/proof.rs b/src/proof.rs index b35507c..0ae48b0 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,21 +1,26 @@ +//! Generate proofs with ark-circom with the circom artifacts and the generated witness + +use std::fs::File; + use ark_bn254::{Bn254, Fr}; use ark_circom::{circom::R1CSFile, CircomBuilder, CircomConfig}; use ark_crypto_primitives::snark::SNARK; use ark_groth16::Groth16; use ark_std::rand::thread_rng; -use std::fs::File; type GrothBn = Groth16; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; -use crate::{push_bytes_as_bits, Witness}; +use crate::{ + utils::{bits_to_u8, parse_bit_from_field, push_bytes_as_bits}, + witness::Witness, +}; -// load up the circom -// generate a witness -// generate the proof -// check plaintext -// check success bit -// key: &[u8], iv: &[u8], ct: &[u8], pt: &[u8] +/// load up the circom +/// generate a witness +/// generate the proof +/// check plaintext +/// check success bit pub fn gen_proof_aes_gcm_siv(witness: &Witness, wtns: &str, r1cs: &str) { println!("prep config"); @@ -23,95 +28,85 @@ pub fn gen_proof_aes_gcm_siv(witness: &Witness, wtns: &str, r1cs: &str) { let cfg = CircomConfig::::new(wtns, r1cs).unwrap(); println!("prep builder"); - let mut builder = CircomBuilder::new(cfg); + let mut circom_builder = CircomBuilder::new(cfg); + // TODO(TK 2024-08-06): replace with const // No AAD, but the circuit is sensitive to it. Needs 128 bits. - let aad = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; // aes ctr has no aad + // aes ctr has no aad + let aad = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + // TODO(TK 2024-08-06): + // code smell: can't tell what this is doing, even by looking at source + // + // abstract deeper to initialize witness builder + // to avoid manipulating circom builder in place + circom_builder = push_bytes_as_bits(circom_builder, "K1", &witness.key); + circom_builder = push_bytes_as_bits(circom_builder, "N", &witness.iv); + circom_builder = push_bytes_as_bits(circom_builder, "AAD", &aad); + circom_builder = push_bytes_as_bits(circom_builder, "CT", &witness.ct); + + // read r1cs + let r1cs = R1CSFile::::new(File::open(r1cs).unwrap()).unwrap(); - builder = push_bytes_as_bits(builder, "K1", &witness.key); - builder = push_bytes_as_bits(builder, "N", &witness.iv); - builder = push_bytes_as_bits(builder, "AAD", &aad); - builder = push_bytes_as_bits(builder, "CT", &witness.ct); - - // read from disk again - let reader = File::open(r1cs).unwrap(); - let r1cs = R1CSFile::::new(reader).unwrap(); println!("header.n_wires={:?}", r1cs.header.n_wires); println!("header.n_pub_out={:?}", r1cs.header.n_pub_out); println!("header.n_pub_in={:?}", r1cs.header.n_pub_in); println!("header.n_prv_in={:?}", r1cs.header.n_prv_in); println!("header.n_labels={:?}", r1cs.header.n_labels); println!("header.n_constraints={:?}", r1cs.header.n_constraints); - println!("inputs={:?}", builder.inputs); + println!("inputs={:?}", circom_builder.inputs); // create an empty instance for setting it up println!("setup builder"); - let circom = builder.setup(); + let circom = circom_builder.setup(); println!("circuit facts: inputs={}", circom.r1cs.num_inputs); - println!("load params"); let mut rng = thread_rng(); + // Generates a random common reference string for + // a circuit using the provided R1CS-to-QAP reduction. println!("gen params"); let params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng).unwrap(); + // Create the circuit populated with the witness corresponding to the previously + // provided inputs println!("build builder"); - let circom = builder.build().unwrap(); + let circom = circom_builder.build().unwrap(); + println!("get pub input"); let inputs = circom.get_public_inputs().unwrap(); println!("len={:?}", inputs.len()); - // convert bits to bytes - fn bits_to_u8(bits: &[u8]) -> u8 { - bits.iter() - .rev() - .enumerate() - .fold(0, |acc, (i, &bit)| acc | ((bit & 1) << i)) - } - let mut output_bytes = Vec::new(); - for i in inputs.chunks(8) { - let mut bits = Vec::new(); - for j in i { - let bit = if *j == Fr::from(1) { - 1u8 - } else if *j == Fr::from(0) { - 0u8 - } else { - panic!("results should be bits"); - }; - bits.push(bit); - } - let out_byte = bits_to_u8(&bits); - output_bytes.push(out_byte); - } + let output_bytes = inputs + .chunks(8) + .map(|i| { + let bits: Vec = i.iter().map(parse_bit_from_field).collect(); + bits_to_u8(&bits) + }) + .collect::>(); + // generate and test constraints let cs = ConstraintSystem::::new_ref(); circom.clone().generate_constraints(cs.clone()).unwrap(); assert!(cs.is_satisfied().unwrap()); let proof = GrothBn::prove(¶ms, circom, &mut rng).unwrap(); - println!( - "proof_a={:?}, proof_b={:?}, proof_c={:?}", - proof.a, proof.b, proof.c - ); + println!("proof_a={:?}, proof_b={:?}, proof_c={:?}", proof.a, proof.b, proof.c); println!("process vk"); let pvk = GrothBn::process_vk(¶ms.vk).unwrap(); + println!("verify"); let verified = GrothBn::verify_with_processed_vk(&pvk, &inputs, &proof).unwrap(); - println!("verified={:?}", verified); assert!(verified); // Duplicate check, but ensure the plaintext is correct. let pt_bytes = &output_bytes[..witness.pt.len()]; println!("Output bytes matches plaintext pt={:?}", pt_bytes); - assert!(pt_bytes - .iter() - .zip(witness.pt.iter()) - .all(|(&a, &b)| a == b)); + assert!(pt_bytes.iter().zip(witness.pt.iter()).all(|(&a, &b)| a == b)); // Check the success bit (auth_tag matches) - let success_bit = output_bytes[output_bytes.len() - 1]; + let success_bit = output_bytes.last().unwrap(); println!("Success bit={:?}", success_bit); - assert!(success_bit == 1); + assert!(success_bit == &1); } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..9584c05 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,66 @@ +use ark_bn254::Fr; +use ark_circom::CircomBuilder; +use ark_ec::pairing::Pairing; + +use crate::{Nonce, AAD}; + +// TODO(TK 2024-08-06): test with test vectors at bottom of rfc 8452 +// @devloper: do you know/couldyou find where make_nonce is specified in rfc8452? +// +/// construct the nonce from the `iv` and `seq` as specified in RFC 8452 +/// https://www.rfc-editor.org/rfc/rfc8452 +pub(crate) fn make_nonce(iv: [u8; 12], seq: u64) -> Nonce { + let mut nonce = [0u8; 12]; + nonce[4..].copy_from_slice(&seq.to_be_bytes()); + + nonce.iter_mut().zip(iv).for_each(|(nonce, iv)| { + *nonce ^= iv; + }); + + nonce +} + +pub(crate) fn make_tls13_aad(len: usize) -> AAD { + [ + 0x17, // ContentType::ApplicationData + 0x3, // ProtocolVersion (major) + 0x3, // ProtocolVersion (minor) + (len >> 8) as u8, + len as u8, + ] +} + +// TODO(TK 2024-08-06): @devloper, document and refactor for transparency +pub(crate) fn push_bytes_as_bits( + mut builder: CircomBuilder, + field: &str, + bytes: &[u8], +) -> CircomBuilder { + for byte in bytes { + for i in 0..8 { + let bit = (byte >> (7 - i)) & 1; + builder.push_input(field, bit as u64); + } + } + + builder +} + +// convert bits to bytes +pub(crate) fn bits_to_u8(bits: &[u8]) -> u8 { + bits.iter().rev().enumerate().fold(0, |acc, (i, &bit)| acc | ((bit & 1) << i)) +} + +pub(crate) fn parse_bit_from_field(j: &Fr) -> u8 { + // TODO(TK 2024-08-06): move to lazy static to avoid duplication + let ONE = Fr::from(1); + let ZERO = Fr::from(0); + + if *j == ONE { + 1u8 + } else if *j == ZERO { + 0u8 + } else { + panic!("results should be bits") + } +} diff --git a/src/witness.rs b/src/witness.rs index 6c5aa03..230ecce 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,3 +1,14 @@ +//! Generate witnesses for different AES cipher modes +//! +//! NOTES on AES +//! - AES-GCM, the authentication is a 16 byte string appended to the ciphertext. +//! - AES-GCM auth tag is encrypted at the end. +//! - AES-GCM the AAD only effects the auth tag +//! - AES-GCM-SIV, AAD impacts all ciphertext. +//! - AES is processed in 16 byte chunks. The chunks are then appended together. +//! - AES-CTR is a subset of GCM with some adjustments to IV prep (16 bytes) +//! - AES-GCM can be decrypted by AES-CTR, by skipping the auth tag and setting up the IV correctly. + use aes::{ cipher::{BlockEncrypt, InnerIvInit, KeyInit, KeyIvInit, StreamCipher, StreamCipherCore}, Aes128, Aes256, @@ -6,90 +17,53 @@ use aes_gcm::{ aead::{generic_array::GenericArray, Aead, NewAead, Payload}, Aes128Gcm, Aes256Gcm, }; +use anyhow::{Context, Result}; use cipher::consts::U16; -use ctr; -use ghash; -use hex; -type Ctr32BE = ctr::CtrCore; -type Aes256Ctr32BE = ctr::Ctr32BE; -type Block = GenericArray; -type Aes128Ctr32BE = ctr::Ctr32BE; // Note: Ctr32BE is used in AES GCM +use crate::{ + consts::*, + utils::{make_nonce, make_tls13_aad}, + Aes128Ctr32BE, Aes256Ctr32BE, Block, Ctr32BE, +}; + +/// Witness bytes generated by this binary +#[derive(Debug)] +pub struct Witness { + pub key: Vec, + pub iv: Vec, + pub ct: Vec, + pub pt: Vec, +} + +impl Witness { + pub fn new(key: &[u8], iv: &[u8], ct: &[u8], pt: &[u8]) -> Self { + Self { key: key.to_vec(), iv: iv.to_vec(), ct: ct.to_vec(), pt: pt.to_vec() } + } +} +/// AES cipher modes. +#[derive(Default)] pub enum CipherMode { Vanilla, // no IV Here Ctr256, GcmSiv, GCM256, Ctr128, + /// AES-GCM-128 bit + #[default] + GCM128, } -use crate::{make_nonce, make_tls13_aad, Witness}; - -const KEY_ASCII: &str = "1111111111111111"; // 16 bytes -const IV_ASCII: &str = "111111111111"; // 12 bytes -const MESSAGE: &str = "test000000000000"; -const KEY_BYTES: [u8; 16] = [ - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, -]; -const KEY_BYTES_256: [u8; 32] = [ - 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -]; -// test ascii -const MESSAGE_BYTES: [u8; 16] = [ - 0x74, 0x65, 0x73, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, -]; -const MESSAGE_BYTES_256: [u8; 16] = [ - 0x74, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -]; - -// 765697b2244f246112a0d551aba59013a51e2eb57a229b92be46bf4e1e1c2068 -// 85a01b63025ba19b7fd3ddfc033b3e76c9eac6fa700942702e90862383c6c366 - -// The TLS version converts the 12-byte IV into 16 bytes by padding with 0001. -const IV_BYTES: [u8; 16] = [ - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x0, 0x0, 0x0, - 0x01, // GCM fills it out like this (when the IV is 12 bytes) -]; -const IV_BYTES_256: [u8; 16] = [ - 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - // This matches the other impl passed in 12 bytes. - // This is how GCM implements it. - 0x0, 0x0, 0x0, 0x1, -]; - -const IV_BYTES_SHORT: [u8; 12] = [ - 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, -]; -const IV_BYTES_SHORT_256: [u8; 12] = [0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]; - -const SIV_AAD: [u8; 16] = [ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -]; -fn encrypt_tls(message: &[u8], key: &[u8], iv: &[u8], seq: u64) -> Vec { +// @devloper: to discuss refactor on call +fn encrypt_tls(message: &[u8], key: &[u8], iv: &[u8], seq: u64) -> Result> { let total_len = message.len() + 1 + 16; let aad = make_tls13_aad(total_len); - let fixed_iv = iv[..12].try_into().unwrap(); - // let fixed_key = key[..16].try_into().unwrap(); - let nonce = make_nonce(fixed_iv, seq); // hmmmm. + let fixed_iv = iv[..12].try_into()?; + let nonce = make_nonce(fixed_iv, seq); - println!( - "ENC: msg={:?}, msg_len={:?}, seq={:?}", - hex::encode(message), - message.len(), - seq - ); - println!( - "ENC: iv={:?}, dec_key={:?}", - hex::encode(iv), - hex::encode(key) - ); - println!( - "ENC: nonce={:?}, aad={:?}", - hex::encode(nonce), - hex::encode(aad) - ); + println!("ENC: msg={:?}, msg_len={:?}, seq={:?}", hex::encode(message), message.len(), seq); + println!("ENC: iv={:?}, dec_key={:?}", hex::encode(iv), hex::encode(key)); + println!("ENC: nonce={:?}, aad={:?}", hex::encode(nonce), hex::encode(aad)); let mut payload = Vec::with_capacity(total_len); payload.extend_from_slice(message); @@ -97,51 +71,34 @@ fn encrypt_tls(message: &[u8], key: &[u8], iv: &[u8], seq: u64) -> Vec { let aes_payload = Payload { msg: &payload, - aad: &[], // empty aad ?? + aad: &[], // todo empty aad ?? }; let cipher = Aes128Gcm::new_from_slice(key).unwrap(); let nonce = GenericArray::from_slice(iv); - cipher - .encrypt(nonce, aes_payload) - .expect("error generating ct") + Ok(cipher.encrypt(nonce, aes_payload).expect("error generating ct")) } - -pub fn aes_witnesses(cipher_mode: CipherMode) -> Witness { - // NOTES on AES - // - AES-GCM, the authentication is a 16 byte string appended to the ciphertext. - // - AES-GCM auth tag is encrypted at the end. - // - AES-GCM the AAD only effects the auth tag - // - AES-GCM-SIV, AAD impacts all ciphertext. - // - AES is processed in 16 byte chunks. The chunks are then appended together. - // - AES-CTR is a subset of GCM with some adjustments to IV prep (16 bytes) - // - AES-GCM can be decrypted by AES-CTR, by skipping the auth tag and setting up the IV correctly. - +// @devloper: to discuss refactor on call +pub fn aes_witnesses(cipher_mode: CipherMode) -> Result { // Base ASCII versions using TLS encryption. - let ct = encrypt_tls( - MESSAGE.as_bytes(), - KEY_ASCII.as_bytes(), - IV_ASCII.as_bytes(), - 1, - ); - println!( - "ENC: cipher_text={:?}, cipher_len={:?}", - hex::encode(ct.clone()), - ct.len() - ); + let ct = encrypt_tls(MESSAGE.as_bytes(), KEY_ASCII.as_bytes(), IV_ASCII.as_bytes(), 1).unwrap(); + println!("ENC: cipher_text={:?}, cipher_len={:?}", hex::encode(ct.clone()), ct.len()); + + // TODO(TK 2024-08-06): move initialization to a constructor let key = GenericArray::from(KEY_BYTES); let key_256 = GenericArray::from(KEY_BYTES_256); let iv = GenericArray::from(IV_BYTES); let mut block = GenericArray::from(MESSAGE_BYTES); - let mut block_256 = GenericArray::from(MESSAGE_BYTES_256); + let mut block_256 = GenericArray::from(ZERO_MESSAGE_BYTES_256); - let cipher_text = match cipher_mode { + // TODO(TK 2024-08-06):move to ciphermode method + let ct = match cipher_mode { CipherMode::Vanilla => { let cipher = Aes128::new(&key); cipher.encrypt_block(&mut block); block.to_vec() - } + }, CipherMode::Ctr256 => { // AES CTR 256, adjusted to match GCM. ✅, matches AES-256-GCM impl let mut cipher_256 = Aes256Ctr32BE::new(&key_256, &IV_BYTES_256.into()); @@ -150,7 +107,7 @@ pub fn aes_witnesses(cipher_mode: CipherMode) -> Witness { cipher_256.apply_keystream(&mut tag_mask_256); cipher_256.apply_keystream(&mut block_256); block_256.to_vec() - } + }, CipherMode::GcmSiv => { // AES GCM SIV, WOO MATCHES CIRCOM!! ✅ use aes_gcm_siv::{ @@ -159,33 +116,23 @@ pub fn aes_witnesses(cipher_mode: CipherMode) -> Witness { }; let cipher = Aes256GcmSiv::new_from_slice(&key_256).unwrap(); let nonce = GenericArray::from_slice(&IV_BYTES_SHORT_256); - let aes_payload = SIVPayload { - msg: &MESSAGE_BYTES_256, - aad: &SIV_AAD, - }; - let ciphertext_siv = cipher - .encrypt(nonce, aes_payload) - .expect("error generating ct"); + let aes_payload = SIVPayload { msg: &ZERO_MESSAGE_BYTES_256, aad: &SIV_AAD }; + let ciphertext_siv = cipher.encrypt(nonce, aes_payload).expect("error generating ct"); println!( "AES GCM 256 SIV: ct={:?}, bytes={:?}", hex::encode(ciphertext_siv.clone()), ciphertext_siv ); ciphertext_siv.to_vec() - } + }, CipherMode::GCM256 => { // Standard AES 256 GCM let cipher = Aes256Gcm::new_from_slice(&key_256).unwrap(); let nonce = GenericArray::from_slice(&IV_BYTES_SHORT_256); - let aes_payload = Payload { - msg: &MESSAGE_BYTES_256, - aad: &SIV_AAD, - }; - let ct = cipher - .encrypt(nonce, aes_payload) - .expect("error generating ct"); + let aes_payload = Payload { msg: &ZERO_MESSAGE_BYTES_256, aad: &SIV_AAD }; + let ct = cipher.encrypt(nonce, aes_payload).expect("error generating ct"); ct.to_vec() - } + }, CipherMode::Ctr128 => { // AES CTR 128, adjusted to match GCM. ✅, matches AES-128-GCM impl let mut cipher = Aes128Ctr32BE::new(&key, &iv); @@ -193,10 +140,13 @@ pub fn aes_witnesses(cipher_mode: CipherMode) -> Witness { cipher.apply_keystream(&mut tag_mask); // In AES-GCM, an empty mask is encrypted first. cipher.apply_keystream(&mut block); block.to_vec() - } - + }, + CipherMode::GCM128 => { + unimplemented!() + }, }; + // todo: document source // TODO: WTF is this // AES-GCM Duplication. NOTE: This is identical to section 246. // Init logic in AES-GCM. This standard procedure can be applied to the TLS IV. @@ -218,11 +168,7 @@ pub fn aes_witnesses(cipher_mode: CipherMode) -> Witness { // WORKING! The aes-ctr and aes-gcm now match. // TODO: Clean up these printlns - println!( - "INPUT iv={:?}, key={:?}", - hex::encode(IV_BYTES), - hex::encode(KEY_BYTES) - ); + println!("INPUT iv={:?}, key={:?}", hex::encode(IV_BYTES), hex::encode(KEY_BYTES)); println!( "AES GCM IV={:?}, tm={:?}, ct={:?}", hex::encode(ghash_iv), @@ -231,19 +177,7 @@ pub fn aes_witnesses(cipher_mode: CipherMode) -> Witness { ); println!("AES CTR: ct={:?}", hex::encode(block)); println!("AES CTR 256, 96 IV: ct={:?}", hex::encode(block)); - println!("AES GCM 256: ct={:?}", hex::encode(cipher_text.clone())); + println!("AES GCM 256: ct={:?}", hex::encode(ct.clone())); - let key_out = key_256.to_vec(); - let ct_out = cipher_text.to_vec(); - - // same for all modes, vanilla has no IV - let iv_out = IV_BYTES_SHORT_256.to_vec(); - let pt_out = MESSAGE_BYTES_256.to_vec(); - - Witness { - key: key_out, - iv: iv_out, - ct: ct_out, - pt: pt_out, - } + Ok(Witness::new(&key_256, &IV_BYTES_SHORT_256, &ct, &ZERO_MESSAGE_BYTES_256)) }