extern crate num; extern crate num_bigint; extern crate num_traits; use blake2::digest::{Input, VariableOutput}; use blake2::VarBlake2b; use num_bigint::{BigInt, Sign}; use num_traits::{One, Zero}; const SEED: &str = "poseidon"; const NROUNDSF: usize = 8; const NROUNDSP: usize = 57; const T: usize = 6; pub struct Constants { r: BigInt, c: Vec, m: Vec>, } pub fn generate_constants() -> Constants { let r: BigInt = BigInt::parse_bytes( b"21888242871839275222246405745257275088548364400416034343698204186575808495617", 10, ) .unwrap(); let c = get_pseudo_random(&r, format!("{}{}", SEED, "_constants"), NROUNDSF + NROUNDSP); let m = get_mds(&r); Constants { r: r, c: c, m: m } } pub fn get_pseudo_random(r: &BigInt, seed: String, n: usize) -> Vec { let mut hasher = VarBlake2b::new(32).unwrap(); hasher.input(seed.as_bytes()); let mut h = hasher.vec_result(); let mut res: Vec = Vec::new(); while res.len() < n { let new_n: BigInt = modulus(&BigInt::from_bytes_le(Sign::Plus, &h), &r); res.push(new_n); let mut hasher = VarBlake2b::new(32).unwrap(); hasher.input(h); h = hasher.vec_result(); } res } pub fn nonce_to_string(n: usize) -> String { let mut r = format!("{}", n); while r.len() < 4 { r = format!("0{}", r); } r } pub fn get_mds(r: &BigInt) -> Vec> { let mut nonce = 0; let mut cauchy_matrix = get_pseudo_random( r, format!("{}_matrix_{}", SEED, nonce_to_string(nonce)), T * 2, ); while !check_all_different(&cauchy_matrix) { nonce = nonce + 1; cauchy_matrix = get_pseudo_random( r, format!("{}_matrix_{}", SEED, nonce_to_string(nonce)), T * 2, ); } let mut m: Vec> = Vec::new(); for i in 0..T { let mut mi: Vec = Vec::new(); for j in 0..T { mi.push(modinv( &modulus(&(&cauchy_matrix[i] - &cauchy_matrix[T + j]), &r), &r, )); } m.push(mi); } m } pub fn check_all_different(v: &Vec) -> bool { let zero: BigInt = Zero::zero(); for i in 0..v.len() { if v[i] == zero { return false; } for j in i + 1..v.len() { if v[i] == v[j] { return false; } } } true } pub fn modulus(a: &BigInt, m: &BigInt) -> BigInt { ((a % m) + m) % m } pub fn modinv(a: &BigInt, q: &BigInt) -> BigInt { let mut mn = (q.clone(), a.clone()); let mut xy: (BigInt, BigInt) = (Zero::zero(), One::one()); let big_zero: BigInt = Zero::zero(); while mn.1 != big_zero { xy = (xy.1.clone(), xy.0 - (mn.0.clone() / mn.1.clone()) * xy.1); mn = (mn.1.clone(), modulus(&mn.0, &mn.1)); } while xy.0 < Zero::zero() { xy.0 = modulus(&xy.0, q); } xy.0 } pub fn check_bigint_in_field(a: &BigInt, q: &BigInt) -> bool { if a >= q { return false; } true } pub fn check_bigint_array_in_field(arr: &Vec, q: &BigInt) -> bool { for a in arr { if !check_bigint_in_field(a, &q) { return false; } } true } pub struct Poseidon { constants: Constants, } impl Poseidon { pub fn new() -> Poseidon { Poseidon { constants: generate_constants(), } } pub fn ark(&self, state: &Vec, c: &BigInt) -> Vec { let mut new_state: Vec = state.clone(); for i in 0..state.len() { new_state[i] = modulus(&(&state[i] + c), &self.constants.r); } new_state } pub fn cubic(&self, a: &BigInt) -> BigInt { modulus(&(a * a * a * a * a), &self.constants.r) } pub fn sbox(&self, state: &Vec, i: usize) -> Vec { let mut new_state: Vec = state.clone(); if i < NROUNDSF / 2 || i >= NROUNDSF / 2 + NROUNDSP { for j in 0..T { new_state[j] = self.cubic(&state[j]); } } else { new_state[0] = self.cubic(&state[0]); } new_state } pub fn mix(&self, state: &Vec, m: &Vec>) -> Vec { let mut new_state: Vec = Vec::new(); for i in 0..state.len() { new_state.push(Zero::zero()); for j in 0..state.len() { new_state[i] = modulus( &(&new_state[i] + modulus(&(&m[i][j] * &state[j]), &self.constants.r)), &self.constants.r, ) } } new_state } pub fn poseidon_hash(&self, inp: Vec) -> Result { if inp.len() == 0 || inp.len() > T { return Err("Wrong inputs length".to_string()); } // check if arr elements are inside the finite field over R if !check_bigint_array_in_field(&inp, &self.constants.r) { return Err("elements not inside the finite field over R".to_string()); } let mut state = inp.clone(); for _ in inp.len()..T { state.push(Zero::zero()); } for i in 0..(NROUNDSF + NROUNDSP) { state = self.ark(&state, &self.constants.c[i]); state = self.sbox(&state, i); state = self.mix(&state, &self.constants.m); } Ok(state[0].clone()) } } #[cfg(test)] mod tests { use super::*; use rustc_hex::ToHex; #[test] fn test_blake2_version() { let mut hasher = VarBlake2b::new(32).unwrap(); hasher.input(b"poseidon_constants"); let h = hasher.vec_result(); assert_eq!( h.to_hex(), "e57ba154fb2c47811dc1a2369b27e25a44915b4e4ece4eb8ec74850cb78e01b1" ); } #[test] fn test_poseidon_hash() { let b1: BigInt = BigInt::parse_bytes(b"1", 10).unwrap(); let b2: BigInt = BigInt::parse_bytes(b"2", 10).unwrap(); let mut big_arr: Vec = Vec::new(); big_arr.push(b1.clone()); big_arr.push(b2.clone()); let poseidon = Poseidon::new(); let h = poseidon.poseidon_hash(big_arr).unwrap(); assert_eq!( h.to_string(), "12242166908188651009877250812424843524687801523336557272219921456462821518061" ); let b3: BigInt = BigInt::parse_bytes(b"3", 10).unwrap(); let b4: BigInt = BigInt::parse_bytes(b"4", 10).unwrap(); let mut big_arr34: Vec = Vec::new(); big_arr34.push(b3.clone()); big_arr34.push(b4.clone()); let h34 = poseidon.poseidon_hash(big_arr34).unwrap(); assert_eq!( h34.to_string(), "17185195740979599334254027721507328033796809509313949281114643312710535000993" ); } }