diff --git a/circuits/Nargo.toml b/circuits/Nargo.toml index 5082c6e..473dcc3 100644 --- a/circuits/Nargo.toml +++ b/circuits/Nargo.toml @@ -3,3 +3,4 @@ authors = [""] compiler_version = "0.1" [dependencies] +keccak = { path = "../lib" } diff --git a/circuits/Prover.toml b/circuits/Prover.toml index 3a91251..d028245 100644 --- a/circuits/Prover.toml +++ b/circuits/Prover.toml @@ -1,3 +1,3 @@ -input = [1,0,0,0,0,0,0,0,0,0] -return = [1,1,0,0,0,0,0,0,0,1] +input = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +return = [1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0] input_length = 1 diff --git a/circuits/Verifier.toml b/circuits/Verifier.toml deleted file mode 100644 index 1fce1b9..0000000 --- a/circuits/Verifier.toml +++ /dev/null @@ -1 +0,0 @@ -return = ["0x0000000000000000000000000000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000001"] diff --git a/circuits/src/main.nr b/circuits/src/main.nr index f6bd13b..6119f84 100644 --- a/circuits/src/main.nr +++ b/circuits/src/main.nr @@ -1,13 +1,8 @@ -mod padding; - -// TODO: Change to final value (<= BLOCK_SIZE - 2). -global INPUT_SIZE: Field = 10; - -// TODO: Change to final value (1088) -global BLOCK_SIZE: Field = 10; // Blocks are 136 bytes. 138 * 8 = 1088 bits. +use dep::keccak::permutations::keccak; +use dep::keccak::constants; // This is a simplified implementation of the Keccak256 hash function. // In particular we assume that the `input_length` will be less than the size of the absorb step's block size. -fn main(input: [u1; INPUT_SIZE], input_length: u64) -> pub [u1; BLOCK_SIZE] { - padding::pad(input, input_length) +fn main(input: [u1; constants::INPUT_SIZE], input_length: u64) -> pub [u1; constants::OUTPUT_SIZE] { + keccak(input, input_length) } diff --git a/circuits/src/permutations.nr b/circuits/src/permutations.nr deleted file mode 100644 index 4bce44f..0000000 --- a/circuits/src/permutations.nr +++ /dev/null @@ -1,23 +0,0 @@ -mod chi; -mod iota; -mod rhoPi; -mod theta; - -const BLOCK_SIZE: Field = 1088; // Blocks are 136 bytes. 138 * 8 = 1088 bits. -const STATE_SIZE: Field = 1600; - -// This is a simplified implementation of the Keccak256 absorb function where we assume the input is smaller than the -// internal state size. -fn absorb(input: [u1; BLOCK_SIZE]) -> [u1; STATE_SIZE] { - let mut state: [u1; STATE_SIZE] = [0 as u1; 1600]; - - // We should in theory XOR the input with the internal state. However we know that X ^ 0 = X so we can just write - // the input into the state. We can do this as the input is guaranteed to be smaller than the state size. - for i in 0..BLOCK_SIZE { - state[i] = input[i]; - }; - - // TODO: apply keccak-p functions - - state -} diff --git a/circuits/src/permutations/chi.nr b/circuits/src/permutations/chi.nr deleted file mode 100644 index 580cf05..0000000 --- a/circuits/src/permutations/chi.nr +++ /dev/null @@ -1,20 +0,0 @@ -global STATE_SIZE: Field = 1600; -global LANE_LENGTH: Field = 64; -global COLUMN_LENGTH: Field = 5 - -fn chi(state: [u1; STATE_SIZE]) -> [u1; STATE_SIZE] { - // The labelling convention for the state array is `state[x, y, z] = state[LANE_LENGTH * (5y + x) + z]`. - let mut new_state = state; - for z in 0..LANE_LENGTH { - // Iterate over each slice... - for y in 0..COLUMN_LENGTH { - // and write updated values for each row. - // new_state[x, y, z] = new_state[x, y, z] ^ new_state[(x + 1) % 5, y, z] ^ new_state[(x + 2) % 5, y, z] - new_state[LANE_LENGTH * (5 * y + 0) + z] = state[LANE_LENGTH * (5 * y + 0) + z] ^ state[LANE_LENGTH * (5 * y + 1) + z] ^ state[LANE_LENGTH * (5 * y + 2) + z]; - new_state[LANE_LENGTH * (5 * y + 1) + z] = state[LANE_LENGTH * (5 * y + 1) + z] ^ state[LANE_LENGTH * (5 * y + 2) + z] ^ state[LANE_LENGTH * (5 * y + 3) + z]; - new_state[LANE_LENGTH * (5 * y + 2) + z] = state[LANE_LENGTH * (5 * y + 2) + z] ^ state[LANE_LENGTH * (5 * y + 3) + z] ^ state[LANE_LENGTH * (5 * y + 4) + z]; - new_state[LANE_LENGTH * (5 * y + 3) + z] = state[LANE_LENGTH * (5 * y + 3) + z] ^ state[LANE_LENGTH * (5 * y + 4) + z] ^ state[LANE_LENGTH * (5 * y + 0) + z]; - new_state[LANE_LENGTH * (5 * y + 4) + z] = state[LANE_LENGTH * (5 * y + 4) + z] ^ state[LANE_LENGTH * (5 * y + 0) + z] ^ state[LANE_LENGTH * (5 * y + 1) + z]; - }; - }; -} diff --git a/circuits/src/permutations/theta.nr b/circuits/src/permutations/theta.nr deleted file mode 100644 index c439c88..0000000 --- a/circuits/src/permutations/theta.nr +++ /dev/null @@ -1,56 +0,0 @@ -global STATE_SIZE: Field = 1600; -global LANE_LENGTH: Field = 64; - -fn theta(state: [u1; STATE_SIZE]) -> [u1; STATE_SIZE] { - // The theta function works by calculating the parity of each of the columns in the state array. We store these - // in the C[x, z] arrays. - // C[x, z] = A[x, 0, z] ^ A[x, 1, z] ^ A[x, 2, z] ^ A[x, 3, z] ^ A[x, 4, z] - let mut c0: [u1; LANE_LENGTH] = [0 as u1; 64]; - let mut c1: [u1; LANE_LENGTH] = [0 as u1; 64]; - let mut c2: [u1; LANE_LENGTH] = [0 as u1; 64]; - let mut c3: [u1; LANE_LENGTH] = [0 as u1; 64]; - let mut c4: [u1; LANE_LENGTH] = [0 as u1; 64]; - for i in 0..LANE_LENGTH { - c0[i] = state[0 * LANE_LENGTH + i] ^ state[5 * LANE_LENGTH + i] ^ state[10 * LANE_LENGTH + i] ^ state[15 * LANE_LENGTH + i] ^ state[20 * LANE_LENGTH + i]; - c1[i] = state[1 * LANE_LENGTH + i] ^ state[6 * LANE_LENGTH + i] ^ state[11 * LANE_LENGTH + i] ^ state[16 * LANE_LENGTH + i] ^ state[21 * LANE_LENGTH + i]; - c2[i] = state[2 * LANE_LENGTH + i] ^ state[7 * LANE_LENGTH + i] ^ state[12 * LANE_LENGTH + i] ^ state[17 * LANE_LENGTH + i] ^ state[22 * LANE_LENGTH + i]; - c3[i] = state[3 * LANE_LENGTH + i] ^ state[8 * LANE_LENGTH + i] ^ state[13 * LANE_LENGTH + i] ^ state[18 * LANE_LENGTH + i] ^ state[23 * LANE_LENGTH + i]; - c4[i] = state[4 * LANE_LENGTH + i] ^ state[9 * LANE_LENGTH + i] ^ state[14 * LANE_LENGTH + i] ^ state[19 * LANE_LENGTH + i] ^ state[24 * LANE_LENGTH + i]; - }; - - // D[x, z] = C[(x - 1) mod 5, z] ^ C[(x + 1) mod 5, (z - 1) mod LANE_LENGTH] - let mut d0: [u1; LANE_LENGTH] = [0 as u1; 64]; // D[0, Z] = C[4, z] ^ C[1, (z-1) mod LANE_LENGTH] - let mut d1: [u1; LANE_LENGTH] = [0 as u1; 64]; // D[1, Z] = C[0, z] ^ C[2, (z-1) mod LANE_LENGTH] - let mut d2: [u1; LANE_LENGTH] = [0 as u1; 64]; // D[2, Z] = C[1, z] ^ C[3, (z-1) mod LANE_LENGTH] - let mut d3: [u1; LANE_LENGTH] = [0 as u1; 64]; // D[3, Z] = C[2, z] ^ C[4, (z-1) mod LANE_LENGTH] - let mut d4: [u1; LANE_LENGTH] = [0 as u1; 64]; // D[4, Z] = C[3, z] ^ C[0, (z-1) mod LANE_LENGTH] - // The modulus only affects the first cell in the lane so we handle this outside of the for-loop. - d0[0] = c4[0] ^ c1[LANE_LENGTH - 1]; - d1[0] = c0[0] ^ c2[LANE_LENGTH - 1]; - d2[0] = c1[0] ^ c3[LANE_LENGTH - 1]; - d3[0] = c2[0] ^ c4[LANE_LENGTH - 1]; - d4[0] = c3[0] ^ c0[LANE_LENGTH - 1]; - for i in 1..LANE_LENGTH { - d0[i] = c4[i] ^ c1[i-1]; - d1[i] = c0[i] ^ c2[i-1]; - d2[i] = c1[i] ^ c3[i-1]; - d3[i] = c2[i] ^ c4[i-1]; - d4[i] = c3[i] ^ c0[i-1]; - }; - - // The labelling convention for the state array is `state[x, y, z] = state[LANE_LENGTH * (5y + x) + z]`. - let mut new_state = state; - for y in 0..4 { - // Iterate over each row... - for i in 0..LANE_LENGTH { - // and write updated values for each lane. - new_state[LANE_LENGTH * (5 * y + 0) + i] = state[LANE_LENGTH * (5 * y + 0) + i] ^ d0[i]; - new_state[LANE_LENGTH * (5 * y + 1) + i] = state[LANE_LENGTH * (5 * y + 1) + i] ^ d1[i]; - new_state[LANE_LENGTH * (5 * y + 2) + i] = state[LANE_LENGTH * (5 * y + 2) + i] ^ d2[i]; - new_state[LANE_LENGTH * (5 * y + 3) + i] = state[LANE_LENGTH * (5 * y + 3) + i] ^ d3[i]; - new_state[LANE_LENGTH * (5 * y + 4) + i] = state[LANE_LENGTH * (5 * y + 4) + i] ^ d4[i]; - }; - }; - - new_state -} diff --git a/lib/Nargo.toml b/lib/Nargo.toml new file mode 100644 index 0000000..e0b467c --- /dev/null +++ b/lib/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/lib/src/constants.nr b/lib/src/constants.nr new file mode 100644 index 0000000..b63a274 --- /dev/null +++ b/lib/src/constants.nr @@ -0,0 +1,17 @@ +// Input size related + +// The input must be at least 2 bits smaller than the block size to accommodate the padding bits. +global INPUT_SIZE: Field = 1086; +// Blocks are 136 bytes. 138 * 8 = 1088 bits. +global BLOCK_SIZE: Field = 1088; + +global OUTPUT_SIZE: Field = 256; + +// State size related +global STATE_SIZE: Field = 1600; +global LANE_LENGTH: Field = 64; +global COLUMN_LENGTH: Field = 5; +global NUM_LANES: Field = 25; + +// Misc +global NUM_ROUNDS: Field = 24; diff --git a/lib/src/lib.nr b/lib/src/lib.nr new file mode 100644 index 0000000..ecd1b60 --- /dev/null +++ b/lib/src/lib.nr @@ -0,0 +1,3 @@ +mod constants; +mod padding; +mod permutations; diff --git a/circuits/src/padding.nr b/lib/src/padding.nr similarity index 63% rename from circuits/src/padding.nr rename to lib/src/padding.nr index 75b4b75..fe2ab39 100644 --- a/circuits/src/padding.nr +++ b/lib/src/padding.nr @@ -1,19 +1,14 @@ -// TODO: Change to final value (<= BLOCK_SIZE - 2). -// Could likely make this generic even (need to check on language support). -global INPUT_SIZE: Field = 10; - -// TODO: Change to final value (1088) -global BLOCK_SIZE: Field = 10; // Blocks are 136 bytes. 138 * 8 = 1088 bits. +use crate::constants; // This is a simplified implementation of the pad10*1 algorithm. // As we assume that the input length is smaller than the block size, we can ignore the potential for the padding // sequence to be spread over multiple blocks. -fn pad(input: [u1; INPUT_SIZE], input_length: u64) -> [u1; BLOCK_SIZE] { +fn pad(input: [u1; constants::INPUT_SIZE], input_length: u64) -> [u1; constants::BLOCK_SIZE] { // We require 2 bits of space after the message in order to include the padding bits. // constrain input_length < BLOCK_SIZE - 2; - let mut padded_input: [u1; BLOCK_SIZE] = [0 as u1; 10]; - for i in 0..BLOCK_SIZE { + let mut padded_input: [u1; constants::BLOCK_SIZE] = [0 as u1; constants::BLOCK_SIZE]; + for i in 0..constants::INPUT_SIZE { if (i as u64) < input_length { // Copy input into padded array. padded_input[i] = input[i]; @@ -28,7 +23,7 @@ fn pad(input: [u1; INPUT_SIZE], input_length: u64) -> [u1; BLOCK_SIZE] { } }; // Place the second 1 bit of the padding in the last bit of the block. - padded_input[BLOCK_SIZE - 1] = 1; + padded_input[constants::BLOCK_SIZE - 1] = 1; padded_input } diff --git a/lib/src/permutations.nr b/lib/src/permutations.nr new file mode 100644 index 0000000..86da361 --- /dev/null +++ b/lib/src/permutations.nr @@ -0,0 +1,71 @@ +use crate::constants; + +mod chi; +mod iota; +mod rhoPi; +mod theta; + +// This is a simplified implementation of the Keccak256 absorb function where we assume the input is smaller than the +// internal state size. +fn absorb(input: [u1; constants::BLOCK_SIZE]) -> [u1; constants::STATE_SIZE] { + let mut state: [u1; constants::STATE_SIZE] = [0 as u1; constants::STATE_SIZE]; + + // We should in theory XOR the input with the internal state. However we know that X ^ 0 = X so we can just write + // the input into the state. We can do this as the input is guaranteed to be smaller than the state size. + for i in 0..constants::BLOCK_SIZE { + state[i] = input[i]; + }; + + // TODO: apply keccak-p functions + state = keccakf(state); + + state +} + +fn squeeze(input: [u1; constants::STATE_SIZE]) -> [u1; constants::OUTPUT_SIZE] { + + let mut result: [u1; constants::OUTPUT_SIZE] = [0 as u1; constants::OUTPUT_SIZE]; + + for i in 0..constants::OUTPUT_SIZE { + result[i] = input[i]; + }; + result +} + +fn keccakfRound(state: [u1; constants::STATE_SIZE], round_number: comptime Field) -> [u1; constants::STATE_SIZE] { + + let state_after_theta = theta::theta(state); + let state_after_rhoPi = rhoPi::rhoPi(state_after_theta); + let state_after_chi = chi::chi(state_after_rhoPi); + let new_state = iota::iota(state_after_chi, round_number); + + new_state +} + +fn keccakf(input: [u1; constants::STATE_SIZE]) -> [u1; constants::STATE_SIZE] { + let mut state: [u1; constants::STATE_SIZE] = [0 as u1; constants::STATE_SIZE]; + for j in 0..constants::STATE_SIZE { + state[j] = input[j]; + }; + for i in 0..constants::NUM_ROUNDS { + state = keccakfRound(state, i); + }; + state +} + +fn keccakFinal(input: [u1; constants::INPUT_SIZE], input_length: u64) -> [u1; constants::STATE_SIZE] { + let padded_input: [u1; constants::BLOCK_SIZE] = crate::padding::pad(input, input_length); + + let absorb_result: [u1; constants::STATE_SIZE] = absorb(padded_input); + + absorb_result +} + +fn keccak(input: [u1; constants::INPUT_SIZE], input_length: u64) -> [u1; constants::OUTPUT_SIZE] { + + let final_state = keccakFinal(input, input_length); + + let squeezed_state = squeeze(final_state); + + squeezed_state +} diff --git a/lib/src/permutations/chi.nr b/lib/src/permutations/chi.nr new file mode 100644 index 0000000..cf129a0 --- /dev/null +++ b/lib/src/permutations/chi.nr @@ -0,0 +1,20 @@ +use crate::constants; + +fn chi(state: [u1; constants::STATE_SIZE]) -> [u1; constants::STATE_SIZE] { + // The labelling convention for the state array is `state[x, y, z] = state[LANE_LENGTH * (5y + x) + z]`. + let mut new_state = state; + for z in 0..constants::LANE_LENGTH { + // Iterate over each slice... + for y in 0..constants::COLUMN_LENGTH { + // and write updated values for each row. + // new_state[x, y, z] = new_state[x, y, z] ^ new_state[(x + 1) % 5, y, z] ^ new_state[(x + 2) % 5, y, z] + new_state[constants::LANE_LENGTH * (5 * y + 0) + z] = state[constants::LANE_LENGTH * (5 * y + 0) + z] ^ state[constants::LANE_LENGTH * (5 * y + 1) + z] ^ state[constants::LANE_LENGTH * (5 * y + 2) + z]; + new_state[constants::LANE_LENGTH * (5 * y + 1) + z] = state[constants::LANE_LENGTH * (5 * y + 1) + z] ^ state[constants::LANE_LENGTH * (5 * y + 2) + z] ^ state[constants::LANE_LENGTH * (5 * y + 3) + z]; + new_state[constants::LANE_LENGTH * (5 * y + 2) + z] = state[constants::LANE_LENGTH * (5 * y + 2) + z] ^ state[constants::LANE_LENGTH * (5 * y + 3) + z] ^ state[constants::LANE_LENGTH * (5 * y + 4) + z]; + new_state[constants::LANE_LENGTH * (5 * y + 3) + z] = state[constants::LANE_LENGTH * (5 * y + 3) + z] ^ state[constants::LANE_LENGTH * (5 * y + 4) + z] ^ state[constants::LANE_LENGTH * (5 * y + 0) + z]; + new_state[constants::LANE_LENGTH * (5 * y + 4) + z] = state[constants::LANE_LENGTH * (5 * y + 4) + z] ^ state[constants::LANE_LENGTH * (5 * y + 0) + z] ^ state[constants::LANE_LENGTH * (5 * y + 1) + z]; + }; + }; + + new_state +} diff --git a/circuits/src/permutations/iota.nr b/lib/src/permutations/iota.nr similarity index 78% rename from circuits/src/permutations/iota.nr rename to lib/src/permutations/iota.nr index 699e26a..e30f0ac 100644 --- a/circuits/src/permutations/iota.nr +++ b/lib/src/permutations/iota.nr @@ -1,10 +1,8 @@ -global STATE_SIZE: Field = 1600; -global LANE_LENGTH: Field = 64; -global NUM_ROUNDS: Field = 24; +use crate::constants; -fn iota(state: [u1; STATE_SIZE], round_number: Field) -> [u1; STATE_SIZE] { +fn iota(state: [u1; constants::STATE_SIZE], round_number: comptime Field) -> [u1; constants::STATE_SIZE] { // Each element of RC is a bitmap for the mask to apply to the lane. - let RC: [u64; NUM_ROUNDS] = [ + let RC: [u64; constants::NUM_ROUNDS] = [ 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, 0x000000000000808B, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008A, @@ -19,8 +17,8 @@ fn iota(state: [u1; STATE_SIZE], round_number: Field) -> [u1; STATE_SIZE] { // The labelling convention for the state array is `state[x, y, z] = state[LANE_LENGTH * (5y + x) + z]`. // In order to update Lane(0,0) we must only update the first `LANE_LENGTH` values of the state array. let mut new_state = state; - for i in 0..LANE_LENGTH { - new_state[i] = state[i] ^ (rc & 1); + for i in 0..constants::LANE_LENGTH { + new_state[i] = state[i] ^ (rc as u1); // Equivalent to a bitshift right. rc = rc / 2; }; diff --git a/circuits/src/permutations/rhoPi.nr b/lib/src/permutations/rhoPi.nr similarity index 63% rename from circuits/src/permutations/rhoPi.nr rename to lib/src/permutations/rhoPi.nr index f6a5d11..f609578 100644 --- a/circuits/src/permutations/rhoPi.nr +++ b/lib/src/permutations/rhoPi.nr @@ -1,34 +1,33 @@ -global STATE_SIZE: Field = 1600; -global LANE_LENGTH: Field = 64; +use crate::constants; // This function is a combination of the rho and pi functions as defined in the Keccak256 specification. // We merge these two functions as rho consists of a rotation of the bits within each lane and pi is a remapping of // the lane indices. We can then efficiently perform both of these steps simultaneously by writing the output of rho // directly into the remapped lane specified by pi. -fn rhoPi(state: [u1; STATE_SIZE]) -> [u1; STATE_SIZE] { +fn rhoPi(state: [u1; constants::STATE_SIZE]) -> [u1; constants::STATE_SIZE] { // These are precomputed pairs of indices within the state array which specify how to perform the pi mapping. // Lanes are remapped such that the lane sitting at index READ_LANE_OFFSETS[i] is remapped to WRITE_LANE_OFFSETS[i]. - let READ_LANE_OFFSETS: [Field; 24] = [64, 640, 448, 704, 1088, 1152, 192, 320, 1024, 512, 1344, 1536, 256, 960, 1472, 1216, 832, 768, 128, 1280, 896, 1408, 576, 384]; - let WRITE_LANE_OFFSETS: [Field; 24] = [384, 64, 640, 448, 704, 1088, 1152, 192, 320, 1024, 512, 1344, 1536, 256, 960, 1472, 1216, 832, 768, 128, 1280, 896, 1408, 576]; + let READ_LANE_OFFSETS: [comptime Field; 24] = [64, 640, 448, 704, 1088, 1152, 192, 320, 1024, 512, 1344, 1536, 256, 960, 1472, 1216, 832, 768, 128, 1280, 896, 1408, 576, 384]; + let WRITE_LANE_OFFSETS: [comptime Field; 24] = [384, 64, 640, 448, 704, 1088, 1152, 192, 320, 1024, 512, 1344, 1536, 256, 960, 1472, 1216, 832, 768, 128, 1280, 896, 1408, 576]; // This array defines the rho mapping to be applied to each lane. // Each lane is rotated by the specified number of bits, mapping the z coordinate as `z -> (z - T[i]) % LANE_LENGTH` // This array is defined as `T[i] = ((i+1)(i+2) / 2) % LANE_LENGTH` // This definition adds an additional modulo compared to the spec but makes it easier to calculate correct offsets. - let T: [Field; 24] = [1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44]; + let T: [comptime Field; 24] = [1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44]; - let mut new_state: [u1; STATE_SIZE] = [0 as u1; 1600]; + let mut new_state: [u1; constants::STATE_SIZE] = [0 as u1; constants::STATE_SIZE]; // The center lane is unaffected by the rho and pi functions so we write it directly into the new state. - for i in 0..LANE_LENGTH { - new_state[i] = state[i] + for i in 0..constants::LANE_LENGTH { + new_state[i] = state[i]; }; // Now for the remaining lanes we write the rotated lane outputted from rho into the remapped lane specified by pi. - for i in 0..24 { - for z in 0..LANE_LENGTH { + for i in 0..constants::NUM_LANES - 1 { + for z in 0..constants::LANE_LENGTH { // This is equivalent to `(z - T[i]) % LANE_LENGTH` - let shifted_z_position = if z >= T[i] { z - T[i] } else { LANE_LENGTH - T[i] + z } + let shifted_z_position = if z >= T[i] { z - T[i] } else { constants::LANE_LENGTH - T[i] + z }; new_state[WRITE_LANE_OFFSETS[i] + z] = state[READ_LANE_OFFSETS[i] + shifted_z_position]; }; }; diff --git a/lib/src/permutations/theta.nr b/lib/src/permutations/theta.nr new file mode 100644 index 0000000..efcb47a --- /dev/null +++ b/lib/src/permutations/theta.nr @@ -0,0 +1,55 @@ +use crate::constants; + +fn theta(state: [u1; constants::STATE_SIZE]) -> [u1; constants::STATE_SIZE] { + // The theta function works by calculating the parity of each of the columns in the state array. We store these + // in the C[x, z] arrays. + // C[x, z] = A[x, 0, z] ^ A[x, 1, z] ^ A[x, 2, z] ^ A[x, 3, z] ^ A[x, 4, z] + let mut c0: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; + let mut c1: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; + let mut c2: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; + let mut c3: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; + let mut c4: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; + for i in 0..constants::LANE_LENGTH { + c0[i] = state[0 * constants::LANE_LENGTH + i] ^ state[5 * constants::LANE_LENGTH + i] ^ state[10 * constants::LANE_LENGTH + i] ^ state[15 * constants::LANE_LENGTH + i] ^ state[20 * constants::LANE_LENGTH + i]; + c1[i] = state[1 * constants::LANE_LENGTH + i] ^ state[6 * constants::LANE_LENGTH + i] ^ state[11 * constants::LANE_LENGTH + i] ^ state[16 * constants::LANE_LENGTH + i] ^ state[21 * constants::LANE_LENGTH + i]; + c2[i] = state[2 * constants::LANE_LENGTH + i] ^ state[7 * constants::LANE_LENGTH + i] ^ state[12 * constants::LANE_LENGTH + i] ^ state[17 * constants::LANE_LENGTH + i] ^ state[22 * constants::LANE_LENGTH + i]; + c3[i] = state[3 * constants::LANE_LENGTH + i] ^ state[8 * constants::LANE_LENGTH + i] ^ state[13 * constants::LANE_LENGTH + i] ^ state[18 * constants::LANE_LENGTH + i] ^ state[23 * constants::LANE_LENGTH + i]; + c4[i] = state[4 * constants::LANE_LENGTH + i] ^ state[9 * constants::LANE_LENGTH + i] ^ state[14 * constants::LANE_LENGTH + i] ^ state[19 * constants::LANE_LENGTH + i] ^ state[24 * constants::LANE_LENGTH + i]; + }; + + // D[x, z] = C[(x - 1) mod 5, z] ^ C[(x + 1) mod 5, (z - 1) mod LANE_LENGTH] + let mut d0: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; // D[0, Z] = C[4, z] ^ C[1, (z-1) mod LANE_LENGTH] + let mut d1: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; // D[1, Z] = C[0, z] ^ C[2, (z-1) mod LANE_LENGTH] + let mut d2: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; // D[2, Z] = C[1, z] ^ C[3, (z-1) mod LANE_LENGTH] + let mut d3: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; // D[3, Z] = C[2, z] ^ C[4, (z-1) mod LANE_LENGTH] + let mut d4: [u1; constants::LANE_LENGTH] = [0 as u1; constants::LANE_LENGTH]; // D[4, Z] = C[3, z] ^ C[0, (z-1) mod LANE_LENGTH] + // The modulus only affects the first cell in the lane so we handle this outside of the for-loop. + d0[0] = c4[0] ^ c1[constants::LANE_LENGTH - 1]; + d1[0] = c0[0] ^ c2[constants::LANE_LENGTH - 1]; + d2[0] = c1[0] ^ c3[constants::LANE_LENGTH - 1]; + d3[0] = c2[0] ^ c4[constants::LANE_LENGTH - 1]; + d4[0] = c3[0] ^ c0[constants::LANE_LENGTH - 1]; + for i in 1..constants::LANE_LENGTH { + d0[i] = c4[i] ^ c1[i-1]; + d1[i] = c0[i] ^ c2[i-1]; + d2[i] = c1[i] ^ c3[i-1]; + d3[i] = c2[i] ^ c4[i-1]; + d4[i] = c3[i] ^ c0[i-1]; + }; + + // The labelling convention for the state array is `state[x, y, z] = state[LANE_LENGTH * (5y + x) + z]`. + let mut new_state = state; + for y in 0..4 { + // Iterate over each row... + for i in 0..constants::LANE_LENGTH { + // and write updated values for each lane. + new_state[constants::LANE_LENGTH * (5 * y + 0) + i] = state[constants::LANE_LENGTH * (5 * y + 0) + i] ^ d0[i]; + new_state[constants::LANE_LENGTH * (5 * y + 1) + i] = state[constants::LANE_LENGTH * (5 * y + 1) + i] ^ d1[i]; + new_state[constants::LANE_LENGTH * (5 * y + 2) + i] = state[constants::LANE_LENGTH * (5 * y + 2) + i] ^ d2[i]; + new_state[constants::LANE_LENGTH * (5 * y + 3) + i] = state[constants::LANE_LENGTH * (5 * y + 3) + i] ^ d3[i]; + new_state[constants::LANE_LENGTH * (5 * y + 4) + i] = state[constants::LANE_LENGTH * (5 * y + 4) + i] ^ d4[i]; + }; + }; + + new_state +} diff --git a/test/inequality.test.ts b/test/inequality.test.ts deleted file mode 100644 index 0533762..0000000 --- a/test/inequality.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { compile } from '@noir-lang/noir_wasm'; -import { - setup_generic_prover_and_verifier, - create_proof, - verify_proof, - StandardExampleProver, - StandardExampleVerifier, -} from '@noir-lang/barretenberg/dest/client_proofs'; -import { resolve } from 'path'; -import { expect } from 'chai'; - -type ProofInput = { - x: number; - y: number; -}; - -describe('Tests using typescript wrapper', function () { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let acir: any; - let prover: StandardExampleProver; - let verifier: StandardExampleVerifier; - - before(async () => { - const compiled_program = compile(resolve(__dirname, '../circuits/src/main.nr')); - acir = compiled_program.circuit; - [prover, verifier] = await setup_generic_prover_and_verifier(acir); - }); - - async function createAndVerifyProof(abi: ProofInput): Promise { - const proof = await create_proof(prover, acir, abi); - - return verify_proof(verifier, proof); - } - - context('when inputs are equal', () => { - it('rejects the proof', async () => { - const abi: ProofInput = { x: 3, y: 3 }; - const verified = await createAndVerifyProof(abi); - - expect(verified).to.be.false; - }); - }); - - context('when inputs are unequal', () => { - it('accepts the proof', async () => { - const abi: ProofInput = { x: 3, y: 4 }; - const verified = await createAndVerifyProof(abi); - - expect(verified).to.be.true; - }); - }); -}); diff --git a/test/padding.test.ts b/test/padding.test.ts new file mode 100644 index 0000000..8507373 --- /dev/null +++ b/test/padding.test.ts @@ -0,0 +1,91 @@ +import { acir_from_bytes } from '@noir-lang/noir_wasm'; +import { + setup_generic_prover_and_verifier, + create_proof, + verify_proof, + StandardExampleProver, + StandardExampleVerifier, +} from '@noir-lang/barretenberg/dest/client_proofs'; +import { resolve } from 'path'; +import { expect } from 'chai'; +import { readFileSync } from 'fs'; + +const INPUT_SIZE = 1086; +const BLOCK_SIZE = 1088; + +type ProofInput = { + input: number[]; + input_length: number; + return: number[]; +}; + +describe('pad10*1', function () { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let acir: any; + let prover: StandardExampleProver; + let verifier: StandardExampleVerifier; + + before(async () => { + const acirByteArray = path_to_uint8array(resolve(__dirname, './test-circuits/padding/build/test.acir')); + acir = acir_from_bytes(acirByteArray); + [prover, verifier] = await setup_generic_prover_and_verifier(acir); + }); + + async function createAndVerifyProof(abi: ProofInput): Promise { + const proof = await create_proof(prover, acir, abi); + return verify_proof(verifier, proof); + } + + const padMessage = (input: number[], input_length: number, padded_length: number): number[] => { + if (input.slice(input_length).some((x) => x !== 0)) + throw new Error('input_length greater than actual input length'); + if (input_length > padded_length - 2) throw new Error('input_length greater than max allowed'); + + const paddingArrayLength = padded_length - input_length; + const paddingArray = Array(paddingArrayLength).fill(0); + paddingArray[0] = 1; + paddingArray[paddingArrayLength - 1] = 1; + + const paddedInput = [...input.slice(0, input_length), ...paddingArray]; + + return paddedInput; + }; + + context('when input is longer than reported length', () => { + it('rejects the proof', async () => { + const input_length = 2; + + const input = Array.from({ length: INPUT_SIZE }, () => 0); + + const paddedInput = padMessage(input, input_length, BLOCK_SIZE); + + // We now write a 1 in the position after where the message should end to invalidate it. + input[input_length] = 1; + + const abi: ProofInput = { input, input_length, return: paddedInput }; + const verified = await createAndVerifyProof(abi); + + expect(verified).to.be.false; + }); + }); + + context('when input matches expected input length', () => { + it('pads the input correctly', async () => { + const input_length = 2; + + // const input = Array.from({ length: INPUT_SIZE }, (_, i) => (i < input_length && i % 3 == 0 ? 1 : 0)); + const input = Array(INPUT_SIZE).fill(0); + const paddedInput = padMessage(input, input_length, BLOCK_SIZE); + + const abi: ProofInput = { input, input_length, return: paddedInput }; + const verified = await createAndVerifyProof(abi); + + expect(verified).to.be.true; + }); + }); +}); + +function path_to_uint8array(path: string) { + const buffer = readFileSync(path); + return new Uint8Array(buffer); +} diff --git a/test/test-circuits/padding/Nargo.toml b/test/test-circuits/padding/Nargo.toml new file mode 100644 index 0000000..91a59d5 --- /dev/null +++ b/test/test-circuits/padding/Nargo.toml @@ -0,0 +1,6 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] +keccak = { path = "../../../lib"} diff --git a/test/test-circuits/padding/Prover.toml b/test/test-circuits/padding/Prover.toml new file mode 100644 index 0000000..5a60a4d --- /dev/null +++ b/test/test-circuits/padding/Prover.toml @@ -0,0 +1,3 @@ +input = [] +input_length = "" +return = [] diff --git a/test/test-circuits/padding/src/main.nr b/test/test-circuits/padding/src/main.nr new file mode 100644 index 0000000..f90c756 --- /dev/null +++ b/test/test-circuits/padding/src/main.nr @@ -0,0 +1,6 @@ +use dep::keccak::padding::pad; +use dep::keccak::constants; + +fn main(input: [u1; constants::INPUT_SIZE], input_length: u64) -> pub [u1; constants::BLOCK_SIZE] { + pad(input, input_length) +}