mod bn254;
mod mds;
mod traces;
pub use traces::*;
#[cfg(test)]
mod tests;
use ark_ff::PrimeField;
pub use bn254::*;
use crate::anemoi::mds::{ApplicableMDSMatrix, MDSMatrix};
pub trait AnemoiJive<F: PrimeField, const N: usize, const NUM_ROUNDS: usize>
where
MDSMatrix<F, N>: ApplicableMDSMatrix<F, N>,
{
const ALPHA: u32;
const GENERATOR: F;
const GENERATOR_INV: F;
const GENERATOR_SQUARE_PLUS_ONE: F;
const ROUND_KEYS_X: [[F; N]; NUM_ROUNDS];
const ROUND_KEYS_Y: [[F; N]; NUM_ROUNDS];
const PREPROCESSED_ROUND_KEYS_X: [[F; N]; NUM_ROUNDS];
const PREPROCESSED_ROUND_KEYS_Y: [[F; N]; NUM_ROUNDS];
const MDS_MATRIX: [[F; N]; N];
fn get_alpha_inv() -> Vec<u64>;
fn eval_variable_length_hash(input: &[F]) -> F {
let mut input = input.to_vec();
let sigma = if input.len() % (2 * N - 1) == 0 && !input.is_empty() {
F::ONE
} else {
input.push(F::ONE);
if input.len() % (2 * N - 1) != 0 {
input.extend_from_slice(&[F::ZERO].repeat(2 * N - 1 - (input.len() % (2 * N - 1))));
}
F::ZERO
};
assert_eq!(input.len() % (2 * N - 1), 0);
let mut x = [F::ZERO; N];
let mut y = [F::ZERO; N];
for chunk in input.chunks_exact(2 * N - 1) {
for i in 0..N {
x[i] += &chunk[i];
}
for i in 0..(N - 1) {
y[i] += &chunk[N + i];
}
Self::anemoi_permutation(&mut x, &mut y)
}
y[N - 1] += σ
x[0]
}
fn eval_variable_length_hash_with_trace(input: &[F]) -> AnemoiVLHTrace<F, N, NUM_ROUNDS> {
let mut trace = AnemoiVLHTrace::<F, N, NUM_ROUNDS>::default();
let mut input = input.to_vec();
trace.input = input.clone();
let mds = MDSMatrix::<F, N>(Self::MDS_MATRIX);
let alpha_inv = Self::get_alpha_inv();
let sigma = if input.len() % (2 * N - 1) == 0 && !input.is_empty() {
F::ONE
} else {
input.push(F::ONE);
if input.len() % (2 * N - 1) != 0 {
input.extend_from_slice(&[F::ZERO].repeat(2 * N - 1 - (input.len() % (2 * N - 1))));
}
F::ZERO
};
assert_eq!(input.len() % (2 * N - 1), 0);
let mut x = [F::ZERO; N];
let mut y = [F::ZERO; N];
for chunk in input.chunks_exact(2 * N - 1) {
for i in 0..N {
x[i] += &chunk[i];
}
for i in 0..(N - 1) {
y[i] += &chunk[N + i];
}
trace.before_permutation.push((x.clone(), y.clone()));
let mut intermediate_values_before_constant_additions =
([[F::ZERO; N]; NUM_ROUNDS], [[F::ZERO; N]; NUM_ROUNDS]);
for r in 0..NUM_ROUNDS {
for i in 0..N {
x[i] += &Self::ROUND_KEYS_X[r][i];
y[i] += &Self::ROUND_KEYS_Y[r][i];
}
mds.permute_in_place(&mut x, &mut y);
for i in 0..N {
y[i] += &x[i];
x[i] += &y[i];
}
for i in 0..N {
x[i] -= &(Self::GENERATOR * &(y[i].square()));
y[i] -= &x[i].pow(&alpha_inv);
x[i] += &(Self::GENERATOR * &(y[i].square()) + Self::GENERATOR_INV);
}
intermediate_values_before_constant_additions.0[r] = x.clone();
intermediate_values_before_constant_additions.1[r] = y.clone();
}
mds.permute_in_place(&mut x, &mut y);
for i in 0..N {
y[i] += &x[i];
x[i] += &y[i];
}
trace
.intermediate_values_before_constant_additions
.push(intermediate_values_before_constant_additions);
trace.after_permutation.push((x.clone(), y.clone()));
}
y[N - 1] += σ
trace.output = x[0];
trace
}
fn eval_stream_cipher(input: &[F], output_len: usize) -> Vec<F> {
let mut input = input.to_vec();
let mut output = Vec::with_capacity(output_len);
let sigma = if input.len() % (2 * N - 1) == 0 && !input.is_empty() {
F::one()
} else {
input.push(F::one());
if input.len() % (2 * N - 1) != 0 {
input.extend_from_slice(
&[F::zero()].repeat(2 * N - 1 - (input.len() % (2 * N - 1))),
);
}
F::zero()
};
assert_eq!(input.len() % (2 * N - 1), 0);
let mut x = [F::zero(); N];
let mut y = [F::zero(); N];
for chunk in input.chunks_exact(2 * N - 1) {
for i in 0..N {
x[i] += &chunk[i];
}
for i in 0..(N - 1) {
y[i] += &chunk[N + i];
}
Self::anemoi_permutation(&mut x, &mut y)
}
y[N - 1] += σ
if output_len <= N {
output.extend_from_slice(&x[..output_len])
} else if output_len > N && output_len <= (2 * N - 1) {
output.extend_from_slice(&x);
output.extend_from_slice(&y[..output_len - N])
} else if output_len > (2 * N - 1) {
output.extend_from_slice(&x);
output.extend_from_slice(&y[..N - 1]);
let squeezing_times = output_len / (2 * N - 1) - 1;
let remaining = output_len % (2 * N - 1);
for _ in 0..squeezing_times {
Self::anemoi_permutation(&mut x, &mut y);
output.extend_from_slice(&x);
output.extend_from_slice(&y[..N - 1]);
}
if remaining > 0 {
Self::anemoi_permutation(&mut x, &mut y);
let mut x = x.to_vec();
x.extend_from_slice(&y);
output.extend_from_slice(&x[..remaining]);
}
}
output
}
fn eval_stream_cipher_with_trace(
input: &[F],
output_len: usize,
) -> AnemoiStreamCipherTrace<F, N, NUM_ROUNDS> {
let mut trace = AnemoiStreamCipherTrace::<F, N, NUM_ROUNDS>::default();
let mut input = input.to_vec();
trace.input = input.clone();
let mds = MDSMatrix::<F, N>(Self::MDS_MATRIX);
let alpha_inv = Self::get_alpha_inv();
let sigma = if input.len() % (2 * N - 1) == 0 && !input.is_empty() {
F::one()
} else {
input.push(F::one());
if input.len() % (2 * N - 1) != 0 {
input.extend_from_slice(
&[F::zero()].repeat(2 * N - 1 - (input.len() % (2 * N - 1))),
);
}
F::zero()
};
assert_eq!(input.len() % (2 * N - 1), 0);
let mut anemoi_permutation_with_trace = |x: &mut [F; N], y: &mut [F; N]| {
trace.before_permutation.push((*x, *y));
let mut intermediate_values_before_constant_additions =
([[F::zero(); N]; NUM_ROUNDS], [[F::zero(); N]; NUM_ROUNDS]);
for r in 0..NUM_ROUNDS {
for i in 0..N {
x[i] += &Self::ROUND_KEYS_X[r][i];
y[i] += &Self::ROUND_KEYS_Y[r][i];
}
mds.permute_in_place(x, y);
for i in 0..N {
y[i] += &x[i];
x[i] += &y[i];
}
for i in 0..N {
x[i] -= &(Self::GENERATOR * (y[i].square()));
y[i] -= &x[i].pow(&alpha_inv);
x[i] += &(Self::GENERATOR * (y[i].square()) + Self::GENERATOR_INV);
}
intermediate_values_before_constant_additions.0[r] = *x;
intermediate_values_before_constant_additions.1[r] = *y;
}
mds.permute_in_place(x, y);
for i in 0..N {
y[i] += &x[i];
x[i] += &y[i];
}
trace
.intermediate_values_before_constant_additions
.push(intermediate_values_before_constant_additions);
trace.after_permutation.push((*x, *y));
};
let mut x = [F::zero(); N];
let mut y = [F::zero(); N];
for chunk in input.chunks_exact(2 * N - 1) {
for i in 0..N {
x[i] += &chunk[i];
}
for i in 0..(N - 1) {
y[i] += &chunk[N + i];
}
anemoi_permutation_with_trace(&mut x, &mut y);
}
y[N - 1] += σ
if output_len <= N {
trace.output.extend_from_slice(&x[..output_len])
} else if output_len > N && output_len <= (2 * N - 1) {
trace.output.extend_from_slice(&x);
trace.output.extend_from_slice(&y[..output_len - N])
} else if output_len > (2 * N - 1) {
trace.output.extend_from_slice(&x);
trace.output.extend_from_slice(&y[..N - 1]);
let squeezing_times = output_len / (2 * N - 1) - 1;
let remaining = output_len % (2 * N - 1);
for _ in 0..squeezing_times {
anemoi_permutation_with_trace(&mut x, &mut y);
trace.output.extend_from_slice(&x);
trace.output.extend_from_slice(&y[..N - 1]);
}
if remaining > 0 {
anemoi_permutation_with_trace(&mut x, &mut y);
let mut x = x.to_vec();
x.extend_from_slice(&y);
trace.output.extend_from_slice(&x[..remaining]);
}
}
trace
}
fn anemoi_permutation(x: &mut [F; N], y: &mut [F; N]) {
let mds = MDSMatrix::<F, N>(Self::MDS_MATRIX);
let alpha_inv = Self::get_alpha_inv();
for r in 0..NUM_ROUNDS {
for i in 0..N {
x[i] += &Self::ROUND_KEYS_X[r][i];
y[i] += &Self::ROUND_KEYS_Y[r][i];
}
mds.permute_in_place(x, y);
for i in 0..N {
y[i] += &x[i];
x[i] += &y[i];
}
for i in 0..N {
x[i] -= &(Self::GENERATOR * &(y[i].square()));
y[i] -= &x[i].pow(&alpha_inv);
x[i] += &(Self::GENERATOR * &(y[i].square()) + Self::GENERATOR_INV);
}
}
mds.permute_in_place(x, y);
for i in 0..N {
y[i] += &x[i];
x[i] += &y[i];
}
}
}