pub mod circuitgates;
pub mod constants;
pub mod gadget;
pub mod witness;
use crate::circuits::expr::constraints::ExprOps;
use ark_ff::PrimeField;
use self::constants::{DIM, QUARTERS, RATE_IN_BYTES, ROUNDS};
use super::super::berkeley_columns::BerkeleyChallengeTerm;
#[macro_export]
macro_rules! grid {
(5, $v:expr) => {{
|x: usize| $v[x].clone()
}};
(20, $v:expr) => {{
|x: usize, q: usize| $v[q + QUARTERS * x].clone()
}};
(80, $v:expr) => {{
|i: usize, x: usize, q: usize| $v[q + QUARTERS * (x + DIM * i)].clone()
}};
(100, $v:expr) => {{
|y: usize, x: usize, q: usize| $v[q + QUARTERS * (x + DIM * y)].clone()
}};
(400, $v:expr) => {{
|i: usize, y: usize, x: usize, q: usize| {
$v[q + QUARTERS * (x + DIM * (y + DIM * i))].clone()
}
}};
}
pub const OFF: [[u64; DIM]; DIM] = [
[0, 1, 62, 28, 27],
[36, 44, 6, 55, 20],
[3, 10, 43, 25, 39],
[41, 45, 15, 21, 8],
[18, 2, 61, 56, 14],
];
pub const RC: [u64; ROUNDS] = [
0x0000000000000001,
0x0000000000008082,
0x800000000000808a,
0x8000000080008000,
0x000000000000808b,
0x0000000080000001,
0x8000000080008081,
0x8000000000008009,
0x000000000000008a,
0x0000000000000088,
0x0000000080008009,
0x000000008000000a,
0x000000008000808b,
0x800000000000008b,
0x8000000000008089,
0x8000000000008003,
0x8000000000008002,
0x8000000000000080,
0x000000000000800a,
0x800000008000000a,
0x8000000080008081,
0x8000000000008080,
0x0000000080000001,
0x8000000080008008,
];
pub struct Keccak {}
impl Keccak {
pub fn compose(quarters: &[u64]) -> u64 {
quarters[0] + (1 << 16) * quarters[1] + (1 << 32) * quarters[2] + (1 << 48) * quarters[3]
}
pub fn decompose(word: u64) -> Vec<u64> {
vec![
word % (1 << 16),
(word / (1 << 16)) % (1 << 16),
(word / (1 << 32)) % (1 << 16),
(word / (1 << 48)) % (1 << 16),
]
}
pub fn expand(quarter: u64) -> u64 {
u64::from_str_radix(&format!("{:b}", quarter), 16).unwrap()
}
pub fn expand_word<F: PrimeField, T: ExprOps<F, BerkeleyChallengeTerm>>(word: u64) -> Vec<T> {
Self::decompose(word)
.iter()
.map(|q| T::literal(F::from(Self::expand(*q))))
.collect::<Vec<T>>()
}
pub fn sparse(word: u64) -> Vec<u64> {
Self::decompose(word)
.iter()
.map(|q| Self::expand(*q))
.collect::<Vec<u64>>()
}
pub fn shift(state: &[u64]) -> Vec<u64> {
let n = state.len();
let mut shifts = vec![0; QUARTERS * n];
let aux = Self::expand(0xFFFF);
for (i, term) in state.iter().enumerate() {
shifts[i] = aux & term; shifts[n + i] = ((aux << 1) & term) / 2; shifts[2 * n + i] = ((aux << 2) & term) / 4; shifts[3 * n + i] = ((aux << 3) & term) / 8; }
shifts
}
pub fn reset(shifts: &[u64]) -> Vec<u64> {
shifts[0..shifts.len() / QUARTERS].to_vec()
}
pub fn collapse(state: &[u64]) -> Vec<u64> {
state
.iter()
.map(|&reset| u64::from_str_radix(&format!("{:x}", reset), 2).unwrap())
.collect::<Vec<u64>>()
}
pub fn quarters(state: &[u8]) -> Vec<u64> {
let mut quarters = vec![];
for pair in state.chunks(2) {
quarters.push(u16::from_le_bytes([pair[0], pair[1]]) as u64);
}
quarters
}
pub fn bytestring(dense: &[u64]) -> Vec<u64> {
dense
.iter()
.map(|x| vec![x % 256, x / 256])
.collect::<Vec<Vec<u64>>>()
.iter()
.flatten()
.copied()
.collect()
}
pub fn expand_state(state: &[u8]) -> Vec<u64> {
let mut expanded = vec![];
for pair in state.chunks(2) {
let quarter = u16::from_le_bytes([pair[0], pair[1]]);
expanded.push(Self::expand(quarter as u64));
}
expanded
}
pub fn padded_length(bytelength: usize) -> usize {
Self::num_blocks(bytelength) * RATE_IN_BYTES
}
pub fn pad(message: &[u8]) -> Vec<u8> {
let msg_len = message.len();
let pad_len = Self::padded_length(msg_len);
let mut padded = vec![0; pad_len];
for (i, byte) in message.iter().enumerate() {
padded[i] = *byte;
}
padded[msg_len] = 0x01;
padded[pad_len - 1] += 0x80;
padded
}
pub fn num_blocks(bytelength: usize) -> usize {
bytelength / RATE_IN_BYTES + 1
}
}
#[cfg(test)]
mod tests {
use rand::{rngs::StdRng, thread_rng, Rng};
use rand_core::SeedableRng;
use super::*;
#[test]
fn test_bitwise_sparse_representation() {
assert_eq!(Keccak::expand(0xFFFF), 0x1111111111111111);
assert_eq!(Keccak::expand(0x0000), 0x0000000000000000);
assert_eq!(Keccak::expand(0x1234), 0x0001001000110100)
}
#[test]
fn test_compose_decompose() {
let word: u64 = 0x70d324ac9215fd8e;
let dense = Keccak::decompose(word);
let expected_dense = [0xfd8e, 0x9215, 0x24ac, 0x70d3];
for i in 0..QUARTERS {
assert_eq!(dense[i], expected_dense[i]);
}
assert_eq!(word, Keccak::compose(&dense));
}
#[test]
fn test_quarter_expansion() {
let quarter: u16 = 0b01011010111011011; let expected_expansion = 0b0001000000010001000000010000000100010001000000010001000000010001; assert_eq!(expected_expansion, Keccak::expand(quarter as u64));
}
#[test]
fn test_sparse() {
let word: u64 = 0x1234567890abcdef;
let sparse = Keccak::sparse(word);
let expected_sparse: Vec<u64> = vec![
0x1100110111101111, 0x1001000010101011, 0x0101011001111000, 0x0001001000110100, ];
for i in 0..QUARTERS {
assert_eq!(sparse[i], expected_sparse[i]);
}
}
#[test]
fn test_shifts() {
let seed: [u8; 32] = thread_rng().gen();
eprintln!("Seed: {:?}", seed);
let mut rng = StdRng::from_seed(seed);
let word: u64 = rng.gen_range(0..2u128.pow(64)) as u64;
let sparse = Keccak::sparse(word);
let shifts = Keccak::shift(&sparse);
for i in 0..QUARTERS {
assert_eq!(
sparse[i],
shifts[i] + shifts[4 + i] * 2 + shifts[8 + i] * 4 + shifts[12 + i] * 8
)
}
}
#[test]
fn test_reset() {
let seed: [u8; 32] = thread_rng().gen();
eprintln!("Seed: {:?}", seed);
let mut rng = StdRng::from_seed(seed);
let word: u64 = rng.gen_range(0..2u128.pow(64)) as u64;
let shifts = Keccak::shift(&Keccak::sparse(word));
let reset = Keccak::reset(&shifts);
assert_eq!(reset.len(), 4);
assert_eq!(shifts.len(), 16);
for i in 0..QUARTERS {
assert_eq!(reset[i], shifts[i])
}
}
#[test]
fn test_collapse() {
let seed: [u8; 32] = thread_rng().gen();
eprintln!("Seed: {:?}", seed);
let mut rng = StdRng::from_seed(seed);
let word: u64 = rng.gen_range(0..2u128.pow(64)) as u64;
let dense = Keccak::compose(&Keccak::collapse(&Keccak::reset(&Keccak::shift(
&Keccak::sparse(word),
))));
assert_eq!(word, dense);
}
#[test]
fn test_max_carries() {
let seed: [u8; 32] = thread_rng().gen();
eprintln!("Seed: {:?}", seed);
let mut rng = StdRng::from_seed(seed);
let word: u64 = rng.gen_range(0..2u128.pow(64)) as u64;
let carries = 0xEEEE;
let mut sparse = Keccak::sparse(word)
.iter()
.map(|x| *x + carries)
.collect::<Vec<u64>>();
let dense = Keccak::compose(&Keccak::collapse(&Keccak::reset(&Keccak::shift(&sparse))));
assert_eq!(word, dense);
sparse[0] += 1;
let wrong_dense =
Keccak::compose(&Keccak::collapse(&Keccak::reset(&Keccak::shift(&sparse))));
assert_ne!(word, wrong_dense);
}
#[test]
fn test_sparse_xor() {
let seed: [u8; 32] = thread_rng().gen();
eprintln!("Seed: {:?}", seed);
let mut rng = StdRng::from_seed(seed);
let a: u64 = rng.gen_range(0..2u128.pow(64)) as u64;
let b: u64 = rng.gen_range(0..2u128.pow(64)) as u64;
let xor = a ^ b;
let sparse_a = Keccak::sparse(a);
let sparse_b = Keccak::sparse(b);
let sparse_sum = sparse_a
.iter()
.zip(sparse_b.iter())
.map(|(a, b)| a + b)
.collect::<Vec<u64>>();
let reset_sum = Keccak::reset(&Keccak::shift(&sparse_sum));
assert_eq!(Keccak::sparse(xor), reset_sum);
}
#[test]
fn test_sparse_and() {
let seed: [u8; 32] = thread_rng().gen();
eprintln!("Seed: {:?}", seed);
let mut rng = StdRng::from_seed(seed);
let a: u64 = rng.gen_range(0..2u128.pow(64)) as u64;
let b: u64 = rng.gen_range(0..2u128.pow(64)) as u64;
let and = a & b;
let sparse_a = Keccak::sparse(a);
let sparse_b = Keccak::sparse(b);
let sparse_sum = sparse_a
.iter()
.zip(sparse_b.iter())
.map(|(a, b)| a + b)
.collect::<Vec<u64>>();
let carries_sum = &Keccak::shift(&sparse_sum)[4..8];
assert_eq!(Keccak::sparse(and), carries_sum);
}
#[test]
fn test_sparse_not() {
let seed: [u8; 32] = thread_rng().gen();
eprintln!("Seed: {:?}", seed);
let mut rng = StdRng::from_seed(seed);
let word = rng.gen_range(0..2u64.pow(16));
let expanded = Keccak::expand(word);
let all_ones = 0xFFFF;
let not = all_ones - word;
let sparse_not = Keccak::expand(all_ones) - expanded;
assert_eq!(not, Keccak::collapse(&[sparse_not])[0]);
}
#[test]
fn test_pad_length() {
assert_eq!(Keccak::padded_length(0), RATE_IN_BYTES);
assert_eq!(Keccak::padded_length(1), RATE_IN_BYTES);
assert_eq!(Keccak::padded_length(RATE_IN_BYTES - 1), RATE_IN_BYTES);
assert_eq!(Keccak::padded_length(RATE_IN_BYTES), 2 * RATE_IN_BYTES);
assert_eq!(
Keccak::padded_length(RATE_IN_BYTES * 2 - 1),
2 * RATE_IN_BYTES
);
assert_eq!(Keccak::padded_length(RATE_IN_BYTES * 2), 3 * RATE_IN_BYTES);
}
#[test]
fn test_pad() {
let message = vec![0xFF; RATE_IN_BYTES - 1];
let padded = Keccak::pad(&message);
assert_eq!(padded.len(), RATE_IN_BYTES);
assert_eq!(padded[padded.len() - 1], 0x81);
let message = vec![0x01; RATE_IN_BYTES];
let padded = Keccak::pad(&message);
assert_eq!(padded.len(), 2 * RATE_IN_BYTES);
assert_eq!(padded[message.len()], 0x01);
assert_eq!(padded[padded.len() - 1], 0x80);
}
}