From 8aa8b0895aa9a19aa81a98431043fab199e9dcec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jepsen=20=E2=9C=A8?= <57912727+0xJepsen@users.noreply.github.com> Date: Tue, 19 Nov 2024 22:49:54 +0700 Subject: [PATCH] Revert "Chacha20" (#50) --- .../target_1024b/aes_gctr_nivc_1024b.circom | 5 + builds/target_1024b/chacha20_nivc_1024.circom | 5 - .../target_1024b/http_body_mask_1024b.circom | 6 + .../http_lock_header_1024b.circom | 5 + ...ttp_parse_and_lock_start_line_1024b.circom | 5 + builds/target_512b/aes_gctr_nivc_512b.circom | 5 + builds/target_512b/chacha20_nivc_512b.circom | 5 - builds/target_512b/http_body_mask_512b.circom | 6 + .../target_512b/http_lock_header_512b.circom | 5 + ...http_parse_and_lock_start_line_512b.circom | 5 + circuits/chacha20/chacha-qr.circom | 88 ----------- circuits/chacha20/chacha-round.circom | 112 -------------- circuits/chacha20/chacha20.circom | 108 -------------- circuits/chacha20/nivc/chacha20_nivc.circom | 139 ------------------ .../test/aes-gcm/nivc/aes-gctr-nivc.test.ts | 12 +- circuits/test/chacha20/chacha20-nivc.test.ts | 83 ----------- circuits/test/chacha20/chacha20.test.ts | 123 ---------------- circuits/test/common/index.ts | 136 ----------------- circuits/test/full/full.test.ts | 117 +++++++++------ circuits/test/utils/array.test.ts | 23 +-- circuits/utils/array.circom | 21 --- circuits/utils/generics-bits.circom | 74 ---------- package.json | 2 +- 23 files changed, 123 insertions(+), 967 deletions(-) create mode 100644 builds/target_1024b/aes_gctr_nivc_1024b.circom delete mode 100644 builds/target_1024b/chacha20_nivc_1024.circom create mode 100644 builds/target_1024b/http_body_mask_1024b.circom create mode 100644 builds/target_1024b/http_lock_header_1024b.circom create mode 100644 builds/target_1024b/http_parse_and_lock_start_line_1024b.circom create mode 100644 builds/target_512b/aes_gctr_nivc_512b.circom delete mode 100644 builds/target_512b/chacha20_nivc_512b.circom create mode 100644 builds/target_512b/http_body_mask_512b.circom create mode 100644 builds/target_512b/http_lock_header_512b.circom create mode 100644 builds/target_512b/http_parse_and_lock_start_line_512b.circom delete mode 100644 circuits/chacha20/chacha-qr.circom delete mode 100644 circuits/chacha20/chacha-round.circom delete mode 100644 circuits/chacha20/chacha20.circom delete mode 100644 circuits/chacha20/nivc/chacha20_nivc.circom delete mode 100644 circuits/test/chacha20/chacha20-nivc.test.ts delete mode 100644 circuits/test/chacha20/chacha20.test.ts delete mode 100644 circuits/utils/generics-bits.circom diff --git a/builds/target_1024b/aes_gctr_nivc_1024b.circom b/builds/target_1024b/aes_gctr_nivc_1024b.circom new file mode 100644 index 0000000..24a2834 --- /dev/null +++ b/builds/target_1024b/aes_gctr_nivc_1024b.circom @@ -0,0 +1,5 @@ +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/chacha20_nivc_1024.circom b/builds/target_1024b/chacha20_nivc_1024.circom deleted file mode 100644 index 9794058..0000000 --- a/builds/target_1024b/chacha20_nivc_1024.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.9; - -include "../../circuits/chacha20/chacha20.circom"; - -component main = ChaCha20_NIVC(256); \ No newline at end of file diff --git a/builds/target_1024b/http_body_mask_1024b.circom b/builds/target_1024b/http_body_mask_1024b.circom new file mode 100644 index 0000000..8309267 --- /dev/null +++ b/builds/target_1024b/http_body_mask_1024b.circom @@ -0,0 +1,6 @@ +pragma circom 2.1.9; + +include "../../circuits/http/nivc/body_mask.circom"; + +component main { public [step_in] } = HTTPMaskBodyNIVC(1024); + diff --git a/builds/target_1024b/http_lock_header_1024b.circom b/builds/target_1024b/http_lock_header_1024b.circom new file mode 100644 index 0000000..de4863d --- /dev/null +++ b/builds/target_1024b/http_lock_header_1024b.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../../circuits/http/nivc/lock_header.circom"; + +component main { public [step_in] } = LockHeader(1024, 50, 100); \ No newline at end of file diff --git a/builds/target_1024b/http_parse_and_lock_start_line_1024b.circom b/builds/target_1024b/http_parse_and_lock_start_line_1024b.circom new file mode 100644 index 0000000..b70daa1 --- /dev/null +++ b/builds/target_1024b/http_parse_and_lock_start_line_1024b.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../../circuits/http/nivc/parse_and_lock_start_line.circom"; + +component main { public [step_in] } = ParseAndLockStartLine(1024, 50, 200, 50); \ No newline at end of file diff --git a/builds/target_512b/aes_gctr_nivc_512b.circom b/builds/target_512b/aes_gctr_nivc_512b.circom new file mode 100644 index 0000000..24a2834 --- /dev/null +++ b/builds/target_512b/aes_gctr_nivc_512b.circom @@ -0,0 +1,5 @@ +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 ce593a0..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 = ChaCha20_NIVC(128); \ No newline at end of file diff --git a/builds/target_512b/http_body_mask_512b.circom b/builds/target_512b/http_body_mask_512b.circom new file mode 100644 index 0000000..dea77d1 --- /dev/null +++ b/builds/target_512b/http_body_mask_512b.circom @@ -0,0 +1,6 @@ +pragma circom 2.1.9; + +include "../../circuits/http/nivc/body_mask.circom"; + +component main { public [step_in] } = HTTPMaskBodyNIVC(512); + diff --git a/builds/target_512b/http_lock_header_512b.circom b/builds/target_512b/http_lock_header_512b.circom new file mode 100644 index 0000000..5017fe0 --- /dev/null +++ b/builds/target_512b/http_lock_header_512b.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../../circuits/http/nivc/lock_header.circom"; + +component main { public [step_in] } = LockHeader(512, 50, 100); \ No newline at end of file diff --git a/builds/target_512b/http_parse_and_lock_start_line_512b.circom b/builds/target_512b/http_parse_and_lock_start_line_512b.circom new file mode 100644 index 0000000..6463e4c --- /dev/null +++ b/builds/target_512b/http_parse_and_lock_start_line_512b.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../../circuits/http/nivc/parse_and_lock_start_line.circom"; + +component main { public [step_in] } = ParseAndLockStartLine(512, 50, 200, 50); \ No newline at end of file diff --git a/circuits/chacha20/chacha-qr.circom b/circuits/chacha20/chacha-qr.circom deleted file mode 100644 index 7ce1487..0000000 --- a/circuits/chacha20/chacha-qr.circom +++ /dev/null @@ -1,88 +0,0 @@ -// initially from https://github.com/reclaimprotocol/zk-symmetric-crypto -// modified for our needs -pragma circom 2.1.9; - -include "../utils/generics-bits.circom"; - -/** - * Perform ChaCha Quarter Round - * Assume 4 words of 32 bits each - * Each word must be little endian - */ -template QR() { - signal input in[4][32]; - signal output out[4][32]; - - var tmp[4][32] = in; - - // a += b - component add1 = AddBits(32); - add1.a <== tmp[0]; - add1.b <== tmp[1]; - - tmp[0] = add1.out; - - // d ^= a - component xor1 = XorBits(32); - xor1.a <== tmp[3]; - xor1.b <== tmp[0]; - tmp[3] = xor1.out; - - // d = RotateLeft32BitsUnsafe(d, 16) - component rot1 = RotateLeftBits(32, 16); - rot1.in <== tmp[3]; - tmp[3] = rot1.out; - - // c += d - component add2 = AddBits(32); - add2.a <== tmp[2]; - add2.b <== tmp[3]; - tmp[2] = add2.out; - - // b ^= c - component xor2 = XorBits(32); - xor2.a <== tmp[1]; - xor2.b <== tmp[2]; - tmp[1] = xor2.out; - - // b = RotateLeft32BitsUnsafe(b, 12) - component rot2 = RotateLeftBits(32, 12); - rot2.in <== tmp[1]; - tmp[1] = rot2.out; - - // a += b - component add3 = AddBits(32); - add3.a <== tmp[0]; - add3.b <== tmp[1]; - tmp[0] = add3.out; - - // d ^= a - component xor3 = XorBits(32); - xor3.a <== tmp[3]; - xor3.b <== tmp[0]; - tmp[3] = xor3.out; - - // d = RotateLeft32BitsUnsafe(d, 8) - component rot3 = RotateLeftBits(32, 8); - rot3.in <== tmp[3]; - tmp[3] = rot3.out; - - // c += d - component add4 = AddBits(32); - add4.a <== tmp[2]; - add4.b <== tmp[3]; - tmp[2] = add4.out; - - // b ^= c - component xor4 = XorBits(32); - xor4.a <== tmp[1]; - xor4.b <== tmp[2]; - tmp[1] = xor4.out; - - // b = RotateLeft32BitsUnsafe(b, 7) - component rot4 = RotateLeftBits(32, 7); - rot4.in <== tmp[1]; - tmp[1] = rot4.out; - - out <== tmp; -} \ No newline at end of file diff --git a/circuits/chacha20/chacha-round.circom b/circuits/chacha20/chacha-round.circom deleted file mode 100644 index 95e4340..0000000 --- a/circuits/chacha20/chacha-round.circom +++ /dev/null @@ -1,112 +0,0 @@ -// initially from https://github.com/reclaimprotocol/zk-symmetric-crypto -// modified for our needs -pragma circom 2.1.9; - -include "./chacha-qr.circom"; -include "../utils/generics-bits.circom"; - -template Round() { - // in => 16 32-bit words - signal input in[16][32]; - // out => 16 32-bit words - signal output out[16][32]; - - var tmp[16][32] = in; - - component rounds[10 * 8]; - component finalAdd[16]; - // i-th round - var i = 0; - // col loop counter - var j = 0; - // counter for the rounds array - var k = 0; - for(i = 0; i < 10; i++) { - // columns of the matrix in a loop - // 0, 4, 8, 12 - // 1, 5, 9, 13 - // 2, 6, 10, 14 - // 3, 7, 11, 15 - for(j = 0; j < 4; j++) { - rounds[k] = QR(); - rounds[k].in[0] <== tmp[j]; - rounds[k].in[1] <== tmp[j + 4]; - rounds[k].in[2] <== tmp[j + 8]; - rounds[k].in[3] <== tmp[j + 12]; - - tmp[j] = rounds[k].out[0]; - tmp[j + 4] = rounds[k].out[1]; - tmp[j + 8] = rounds[k].out[2]; - tmp[j + 12] = rounds[k].out[3]; - - k ++; - } - - // 4 diagnals - // 0, 5, 10, 15 - rounds[k] = QR(); - rounds[k].in[0] <== tmp[0]; - rounds[k].in[1] <== tmp[5]; - rounds[k].in[2] <== tmp[10]; - rounds[k].in[3] <== tmp[15]; - - tmp[0] = rounds[k].out[0]; - tmp[5] = rounds[k].out[1]; - tmp[10] = rounds[k].out[2]; - tmp[15] = rounds[k].out[3]; - - k ++; - - // 1, 6, 11, 12 - rounds[k] = QR(); - rounds[k].in[0] <== tmp[1]; - rounds[k].in[1] <== tmp[6]; - rounds[k].in[2] <== tmp[11]; - rounds[k].in[3] <== tmp[12]; - - tmp[1] = rounds[k].out[0]; - tmp[6] = rounds[k].out[1]; - tmp[11] = rounds[k].out[2]; - tmp[12] = rounds[k].out[3]; - - k ++; - - // 2, 7, 8, 13 - rounds[k] = QR(); - rounds[k].in[0] <== tmp[2]; - rounds[k].in[1] <== tmp[7]; - rounds[k].in[2] <== tmp[8]; - rounds[k].in[3] <== tmp[13]; - - tmp[2] = rounds[k].out[0]; - tmp[7] = rounds[k].out[1]; - tmp[8] = rounds[k].out[2]; - tmp[13] = rounds[k].out[3]; - - k ++; - - // 3, 4, 9, 14 - rounds[k] = QR(); - rounds[k].in[0] <== tmp[3]; - rounds[k].in[1] <== tmp[4]; - rounds[k].in[2] <== tmp[9]; - rounds[k].in[3] <== tmp[14]; - - tmp[3] = rounds[k].out[0]; - tmp[4] = rounds[k].out[1]; - tmp[9] = rounds[k].out[2]; - tmp[14] = rounds[k].out[3]; - - k ++; - } - - // add the result to the input - for(i = 0; i < 16; i++) { - finalAdd[i] = AddBits(32); - finalAdd[i].a <== tmp[i]; - finalAdd[i].b <== in[i]; - tmp[i] = finalAdd[i].out; - } - - out <== tmp; -} diff --git a/circuits/chacha20/chacha20.circom b/circuits/chacha20/chacha20.circom deleted file mode 100644 index 4ba00f8..0000000 --- a/circuits/chacha20/chacha20.circom +++ /dev/null @@ -1,108 +0,0 @@ -// initially from https://github.com/reclaimprotocol/zk-symmetric-crypto -// modified for our needs -pragma circom 2.1.9; - -include "./chacha-round.circom"; -include "./chacha-qr.circom"; -include "../utils/generics-bits.circom"; - -/** ChaCha20 in counter mode */ -// Chacha20 opperates a 4x4 matrix of 32-bit words where the first 4 words are constants: C -// and the next 8 words are the 256 bit key: K. The next 2 words are the block counter: # -// and the last 2 words are the nonce: N. -// +---+---+---+---+ -// | C | C | C | C | -// +---+---+---+---+ -// | K | K | K | K | -// +---+---+---+---+ -// | K | K | K | K | -// +---+---+---+---+ -// | # | # | N | N | -// +---+---+---+---+ -// paramaterized by n which is the number of 32-bit words to encrypt -template ChaCha20(N) { - // key => 8 32-bit words = 32 bytes - signal input key[8][32]; - // nonce => 3 32-bit words = 12 bytes - signal input nonce[3][32]; - // counter => 32-bit word to apply w nonce - signal input counter[32]; - - // the below can be both ciphertext or plaintext depending on the direction - // in => N 32-bit words => N 4 byte words - signal input in[N][32]; - // out => N 32-bit words => N 4 byte words - signal output out[N][32]; - - var tmp[16][32] = [ - [ - // constant 0x61707865 - 0, 1, 1, 0, 0, 0, 0, 1, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 0, 0, 0, 0, 1, 1, - 0, 0, 1, 0, 1 - ], - [ - // constant 0x3320646e - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 1, 0, 0, 0, 1, 1, - 0, 1, 1, 1, 0 - ], - [ - // constant 0x79622d32 - 0, 1, 1, 1, 1, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 1, 0, 0, 0, - 1, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0 - ], - [ - // constant 0x6b206574 - 0, 1, 1, 0, 1, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 0, 1, 1, - 1, 0, 1, 0, 0 - ], - key[0], key[1], key[2], key[3], - key[4], key[5], key[6], key[7], - counter, nonce[0], nonce[1], nonce[2] - ]; - - // 1 in 32-bit words - signal one[32]; - one <== [ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1 - ]; - - var i = 0; - var j = 0; - - // do the ChaCha20 rounds - component rounds[N/16]; - component xors[N]; - component counter_adder[N/16 - 1]; - - for(i = 0; i < N/16; i++) { - rounds[i] = Round(); - rounds[i].in <== tmp; - // XOR block with input - for(j = 0; j < 16; j++) { - xors[i*16 + j] = XorBits(32); - xors[i*16 + j].a <== in[i*16 + j]; - xors[i*16 + j].b <== rounds[i].out[j]; - out[i*16 + j] <== xors[i*16 + j].out; - } - - if(i < N/16 - 1) { - counter_adder[i] = AddBits(32); - counter_adder[i].a <== tmp[12]; - counter_adder[i].b <== one; - - // increment the counter - tmp[12] = counter_adder[i].out; - } - } -} \ No newline at end of file diff --git a/circuits/chacha20/nivc/chacha20_nivc.circom b/circuits/chacha20/nivc/chacha20_nivc.circom deleted file mode 100644 index 16d0a31..0000000 --- a/circuits/chacha20/nivc/chacha20_nivc.circom +++ /dev/null @@ -1,139 +0,0 @@ -// initially from https://github.com/reclaimprotocol/zk-symmetric-crypto -// modified for our needs -pragma circom 2.1.9; - -include "../chacha-round.circom"; -include "../chacha-qr.circom"; -include "../../utils/generics-bits.circom"; -include "../../utils/hash.circom"; -include "../../utils/array.circom"; - - -/** ChaCha20 in counter mode */ -// Chacha20 opperates a 4x4 matrix of 32-bit words where the first 4 words are constants: C -// and the next 8 words are the 256 bit key: K. The next 2 words are the block counter: # -// and the last 2 words are the nonce: N. -// +---+---+---+---+ -// | C | C | C | C | -// +---+---+---+---+ -// | K | K | K | K | -// +---+---+---+---+ -// | K | K | K | K | -// +---+---+---+---+ -// | # | N | N | N | -// +---+---+---+---+ -// paramaterized by n which is the number of 32-bit words to encrypt -template ChaCha20_NIVC(N) { - // key => 8 32-bit words = 32 bytes - signal input key[8][32]; - // nonce => 3 32-bit words = 12 bytes - signal input nonce[3][32]; - // counter => 32-bit word to apply w nonce - signal input counter[32]; - - // the below can be both ciphertext or plaintext depending on the direction - // in => N 32-bit words => N 4 byte words - signal input plainText[N][32]; - // out => N 32-bit words => N 4 byte words - signal input cipherText[N][32]; - - signal input step_in[1]; - signal output step_out[1]; - - var tmp[16][32] = [ - [ - // constant 0x61707865 - 0, 1, 1, 0, 0, 0, 0, 1, 0, - 1, 1, 1, 0, 0, 0, 0, 0, 1, - 1, 1, 1, 0, 0, 0, 0, 1, 1, - 0, 0, 1, 0, 1 - ], - [ - // constant 0x3320646e - 0, 0, 1, 1, 0, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 1, 0, 0, 0, 1, 1, - 0, 1, 1, 1, 0 - ], - [ - // constant 0x79622d32 - 0, 1, 1, 1, 1, 0, 0, 1, 0, - 1, 1, 0, 0, 0, 1, 0, 0, 0, - 1, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0 - ], - [ - // constant 0x6b206574 - 0, 1, 1, 0, 1, 0, 1, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 0, 1, 1, - 1, 0, 1, 0, 0 - ], - key[0], key[1], key[2], key[3], - key[4], key[5], key[6], key[7], - counter, nonce[0], nonce[1], nonce[2] - ]; - - // 1 in 32-bit words - signal one[32]; - one <== [ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1 - ]; - - var i = 0; - var j = 0; - - // do the ChaCha20 rounds - // rounds opperates on 4 words at a time - component rounds[N/16]; - component xors[N]; - component counter_adder[N/16 - 1]; - - signal computedCipherText[N][32]; - - for(i = 0; i < N/16; i++) { - rounds[i] = Round(); - rounds[i].in <== tmp; - // XOR block with input - for(j = 0; j < 16; j++) { - xors[i*16 + j] = XorBits(32); - xors[i*16 + j].a <== plainText[i*16 + j]; - xors[i*16 + j].b <== rounds[i].out[j]; - computedCipherText[i*16 + j] <== xors[i*16 + j].out; - } - - if(i < N/16 - 1) { - counter_adder[i] = AddBits(32); - counter_adder[i].a <== tmp[12]; - counter_adder[i].b <== one; - - // increment the counter - tmp[12] = counter_adder[i].out; - } - } - - signal ciphertext_equal_check[N][32]; - for(var i = 0 ; i < N; i++) { - for(var j = 0 ; j < 32 ; j++) { - ciphertext_equal_check[i][j] <== IsEqual()([computedCipherText[i][j], cipherText[i][j]]); - ciphertext_equal_check[i][j] === 1; - } - } - - component toBytes[N]; - signal bigEndianPlaintext[N*4]; - for(var i = 0 ; i < N; i++) { - toBytes[i] = fromLittleEndianToWords32(); - for(var j = 0 ; j < 32 ; j++) { - toBytes[i].data[j] <== plainText[i][j]; - } - for(var j = 0; j < 4; j++) { - bigEndianPlaintext[i*4 + j] <== toBytes[i].words[j]; - } - } - signal data_hash <== DataHasher(N*4)(bigEndianPlaintext); - step_out[0] <== data_hash; -} \ No newline at end of file diff --git a/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts b/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts index 8a623d2..c1f2de2 100644 --- a/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts +++ b/circuits/test/aes-gcm/nivc/aes-gctr-nivc.test.ts @@ -1,8 +1,18 @@ import { assert } from "chai"; import { WitnessTester } from "circomkit"; -import { circomkit, bytesToBigInt } from "../../common"; +import { circomkit } from "../../common"; import { PoseidonModular } from "../../common/poseidon"; +function bytesToBigInt(bytes: number[] | Uint8Array): bigint { + let result = BigInt(0); + + for (let i = 0; i < 16; i++) { + result += BigInt(bytes[i]) * BigInt(2 ** (8 * i)); + } + + return result; +} + describe("aes-gctr-nivc", () => { let circuit_one_block: WitnessTester<["key", "iv", "plainText", "aad", "ctr", "cipherText", "step_in"], ["step_out"]>; diff --git a/circuits/test/chacha20/chacha20-nivc.test.ts b/circuits/test/chacha20/chacha20-nivc.test.ts deleted file mode 100644 index 9b09dc1..0000000 --- a/circuits/test/chacha20/chacha20-nivc.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit, toUint32Array, uintArray32ToBits } from "../common"; -import { DataHasher } from "../common/poseidon"; -import { assert } from "chai"; - - -describe("chacha20-nivc", () => { - describe("2 block test", () => { - let circuit: WitnessTester<["key", "nonce", "counter", "plainText", "cipherText", "step_in"], ["step_out"]>; - it("should perform encryption", async () => { - circuit = await circomkit.WitnessTester(`ChaCha20`, { - file: "chacha20/nivc/chacha20_nivc", - template: "ChaCha20_NIVC", - params: [16] // number of 32-bit words in the key, 32 * 16 = 512 bits - }); - // 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 = - [ - 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, - 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, - 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, - 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, - ]; - let ciphertextBytes = - [ - 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81, - 0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b, - 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57, - 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8 - ]; - const ciphertextBits = toInput(Buffer.from(ciphertextBytes)) - const plaintextBits = toInput(Buffer.from(plaintextBytes)) - const counterBits = uintArray32ToBits([1])[0] - let w = await circuit.compute({ - key: toInput(Buffer.from(keyBytes)), - nonce: toInput(Buffer.from(nonceBytes)), - counter: counterBits, - cipherText: ciphertextBits, - plainText: plaintextBits, - step_in: 0 - }, (["step_out"])); - assert.deepEqual(w.step_out, DataHasher(plaintextBytes)); - }); - }); -}); - - -export function toInput(bytes: Buffer) { - return uintArray32ToBits(toUint32Array(bytes)) -} - -export function fromInput(bits: number[]) { - const uint32Array = new Uint32Array(bits.length / 32); - for (let i = 0; i < uint32Array.length; i++) { - uint32Array[i] = parseInt(bits.slice(i * 32, (i + 1) * 32).join(''), 2); - } - const buffer = Buffer.alloc(uint32Array.length * 4); - for (let i = 0; i < uint32Array.length; i++) { - buffer.writeUInt32LE(uint32Array[i], i * 4); - } - return buffer; -} \ No newline at end of file diff --git a/circuits/test/chacha20/chacha20.test.ts b/circuits/test/chacha20/chacha20.test.ts deleted file mode 100644 index bfe5a38..0000000 --- a/circuits/test/chacha20/chacha20.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit, hexToBits, toUint32Array, uintArray32ToBits } from "../common"; - -describe("chacha20", () => { - describe("qtr-round", () => { - let circuit: WitnessTester<["in"], ["out"]>; - it("should perform qtr-round", async () => { - circuit = await circomkit.WitnessTester(`QR`, { - file: "chacha20/chacha-qr", - template: "QR", - }); - // Test case from RCF https://www.rfc-editor.org/rfc/rfc7539.html#section-2.1 - let input = [ - hexToBits("0x11111111"), - hexToBits("0x01020304"), - hexToBits("0x9b8d6f43"), - hexToBits("0x01234567") - ]; - let expected = [ - hexToBits("0xea2a92f4"), - hexToBits("0xcb1cf8ce"), - hexToBits("0x4581472e"), - hexToBits("0x5881c4bb") - ]; - await circuit.expectPass({ in: input }, { out: expected }); - }); - }); - - describe("full-round", () => { - let circuit: WitnessTester<["in"], ["out"]>; - it("should perform qtr-round", async () => { - circuit = await circomkit.WitnessTester(`QR`, { - file: "chacha20/chacha-round", - template: "Round", - }); - // Test case from RCF https://www.rfc-editor.org/rfc/rfc7539.html#section-2.1 - let input = [ - hexToBits("61707865"), hexToBits("3320646e"), hexToBits("79622d32"), hexToBits("6b206574"), - hexToBits("03020100"), hexToBits("07060504"), hexToBits("0b0a0908"), hexToBits("0f0e0d0c"), - hexToBits("13121110"), hexToBits("17161514"), hexToBits("1b1a1918"), hexToBits("1f1e1d1c"), - hexToBits("00000001"), hexToBits("09000000"), hexToBits("4a000000"), hexToBits("00000000") - ]; - let expected = [ - hexToBits("e4e7f110"), hexToBits("15593bd1"), hexToBits("1fdd0f50"), hexToBits("c47120a3"), - hexToBits("c7f4d1c7"), hexToBits("0368c033"), hexToBits("9aaa2204"), hexToBits("4e6cd4c3"), - hexToBits("466482d2"), hexToBits("09aa9f07"), hexToBits("05d7c214"), hexToBits("a2028bd9"), - hexToBits("d19c12b5"), hexToBits("b94e16de"), hexToBits("e883d0cb"), hexToBits("4e3c50a2") - ]; - await circuit.expectPass({ in: input }, { out: expected }); - }); - }); - - // this is failing right now - describe("2 block test", () => { - let circuit: WitnessTester<["key", "nonce", "counter", "in"], ["out"]>; - it("should perform encryption", async () => { - circuit = await circomkit.WitnessTester(`ChaCha20`, { - file: "chacha20/chacha20", - template: "ChaCha20", - params: [16] // number of 32-bit words in the key, 512 / 32 = 16 - }); - // 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 test = { - keyBytes: Buffer.from( - [ - 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 - ] - ), - nonceBytes: Buffer.from( - [ - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x4a, - 0x00, 0x00, 0x00, 0x00 - ] - ), - counter: 1, - plaintextBytes: Buffer.from( - [ - 0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c, - 0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73, - 0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39, 0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63, - 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f, - ] - ), - ciphertextBytes: Buffer.from( - [ - 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81, - 0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b, - 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57, - 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8 - ] - )} - const ciphertextBits = uintArray32ToBits(toUint32Array(test.ciphertextBytes)) - const plaintextBits = uintArray32ToBits(toUint32Array(test.plaintextBytes)) - const counterBits = uintArray32ToBits([test.counter])[0] - await circuit.expectPass({ - key: uintArray32ToBits(toUint32Array(test.keyBytes)), - nonce: uintArray32ToBits(toUint32Array(test.nonceBytes)), - counter: counterBits, - in: plaintextBits, - }, { out: ciphertextBits }); - - /// decryption since symmetric - const w2 = await circuit.expectPass({ - key: uintArray32ToBits(toUint32Array(test.keyBytes)), - nonce: uintArray32ToBits(toUint32Array(test.nonceBytes)), - counter: counterBits, - in: ciphertextBits, - }, { out: plaintextBits }); - }); - }); -}); \ No newline at end of file diff --git a/circuits/test/common/index.ts b/circuits/test/common/index.ts index d65d7c6..b659a16 100644 --- a/circuits/test/common/index.ts +++ b/circuits/test/common/index.ts @@ -97,140 +97,4 @@ return hexBytes.map(byte => { let n = BigInt(byte); return n; }); -} - -export function hexToBits(hex: string): number[] { - if (hex.startsWith('0x')) { - hex = hex.slice(2); - } - const bits: number[] = []; - for (let i = 0; i < hex.length; i++) { - const nibble = parseInt(hex[i], 16); - for (let j = 3; j >= 0; j--) { - bits.push((nibble >> j) & 1); - } - } - return bits; -} - -export function bitsToHex(bits: number[]): string { - let hex = ''; - for (let i = 0; i < bits.length; i += 4) { - let nibble = 0; - for (let j = 0; j < 4; j++) { - nibble = (nibble << 1) | (bits[i + j] || 0); - } - hex += nibble.toString(16); - } - return hex; -} - -export function bitsToBytes(bits: number[]): number[] { - const bytes: number[] = []; - for (let i = 0; i < bits.length; i += 8) { - let byte = 0; - for (let j = 0; j < 8; j++) { - byte = (byte << 1) | (bits[i + j] || 0); - } - bytes.push(byte); - } - return bytes; -} - -export function BytesToInput(bytes: number[]): number[] { - const output: number[][] = []; - let counter = 1; - let bits: number[] = []; - for (const byte of bytes) { - for (let i = 7; i >= 0; i--) { - bits.push((byte >> i) & 1); - } - if (counter % 4 == 0) { - output.push(bits); - bits = []; - } - - } - return bits; -} -export function hexArrayToBits(bytes: number[]): number[] { - const bits: number[] = []; - for (const byte of bytes) { - for (let i = 7; i >= 0; i--) { - bits.push((byte >> i) & 1); - } - } - return bits; -} - -export function binaryStringToHex(binaryString: string): string { - let hex = ''; - for (let i = 0; i < binaryString.length; i += 4) { - const chunk = binaryString.slice(i, i + 4); - const hexDigit = parseInt(chunk, 2).toString(16); - hex += hexDigit; - } - return hex; -} - -/** - * Converts a Uint8Array to an array of bits. - * BE order. - */ -export function uint8ArrayToBitsBE(buff: Uint8Array | number[]) { - const res: number[] = [] - for (let i = 0; i < buff.length; i++) { - for (let j = 0; j < 8; j++) { - if ((buff[i] >> 7-j) & 1) { - res.push(1); - } else { - res.push(0); - } - } - } - return res; -} - -export function toUint32Array(buf: Uint8Array) { - const arr = new Uint32Array(buf.length / 4) - const arrView = new DataView(buf.buffer, buf.byteOffset, buf.byteLength) - for(let i = 0;i < arr.length;i++) { - arr[i] = arrView.getUint32(i * 4, true) - } - return arr -} - -/** - * Converts a Uint32Array to an array of bits. - * LE order. - */ -export function uintArray32ToBits(uintArray: Uint32Array | number[]) { - const bits: number[][] = [] - for (let i = 0; i < uintArray.length; i++) { - const uint = uintArray[i] - bits.push(numToBitsNumerical(uint)) - } - - return bits -} - -export function numToBitsNumerical(num: number, bitCount = 32) { - const bits: number[] = [] - for(let i = 2 ** (bitCount - 1);i >= 1;i /= 2) { - const bit = num >= i ? 1 : 0 - bits.push(bit) - num -= bit * i - } - - return bits -} - -export function bytesToBigInt(bytes: number[] | Uint8Array): bigint { - let result = BigInt(0); - - for (let i = 0; i < 16; i++) { - result += BigInt(bytes[i]) * BigInt(2 ** (8 * i)); - } - - return result; } \ No newline at end of file diff --git a/circuits/test/full/full.test.ts b/circuits/test/full/full.test.ts index 4c7978d..af4f8ea 100644 --- a/circuits/test/full/full.test.ts +++ b/circuits/test/full/full.test.ts @@ -1,8 +1,6 @@ import { assert } from "chai"; -import { circomkit, WitnessTester, toByte, uintArray32ToBits, toUint32Array } from "../common"; +import { circomkit, WitnessTester, toByte } from "../common"; import { DataHasher } from "../common/poseidon"; -import { toInput } from "../chacha20/chacha20-nivc.test"; -import { buffer } from "stream/consumers"; // HTTP/1.1 200 OK // content-type: application/json; charset=utf-8 @@ -41,18 +39,26 @@ const http_response_plaintext = [ 10, 32, 32, 32, 125, 13, 10, 125]; const http_response_ciphertext = [ - 2,125,219,141,140,93,49,129,95,178,135,109,48,36,194,46,239,155,160,70,208,147,37,212,17,195,149, - 190,38,215,23,241,84,204,167,184,179,172,187,145,38,75,123,96,81,6,149,36,135,227,226,254,177,90, - 241,159,0,230,183,163,210,88,133,176,9,122,225,83,171,157,185,85,122,4,110,52,2,90,36,189,145,63, - 122,75,94,21,163,24,77,85,110,90,228,157,103,41,59,128,233,149,57,175,121,163,185,144,162,100,17, - 34,9,252,162,223,59,221,106,127,104,11,121,129,154,49,66,220,65,130,171,165,43,8,21,248,12,214,33, - 6,109,3,144,52,124,225,206,223,213,86,186,93,170,146,141,145,140,57,152,226,218,57,30,4,131,161,0, - 248,172,49,206,181,47,231,87,72,96,139,145,117,45,77,134,249,71,87,178,239,30,244,156,70,118,180, - 176,90,92,80,221,177,86,120,222,223,244,109,150,226,142,97,171,210,38,117,143,163,204,25,223,238, - 209,58,59,100,1,86,241,103,152,228,37,187,79,36,136,133,171,41,184,145,146,45,192,173,219,146,133, - 12,246,190,5,54,99,155,8,198,156,174,99,12,210,95,5,128,166,118,50,66,26,20,3,129,232,1,192,104, - 23,152,212,94,97,138,162,90,185,108,221,211,247,184,253,15,16,24,32,240,240,3,148,89,30,54,161, - 131,230,161,217,29,229,251,33,220,230,102,131,245,27,141,220,67,16,26 + 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 = [ @@ -206,7 +212,7 @@ const json_key3_mask = [ const json_key3_mask_hash = DataHasher(json_key3_mask); describe("NIVC_FULL", async () => { - let chacha20Circuit: WitnessTester<["key", "nonce", "counter", "plainText", "cipherText", "step_in"], ["step_out"]>; + 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"]>; @@ -221,12 +227,12 @@ describe("NIVC_FULL", async () => { const MAX_VALUE_LENGTH = 32; before(async () => { - chacha20Circuit = await circomkit.WitnessTester("CHACHA20", { - file: "chacha20/nivc/chacha20_nivc", - template: "ChaCha20_NIVC", - params: [80] // 80 * 32 = 2560 bits / 8 = 320 bytes + aesCircuit = await circomkit.WitnessTester("AESGCTRFOLD", { + file: "aes-gcm/nivc/aes-gctr-nivc", + template: "AESGCTRFOLD", + params: [1] }); - console.log("#constraints (CHACHA20):", await chacha20Circuit.getConstraintCount()); + console.log("#constraints (AES-GCTR):", await aesCircuit.getConstraintCount()); httpCircuit = await circomkit.WitnessTester(`HttpNIVC`, { file: "http/nivc/http_nivc", @@ -258,18 +264,25 @@ describe("NIVC_FULL", async () => { }); it("NIVC_CHAIN", async () => { - // Run ChaCha20 + // Run AES chain + let ctr = [0x00, 0x00, 0x00, 0x01]; const init_nivc_input = 0; - const counterBits = uintArray32ToBits([1])[0] - const ptIn = toInput(Buffer.from(http_response_plaintext)); - const ctIn = toInput(Buffer.from(http_response_ciphertext)); - 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: ptIn, cipherText: ctIn, step_in: init_nivc_input }, ["step_out"]); - console.log("ChaCha20 `step_out`:", chacha20.step_out); - assert.deepEqual(DataHasher(http_response_plaintext), chacha20.step_out); - - let http = await httpCircuit.compute({ step_in: chacha20.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"]); + + let pt = http_response_plaintext.slice(0, 16); + let ct = 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 = 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" @@ -313,7 +326,7 @@ describe("NIVC_FULL", async () => { describe("NIVC_FULL_2", async () => { - let chacha20Circuit: WitnessTester<["key", "nonce", "counter", "plainText", "cipherText", "step_in"], ["step_out"]>; + 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"]>; @@ -328,12 +341,12 @@ describe("NIVC_FULL_2", async () => { const MAX_VALUE_LENGTH = 32; before(async () => { - chacha20Circuit = await circomkit.WitnessTester("CHACHA20", { - file: "chacha20/nivc/chacha20_nivc", - template: "ChaCha20_NIVC", - params: [80] // 80 * 32 = 2560 bits / 8 = 320 bytes + aesCircuit = await circomkit.WitnessTester("AESGCTRFOLD", { + file: "aes-gcm/nivc/aes-gctr-nivc", + template: "AESGCTRFOLD", + params: [2] }); - console.log("#constraints (CHACHA20):", await chacha20Circuit.getConstraintCount()); + console.log("#constraints (AES-GCTR):", await aesCircuit.getConstraintCount()); httpCircuit = await circomkit.WitnessTester(`HttpNIVC`, { file: "http/nivc/http_nivc", @@ -365,19 +378,25 @@ describe("NIVC_FULL_2", async () => { }); it("NIVC_CHAIN_2", async () => { - // Run ChaCha20 + // Run AES chain + let ctr = [0x00, 0x00, 0x00, 0x01]; const init_nivc_input = 0; - const counterBits = uintArray32ToBits([1])[0] - const ptIn = toInput(Buffer.from(http_response_plaintext)); - const ctIn = toInput(Buffer.from(http_response_ciphertext)); - 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: ptIn, cipherText: ctIn, step_in: init_nivc_input }, ["step_out"]); - console.log("ChaCha20 `step_out`:", chacha20.step_out); - assert.deepEqual(DataHasher(http_response_plaintext), chacha20.step_out); - let http = await httpCircuit.compute({ step_in: chacha20.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"]); + let pt = [http_response_plaintext.slice(0, 16), http_response_plaintext.slice(16, 32)]; + let ct = [http_response_ciphertext.slice(0, 16), 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 = [http_response_ciphertext.slice(i * 32, i * 32 + 16), 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" diff --git a/circuits/test/utils/array.test.ts b/circuits/test/utils/array.test.ts index 7069932..b8538e6 100644 --- a/circuits/test/utils/array.test.ts +++ b/circuits/test/utils/array.test.ts @@ -461,25 +461,4 @@ describe("ToBlocks", () => { } ); }); - }); - - -describe("fromLittleEndianToWords32", () => { - let circuit: WitnessTester<["data"], ["words"]>; - it("fromLittleEndianToWords32", async () => { - circuit = await circomkit.WitnessTester(`fromLittleEndianToWords32`, { - file: "utils/array", - template: "fromLittleEndianToWords32", - }); - console.log("#constraints:", await circuit.getConstraintCount()); - - let input = [ - 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({data: input}, {words: [72, 84, 84, 80]}) - }); -}); - + }); \ No newline at end of file diff --git a/circuits/utils/array.circom b/circuits/utils/array.circom index 75f7380..5b393f5 100644 --- a/circuits/utils/array.circom +++ b/circuits/utils/array.circom @@ -511,25 +511,4 @@ template IncrementWord() { carry[i - 1] <== IsGreaterThan[i].out; } } -} - -// // from little endian to 32 bit words -// // example: -// 0, 1, 0, 1, 0, 0, 0, 0, => 80 -// 0, 1, 0, 1, 0, 1, 0, 0, => 84 -// 0, 1, 0, 1, 0, 1, 0, 0, => 84 -// 0, 1, 0, 0, 1, 0, 0, 0, => 72 // getting ten here -// shoud be encoded as -// 72, 84, 84, 80 -template fromLittleEndianToWords32() { - signal input data[32]; - signal output words[4]; - component Bits2Num[4]; - for(var i = 3; i >= 0; i--) { - Bits2Num[i] = Bits2Num(8); - for(var j = 7; j >= 0; j--) { - Bits2Num[i].in[7-j] <== data[i*8 + j]; - } - words[3-i] <== Bits2Num[i].out; - } } \ No newline at end of file diff --git a/circuits/utils/generics-bits.circom b/circuits/utils/generics-bits.circom deleted file mode 100644 index b1311e9..0000000 --- a/circuits/utils/generics-bits.circom +++ /dev/null @@ -1,74 +0,0 @@ -// initially from https://github.com/reclaimprotocol/zk-symmetric-crypto -// modified for our needs -pragma circom 2.1.9; - -/** - * Add N bit numbers together - * copied in from: https://github.com/iden3/circomlib/blob/master/circuits/binsum.circom - * but rewritten slightly to reduce the final number of wires & labels - * and possibly look at reducing the number of constraints - */ -template AddBits(BITS) { - signal input a[BITS]; - signal input b[BITS]; - signal output out[BITS]; - signal carrybit; - - var lin = 0; - var lout = 0; - - var k; - var j = 0; - - var e2; - - // create e2 which - // is the numerical sum of 2^k - e2 = 1; - for (k = BITS - 1; k >= 0; k--) { - lin += (a[k] + b[k]) * e2; - e2 *= 2; - } - - e2 = 1; - for (k = BITS - 1; k >= 0; k--) { - out[k] <-- (lin >> j) & 1; - // Ensure out is binary - out[k] * (out[k] - 1) === 0; - lout += out[k] * e2; - e2 *= 2; - j += 1; - } - - carrybit <-- (lin >> j) & 1; - // Ensure out is binary - carrybit * (carrybit - 1) === 0; - lout += carrybit * e2; - - // Ensure the sum matches - lin === lout; -} - -/** - * Rotate left a BITS bit integer L bits - */ -template RotateLeftBits(BITS, L) { - signal input in[BITS]; - signal output out[BITS]; - for (var i = 0; i < BITS; i++) { - out[i] <== in[(i + L) % BITS]; - } -} - -/** - * XOR BITS-bit words -*/ -template XorBits(BITS) { - signal input a[BITS]; - signal input b[BITS]; - signal output out[BITS]; - - for (var k=0; k