Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: foldable chacha20 #51

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions builds/target_1024b/aes_gctr_nivc_1024b.circom
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't remove this please! No harm in keeping it in here for now.

This file was deleted.

5 changes: 5 additions & 0 deletions builds/target_1024b/chacha20_nivc_1024.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma circom 2.1.9;

include "../../circuits/chacha20/chacha20.circom";

component main = ChaCha20_NIVC(256);
6 changes: 0 additions & 6 deletions builds/target_1024b/http_body_mask_1024b.circom

This file was deleted.

5 changes: 0 additions & 5 deletions builds/target_1024b/http_lock_header_1024b.circom

This file was deleted.

This file was deleted.

5 changes: 0 additions & 5 deletions builds/target_512b/aes_gctr_nivc_512b.circom
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't remove this either.

This file was deleted.

5 changes: 5 additions & 0 deletions builds/target_512b/chacha20_nivc_512b.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma circom 2.1.9;

include "../../circuits/chacha20/nivc/chacha20_nivc.circom";

component main = ChaCha20_NIVC(128);
6 changes: 0 additions & 6 deletions builds/target_512b/http_body_mask_512b.circom

This file was deleted.

5 changes: 0 additions & 5 deletions builds/target_512b/http_lock_header_512b.circom

This file was deleted.

5 changes: 0 additions & 5 deletions builds/target_512b/http_parse_and_lock_start_line_512b.circom

This file was deleted.

88 changes: 88 additions & 0 deletions circuits/chacha20/chacha-qr.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like surely we can do this 32 bit add more efficiently, right?

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;
}
112 changes: 112 additions & 0 deletions circuits/chacha20/chacha-round.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// 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;
}
108 changes: 108 additions & 0 deletions circuits/chacha20/chacha20.circom
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment for the future:

I think we may be able to byte pack all of this. Expanding 32 field elements for 32 bits is likely not necessary. We can fit that amount of data into 2 field elements!

I'm not sure if this would help given we have to do bitwise XOR, but I do think there are probably tricks to do bitwise XOR on bytes or, say, 128bit numbers. Maybe not. Food for thought!

This circuit already seems wildly more efficient, so I'm not really worried about it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also goes for all the chacha circuits we use.

Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// 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;
}
}
}
Loading