From 6c8fa674978e7ad0eea5761d5160787270f4e879 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Thu, 19 Sep 2024 10:09:09 -0300 Subject: [PATCH 01/17] first commit --- crypto/src/hash/mod.rs | 1 + crypto/src/hash/rescue_prime/mod.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 crypto/src/hash/rescue_prime/mod.rs diff --git a/crypto/src/hash/mod.rs b/crypto/src/hash/mod.rs index 630903112..ad9e21a1c 100644 --- a/crypto/src/hash/mod.rs +++ b/crypto/src/hash/mod.rs @@ -2,4 +2,5 @@ pub mod hash_to_field; pub mod monolith; pub mod pedersen; pub mod poseidon; +pub mod rescue_prime; pub mod sha3; diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs new file mode 100644 index 000000000..43d16f641 --- /dev/null +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -0,0 +1,12 @@ +// Need to implement a Field over the prime field p +// p = 2^64 - 2^32 + 1 + +// I have GoldilockPrimeField + +// MiniGoldilockPrimeField +// 2^32 - 2^16 + 1 + +use lambdaworks_math::field::{fields::fft_friendly::u64_goldilocks, traits::IsField}; + + +fn get_round_constants(security_level:u32)-> Vec From 7b30c247c29adda6ee74ce1118426696568a20b4 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Thu, 19 Sep 2024 18:54:46 -0300 Subject: [PATCH 02/17] first draft, save work --- crypto/src/hash/rescue_prime/mod.rs | 184 +++++++++++++++++++++++++++- 1 file changed, 182 insertions(+), 2 deletions(-) diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index 43d16f641..c879ae66c 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -6,7 +6,187 @@ // MiniGoldilockPrimeField // 2^32 - 2^16 + 1 -use lambdaworks_math::field::{fields::fft_friendly::u64_goldilocks, traits::IsField}; +use std::io::Read; +use lambdaworks_math::field::fields::fft_friendly::u64_goldilocks::U64GoldilocksPrimeField; +use lambdaworks_math::{field::element::FieldElement, unsigned_integer::element::U64}; -fn get_round_constants(security_level:u32)-> Vec +use sha3::{ + digest::{ExtendableOutput, Update}, + Shake256, +}; +const P: U64 = U64::from_u64(18446744069414584321); // p = 2^64 - 2^32 + 1 +const NUM_FULL_ROUNDS: usize = 7; +const ALPHA: u64 = 7; +const ALPHA_INV: u64 = 10540996611094048183; + +type Fp = FieldElement; + +fn get_round_constants(p: u64, m: usize, c: usize, lambda: usize, num_rounds: usize) -> Vec { + let seed_string = format!("RPO({},{},{},{})", p, m, c, lambda); + let mut shake = Shake256::default(); + shake.update(seed_string.as_bytes()); + + let num_constants = 2 * m * num_rounds; + let mut shake_output = shake.finalize_xof(); + let mut round_constants = Vec::new(); + + for _ in 0..num_constants { + let mut bytes = [0u8; 8]; + shake_output.read_exact(&mut bytes).unwrap(); + let constant = Fp::from(u64::from_le_bytes(bytes)); // Convert to field element + round_constants.push(constant); + } + round_constants +} +fn get_mds(m: usize) -> Vec { + match m { + 12 => vec![7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8], + 16 => vec![ + 256, 2, 1073741824, 2048, 16777216, 128, 8, 16, 524288, 4194304, 1, 268435456, 1, 1024, + 2, 8192, + ], + _ => panic!("Unsupported state size"), // Avoid using panic + } +} + +fn ntt(state: &[u64], omega: u64, order: usize, p: u64) -> Vec { + let mut result = vec![0u64; order]; + for i in 0..order { + let mut sum = 0u64; + for (j, &val) in state.iter().enumerate() { + sum = (sum + val * mod_exp(omega, (i * j) as u64, p)) % p; + } + result[i] = sum; + } + result +} + +fn intt(state: &[u64], omega: u64, order: usize, p: u64) -> Vec { + let inv_order = mod_inv(order as u64, p); + let mut result = ntt(state, mod_inv(omega, p), order, p); + for res in result.iter_mut() { + *res = (*res * inv_order) % p; + } + result +} + +fn mod_exp(base: u64, exp: u64, modulus: u64) -> u64 { + let mut result = 1; + let mut base = base % modulus; + let mut exp = exp; + while exp > 0 { + if exp % 2 == 1 { + result = (result * base) % modulus; + } + base = (base * base) % modulus; + exp /= 2; + } + result +} +fn mod_inv(a: Fp) -> Fp { + Fp::inv(&a) // Use Lambdaworks' field inversion +} + +fn apply_sbox(state: &mut [Fp], alpha: u64) { + for x in state.iter_mut() { + *x = x.pow(alpha); // Using the pow method of FieldElement + } +} + +fn apply_inverse_sbox(state: &mut [Fp], alpha_inv: u64) { + for x in state.iter_mut() { + *x = x.pow(alpha_inv); // Inverse S-Box using pow + } +} +fn rescue_prime_optimized( + state: &mut [Fp], + round_constants: &[Fp], + mds_matrix: &[Fp], + alpha: u64, + alpha_inv: u64, + num_rounds: usize, +) { + let m = state.len(); + + for round in 0..num_rounds { + // Apply MDS matrix + *state = mds_matrix_vector_multiplication(mds_matrix, state); + + // Add round constants + for j in 0..m { + state[j] = state[j].add(&round_constants[round * 2 * m + j]); + } + + // Apply S-Box + apply_sbox(state, alpha); + + // Apply MDS again + *state = mds_matrix_vector_multiplication(mds_matrix, state); + + // Add round constants again + for j in 0..m { + state[j] = state[j].add(&round_constants[round * 2 * m + m + j]); + } + + // Apply Inverse S-Box + apply_inverse_sbox(state, alpha_inv); + } +} + +fn mds_matrix_vector_multiplication(mds: &[Fp], state: &[Fp]) -> Vec { + let mut new_state = vec![Fp::zero(); state.len()]; + let m = state.len(); + + for i in 0..m { + for j in 0..m { + new_state[i] = new_state[i].add(&mds[(i + j) % m].mul(&state[j])); + } + } + new_state +} + +fn rpo_hash(security_level: usize, input_sequence: Vec) -> Vec { + let (m, capacity) = if security_level == 128 { + (12, 4) + } else { + (16, 6) + }; + let p = P; + let rate = m - capacity; + + // Get MDS matrix, round constants, alpha and inverse alpha + let mds = get_mds(m); + let round_constants = get_round_constants(p, m, capacity, security_level, NUM_FULL_ROUNDS); + let (alpha, alpha_inv) = (ALPHA, ALPHA_INV); + + let mut state = vec![Fp::zero(); m]; + let mut padded_input = input_sequence.clone(); + + // Padding + if input_sequence.len() % rate != 0 { + padded_input.push(Fp::one()); + while padded_input.len() % rate != 0 { + padded_input.push(Fp::zero()); + } + state[0] = Fp::one(); // Domain separation + } + + // Absorb the input + for chunk in padded_input.chunks(rate) { + for i in 0..rate { + state[capacity + i] = chunk[i]; + } + rescue_prime_optimized( + &mut state, + &round_constants, + &mds, + alpha, + alpha_inv, + NUM_FULL_ROUNDS, + ); + } + + // Return squeezed output + state[capacity..capacity + rate / 2].to_vec() +} From 6a9979ca0afa15a11dca70359c8579d34e52b31b Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Tue, 24 Sep 2024 18:02:27 -0300 Subject: [PATCH 03/17] first working commit, passes all tests. Save work --- crypto/src/hash/rescue_prime/mod.rs | 803 +++++++++++++++++++++++----- 1 file changed, 656 insertions(+), 147 deletions(-) diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index c879ae66c..ef5cb0793 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -1,192 +1,701 @@ -// Need to implement a Field over the prime field p -// p = 2^64 - 2^32 + 1 - -// I have GoldilockPrimeField - -// MiniGoldilockPrimeField -// 2^32 - 2^16 + 1 - +use lambdaworks_math::field::element::FieldElement; +use lambdaworks_math::field::fields::u64_goldilocks_field::Goldilocks64Field; use std::io::Read; -use lambdaworks_math::field::fields::fft_friendly::u64_goldilocks::U64GoldilocksPrimeField; -use lambdaworks_math::{field::element::FieldElement, unsigned_integer::element::U64}; - use sha3::{ digest::{ExtendableOutput, Update}, Shake256, }; -const P: U64 = U64::from_u64(18446744069414584321); // p = 2^64 - 2^32 + 1 -const NUM_FULL_ROUNDS: usize = 7; -const ALPHA: u64 = 7; -const ALPHA_INV: u64 = 10540996611094048183; - -type Fp = FieldElement; +pub const ALPHA: u64 = 7; +pub const ALPHA_INV: u64 = 10540996611094048183; +// Define the field type for clarity +type Fp = FieldElement; -fn get_round_constants(p: u64, m: usize, c: usize, lambda: usize, num_rounds: usize) -> Vec { - let seed_string = format!("RPO({},{},{},{})", p, m, c, lambda); - let mut shake = Shake256::default(); - shake.update(seed_string.as_bytes()); +#[derive(Clone)] +enum MdsMethod { + MatrixMultiplication, + Ntt, + Karatsuba, +} - let num_constants = 2 * m * num_rounds; - let mut shake_output = shake.finalize_xof(); - let mut round_constants = Vec::new(); +pub struct RescuePrimeOptimized { + m: usize, + capacity: usize, + rate: usize, + round_constants: Vec, + mds_matrix: Vec>, + mds_vector: Vec, + alpha: u64, + alpha_inv: u64, + mds_method: MdsMethod, +} - for _ in 0..num_constants { - let mut bytes = [0u8; 8]; - shake_output.read_exact(&mut bytes).unwrap(); - let constant = Fp::from(u64::from_le_bytes(bytes)); // Convert to field element - round_constants.push(constant); +impl Default + for RescuePrimeOptimized +{ + fn default() -> Self { + Self::new(MdsMethod::MatrixMultiplication) } - round_constants } -fn get_mds(m: usize) -> Vec { - match m { - 12 => vec![7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8], - 16 => vec![ - 256, 2, 1073741824, 2048, 16777216, 128, 8, 16, 524288, 4194304, 1, 268435456, 1, 1024, - 2, 8192, - ], - _ => panic!("Unsupported state size"), // Avoid using panic + +impl + RescuePrimeOptimized +{ + const P: u64 = 18446744069414584321; // p = 2^64 - 2^32 + 1 + const ALPHA: u64 = 7; + const ALPHA_INV: u64 = 10540996611094048183; + + pub fn new(mds_method: MdsMethod) -> Self { + assert!(SECURITY_LEVEL == 128 || SECURITY_LEVEL == 160); + + let (m, capacity) = if SECURITY_LEVEL == 128 { + (12, 4) + } else { + (16, 6) + }; + let rate = m - capacity; + + let mds_vector = Self::get_mds_vector(m); + let mds_matrix = Self::generate_circulant_matrix(&mds_vector); + + let round_constants = Self::instantiate_round_constants( + Self::P, + m, + capacity, + SECURITY_LEVEL, + NUM_FULL_ROUNDS, + ); + + Self { + m, + capacity, + rate, + round_constants, + mds_matrix, + mds_vector, + alpha: Self::ALPHA, + alpha_inv: Self::ALPHA_INV, + mds_method, + } + } + + fn get_mds_vector(m: usize) -> Vec { + match m { + 12 => vec![7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8] + .into_iter() + .map(Fp::from) + .collect(), + 16 => vec![ + 256, 2, 1073741824, 2048, 16777216, 128, 8, 16, 524288, 4194304, 1, 268435456, 1, + 1024, 2, 8192, + ] + .into_iter() + .map(Fp::from) + .collect(), + _ => panic!("Unsupported state size"), + } + } + + fn generate_circulant_matrix(mds_vector: &[Fp]) -> Vec> { + let m = mds_vector.len(); + let mut mds_matrix = vec![vec![Fp::zero(); m]; m]; + for i in 0..m { + for j in 0..m { + mds_matrix[i][j] = mds_vector[(j + m - i) % m].clone(); + } + } + mds_matrix + } + + fn instantiate_round_constants( + p: u64, + m: usize, + capacity: usize, + security_level: usize, + num_rounds: usize, + ) -> Vec { + let seed_string = format!("RPO({},{},{},{})", p, m, capacity, security_level); + let mut shake = Shake256::default(); + shake.update(seed_string.as_bytes()); + + let num_constants = 2 * m * num_rounds; + let mut shake_output = shake.finalize_xof(); + let mut round_constants = Vec::new(); + + for _ in 0..num_constants { + let mut bytes = [0u8; 8]; + shake_output.read_exact(&mut bytes).unwrap(); + let constant = Fp::from(u64::from_le_bytes(bytes)); + round_constants.push(constant); + } + round_constants + } + + pub fn apply_sbox(state: &mut [Fp], alpha: u64) { + for x in state.iter_mut() { + *x = x.pow(alpha); + } + } + + pub fn apply_inverse_sbox(state: &mut [Fp], alpha_inv: u64) { + for x in state.iter_mut() { + *x = x.pow(alpha_inv); + } + } + + fn mds_matrix_vector_multiplication(&self, state: &[Fp]) -> Vec { + let m = state.len(); + let mut new_state = vec![Fp::zero(); m]; + for i in 0..m { + for j in 0..m { + new_state[i] = new_state[i] + self.mds_matrix[i][j] * state[j]; + } + } + new_state + } + + fn mds_ntt(&self, state: &[Fp]) -> Vec { + let m = state.len(); + let omega = match m { + 12 => Fp::from(281474976645120u64), + 16 => Fp::from(17293822564807737345u64), + _ => panic!("Unsupported state size for NTT"), + }; + + // NTT of MDS vector and state + let mds_ntt = ntt(&self.mds_vector, omega); + let state_rev: Vec = std::iter::once(state[0].clone()) + .chain(state[1..].iter().rev().cloned()) + .collect(); + let state_ntt = ntt(&state_rev, omega); + + // Point-wise multiplication + let mut product_ntt = vec![Fp::zero(); m]; + for i in 0..m { + product_ntt[i] = mds_ntt[i] * state_ntt[i]; + } + + // Inverse NTT + let omega_inv = omega.inv().unwrap(); + let result = intt(&product_ntt, omega_inv); + + // Adjust the result + std::iter::once(result[0].clone()) + .chain(result[1..].iter().rev().cloned()) + .collect() + } + + fn mds_karatsuba(&self, state: &[Fp]) -> Vec { + let m = state.len(); + let mds_rev: Vec = std::iter::once(self.mds_vector[0].clone()) + .chain(self.mds_vector[1..].iter().rev().cloned()) + .collect(); + + let conv = karatsuba(&mds_rev, state); + + let mut result = vec![Fp::zero(); m]; + for i in 0..m { + result[i] = conv[i].clone(); + } + for i in m..conv.len() { + result[i - m] = result[i - m] + conv[i].clone(); + } + + result + } + + fn apply_mds(&self, state: &mut [Fp]) { + let new_state = match self.mds_method { + MdsMethod::MatrixMultiplication => self.mds_matrix_vector_multiplication(state), + MdsMethod::Ntt => self.mds_ntt(state), + MdsMethod::Karatsuba => self.mds_karatsuba(state), + }; + state.copy_from_slice(&new_state); + } + + fn add_round_constants(&self, state: &mut [Fp], round: usize) { + let m = self.m; + let round_constants = &self.round_constants; + + for j in 0..m { + state[j] = state[j] + round_constants[round * 2 * m + j]; + } + } + + fn add_round_constants_second(&self, state: &mut [Fp], round: usize) { + let m = self.m; + let round_constants = &self.round_constants; + + for j in 0..m { + state[j] = state[j] + round_constants[round * 2 * m + m + j]; + } + } + + pub fn permutation(&self, state: &mut [Fp]) { + let num_rounds = NUM_FULL_ROUNDS; + let alpha = self.alpha; + let alpha_inv = self.alpha_inv; + + for round in 0..num_rounds { + self.apply_mds(state); + self.add_round_constants(state, round); + Self::apply_sbox(state, alpha); + self.apply_mds(state); + self.add_round_constants_second(state, round); + Self::apply_inverse_sbox(state, alpha_inv); + } + } + + pub fn hash(&self, input_sequence: &[Fp]) -> Vec { + let m = self.m; + let capacity = self.capacity; + let rate = self.rate; + + let mut state = vec![Fp::zero(); m]; + let mut padded_input = input_sequence.to_vec(); + + // Padding + if input_sequence.len() % rate != 0 { + padded_input.push(Fp::one()); + while padded_input.len() % rate != 0 { + padded_input.push(Fp::zero()); + } + state[0] = Fp::one(); // Domain separation + } + + // Absorb the input + for chunk in padded_input.chunks(rate) { + for i in 0..rate { + state[capacity + i] = state[capacity + i] + chunk[i].clone(); + } + self.permutation(&mut state); + } + + // Return squeezed output + state[capacity..capacity + rate / 2].to_vec() + } + + pub fn hash_bytes(&self, input: &[u8]) -> Vec { + let field_elements = bytes_to_field_elements(input); + self.hash(&field_elements) } } -fn ntt(state: &[u64], omega: u64, order: usize, p: u64) -> Vec { - let mut result = vec![0u64; order]; - for i in 0..order { - let mut sum = 0u64; - for (j, &val) in state.iter().enumerate() { - sum = (sum + val * mod_exp(omega, (i * j) as u64, p)) % p; +fn bytes_to_field_elements(input: &[u8]) -> Vec { + // Include the length of the input as the first field element + let mut elements = vec![Fp::from(input.len() as u64)]; + + // Convert the input bytes into field elements + let padded_input = input.to_vec(); + // Optionally, pad the input to a multiple of 8 bytes if needed + + elements.extend(padded_input.chunks(8).map(|chunk| { + let mut bytes = [0u8; 8]; + for (i, &b) in chunk.iter().enumerate() { + bytes[i] = b; } - result[i] = sum; + Fp::from(u64::from_le_bytes(bytes)) + })); + elements +} +// Implement NTT and INTT functions +fn ntt(input: &[Fp], omega: Fp) -> Vec { + let n = input.len(); + let mut output = vec![Fp::zero(); n]; + for i in 0..n { + let mut sum = Fp::zero(); + for (j, val) in input.iter().enumerate() { + sum = sum + *val * omega.pow((i * j) as u64); + } + output[i] = sum; } - result + output } -fn intt(state: &[u64], omega: u64, order: usize, p: u64) -> Vec { - let inv_order = mod_inv(order as u64, p); - let mut result = ntt(state, mod_inv(omega, p), order, p); - for res in result.iter_mut() { - *res = (*res * inv_order) % p; +fn intt(input: &[Fp], omega_inv: Fp) -> Vec { + let n = input.len(); + let inv_n = Fp::from(n as u64).inv().unwrap(); + let mut output = ntt(input, omega_inv); + for val in output.iter_mut() { + *val = *val * inv_n; } - result + output } -fn mod_exp(base: u64, exp: u64, modulus: u64) -> u64 { - let mut result = 1; - let mut base = base % modulus; - let mut exp = exp; - while exp > 0 { - if exp % 2 == 1 { - result = (result * base) % modulus; +// Implement Karatsuba multiplication +fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { + let n = lhs.len(); + if n <= 32 { + // For small n, use the standard multiplication + let mut result = vec![Fp::zero(); 2 * n - 1]; + for i in 0..n { + for j in 0..n { + result[i + j] = result[i + j] + lhs[i] * rhs[j]; + } } - base = (base * base) % modulus; - exp /= 2; + return result; } + + let half = n / 2; + + let lhs_low = &lhs[..half]; + let lhs_high = &lhs[half..]; + let rhs_low = &rhs[..half]; + let rhs_high = &rhs[half..]; + + let z0 = karatsuba(lhs_low, rhs_low); + let z2 = karatsuba(lhs_high, rhs_high); + + let lhs_sum: Vec = lhs_low + .iter() + .zip(lhs_high.iter()) + .map(|(a, b)| *a + *b) + .collect(); + + let rhs_sum: Vec = rhs_low + .iter() + .zip(rhs_high.iter()) + .map(|(a, b)| *a + *b) + .collect(); + + let z1 = karatsuba(&lhs_sum, &rhs_sum); + + let mut result = vec![Fp::zero(); 2 * n - 1]; + + for i in 0..z0.len() { + result[i] = result[i] + z0[i]; + } + + for i in 0..z1.len() { + result[i + half] = result[i + half] + z1[i] + - z0.get(i).cloned().unwrap_or(Fp::zero()) + - z2.get(i).cloned().unwrap_or(Fp::zero()); + } + + for i in 0..z2.len() { + result[i + 2 * half] = result[i + 2 * half] + z2[i]; + } + result } -fn mod_inv(a: Fp) -> Fp { - Fp::inv(&a) // Use Lambdaworks' field inversion -} -fn apply_sbox(state: &mut [Fp], alpha: u64) { - for x in state.iter_mut() { - *x = x.pow(alpha); // Using the pow method of FieldElement +#[cfg(test)] +mod tests { + use super::*; + use lambdaworks_math::traits::ByteConversion; + //use proptest::prelude::*; + use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; + + //const ALPHA: u64 = 7; + //const ALPHA_INV: u64 = 10540996611094048183; + + fn rand_field_element(rng: &mut R) -> Fp { + Fp::from(rng.gen::()) } -} -fn apply_inverse_sbox(state: &mut [Fp], alpha_inv: u64) { - for x in state.iter_mut() { - *x = x.pow(alpha_inv); // Inverse S-Box using pow + #[test] + fn test_alphas() { + let mut rng = StdRng::seed_from_u64(0); + let e = rand_field_element(&mut rng); + let e_exp = e.pow(ALPHA); + assert_eq!(e, e_exp.pow(ALPHA_INV)); } -} -fn rescue_prime_optimized( - state: &mut [Fp], - round_constants: &[Fp], - mds_matrix: &[Fp], - alpha: u64, - alpha_inv: u64, - num_rounds: usize, -) { - let m = state.len(); - for round in 0..num_rounds { - // Apply MDS matrix - *state = mds_matrix_vector_multiplication(mds_matrix, state); + #[test] + fn test_sbox() { + let mut rng = StdRng::seed_from_u64(1); + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let mut state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); - // Add round constants - for j in 0..m { - state[j] = state[j].add(&round_constants[round * 2 * m + j]); - } + let mut expected = state.clone(); + expected.iter_mut().for_each(|v| *v = v.pow(ALPHA)); - // Apply S-Box - apply_sbox(state, alpha); + RescuePrimeOptimized::<128, 7>::apply_sbox(&mut state, ALPHA); + assert_eq!(expected, state); + } - // Apply MDS again - *state = mds_matrix_vector_multiplication(mds_matrix, state); + #[test] + fn test_inv_sbox() { + let mut rng = StdRng::seed_from_u64(2); + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let mut state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); - // Add round constants again - for j in 0..m { - state[j] = state[j].add(&round_constants[round * 2 * m + m + j]); + let mut expected = state.clone(); + expected.iter_mut().for_each(|v| *v = v.pow(ALPHA_INV)); + + RescuePrimeOptimized::<128, 7>::apply_inverse_sbox(&mut state, ALPHA_INV); + assert_eq!(expected, state); + } + + #[test] + fn hash_padding() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + + let input1 = vec![1u8, 2, 3]; + let input2 = vec![1u8, 2, 3, 0]; + + let hash1 = rescue.hash_bytes(&input1); + let hash2 = rescue.hash_bytes(&input2); + + assert_ne!(hash1, hash2); + + let input1 = vec![1_u8, 2, 3, 4, 5, 6]; + let input2 = vec![1_u8, 2, 3, 4, 5, 6, 0]; + + let hash1 = rescue.hash_bytes(&input1); + let hash2 = rescue.hash_bytes(&input2); + assert_ne!(hash1, hash2); + + let input1 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0]; + let input2 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0]; + + let hash1 = rescue.hash_bytes(&input1); + let hash2 = rescue.hash_bytes(&input2); + assert_ne!(hash1, hash2); + } + + #[test] + fn sponge_zeroes_collision() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + + let mut zeroes = Vec::new(); + let mut hashes = std::collections::HashSet::new(); + + for _ in 0..255 { + let hash = rescue.hash(&zeroes); + assert!(hashes.insert(hash)); + zeroes.push(Fp::zero()); } + } + + #[test] + fn test_mds_methods_consistency() { + let rescue_matrix = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let rescue_ntt = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Ntt); + let rescue_karatsuba = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Karatsuba); + + let input = vec![ + Fp::from(1u64), + Fp::from(2u64), + Fp::from(3u64), + Fp::from(4u64), + ]; + + let hash_matrix = rescue_matrix.hash(&input); + let hash_ntt = rescue_ntt.hash(&input); + let hash_karatsuba = rescue_karatsuba.hash(&input); - // Apply Inverse S-Box - apply_inverse_sbox(state, alpha_inv); + assert_eq!(hash_matrix, hash_ntt); + assert_eq!(hash_ntt, hash_karatsuba); } -} -fn mds_matrix_vector_multiplication(mds: &[Fp], state: &[Fp]) -> Vec { - let mut new_state = vec![Fp::zero(); state.len()]; - let m = state.len(); + // Creaate a test function that generates test vectors, maybe it would be better to + // generate them using proptest or in a separate file + #[test] + fn generate_test_vectors() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let elements = vec![ + Fp::from(0u64), + Fp::from(1u64), + Fp::from(2u64), + Fp::from(3u64), + Fp::from(4u64), + Fp::from(5u64), + Fp::from(6u64), + Fp::from(7u64), + Fp::from(8u64), + Fp::from(9u64), + Fp::from(10u64), + Fp::from(11u64), + Fp::from(12u64), + Fp::from(13u64), + Fp::from(14u64), + Fp::from(15u64), + Fp::from(16u64), + Fp::from(17u64), + Fp::from(18u64), + ]; - for i in 0..m { - for j in 0..m { - new_state[i] = new_state[i].add(&mds[(i + j) % m].mul(&state[j])); + println!("let expected_hashes = vec!["); + for i in 0..elements.len() { + let input = &elements[..=i]; // Take prefix up to i + let hash_output = rescue.hash(input); + + print!(" vec!["); + for value in &hash_output { + print!("Fp::from({}u64), ", value.value()); + } + println!("],"); } + println!("];"); } - new_state -} -fn rpo_hash(security_level: usize, input_sequence: Vec) -> Vec { - let (m, capacity) = if security_level == 128 { - (12, 4) - } else { - (16, 6) - }; - let p = P; - let rate = m - capacity; - - // Get MDS matrix, round constants, alpha and inverse alpha - let mds = get_mds(m); - let round_constants = get_round_constants(p, m, capacity, security_level, NUM_FULL_ROUNDS); - let (alpha, alpha_inv) = (ALPHA, ALPHA_INV); - - let mut state = vec![Fp::zero(); m]; - let mut padded_input = input_sequence.clone(); - - // Padding - if input_sequence.len() % rate != 0 { - padded_input.push(Fp::one()); - while padded_input.len() % rate != 0 { - padded_input.push(Fp::zero()); - } - state[0] = Fp::one(); // Domain separation - } - - // Absorb the input - for chunk in padded_input.chunks(rate) { - for i in 0..rate { - state[capacity + i] = chunk[i]; - } - rescue_prime_optimized( - &mut state, - &round_constants, - &mds, - alpha, - alpha_inv, - NUM_FULL_ROUNDS, - ); + #[test] + fn hash_test_vectors() { + let elements = vec![ + Fp::from(0u64), + Fp::from(1u64), + Fp::from(2u64), + Fp::from(3u64), + Fp::from(4u64), + Fp::from(5u64), + Fp::from(6u64), + Fp::from(7u64), + Fp::from(8u64), + Fp::from(9u64), + Fp::from(10u64), + Fp::from(11u64), + Fp::from(12u64), + Fp::from(13u64), + Fp::from(14u64), + Fp::from(15u64), + Fp::from(16u64), + Fp::from(17u64), + Fp::from(18u64), + ]; + + let expected_hashes = vec![ + vec![ + Fp::from(17254761148825111576u64), + Fp::from(5748658173944016543u64), + Fp::from(15507216263332757191u64), + Fp::from(3983249929669394122u64), + ], + vec![ + Fp::from(17712221936341625086u64), + Fp::from(2943298861192409284u64), + Fp::from(2494572860652577379u64), + Fp::from(2378199810979427322u64), + ], + vec![ + Fp::from(3876314992495866678u64), + Fp::from(17611388455687538623u64), + Fp::from(3911042865754506040u64), + Fp::from(16776766772018109848u64), + ], + vec![ + Fp::from(6056716262596077506u64), + Fp::from(16158290354505703086u64), + Fp::from(17447029989314528820u64), + Fp::from(1567470650296395962u64), + ], + vec![ + Fp::from(13380585531133108000u64), + Fp::from(3137417240495852984u64), + Fp::from(3098660641723081460u64), + Fp::from(5150917506181658097u64), + ], + vec![ + Fp::from(7024209367141755347u64), + Fp::from(16246734205622419915u64), + Fp::from(7503077358698812671u64), + Fp::from(12133031123118477720u64), + ], + vec![ + Fp::from(8402140550106217856u64), + Fp::from(7956967817259077006u64), + Fp::from(7462144441524583670u64), + Fp::from(16871123896451099924u64), + ], + vec![ + Fp::from(3306774948807044313u64), + Fp::from(9076368178691092936u64), + Fp::from(2759350540710171864u64), + Fp::from(3210614416697826413u64), + ], + vec![ + Fp::from(9674626283660829003u64), + Fp::from(7912357911043410654u64), + Fp::from(11533209507830100464u64), + Fp::from(10170478333989115619u64), + ], + vec![ + Fp::from(10555879534445000475u64), + Fp::from(7964878826308278072u64), + Fp::from(9043911582507760001u64), + Fp::from(17545606895180851135u64), + ], + vec![ + Fp::from(11622987728342977762u64), + Fp::from(6359923222102476051u64), + Fp::from(13174910429968985338u64), + Fp::from(15940765068206503257u64), + ], + vec![ + Fp::from(9317736379809246942u64), + Fp::from(1131339481094147037u64), + Fp::from(7694193714172738957u64), + Fp::from(3021948153504618318u64), + ], + vec![ + Fp::from(15452378822943328512u64), + Fp::from(8092427130361699902u64), + Fp::from(4291324087873637870u64), + Fp::from(7948600115971845347u64), + ], + vec![ + Fp::from(7316340811681411604u64), + Fp::from(12607667321921907916u64), + Fp::from(1038271716522639176u64), + Fp::from(14471693297474155620u64), + ], + vec![ + Fp::from(12773488141023465780u64), + Fp::from(6221729007489926125u64), + Fp::from(1486696495069601684u64), + Fp::from(5967114319922573516u64), + ], + vec![ + Fp::from(12321222925119640558u64), + Fp::from(3287873714193039570u64), + Fp::from(14642702142995841541u64), + Fp::from(15416139920349778032u64), + ], + vec![ + Fp::from(1397847368400388990u64), + Fp::from(10132081287571009963u64), + Fp::from(8992380008215239242u64), + Fp::from(13825336864150598095u64), + ], + vec![ + Fp::from(11938166169298599670u64), + Fp::from(6941295435497807075u64), + Fp::from(1474794246787649407u64), + Fp::from(13435514261569247470u64), + ], + vec![ + Fp::from(488103042505954048u64), + Fp::from(953948736820844501u64), + Fp::from(18197062251142516718u64), + Fp::from(459513752465468917u64), + ], + ]; + + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + + for i in 0..elements.len() { + let input = &elements[..=i]; // Take prefix up to index i + let hash_output = rescue.hash(input); + + let expected_hash = &expected_hashes[i]; + assert_eq!(hash_output, *expected_hash, "Hash mismatch at index {}", i); + } } - // Return squeezed output - state[capacity..capacity + rate / 2].to_vec() + // This repo https://github.com/jonathanxuu/RescuePrimeOptimiezd/tree/main + // uses the crate proptest to generate random inputs and compare the results + // should we do the same? + /* + proptest! { + #[test] + fn rescue_hash_wont_panic_with_arbitrary_input(input in any::>()) { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let _ = rescue.hash_bytes(&input); + } + } */ } From ccfcdcae2b78fc3b0d1923be1971e963f44bc0ec Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Tue, 24 Sep 2024 18:47:23 -0300 Subject: [PATCH 04/17] save work --- crypto/Cargo.toml | 15 +++++++++------ crypto/src/hash/rescue_prime/mod.rs | 5 +++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 72efea7ee..f595eab2f 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -13,7 +13,10 @@ sha3 = { version = "0.10", default-features = false } sha2 = { version = "0.10", default-features = false } # Optional -serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } +serde = { version = "1.0", default-features = false, features = [ + "derive", + "alloc", +], optional = true } rayon = { version = "1.8.0", optional = true } [dev-dependencies] @@ -21,7 +24,7 @@ criterion = "0.4" iai-callgrind.workspace = true rand = "0.8.5" rand_chacha = "0.3.1" - +proptest = "1.5.0" [features] default = ["asm", "std"] asm = ["sha3/asm"] @@ -40,9 +43,9 @@ name = "iai_merkle" harness = false [[bench]] -name= "criterion_poseidon" -harness=false +name = "criterion_poseidon" +harness = false [[bench]] -name= "criterion_pedersen" -harness=false +name = "criterion_pedersen" +harness = false diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index ef5cb0793..ace24a1ae 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -384,6 +384,7 @@ mod tests { use super::*; use lambdaworks_math::traits::ByteConversion; //use proptest::prelude::*; + use proptest::prelude::*; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; @@ -690,12 +691,12 @@ mod tests { // This repo https://github.com/jonathanxuu/RescuePrimeOptimiezd/tree/main // uses the crate proptest to generate random inputs and compare the results // should we do the same? - /* + proptest! { #[test] fn rescue_hash_wont_panic_with_arbitrary_input(input in any::>()) { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); let _ = rescue.hash_bytes(&input); } - } */ + } } From 5174b11653e6726e05fb62c6990a03a5d6d92cda Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 25 Sep 2024 16:51:23 -0300 Subject: [PATCH 05/17] save work --- crypto/src/hash/rescue_prime/mod.rs | 300 +++++++++++++++++++++++----- 1 file changed, 245 insertions(+), 55 deletions(-) diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index ace24a1ae..a8ccb4ed1 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -6,12 +6,15 @@ use sha3::{ digest::{ExtendableOutput, Update}, Shake256, }; + pub const ALPHA: u64 = 7; pub const ALPHA_INV: u64 = 10540996611094048183; -// Define the field type for clarity +//pub const P: u64 = 18446744069414584321; // p = 2^64 - 2^32 + 1 + type Fp = FieldElement; #[derive(Clone)] +#[allow(dead_code)] // Is this needed? enum MdsMethod { MatrixMultiplication, Ntt, @@ -45,7 +48,7 @@ impl const ALPHA: u64 = 7; const ALPHA_INV: u64 = 10540996611094048183; - pub fn new(mds_method: MdsMethod) -> Self { + fn new(mds_method: MdsMethod) -> Self { assert!(SECURITY_LEVEL == 128 || SECURITY_LEVEL == 160); let (m, capacity) = if SECURITY_LEVEL == 128 { @@ -79,6 +82,52 @@ impl } } + pub fn apply_inverse_sbox(state: &mut [Fp]) { + // Compute x^(ALPHA_INV) using an optimized method + // ALPHA_INV = 10540996611094048183 + // Binary representation: 1001001001001001001001001001000110110110110110110110110110110111 + + // Precompute necessary powers + let mut t1 = state.to_vec(); // x^2 + for x in t1.iter_mut() { + *x = x.square(); + } + + let mut t2 = t1.clone(); // x^4 + for x in t2.iter_mut() { + *x = x.square(); + } + + let t3 = Self::exp_acc(&t2, &t2, 3); // x^100100 + + let t4 = Self::exp_acc(&t3, &t3, 6); // x^100100100100 + + let t5 = Self::exp_acc(&t4, &t4, 12); // x^100100100100100100100100 + + let t6 = Self::exp_acc(&t5, &t3, 6); // x^100100100100100100100100100100 + + let t7 = Self::exp_acc(&t6, &t6, 31); // x^1001001001001001001001001001000100100100100100100100100100100 + + for i in 0..state.len() { + let a = (t7[i].square() * t6[i].clone()).square().square(); + let b = t1[i].clone() * t2[i].clone() * state[i].clone(); + state[i] = a * b; + } + } + #[inline(always)] + fn exp_acc(base: &[Fp], tail: &[Fp], num_squarings: usize) -> Vec { + let mut result = base.to_vec(); + for x in result.iter_mut() { + for _ in 0..num_squarings { + *x = x.square(); + } + } + result + .iter_mut() + .zip(tail.iter()) + .for_each(|(r, t)| *r = *r * t.clone()); + result + } fn get_mds_vector(m: usize) -> Vec { match m { 12 => vec![7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8] @@ -106,7 +155,7 @@ impl } mds_matrix } - + /* fn instantiate_round_constants( p: u64, m: usize, @@ -130,19 +179,96 @@ impl } round_constants } + */ + /*pub fn instantiate_round_constants( + p: u64, + m: usize, + capacity: usize, + security_level: usize, + num_rounds: usize, + ) -> Vec { + let seed_string = format!("RPO({},{},{},{})", p, m, capacity, security_level); + let mut shake = Shake256::default(); + shake.update(seed_string.as_bytes()); - pub fn apply_sbox(state: &mut [Fp], alpha: u64) { - for x in state.iter_mut() { - *x = x.pow(alpha); + let num_constants = 2 * m * num_rounds; + let bytes_per_int = ((p as f64).log2() / 8.0).ceil() as usize + 1; // Should be 9 + let num_bytes = bytes_per_int * num_constants; + + let mut shake_output = shake.finalize_xof(); + let mut test_bytes = vec![0u8; num_bytes]; + shake_output.read_exact(&mut test_bytes).unwrap(); + + // Output the first few bytes for comparison + println!("SHAKE256 output (Rust): {:?}", &test_bytes[..64]); + + let mut round_constants = Vec::new(); + + for i in 0..num_constants { + let start = i * bytes_per_int; + let end = start + bytes_per_int; + let bytes = &test_bytes[start..end]; + let integer = bytes + .iter() + .enumerate() + .fold(0u128, |acc, (i, &b)| acc + (b as u128) << (8 * i)); + let reduced = integer % (p as u128); + let constant = Fp::from(reduced as u64); + round_constants.push(constant); } + round_constants + }*/ + fn instantiate_round_constants( + p: u64, + m: usize, + capacity: usize, + security_level: usize, + num_rounds: usize, + ) -> Vec { + let seed_string = format!("RPO({},{},{},{})", p, m, capacity, security_level); + let mut shake = Shake256::default(); + shake.update(seed_string.as_bytes()); + + let num_constants = 2 * m * num_rounds; + let bytes_per_int = 8; // Use 8 bytes per integer + let num_bytes = bytes_per_int * num_constants; + + let mut shake_output = shake.finalize_xof(); + let mut test_bytes = vec![0u8; num_bytes]; + shake_output.read_exact(&mut test_bytes).unwrap(); + + // Output the first few bytes for comparison + println!("SHAKE256 output (Rust): {:?}", &test_bytes[..64]); + + let mut round_constants = Vec::new(); + + for i in 0..num_constants { + let start = i * bytes_per_int; + let end = start + bytes_per_int; + let bytes = &test_bytes[start..end]; + + // Convert bytes to integer in **little-endian** order + let integer = u64::from_le_bytes(bytes.try_into().unwrap()); + let constant = Fp::from(integer); + round_constants.push(constant); + } + round_constants } - pub fn apply_inverse_sbox(state: &mut [Fp], alpha_inv: u64) { + pub fn apply_sbox(state: &mut [Fp]) { for x in state.iter_mut() { - *x = x.pow(alpha_inv); + *x = x.pow(Self::ALPHA); } } + /* + pub fn apply_inverse_sbox(state: &mut [Fp], alpha_inv: u64) { + for x in state.iter_mut() { + *x = x.pow(alpha_inv); + } + } + */ + fn mds_matrix_vector_multiplication(&self, state: &[Fp]) -> Vec { let m = state.len(); let mut new_state = vec![Fp::zero(); m]; @@ -239,10 +365,10 @@ impl for round in 0..num_rounds { self.apply_mds(state); self.add_round_constants(state, round); - Self::apply_sbox(state, alpha); + Self::apply_sbox(state); self.apply_mds(state); self.add_round_constants_second(state, round); - Self::apply_inverse_sbox(state, alpha_inv); + Self::apply_inverse_sbox(state); } } @@ -254,20 +380,39 @@ impl let mut state = vec![Fp::zero(); m]; let mut padded_input = input_sequence.to_vec(); - // Padding - if input_sequence.len() % rate != 0 { - padded_input.push(Fp::one()); - while padded_input.len() % rate != 0 { - padded_input.push(Fp::zero()); + // Check if padding is needed + let needs_padding = input_sequence.len() % rate != 0; + + if needs_padding { + // Set domain separation flag + state[0] = Fp::one(); + } + + // Absorb full chunks + let full_chunks = padded_input.len() / rate; + for i in 0..full_chunks { + let chunk = &padded_input[i * rate..(i + 1) * rate]; + for j in 0..rate { + state[capacity + j] += chunk[j]; } - state[0] = Fp::one(); // Domain separation + self.permutation(&mut state); } - // Absorb the input - for chunk in padded_input.chunks(rate) { - for i in 0..rate { - state[capacity + i] = state[capacity + i] + chunk[i].clone(); + // Handle the last chunk if it's incomplete + let remaining = padded_input.len() % rate; + if remaining != 0 { + let start = full_chunks * rate; + let chunk = &padded_input[start..]; + + // Absorb the remaining elements + for j in 0..chunk.len() { + state[capacity + j] += chunk[j]; } + + // Add padding + state[capacity + chunk.len()] += Fp::one(); + + // Perform permutation self.permutation(&mut state); } @@ -282,22 +427,27 @@ impl } fn bytes_to_field_elements(input: &[u8]) -> Vec { - // Include the length of the input as the first field element - let mut elements = vec![Fp::from(input.len() as u64)]; + let mut elements = Vec::new(); - // Convert the input bytes into field elements - let padded_input = input.to_vec(); - // Optionally, pad the input to a multiple of 8 bytes if needed + let chunk_size = 7; // Use 7 bytes per chunk + let mut buf = [0u8; 8]; // Buffer for 8 bytes, last byte for padding - elements.extend(padded_input.chunks(8).map(|chunk| { - let mut bytes = [0u8; 8]; - for (i, &b) in chunk.iter().enumerate() { - bytes[i] = b; + let mut chunks = input.chunks(chunk_size).peekable(); + + while let Some(chunk) = chunks.next() { + buf.fill(0); + buf[..chunk.len()].copy_from_slice(chunk); + if chunk.len() < chunk_size { + // Add a 1 after the last byte if the chunk is not full + buf[chunk.len()] = 1; } - Fp::from(u64::from_le_bytes(bytes)) - })); + let value = u64::from_le_bytes(buf); + elements.push(Fp::from(value)); + } + elements } + // Implement NTT and INTT functions fn ntt(input: &[Fp], omega: Fp) -> Vec { let n = input.len(); @@ -382,8 +532,7 @@ fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { #[cfg(test)] mod tests { use super::*; - use lambdaworks_math::traits::ByteConversion; - //use proptest::prelude::*; + use crate::hash::rescue_prime::*; use proptest::prelude::*; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; @@ -414,7 +563,7 @@ mod tests { let mut expected = state.clone(); expected.iter_mut().for_each(|v| *v = v.pow(ALPHA)); - RescuePrimeOptimized::<128, 7>::apply_sbox(&mut state, ALPHA); + RescuePrimeOptimized::<128, 7>::apply_sbox(&mut state); assert_eq!(expected, state); } @@ -429,7 +578,7 @@ mod tests { let mut expected = state.clone(); expected.iter_mut().for_each(|v| *v = v.pow(ALPHA_INV)); - RescuePrimeOptimized::<128, 7>::apply_inverse_sbox(&mut state, ALPHA_INV); + RescuePrimeOptimized::<128, 7>::apply_inverse_sbox(&mut state); assert_eq!(expected, state); } @@ -485,6 +634,11 @@ mod tests { Fp::from(2u64), Fp::from(3u64), Fp::from(4u64), + Fp::from(5u64), + Fp::from(6u64), + Fp::from(7u64), + Fp::from(8u64), + Fp::from(9u64), ]; let hash_matrix = rescue_matrix.hash(&input); @@ -536,28 +690,29 @@ mod tests { println!("];"); } + // This test is wrong, it should be the same that the values from the paper #[test] fn hash_test_vectors() { let elements = vec![ - Fp::from(0u64), - Fp::from(1u64), - Fp::from(2u64), - Fp::from(3u64), - Fp::from(4u64), - Fp::from(5u64), - Fp::from(6u64), - Fp::from(7u64), - Fp::from(8u64), - Fp::from(9u64), - Fp::from(10u64), - Fp::from(11u64), - Fp::from(12u64), - Fp::from(13u64), - Fp::from(14u64), - Fp::from(15u64), - Fp::from(16u64), - Fp::from(17u64), - Fp::from(18u64), + Fp::new(0u64), + Fp::new(1u64), + Fp::new(2u64), + Fp::new(3u64), + Fp::new(4u64), + Fp::new(5u64), + Fp::new(6u64), + Fp::new(7u64), + Fp::new(8u64), + Fp::new(9u64), + Fp::new(10u64), + Fp::new(11u64), + Fp::new(12u64), + Fp::new(13u64), + Fp::new(14u64), + Fp::new(15u64), + Fp::new(16u64), + Fp::new(17u64), + Fp::new(18u64), ]; let expected_hashes = vec![ @@ -687,7 +842,6 @@ mod tests { assert_eq!(hash_output, *expected_hash, "Hash mismatch at index {}", i); } } - // This repo https://github.com/jonathanxuu/RescuePrimeOptimiezd/tree/main // uses the crate proptest to generate random inputs and compare the results // should we do the same? @@ -699,4 +853,40 @@ mod tests { let _ = rescue.hash_bytes(&input); } } + + #[test] + fn test_instantiate_round_constants() { + let p = 18446744069414584321u64; // 2^64 - 2^32 + 1 + let m = 12; + let capacity = 4; + let security_level = 128; + let num_rounds = 7; + + // Call the function to instantiate round constants + let round_constants = RescuePrimeOptimized::<128, 7>::instantiate_round_constants( + p, + m, + capacity, + security_level, + num_rounds, + ); + + // Print the first few round constants for inspection + println!("First few round constants:"); + for (i, constant) in round_constants.iter().enumerate() { + println!("Constant {}: {:?}", i, constant); + } + } + #[test] + fn test_hash_outputs() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Ntt); + + for i in 1..6 { + let input_sequence: Vec = (0..i as u64).map(Fp::from).collect(); + let hash_output = rescue.hash(&input_sequence); + + println!("Input {}: {:?}", i, input_sequence); + println!("Hash {}: {:?}", i, hash_output); + } + } } From 6c4d06f2bbe54f82122d7c20f5f19c37fa151845 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Tue, 15 Oct 2024 11:45:53 -0300 Subject: [PATCH 06/17] fixed padding, now all test pass --- crypto/Cargo.toml | 2 +- crypto/src/hash/rescue_prime/mod.rs | 878 +++++++++++++++++----------- 2 files changed, 535 insertions(+), 345 deletions(-) diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index f595eab2f..f761b8def 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -11,7 +11,7 @@ license.workspace = true lambdaworks-math = { workspace = true, features = ["alloc"] } sha3 = { version = "0.10", default-features = false } sha2 = { version = "0.10", default-features = false } - +lazy_static = "1.5.0" # Optional serde = { version = "1.0", default-features = false, features = [ "derive", diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index a8ccb4ed1..9645e7f7b 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -1,20 +1,22 @@ use lambdaworks_math::field::element::FieldElement; use lambdaworks_math::field::fields::u64_goldilocks_field::Goldilocks64Field; +use lazy_static::lazy_static; use std::io::Read; use sha3::{ digest::{ExtendableOutput, Update}, Shake256, }; - +// Rescue Prime Optimized implementation based on +// https://github.com/ASDiscreteMathematics/rpo and +// https://github.com/0xPolygonMiden/crypto/tree/main/src/hash/rescue/rpo pub const ALPHA: u64 = 7; pub const ALPHA_INV: u64 = 10540996611094048183; -//pub const P: u64 = 18446744069414584321; // p = 2^64 - 2^32 + 1 type Fp = FieldElement; #[derive(Clone)] -#[allow(dead_code)] // Is this needed? +#[allow(dead_code)] enum MdsMethod { MatrixMultiplication, Ntt, @@ -45,8 +47,6 @@ impl RescuePrimeOptimized { const P: u64 = 18446744069414584321; // p = 2^64 - 2^32 + 1 - const ALPHA: u64 = 7; - const ALPHA_INV: u64 = 10540996611094048183; fn new(mds_method: MdsMethod) -> Self { assert!(SECURITY_LEVEL == 128 || SECURITY_LEVEL == 160); @@ -61,13 +61,183 @@ impl let mds_vector = Self::get_mds_vector(m); let mds_matrix = Self::generate_circulant_matrix(&mds_vector); - let round_constants = Self::instantiate_round_constants( - Self::P, - m, - capacity, - SECURITY_LEVEL, - NUM_FULL_ROUNDS, - ); + //let round_constants = Self::instantiate_round_constants( + // Self::P, + // m, + // capacity, + // SECURITY_LEVEL, + // NUM_FULL_ROUNDS, + // ); + let round_constants = vec![ + Fp::from(5789762306288267392u64), + Fp::from(6522564764413701783u64), + Fp::from(17809893479458208203u64), + Fp::from(107145243989736508u64), + Fp::from(6388978042437517382u64), + Fp::from(15844067734406016715u64), + Fp::from(9975000513555218239u64), + Fp::from(3344984123768313364u64), + Fp::from(9959189626657347191u64), + Fp::from(12960773468763563665u64), + Fp::from(9602914297752488475u64), + Fp::from(16657542370200465908u64), + Fp::from(6077062762357204287u64), + Fp::from(15277620170502011191u64), + Fp::from(5358738125714196705u64), + Fp::from(14233283787297595718u64), + Fp::from(13792579614346651365u64), + Fp::from(11614812331536767105u64), + Fp::from(14871063686742261166u64), + Fp::from(10148237148793043499u64), + Fp::from(4457428952329675767u64), + Fp::from(15590786458219172475u64), + Fp::from(10063319113072092615u64), + Fp::from(14200078843431360086u64), + Fp::from(12987190162843096997u64), + Fp::from(653957632802705281u64), + Fp::from(4441654670647621225u64), + Fp::from(4038207883745915761u64), + Fp::from(5613464648874830118u64), + Fp::from(13222989726778338773u64), + Fp::from(3037761201230264149u64), + Fp::from(16683759727265180203u64), + Fp::from(8337364536491240715u64), + Fp::from(3227397518293416448u64), + Fp::from(8110510111539674682u64), + Fp::from(2872078294163232137u64), + Fp::from(6202948458916099932u64), + Fp::from(17690140365333231091u64), + Fp::from(3595001575307484651u64), + Fp::from(373995945117666487u64), + Fp::from(1235734395091296013u64), + Fp::from(14172757457833931602u64), + Fp::from(707573103686350224u64), + Fp::from(15453217512188187135u64), + Fp::from(219777875004506018u64), + Fp::from(17876696346199469008u64), + Fp::from(17731621626449383378u64), + Fp::from(2897136237748376248u64), + Fp::from(18072785500942327487u64), + Fp::from(6200974112677013481u64), + Fp::from(17682092219085884187u64), + Fp::from(10599526828986756440u64), + Fp::from(975003873302957338u64), + Fp::from(8264241093196931281u64), + Fp::from(10065763900435475170u64), + Fp::from(2181131744534710197u64), + Fp::from(6317303992309418647u64), + Fp::from(1401440938888741532u64), + Fp::from(8884468225181997494u64), + Fp::from(13066900325715521532u64), + Fp::from(8023374565629191455u64), + Fp::from(15013690343205953430u64), + Fp::from(4485500052507912973u64), + Fp::from(12489737547229155153u64), + Fp::from(9500452585969030576u64), + Fp::from(2054001340201038870u64), + Fp::from(12420704059284934186u64), + Fp::from(355990932618543755u64), + Fp::from(9071225051243523860u64), + Fp::from(12766199826003448536u64), + Fp::from(9045979173463556963u64), + Fp::from(12934431667190679898u64), + Fp::from(5674685213610121970u64), + Fp::from(5759084860419474071u64), + Fp::from(13943282657648897737u64), + Fp::from(1352748651966375394u64), + Fp::from(17110913224029905221u64), + Fp::from(1003883795902368422u64), + Fp::from(4141870621881018291u64), + Fp::from(8121410972417424656u64), + Fp::from(14300518605864919529u64), + Fp::from(13712227150607670181u64), + Fp::from(17021852944633065291u64), + Fp::from(6252096473787587650u64), + Fp::from(18389244934624494276u64), + Fp::from(16731736864863925227u64), + Fp::from(4440209734760478192u64), + Fp::from(17208448209698888938u64), + Fp::from(8739495587021565984u64), + Fp::from(17000774922218161967u64), + Fp::from(13533282547195532087u64), + Fp::from(525402848358706231u64), + Fp::from(16987541523062161972u64), + Fp::from(5466806524462797102u64), + Fp::from(14512769585918244983u64), + Fp::from(10973956031244051118u64), + Fp::from(4887609836208846458u64), + Fp::from(3027115137917284492u64), + Fp::from(9595098600469470675u64), + Fp::from(10528569829048484079u64), + Fp::from(7864689113198939815u64), + Fp::from(17533723827845969040u64), + Fp::from(5781638039037710951u64), + Fp::from(17024078752430719006u64), + Fp::from(109659393484013511u64), + Fp::from(7158933660534805869u64), + Fp::from(2955076958026921730u64), + Fp::from(7433723648458773977u64), + Fp::from(6982293561042362913u64), + Fp::from(14065426295947720331u64), + Fp::from(16451845770444974180u64), + Fp::from(7139138592091306727u64), + Fp::from(9012006439959783127u64), + Fp::from(14619614108529063361u64), + Fp::from(1394813199588124371u64), + Fp::from(4635111139507788575u64), + Fp::from(16217473952264203365u64), + Fp::from(10782018226466330683u64), + Fp::from(6844229992533662050u64), + Fp::from(7446486531695178711u64), + Fp::from(16308865189192447297u64), + Fp::from(11977192855656444890u64), + Fp::from(12532242556065780287u64), + Fp::from(14594890931430968898u64), + Fp::from(7291784239689209784u64), + Fp::from(5514718540551361949u64), + Fp::from(10025733853830934803u64), + Fp::from(7293794580341021693u64), + Fp::from(6728552937464861756u64), + Fp::from(6332385040983343262u64), + Fp::from(13277683694236792804u64), + Fp::from(2600778905124452676u64), + Fp::from(3736792340494631448u64), + Fp::from(577852220195055341u64), + Fp::from(6689998335515779805u64), + Fp::from(13886063479078013492u64), + Fp::from(14358505101923202168u64), + Fp::from(7744142531772274164u64), + Fp::from(16135070735728404443u64), + Fp::from(12290902521256031137u64), + Fp::from(12059913662657709804u64), + Fp::from(16456018495793751911u64), + Fp::from(4571485474751953524u64), + Fp::from(17200392109565783176u64), + Fp::from(7123075680859040534u64), + Fp::from(1034205548717903090u64), + Fp::from(7717824418247931797u64), + Fp::from(3019070937878604058u64), + Fp::from(11403792746066867460u64), + Fp::from(10280580802233112374u64), + Fp::from(337153209462421218u64), + Fp::from(13333398568519923717u64), + Fp::from(3596153696935337464u64), + Fp::from(8104208463525993784u64), + Fp::from(14345062289456085693u64), + Fp::from(17036731477169661256u64), + Fp::from(17130398059294018733u64), + Fp::from(519782857322261988u64), + Fp::from(9625384390925085478u64), + Fp::from(1664893052631119222u64), + Fp::from(7629576092524553570u64), + Fp::from(3485239601103661425u64), + Fp::from(9755891797164033838u64), + Fp::from(15218148195153269027u64), + Fp::from(16460604813734957368u64), + Fp::from(9643968136937729763u64), + Fp::from(3611348709641382851u64), + Fp::from(18256379591337759196u64), + ]; Self { m, @@ -76,44 +246,40 @@ impl round_constants, mds_matrix, mds_vector, - alpha: Self::ALPHA, - alpha_inv: Self::ALPHA_INV, + alpha: ALPHA, + alpha_inv: ALPHA_INV, mds_method, } } pub fn apply_inverse_sbox(state: &mut [Fp]) { - // Compute x^(ALPHA_INV) using an optimized method - // ALPHA_INV = 10540996611094048183 - // Binary representation: 1001001001001001001001001001000110110110110110110110110110110111 + for x in state.iter_mut() { + *x = x.pow(ALPHA_INV); + } - // Precompute necessary powers - let mut t1 = state.to_vec(); // x^2 + /*let mut t1 = state.to_vec(); for x in t1.iter_mut() { *x = x.square(); } - let mut t2 = t1.clone(); // x^4 + let mut t2 = t1.clone(); for x in t2.iter_mut() { *x = x.square(); } - let t3 = Self::exp_acc(&t2, &t2, 3); // x^100100 - - let t4 = Self::exp_acc(&t3, &t3, 6); // x^100100100100 - - let t5 = Self::exp_acc(&t4, &t4, 12); // x^100100100100100100100100 - - let t6 = Self::exp_acc(&t5, &t3, 6); // x^100100100100100100100100100100 - - let t7 = Self::exp_acc(&t6, &t6, 31); // x^1001001001001001001001001001000100100100100100100100100100100 + let t3 = Self::exp_acc(&t2, &t2, 3); + let t4 = Self::exp_acc(&t3, &t3, 6); + let t5 = Self::exp_acc(&t4, &t4, 12); + let t6 = Self::exp_acc(&t5, &t3, 6); + let t7 = Self::exp_acc(&t6, &t6, 31); for i in 0..state.len() { let a = (t7[i].square() * t6[i].clone()).square().square(); let b = t1[i].clone() * t2[i].clone() * state[i].clone(); state[i] = a * b; - } + }*/ } + #[inline(always)] fn exp_acc(base: &[Fp], tail: &[Fp], num_squarings: usize) -> Vec { let mut result = base.to_vec(); @@ -128,6 +294,7 @@ impl .for_each(|(r, t)| *r = *r * t.clone()); result } + fn get_mds_vector(m: usize) -> Vec { match m { 12 => vec![7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8] @@ -155,32 +322,8 @@ impl } mds_matrix } - /* - fn instantiate_round_constants( - p: u64, - m: usize, - capacity: usize, - security_level: usize, - num_rounds: usize, - ) -> Vec { - let seed_string = format!("RPO({},{},{},{})", p, m, capacity, security_level); - let mut shake = Shake256::default(); - shake.update(seed_string.as_bytes()); - let num_constants = 2 * m * num_rounds; - let mut shake_output = shake.finalize_xof(); - let mut round_constants = Vec::new(); - - for _ in 0..num_constants { - let mut bytes = [0u8; 8]; - shake_output.read_exact(&mut bytes).unwrap(); - let constant = Fp::from(u64::from_le_bytes(bytes)); - round_constants.push(constant); - } - round_constants - } - */ - /*pub fn instantiate_round_constants( + fn instantiate_round_constants( p: u64, m: usize, capacity: usize, @@ -192,83 +335,42 @@ impl shake.update(seed_string.as_bytes()); let num_constants = 2 * m * num_rounds; - let bytes_per_int = ((p as f64).log2() / 8.0).ceil() as usize + 1; // Should be 9 + let bytes_per_int = 8; let num_bytes = bytes_per_int * num_constants; let mut shake_output = shake.finalize_xof(); let mut test_bytes = vec![0u8; num_bytes]; shake_output.read_exact(&mut test_bytes).unwrap(); - // Output the first few bytes for comparison - println!("SHAKE256 output (Rust): {:?}", &test_bytes[..64]); - let mut round_constants = Vec::new(); for i in 0..num_constants { let start = i * bytes_per_int; let end = start + bytes_per_int; let bytes = &test_bytes[start..end]; - let integer = bytes - .iter() - .enumerate() - .fold(0u128, |acc, (i, &b)| acc + (b as u128) << (8 * i)); - let reduced = integer % (p as u128); - let constant = Fp::from(reduced as u64); - round_constants.push(constant); - } - round_constants - }*/ - fn instantiate_round_constants( - p: u64, - m: usize, - capacity: usize, - security_level: usize, - num_rounds: usize, - ) -> Vec { - let seed_string = format!("RPO({},{},{},{})", p, m, capacity, security_level); - let mut shake = Shake256::default(); - shake.update(seed_string.as_bytes()); - - let num_constants = 2 * m * num_rounds; - let bytes_per_int = 8; // Use 8 bytes per integer - let num_bytes = bytes_per_int * num_constants; - - let mut shake_output = shake.finalize_xof(); - let mut test_bytes = vec![0u8; num_bytes]; - shake_output.read_exact(&mut test_bytes).unwrap(); - // Output the first few bytes for comparison - println!("SHAKE256 output (Rust): {:?}", &test_bytes[..64]); + if bytes.len() == 8 { + let integer = u64::from_le_bytes(bytes.try_into().unwrap()); + let constant = Fp::from(integer); - let mut round_constants = Vec::new(); + if constant.value() >= &p { + panic!("Generated constant exceeds field size."); + } - for i in 0..num_constants { - let start = i * bytes_per_int; - let end = start + bytes_per_int; - let bytes = &test_bytes[start..end]; - - // Convert bytes to integer in **little-endian** order - let integer = u64::from_le_bytes(bytes.try_into().unwrap()); - let constant = Fp::from(integer); - round_constants.push(constant); + round_constants.push(constant); + } else { + panic!("Invalid number of bytes extracted for u64 conversion."); + } } round_constants } pub fn apply_sbox(state: &mut [Fp]) { for x in state.iter_mut() { - *x = x.pow(Self::ALPHA); + *x = x.pow(ALPHA); } } - /* - pub fn apply_inverse_sbox(state: &mut [Fp], alpha_inv: u64) { - for x in state.iter_mut() { - *x = x.pow(alpha_inv); - } - } - */ - fn mds_matrix_vector_multiplication(&self, state: &[Fp]) -> Vec { let m = state.len(); let mut new_state = vec![Fp::zero(); m]; @@ -288,24 +390,20 @@ impl _ => panic!("Unsupported state size for NTT"), }; - // NTT of MDS vector and state let mds_ntt = ntt(&self.mds_vector, omega); let state_rev: Vec = std::iter::once(state[0].clone()) .chain(state[1..].iter().rev().cloned()) .collect(); let state_ntt = ntt(&state_rev, omega); - // Point-wise multiplication let mut product_ntt = vec![Fp::zero(); m]; for i in 0..m { product_ntt[i] = mds_ntt[i] * state_ntt[i]; } - // Inverse NTT let omega_inv = omega.inv().unwrap(); let result = intt(&product_ntt, omega_inv); - // Adjust the result std::iter::once(result[0].clone()) .chain(result[1..].iter().rev().cloned()) .collect() @@ -359,8 +457,6 @@ impl pub fn permutation(&self, state: &mut [Fp]) { let num_rounds = NUM_FULL_ROUNDS; - let alpha = self.alpha; - let alpha_inv = self.alpha_inv; for round in 0..num_rounds { self.apply_mds(state); @@ -378,45 +474,39 @@ impl let rate = self.rate; let mut state = vec![Fp::zero(); m]; - let mut padded_input = input_sequence.to_vec(); - - // Check if padding is needed - let needs_padding = input_sequence.len() % rate != 0; + let input_len = input_sequence.len(); - if needs_padding { - // Set domain separation flag + if input_len % rate != 0 { state[0] = Fp::one(); } - // Absorb full chunks - let full_chunks = padded_input.len() / rate; - for i in 0..full_chunks { - let chunk = &padded_input[i * rate..(i + 1) * rate]; + let num_full_chunks = input_len / rate; + + for i in 0..num_full_chunks { + let chunk = &input_sequence[i * rate..(i + 1) * rate]; for j in 0..rate { - state[capacity + j] += chunk[j]; + state[capacity + j] = chunk[j]; } self.permutation(&mut state); } - // Handle the last chunk if it's incomplete - let remaining = padded_input.len() % rate; - if remaining != 0 { - let start = full_chunks * rate; - let chunk = &padded_input[start..]; + let last_chunk_size = input_len % rate; - // Absorb the remaining elements - for j in 0..chunk.len() { - state[capacity + j] += chunk[j]; + if last_chunk_size != 0 { + let mut last_chunk = vec![Fp::zero(); rate]; + for j in 0..last_chunk_size { + last_chunk[j] = input_sequence[num_full_chunks * rate + j]; } + // Apply padding + last_chunk[last_chunk_size] = Fp::one(); - // Add padding - state[capacity + chunk.len()] += Fp::one(); + for j in 0..rate { + state[capacity + j] = last_chunk[j]; + } - // Perform permutation self.permutation(&mut state); } - // Return squeezed output state[capacity..capacity + rate / 2].to_vec() } @@ -429,8 +519,8 @@ impl fn bytes_to_field_elements(input: &[u8]) -> Vec { let mut elements = Vec::new(); - let chunk_size = 7; // Use 7 bytes per chunk - let mut buf = [0u8; 8]; // Buffer for 8 bytes, last byte for padding + let chunk_size = 7; + let mut buf = [0u8; 8]; let mut chunks = input.chunks(chunk_size).peekable(); @@ -438,7 +528,6 @@ fn bytes_to_field_elements(input: &[u8]) -> Vec { buf.fill(0); buf[..chunk.len()].copy_from_slice(chunk); if chunk.len() < chunk_size { - // Add a 1 after the last byte if the chunk is not full buf[chunk.len()] = 1; } let value = u64::from_le_bytes(buf); @@ -448,7 +537,7 @@ fn bytes_to_field_elements(input: &[u8]) -> Vec { elements } -// Implement NTT and INTT functions +// NTT and INTT functions fn ntt(input: &[Fp], omega: Fp) -> Vec { let n = input.len(); let mut output = vec![Fp::zero(); n]; @@ -472,11 +561,10 @@ fn intt(input: &[Fp], omega_inv: Fp) -> Vec { output } -// Implement Karatsuba multiplication +// Karatsuba multiplication fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { let n = lhs.len(); if n <= 32 { - // For small n, use the standard multiplication let mut result = vec![Fp::zero(); 2 * n - 1]; for i in 0..n { for j in 0..n { @@ -528,32 +616,139 @@ fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { result } - #[cfg(test)] mod tests { use super::*; - use crate::hash::rescue_prime::*; - use proptest::prelude::*; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; - //const ALPHA: u64 = 7; - //const ALPHA_INV: u64 = 10540996611094048183; + lazy_static! { + pub static ref EXPECTED: Vec> = vec![ + vec![ + Fp::from(1502364727743950833u64), + Fp::from(5880949717274681448u64), + Fp::from(162790463902224431u64), + Fp::from(6901340476773664264u64), + ], + vec![ + Fp::from(7478710183745780580u64), + Fp::from(3308077307559720969u64), + Fp::from(3383561985796182409u64), + Fp::from(17205078494700259815u64), + ], + vec![ + Fp::from(17439912364295172999u64), + Fp::from(17979156346142712171u64), + Fp::from(8280795511427637894u64), + Fp::from(9349844417834368814u64), + ], + vec![ + Fp::from(5105868198472766874u64), + Fp::from(13090564195691924742u64), + Fp::from(1058904296915798891u64), + Fp::from(18379501748825152268u64), + ], + vec![ + Fp::from(9133662113608941286u64), + Fp::from(12096627591905525991u64), + Fp::from(14963426595993304047u64), + Fp::from(13290205840019973377u64), + ], + vec![ + Fp::from(3134262397541159485u64), + Fp::from(10106105871979362399u64), + Fp::from(138768814855329459u64), + Fp::from(15044809212457404677u64), + ], + vec![ + Fp::from(162696376578462826u64), + Fp::from(4991300494838863586u64), + Fp::from(660346084748120605u64), + Fp::from(13179389528641752698u64), + ], + vec![ + Fp::from(2242391899857912644u64), + Fp::from(12689382052053305418u64), + Fp::from(235236990017815546u64), + Fp::from(5046143039268215739u64), + ], + vec![ + Fp::from(9585630502158073976u64), + Fp::from(1310051013427303477u64), + Fp::from(7491921222636097758u64), + Fp::from(9417501558995216762u64), + ], + vec![ + Fp::from(1994394001720334744u64), + Fp::from(10866209900885216467u64), + Fp::from(13836092831163031683u64), + Fp::from(10814636682252756697u64), + ], + vec![ + Fp::from(17486854790732826405u64), + Fp::from(17376549265955727562u64), + Fp::from(2371059831956435003u64), + Fp::from(17585704935858006533u64), + ], + vec![ + Fp::from(11368277489137713825u64), + Fp::from(3906270146963049287u64), + Fp::from(10236262408213059745u64), + Fp::from(78552867005814007u64), + ], + vec![ + Fp::from(17899847381280262181u64), + Fp::from(14717912805498651446u64), + Fp::from(10769146203951775298u64), + Fp::from(2774289833490417856u64), + ], + vec![ + Fp::from(3794717687462954368u64), + Fp::from(4386865643074822822u64), + Fp::from(8854162840275334305u64), + Fp::from(7129983987107225269u64), + ], + vec![ + Fp::from(7244773535611633983u64), + Fp::from(19359923075859320u64), + Fp::from(10898655967774994333u64), + Fp::from(9319339563065736480u64), + ], + vec![ + Fp::from(4935426252518736883u64), + Fp::from(12584230452580950419u64), + Fp::from(8762518969632303998u64), + Fp::from(18159875708229758073u64), + ], + vec![ + Fp::from(14871230873837295931u64), + Fp::from(11225255908868362971u64), + Fp::from(18100987641405432308u64), + Fp::from(1559244340089644233u64), + ], + vec![ + Fp::from(8348203744950016968u64), + Fp::from(4041411241960726733u64), + Fp::from(17584743399305468057u64), + Fp::from(16836952610803537051u64), + ], + vec![ + Fp::from(16139797453633030050u64), + Fp::from(1090233424040889412u64), + Fp::from(10770255347785669036u64), + Fp::from(16982398877290254028u64), + ], + ]; + } + // Utility function to generate random field elements fn rand_field_element(rng: &mut R) -> Fp { Fp::from(rng.gen::()) } + // Test for S-box operation #[test] - fn test_alphas() { - let mut rng = StdRng::seed_from_u64(0); - let e = rand_field_element(&mut rng); - let e_exp = e.pow(ALPHA); - assert_eq!(e, e_exp.pow(ALPHA_INV)); - } - - #[test] - fn test_sbox() { + fn test_apply_sbox() { let mut rng = StdRng::seed_from_u64(1); let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); let mut state: Vec = (0..rescue.m) @@ -567,8 +762,9 @@ mod tests { assert_eq!(expected, state); } + // Test for inverse S-box operation #[test] - fn test_inv_sbox() { + fn test_apply_inverse_sbox() { let mut rng = StdRng::seed_from_u64(2); let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); let mut state: Vec = (0..rescue.m) @@ -582,6 +778,129 @@ mod tests { assert_eq!(expected, state); } + // Test for MDS matrix multiplication + #[test] + fn test_mds_matrix_multiplication() { + let mut rng = StdRng::seed_from_u64(3); + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let expected_state = rescue.mds_matrix_vector_multiplication(&state); + let mut computed_state = state.clone(); + rescue.apply_mds(&mut computed_state); + + assert_eq!(expected_state, computed_state); + } + + // Test for NTT-based MDS matrix multiplication + #[test] + fn test_mds_ntt() { + let mut rng = StdRng::seed_from_u64(4); + let rescue_ntt = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Ntt); + let state: Vec = (0..rescue_ntt.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let expected_state = rescue_ntt.mds_ntt(&state); + let mut computed_state = state.clone(); + rescue_ntt.apply_mds(&mut computed_state); + + assert_eq!(expected_state, computed_state); + } + + // Test for Karatsuba-based MDS matrix multiplication + #[test] + fn test_mds_karatsuba() { + let mut rng = StdRng::seed_from_u64(5); + let rescue_karatsuba = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Karatsuba); + let state: Vec = (0..rescue_karatsuba.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let expected_state = rescue_karatsuba.mds_karatsuba(&state); + let mut computed_state = state.clone(); + rescue_karatsuba.apply_mds(&mut computed_state); + + assert_eq!(expected_state, computed_state); + } + + // Test for round constant addition in permutation function + #[test] + fn test_add_round_constants() { + let mut rng = StdRng::seed_from_u64(6); + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let mut state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let round = 0; + let expected_state = state + .iter() + .enumerate() + .map(|(i, &x)| x + rescue.round_constants[round * 2 * rescue.m + i]) + .collect::>(); + + rescue.add_round_constants(&mut state, round); + + assert_eq!(expected_state, state); + } + + #[test] + fn test_permutation() { + let mut rng = StdRng::seed_from_u64(7); + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let mut state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let expected_state = { + let mut temp_state = state.clone(); + for round in 0..7 { + rescue.apply_mds(&mut temp_state); + rescue.add_round_constants(&mut temp_state, round); + RescuePrimeOptimized::<128, 7>::apply_sbox(&mut temp_state); + rescue.apply_mds(&mut temp_state); + rescue.add_round_constants_second(&mut temp_state, round); + RescuePrimeOptimized::<128, 7>::apply_inverse_sbox(&mut temp_state); + } + temp_state + }; + + rescue.permutation(&mut state); + + assert_eq!(expected_state, state); + } + + #[test] + fn test_hash_single_chunk() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let input_sequence: Vec = (0..8).map(Fp::from).collect(); + let hash_output = rescue.hash(&input_sequence); + + // Verify the squeezed output + assert_eq!(hash_output.len(), 4); // Half the rate (rate = 8) + } + + #[test] + fn test_hash_multiple_chunks() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let input_sequence: Vec = (0..16).map(Fp::from).collect(); // Two chunks of size 8 + let hash_output = rescue.hash(&input_sequence); + + // Verify the squeezed output + assert_eq!(hash_output.len(), 4); // Half the rate (rate = 8) + } + + #[test] + fn test_hash_with_padding() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let input_sequence: Vec = (0..5).map(Fp::from).collect(); // Input smaller than rate + let hash_output = rescue.hash(&input_sequence); + + assert_eq!(hash_output.len(), 4); + } #[test] fn hash_padding() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); @@ -622,7 +941,27 @@ mod tests { zeroes.push(Fp::zero()); } } + // Test for hash function with byte input + #[test] + fn test_hash_bytes() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let input_bytes = b"Rescue Prime Optimized"; + let hash_output = rescue.hash_bytes(input_bytes); + + // Verify the squeezed output + assert_eq!(hash_output.len(), 4); // Half the rate (rate = 8) + } + + // Test for round constants instantiation + #[test] + fn test_instantiate_round_constants() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + + let round_constants = &rescue.round_constants; + assert_eq!(round_constants.len(), 2 * 12 * 7); // 2 * m * NUM_FULL_ROUNDS + } + // Test for MDS methods consistency #[test] fn test_mds_methods_consistency() { let rescue_matrix = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); @@ -649,8 +988,6 @@ mod tests { assert_eq!(hash_ntt, hash_karatsuba); } - // Creaate a test function that generates test vectors, maybe it would be better to - // generate them using proptest or in a separate file #[test] fn generate_test_vectors() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); @@ -678,7 +1015,7 @@ mod tests { println!("let expected_hashes = vec!["); for i in 0..elements.len() { - let input = &elements[..=i]; // Take prefix up to i + let input = &elements[..=i]; let hash_output = rescue.hash(input); print!(" vec!["); @@ -690,203 +1027,56 @@ mod tests { println!("];"); } - // This test is wrong, it should be the same that the values from the paper #[test] - fn hash_test_vectors() { - let elements = vec![ - Fp::new(0u64), - Fp::new(1u64), - Fp::new(2u64), - Fp::new(3u64), - Fp::new(4u64), - Fp::new(5u64), - Fp::new(6u64), - Fp::new(7u64), - Fp::new(8u64), - Fp::new(9u64), - Fp::new(10u64), - Fp::new(11u64), - Fp::new(12u64), - Fp::new(13u64), - Fp::new(14u64), - Fp::new(15u64), - Fp::new(16u64), - Fp::new(17u64), - Fp::new(18u64), - ]; - - let expected_hashes = vec![ - vec![ - Fp::from(17254761148825111576u64), - Fp::from(5748658173944016543u64), - Fp::from(15507216263332757191u64), - Fp::from(3983249929669394122u64), - ], - vec![ - Fp::from(17712221936341625086u64), - Fp::from(2943298861192409284u64), - Fp::from(2494572860652577379u64), - Fp::from(2378199810979427322u64), - ], - vec![ - Fp::from(3876314992495866678u64), - Fp::from(17611388455687538623u64), - Fp::from(3911042865754506040u64), - Fp::from(16776766772018109848u64), - ], - vec![ - Fp::from(6056716262596077506u64), - Fp::from(16158290354505703086u64), - Fp::from(17447029989314528820u64), - Fp::from(1567470650296395962u64), - ], - vec![ - Fp::from(13380585531133108000u64), - Fp::from(3137417240495852984u64), - Fp::from(3098660641723081460u64), - Fp::from(5150917506181658097u64), - ], - vec![ - Fp::from(7024209367141755347u64), - Fp::from(16246734205622419915u64), - Fp::from(7503077358698812671u64), - Fp::from(12133031123118477720u64), - ], - vec![ - Fp::from(8402140550106217856u64), - Fp::from(7956967817259077006u64), - Fp::from(7462144441524583670u64), - Fp::from(16871123896451099924u64), - ], - vec![ - Fp::from(3306774948807044313u64), - Fp::from(9076368178691092936u64), - Fp::from(2759350540710171864u64), - Fp::from(3210614416697826413u64), - ], - vec![ - Fp::from(9674626283660829003u64), - Fp::from(7912357911043410654u64), - Fp::from(11533209507830100464u64), - Fp::from(10170478333989115619u64), - ], - vec![ - Fp::from(10555879534445000475u64), - Fp::from(7964878826308278072u64), - Fp::from(9043911582507760001u64), - Fp::from(17545606895180851135u64), - ], - vec![ - Fp::from(11622987728342977762u64), - Fp::from(6359923222102476051u64), - Fp::from(13174910429968985338u64), - Fp::from(15940765068206503257u64), - ], - vec![ - Fp::from(9317736379809246942u64), - Fp::from(1131339481094147037u64), - Fp::from(7694193714172738957u64), - Fp::from(3021948153504618318u64), - ], - vec![ - Fp::from(15452378822943328512u64), - Fp::from(8092427130361699902u64), - Fp::from(4291324087873637870u64), - Fp::from(7948600115971845347u64), - ], - vec![ - Fp::from(7316340811681411604u64), - Fp::from(12607667321921907916u64), - Fp::from(1038271716522639176u64), - Fp::from(14471693297474155620u64), - ], - vec![ - Fp::from(12773488141023465780u64), - Fp::from(6221729007489926125u64), - Fp::from(1486696495069601684u64), - Fp::from(5967114319922573516u64), - ], - vec![ - Fp::from(12321222925119640558u64), - Fp::from(3287873714193039570u64), - Fp::from(14642702142995841541u64), - Fp::from(15416139920349778032u64), - ], - vec![ - Fp::from(1397847368400388990u64), - Fp::from(10132081287571009963u64), - Fp::from(8992380008215239242u64), - Fp::from(13825336864150598095u64), - ], - vec![ - Fp::from(11938166169298599670u64), - Fp::from(6941295435497807075u64), - Fp::from(1474794246787649407u64), - Fp::from(13435514261569247470u64), - ], - vec![ - Fp::from(488103042505954048u64), - Fp::from(953948736820844501u64), - Fp::from(18197062251142516718u64), - Fp::from(459513752465468917u64), - ], - ]; - + fn test_print_round_constants() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - for i in 0..elements.len() { - let input = &elements[..=i]; // Take prefix up to index i - let hash_output = rescue.hash(input); - - let expected_hash = &expected_hashes[i]; - assert_eq!(hash_output, *expected_hash, "Hash mismatch at index {}", i); + println!("Round constants:"); + for (i, constant) in rescue.round_constants.iter().enumerate() { + println!("Constant {}: Fp::from({}u64)", i, constant.value()); } - } - // This repo https://github.com/jonathanxuu/RescuePrimeOptimiezd/tree/main - // uses the crate proptest to generate random inputs and compare the results - // should we do the same? - proptest! { - #[test] - fn rescue_hash_wont_panic_with_arbitrary_input(input in any::>()) { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let _ = rescue.hash_bytes(&input); - } + assert_eq!(rescue.round_constants.len(), 2 * rescue.m * 7); // 2 * m * NUM_FULL_ROUNDS } #[test] - fn test_instantiate_round_constants() { - let p = 18446744069414584321u64; // 2^64 - 2^32 + 1 - let m = 12; - let capacity = 4; - let security_level = 128; - let num_rounds = 7; - - // Call the function to instantiate round constants - let round_constants = RescuePrimeOptimized::<128, 7>::instantiate_round_constants( - p, - m, - capacity, - security_level, - num_rounds, - ); - - // Print the first few round constants for inspection - println!("First few round constants:"); - for (i, constant) in round_constants.iter().enumerate() { - println!("Constant {}: {:?}", i, constant); + fn test_hash_vectors() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let elements: Vec = (0..19).map(Fp::from).collect(); + + for (i, expected) in EXPECTED.iter().enumerate() { + let input = &elements[..=i]; // Tomar el prefijo hasta i + let hash_output = rescue.hash(input); + + assert_eq!( + hash_output, + *expected, + "Hash mismatch for input length {}", + i + 1 + ); } } + #[test] - fn test_hash_outputs() { + fn test_hash_example_and_print() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Ntt); - for i in 1..6 { - let input_sequence: Vec = (0..i as u64).map(Fp::from).collect(); - let hash_output = rescue.hash(&input_sequence); + let input = b"Boquita campeon del mundo!"; + + let hash_result = rescue.hash_bytes(input); + + println!("Input: {:?}", input); + println!("Hash result:"); + for (i, value) in hash_result.iter().enumerate() { + println!(" {}: {}", i, value); + } - println!("Input {}: {:?}", i, input_sequence); - println!("Hash {}: {:?}", i, hash_output); + println!("Hash as u64 values:"); + for value in hash_result.iter() { + print!("{}, ", value.value()); } + println!(); + assert_eq!(hash_result.len(), 4); } + // this gives the same in Polygon Miden } From 10878f24acd7c97e1b64a28431866803d0624cf6 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Tue, 15 Oct 2024 16:34:56 -0300 Subject: [PATCH 07/17] refactor --- crypto/src/hash/rescue_prime/mod.rs | 225 +----------- crypto/src/hash/rescue_prime/parameters.rs | 406 +++++++++++++++++++++ crypto/src/hash/rescue_prime/utils.rs | 74 ++++ 3 files changed, 499 insertions(+), 206 deletions(-) create mode 100644 crypto/src/hash/rescue_prime/parameters.rs create mode 100644 crypto/src/hash/rescue_prime/utils.rs diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index 9645e7f7b..7fb019e59 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -1,17 +1,18 @@ use lambdaworks_math::field::element::FieldElement; use lambdaworks_math::field::fields::u64_goldilocks_field::Goldilocks64Field; -use lazy_static::lazy_static; -use std::io::Read; - -use sha3::{ - digest::{ExtendableOutput, Update}, - Shake256, -}; -// Rescue Prime Optimized implementation based on -// https://github.com/ASDiscreteMathematics/rpo and -// https://github.com/0xPolygonMiden/crypto/tree/main/src/hash/rescue/rpo -pub const ALPHA: u64 = 7; -pub const ALPHA_INV: u64 = 10540996611094048183; +//use lazy_static::lazy_static; +//use std::io::Read; + +//use sha3::{ +// digest::{ExtendableOutput, Update}, +// Shake256, +//}; + +mod parameters; +mod utils; + +use parameters::*; +use utils::*; type Fp = FieldElement; @@ -22,7 +23,7 @@ enum MdsMethod { Ntt, Karatsuba, } - +#[warn(dead_code)] pub struct RescuePrimeOptimized { m: usize, capacity: usize, @@ -46,8 +47,6 @@ impl Default impl RescuePrimeOptimized { - const P: u64 = 18446744069414584321; // p = 2^64 - 2^32 + 1 - fn new(mds_method: MdsMethod) -> Self { assert!(SECURITY_LEVEL == 128 || SECURITY_LEVEL == 160); @@ -60,15 +59,8 @@ impl let mds_vector = Self::get_mds_vector(m); let mds_matrix = Self::generate_circulant_matrix(&mds_vector); - - //let round_constants = Self::instantiate_round_constants( - // Self::P, - // m, - // capacity, - // SECURITY_LEVEL, - // NUM_FULL_ROUNDS, - // ); - let round_constants = vec![ + let round_constants = ROUND_CONSTANTS.clone(); + /*let round_constants = vec![ Fp::from(5789762306288267392u64), Fp::from(6522564764413701783u64), Fp::from(17809893479458208203u64), @@ -237,13 +229,13 @@ impl Fp::from(9643968136937729763u64), Fp::from(3611348709641382851u64), Fp::from(18256379591337759196u64), - ]; + ];*/ Self { m, capacity, rate, - round_constants, + round_constants: round_constants, mds_matrix, mds_vector, alpha: ALPHA, @@ -256,43 +248,6 @@ impl for x in state.iter_mut() { *x = x.pow(ALPHA_INV); } - - /*let mut t1 = state.to_vec(); - for x in t1.iter_mut() { - *x = x.square(); - } - - let mut t2 = t1.clone(); - for x in t2.iter_mut() { - *x = x.square(); - } - - let t3 = Self::exp_acc(&t2, &t2, 3); - let t4 = Self::exp_acc(&t3, &t3, 6); - let t5 = Self::exp_acc(&t4, &t4, 12); - let t6 = Self::exp_acc(&t5, &t3, 6); - let t7 = Self::exp_acc(&t6, &t6, 31); - - for i in 0..state.len() { - let a = (t7[i].square() * t6[i].clone()).square().square(); - let b = t1[i].clone() * t2[i].clone() * state[i].clone(); - state[i] = a * b; - }*/ - } - - #[inline(always)] - fn exp_acc(base: &[Fp], tail: &[Fp], num_squarings: usize) -> Vec { - let mut result = base.to_vec(); - for x in result.iter_mut() { - for _ in 0..num_squarings { - *x = x.square(); - } - } - result - .iter_mut() - .zip(tail.iter()) - .for_each(|(r, t)| *r = *r * t.clone()); - result } fn get_mds_vector(m: usize) -> Vec { @@ -323,48 +278,6 @@ impl mds_matrix } - fn instantiate_round_constants( - p: u64, - m: usize, - capacity: usize, - security_level: usize, - num_rounds: usize, - ) -> Vec { - let seed_string = format!("RPO({},{},{},{})", p, m, capacity, security_level); - let mut shake = Shake256::default(); - shake.update(seed_string.as_bytes()); - - let num_constants = 2 * m * num_rounds; - let bytes_per_int = 8; - let num_bytes = bytes_per_int * num_constants; - - let mut shake_output = shake.finalize_xof(); - let mut test_bytes = vec![0u8; num_bytes]; - shake_output.read_exact(&mut test_bytes).unwrap(); - - let mut round_constants = Vec::new(); - - for i in 0..num_constants { - let start = i * bytes_per_int; - let end = start + bytes_per_int; - let bytes = &test_bytes[start..end]; - - if bytes.len() == 8 { - let integer = u64::from_le_bytes(bytes.try_into().unwrap()); - let constant = Fp::from(integer); - - if constant.value() >= &p { - panic!("Generated constant exceeds field size."); - } - - round_constants.push(constant); - } else { - panic!("Invalid number of bytes extracted for u64 conversion."); - } - } - round_constants - } - pub fn apply_sbox(state: &mut [Fp]) { for x in state.iter_mut() { *x = x.pow(ALPHA); @@ -497,7 +410,6 @@ impl for j in 0..last_chunk_size { last_chunk[j] = input_sequence[num_full_chunks * rate + j]; } - // Apply padding last_chunk[last_chunk_size] = Fp::one(); for j in 0..rate { @@ -516,109 +428,10 @@ impl } } -fn bytes_to_field_elements(input: &[u8]) -> Vec { - let mut elements = Vec::new(); - - let chunk_size = 7; - let mut buf = [0u8; 8]; - - let mut chunks = input.chunks(chunk_size).peekable(); - - while let Some(chunk) = chunks.next() { - buf.fill(0); - buf[..chunk.len()].copy_from_slice(chunk); - if chunk.len() < chunk_size { - buf[chunk.len()] = 1; - } - let value = u64::from_le_bytes(buf); - elements.push(Fp::from(value)); - } - - elements -} - -// NTT and INTT functions -fn ntt(input: &[Fp], omega: Fp) -> Vec { - let n = input.len(); - let mut output = vec![Fp::zero(); n]; - for i in 0..n { - let mut sum = Fp::zero(); - for (j, val) in input.iter().enumerate() { - sum = sum + *val * omega.pow((i * j) as u64); - } - output[i] = sum; - } - output -} - -fn intt(input: &[Fp], omega_inv: Fp) -> Vec { - let n = input.len(); - let inv_n = Fp::from(n as u64).inv().unwrap(); - let mut output = ntt(input, omega_inv); - for val in output.iter_mut() { - *val = *val * inv_n; - } - output -} - -// Karatsuba multiplication -fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { - let n = lhs.len(); - if n <= 32 { - let mut result = vec![Fp::zero(); 2 * n - 1]; - for i in 0..n { - for j in 0..n { - result[i + j] = result[i + j] + lhs[i] * rhs[j]; - } - } - return result; - } - - let half = n / 2; - - let lhs_low = &lhs[..half]; - let lhs_high = &lhs[half..]; - let rhs_low = &rhs[..half]; - let rhs_high = &rhs[half..]; - - let z0 = karatsuba(lhs_low, rhs_low); - let z2 = karatsuba(lhs_high, rhs_high); - - let lhs_sum: Vec = lhs_low - .iter() - .zip(lhs_high.iter()) - .map(|(a, b)| *a + *b) - .collect(); - - let rhs_sum: Vec = rhs_low - .iter() - .zip(rhs_high.iter()) - .map(|(a, b)| *a + *b) - .collect(); - - let z1 = karatsuba(&lhs_sum, &rhs_sum); - - let mut result = vec![Fp::zero(); 2 * n - 1]; - - for i in 0..z0.len() { - result[i] = result[i] + z0[i]; - } - - for i in 0..z1.len() { - result[i + half] = result[i + half] + z1[i] - - z0.get(i).cloned().unwrap_or(Fp::zero()) - - z2.get(i).cloned().unwrap_or(Fp::zero()); - } - - for i in 0..z2.len() { - result[i + 2 * half] = result[i + 2 * half] + z2[i]; - } - - result -} #[cfg(test)] mod tests { use super::*; + use lazy_static::lazy_static; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; diff --git a/crypto/src/hash/rescue_prime/parameters.rs b/crypto/src/hash/rescue_prime/parameters.rs new file mode 100644 index 000000000..8e205ed2b --- /dev/null +++ b/crypto/src/hash/rescue_prime/parameters.rs @@ -0,0 +1,406 @@ +use super::Fp; +use lazy_static::lazy_static; +pub const ALPHA: u64 = 7; +pub const ALPHA_INV: u64 = 10540996611094048183; +//pub const P: u64 = 18446744069414584321; // p = 2^64 - 2^32 + 1 + +lazy_static! { + pub static ref ROUND_CONSTANTS: Vec = vec![ + Fp::from(5789762306288267392u64), + Fp::from(6522564764413701783u64), + Fp::from(17809893479458208203u64), + Fp::from(107145243989736508u64), + Fp::from(6388978042437517382u64), + Fp::from(15844067734406016715u64), + Fp::from(9975000513555218239u64), + Fp::from(3344984123768313364u64), + Fp::from(9959189626657347191u64), + Fp::from(12960773468763563665u64), + Fp::from(9602914297752488475u64), + Fp::from(16657542370200465908u64), + Fp::from(6077062762357204287u64), + Fp::from(15277620170502011191u64), + Fp::from(5358738125714196705u64), + Fp::from(14233283787297595718u64), + Fp::from(13792579614346651365u64), + Fp::from(11614812331536767105u64), + Fp::from(14871063686742261166u64), + Fp::from(10148237148793043499u64), + Fp::from(4457428952329675767u64), + Fp::from(15590786458219172475u64), + Fp::from(10063319113072092615u64), + Fp::from(14200078843431360086u64), + Fp::from(12987190162843096997u64), + Fp::from(653957632802705281u64), + Fp::from(4441654670647621225u64), + Fp::from(4038207883745915761u64), + Fp::from(5613464648874830118u64), + Fp::from(13222989726778338773u64), + Fp::from(3037761201230264149u64), + Fp::from(16683759727265180203u64), + Fp::from(8337364536491240715u64), + Fp::from(3227397518293416448u64), + Fp::from(8110510111539674682u64), + Fp::from(2872078294163232137u64), + Fp::from(6202948458916099932u64), + Fp::from(17690140365333231091u64), + Fp::from(3595001575307484651u64), + Fp::from(373995945117666487u64), + Fp::from(1235734395091296013u64), + Fp::from(14172757457833931602u64), + Fp::from(707573103686350224u64), + Fp::from(15453217512188187135u64), + Fp::from(219777875004506018u64), + Fp::from(17876696346199469008u64), + Fp::from(17731621626449383378u64), + Fp::from(2897136237748376248u64), + Fp::from(18072785500942327487u64), + Fp::from(6200974112677013481u64), + Fp::from(17682092219085884187u64), + Fp::from(10599526828986756440u64), + Fp::from(975003873302957338u64), + Fp::from(8264241093196931281u64), + Fp::from(10065763900435475170u64), + Fp::from(2181131744534710197u64), + Fp::from(6317303992309418647u64), + Fp::from(1401440938888741532u64), + Fp::from(8884468225181997494u64), + Fp::from(13066900325715521532u64), + Fp::from(8023374565629191455u64), + Fp::from(15013690343205953430u64), + Fp::from(4485500052507912973u64), + Fp::from(12489737547229155153u64), + Fp::from(9500452585969030576u64), + Fp::from(2054001340201038870u64), + Fp::from(12420704059284934186u64), + Fp::from(355990932618543755u64), + Fp::from(9071225051243523860u64), + Fp::from(12766199826003448536u64), + Fp::from(9045979173463556963u64), + Fp::from(12934431667190679898u64), + Fp::from(5674685213610121970u64), + Fp::from(5759084860419474071u64), + Fp::from(13943282657648897737u64), + Fp::from(1352748651966375394u64), + Fp::from(17110913224029905221u64), + Fp::from(1003883795902368422u64), + Fp::from(4141870621881018291u64), + Fp::from(8121410972417424656u64), + Fp::from(14300518605864919529u64), + Fp::from(13712227150607670181u64), + Fp::from(17021852944633065291u64), + Fp::from(6252096473787587650u64), + Fp::from(18389244934624494276u64), + Fp::from(16731736864863925227u64), + Fp::from(4440209734760478192u64), + Fp::from(17208448209698888938u64), + Fp::from(8739495587021565984u64), + Fp::from(17000774922218161967u64), + Fp::from(13533282547195532087u64), + Fp::from(525402848358706231u64), + Fp::from(16987541523062161972u64), + Fp::from(5466806524462797102u64), + Fp::from(14512769585918244983u64), + Fp::from(10973956031244051118u64), + Fp::from(4887609836208846458u64), + Fp::from(3027115137917284492u64), + Fp::from(9595098600469470675u64), + Fp::from(10528569829048484079u64), + Fp::from(7864689113198939815u64), + Fp::from(17533723827845969040u64), + Fp::from(5781638039037710951u64), + Fp::from(17024078752430719006u64), + Fp::from(109659393484013511u64), + Fp::from(7158933660534805869u64), + Fp::from(2955076958026921730u64), + Fp::from(7433723648458773977u64), + Fp::from(6982293561042362913u64), + Fp::from(14065426295947720331u64), + Fp::from(16451845770444974180u64), + Fp::from(7139138592091306727u64), + Fp::from(9012006439959783127u64), + Fp::from(14619614108529063361u64), + Fp::from(1394813199588124371u64), + Fp::from(4635111139507788575u64), + Fp::from(16217473952264203365u64), + Fp::from(10782018226466330683u64), + Fp::from(6844229992533662050u64), + Fp::from(7446486531695178711u64), + Fp::from(16308865189192447297u64), + Fp::from(11977192855656444890u64), + Fp::from(12532242556065780287u64), + Fp::from(14594890931430968898u64), + Fp::from(7291784239689209784u64), + Fp::from(5514718540551361949u64), + Fp::from(10025733853830934803u64), + Fp::from(7293794580341021693u64), + Fp::from(6728552937464861756u64), + Fp::from(6332385040983343262u64), + Fp::from(13277683694236792804u64), + Fp::from(2600778905124452676u64), + Fp::from(3736792340494631448u64), + Fp::from(577852220195055341u64), + Fp::from(6689998335515779805u64), + Fp::from(13886063479078013492u64), + Fp::from(14358505101923202168u64), + Fp::from(7744142531772274164u64), + Fp::from(16135070735728404443u64), + Fp::from(12290902521256031137u64), + Fp::from(12059913662657709804u64), + Fp::from(16456018495793751911u64), + Fp::from(4571485474751953524u64), + Fp::from(17200392109565783176u64), + Fp::from(7123075680859040534u64), + Fp::from(1034205548717903090u64), + Fp::from(7717824418247931797u64), + Fp::from(3019070937878604058u64), + Fp::from(11403792746066867460u64), + Fp::from(10280580802233112374u64), + Fp::from(337153209462421218u64), + Fp::from(13333398568519923717u64), + Fp::from(3596153696935337464u64), + Fp::from(8104208463525993784u64), + Fp::from(14345062289456085693u64), + Fp::from(17036731477169661256u64), + Fp::from(17130398059294018733u64), + Fp::from(519782857322261988u64), + Fp::from(9625384390925085478u64), + Fp::from(1664893052631119222u64), + Fp::from(7629576092524553570u64), + Fp::from(3485239601103661425u64), + Fp::from(9755891797164033838u64), + Fp::from(15218148195153269027u64), + Fp::from(16460604813734957368u64), + Fp::from(9643968136937729763u64), + Fp::from(3611348709641382851u64), + Fp::from(18256379591337759196u64), + ]; +} +/* +pub const ROUND_CONSTANTS_HEX: [Fp; 224] = [ + Fp::const_from_raw(1965335827333385572), + Fp::const_from_raw(13386940263093285890), + Fp::const_from_raw(2676433512518024499), + Fp::const_from_raw(3265387569419834752), + Fp::const_from_raw(1983410871005483133), + Fp::const_from_raw(9697282293408698131), + Fp::const_from_raw(1272774544215511539), + Fp::const_from_raw(8206289606243220511), + Fp::const_from_raw(1290391036756663400), + Fp::const_from_raw(18219831014774660739), + Fp::const_from_raw(9691367064095402927), + Fp::const_from_raw(1323942862844130786), + Fp::const_from_raw(15151407902520044968), + Fp::const_from_raw(3367241195349533752), + Fp::const_from_raw(4045613938354522492), + Fp::const_from_raw(8414577515806306591), + Fp::const_from_raw(12735791373473705278), + Fp::const_from_raw(3301196190123345788), + Fp::const_from_raw(4934538150586227609), + Fp::const_from_raw(3817643842607407527), + Fp::const_from_raw(13416431558822898318), + Fp::const_from_raw(5832629091408730901), + Fp::const_from_raw(3362368740314001033), + Fp::const_from_raw(11092906639494490385), + Fp::const_from_raw(6071859273097876791), + Fp::const_from_raw(10161425034618716356), + Fp::const_from_raw(7152209120756903545), + Fp::const_from_raw(16380870469663741149), + Fp::const_from_raw(3952136951542576078), + Fp::const_from_raw(17537441052343611097), + Fp::const_from_raw(11551242553047556263), + Fp::const_from_raw(10106900133850428740), + Fp::const_from_raw(11416650542216810040), + Fp::const_from_raw(11422270812969046329), + Fp::const_from_raw(8866991719313052084), + Fp::const_from_raw(11055863001411088108), + Fp::const_from_raw(6180770262849183127), + Fp::const_from_raw(15065904341621422463), + Fp::const_from_raw(6379231142859676194), + Fp::const_from_raw(12898133478008807755), + Fp::const_from_raw(17022976567648776965), + Fp::const_from_raw(9092326911543756291), + Fp::const_from_raw(6030122978628466915), + Fp::const_from_raw(9597034755157312926), + Fp::const_from_raw(994741965321505508), + Fp::const_from_raw(7556490651023083151), + Fp::const_from_raw(13471961853484783473), + Fp::const_from_raw(5530500298270693480), + Fp::const_from_raw(3138602747749119790), + Fp::const_from_raw(14959768162492908516), + Fp::const_from_raw(9134218270579160311), + Fp::const_from_raw(11526344086740032769), + Fp::const_from_raw(18056157006815181954), + Fp::const_from_raw(6800589288408907691), + Fp::const_from_raw(15936640138392473876), + Fp::const_from_raw(2300163192580995689), + Fp::const_from_raw(4526841916921293676), + Fp::const_from_raw(7195881155996340935), + Fp::const_from_raw(2785483023916634674), + Fp::const_from_raw(15081468567893261932), + Fp::const_from_raw(6614707290651872269), + Fp::const_from_raw(13681365294828420351), + Fp::const_from_raw(10664658542323360702), + Fp::const_from_raw(10084964797450915045), + Fp::const_from_raw(4845198022119750202), + Fp::const_from_raw(2607866667643628253), + Fp::const_from_raw(5208104371714885253), + Fp::const_from_raw(12959011109386888563), + Fp::const_from_raw(4000466944391262442), + Fp::const_from_raw(17728719744160665330), + Fp::const_from_raw(7150641948246037689), + Fp::const_from_raw(9776810486328380322), + Fp::const_from_raw(8402715679168885485), + Fp::const_from_raw(3121448252217290414), + Fp::const_from_raw(17436789549778885163), + Fp::const_from_raw(15165907014487612788), + Fp::const_from_raw(11269595316481578714), + Fp::const_from_raw(9914651255870961898), + Fp::const_from_raw(12689101348845299684), + Fp::const_from_raw(11975655653136929369), + Fp::const_from_raw(7372192115875804252), + Fp::const_from_raw(374526648312709133), + Fp::const_from_raw(5985220408386061330), + Fp::const_from_raw(7185802228951619536), + Fp::const_from_raw(1399294693953396201), + Fp::const_from_raw(3261364014951657316), + Fp::const_from_raw(12077409443637692420), + Fp::const_from_raw(9673650825325087603), + Fp::const_from_raw(5569045552142119082), + Fp::const_from_raw(17617312550416673451), + Fp::const_from_raw(6211450796053144311), + Fp::const_from_raw(11274862073326008409), + Fp::const_from_raw(18367233290057731659), + Fp::const_from_raw(13198876392118957255), + Fp::const_from_raw(13272050586507026767), + Fp::const_from_raw(13010781901687851463), + Fp::const_from_raw(11176896862794321170), + Fp::const_from_raw(6638609153583434674), + Fp::const_from_raw(14505835809704498565), + Fp::const_from_raw(17581684280975726513), + Fp::const_from_raw(699795237352602006), + Fp::const_from_raw(9944038704239459812), + Fp::const_from_raw(8047212797227008956), + Fp::const_from_raw(1395744870455664103), + Fp::const_from_raw(18357515964980248812), + Fp::const_from_raw(9097466431298056431), + Fp::const_from_raw(14710664890151992774), + Fp::const_from_raw(6629781383077611287), + Fp::const_from_raw(17573797615501516970), + Fp::const_from_raw(12347664633647440814), + Fp::const_from_raw(11021709264172808686), + Fp::const_from_raw(10955032358008028206), + Fp::const_from_raw(12827014260928926472), + Fp::const_from_raw(14274600229400487385), + Fp::const_from_raw(12031986599882032134), + Fp::const_from_raw(16154104676212634613), + Fp::const_from_raw(18132152994017433356), + Fp::const_from_raw(15441239634310983499), + Fp::const_from_raw(10976597099491887044), + Fp::const_from_raw(3707145841124002094), + Fp::const_from_raw(8720928559638383045), + Fp::const_from_raw(16336200500310468906), + Fp::const_from_raw(6210805750383775651), + Fp::const_from_raw(7719884621977079797), + Fp::const_from_raw(11449042012956416425), + Fp::const_from_raw(9075619080551251971), + Fp::const_from_raw(617668424765806231), + Fp::const_from_raw(12270348236411784037), + Fp::const_from_raw(6186113401837024523), + Fp::const_from_raw(15458192282022704662), + Fp::const_from_raw(3533646002027882636), + Fp::const_from_raw(7323750725122298699), + Fp::const_from_raw(17370102587019252090), + Fp::const_from_raw(1740987243995377904), + Fp::const_from_raw(10219908189144498973), + Fp::const_from_raw(1822464913426161699), + Fp::const_from_raw(13340330593340428766), + Fp::const_from_raw(11476413915876641735), + Fp::const_from_raw(10301877462024259119), + Fp::const_from_raw(17003473479205724655), + Fp::const_from_raw(10899885430087119072), + Fp::const_from_raw(2161571014943847810), + Fp::const_from_raw(10337649388059569402), + Fp::const_from_raw(1627927149280118935), + Fp::const_from_raw(981019442244479500), + Fp::const_from_raw(8080861373146567887), + Fp::const_from_raw(8033636340692269807), + Fp::const_from_raw(1747076424940820198), + Fp::const_from_raw(15430102639810276278), + Fp::const_from_raw(9286420248392647962), + Fp::const_from_raw(11497964697936588530), + Fp::const_from_raw(17639509337065865628), + Fp::const_from_raw(2160917583540985983), + Fp::const_from_raw(6735220140815683510), + Fp::const_from_raw(6183237619116523957), + Fp::const_from_raw(13347893983048485379), + Fp::const_from_raw(4087545433624195113), + Fp::const_from_raw(11701648626105993864), + Fp::const_from_raw(11913677089736238784), + Fp::const_from_raw(271004950317860287), + Fp::const_from_raw(11794070108002091165), + Fp::const_from_raw(15639064309077629849), + Fp::const_from_raw(16481734838884572560), + Fp::const_from_raw(3932918848577657311), + Fp::const_from_raw(16327200574281469287), + Fp::const_from_raw(7060041503065075033), + Fp::const_from_raw(4892761442718320741), + Fp::const_from_raw(8255275116206368067), + Fp::const_from_raw(14957838536671021552), + Fp::const_from_raw(14493715972468567436), + Fp::const_from_raw(7463718209809697261), + Fp::const_from_raw(3440982266989812843), + Fp::const_from_raw(2354199421703013492), + Fp::const_from_raw(2321628279578256047), + Fp::const_from_raw(3746041501354899488), + Fp::const_from_raw(11186576936873825301), + Fp::const_from_raw(15218587616061641074), + Fp::const_from_raw(11844784525417523222), + Fp::const_from_raw(7998727848169056055), + Fp::const_from_raw(7948968711630609066), + Fp::const_from_raw(11805042600408037937), + Fp::const_from_raw(18172588443872800894), + Fp::const_from_raw(13092373363317372568), + Fp::const_from_raw(2169983441195298580), + Fp::const_from_raw(1499680808057735775), + Fp::const_from_raw(7077486803310915643), + Fp::const_from_raw(743612288630452727), + Fp::const_from_raw(11665426394426065172), + Fp::const_from_raw(15533499373769144802), + Fp::const_from_raw(14249183160150274240), + Fp::const_from_raw(13792290235996127743), + Fp::const_from_raw(4995017088228886738), + Fp::const_from_raw(9763845271226970122), + Fp::const_from_raw(1727820159257625458), + Fp::const_from_raw(9681902124347643227), + Fp::const_from_raw(11327574568051933160), + Fp::const_from_raw(10627429556158481577), + Fp::const_from_raw(13984143774797145216), + Fp::const_from_raw(17082059622058840713), + Fp::const_from_raw(16264233536802058333), + Fp::const_from_raw(10077962488096645822), + Fp::const_from_raw(5057253598123536060), + Fp::const_from_raw(2301672207952647376), + Fp::const_from_raw(17506877517896521554), + Fp::const_from_raw(14583366393971011156), + Fp::const_from_raw(6226877164823354372), + Fp::const_from_raw(2260055134098203623), + Fp::const_from_raw(12945296184826522120), + Fp::const_from_raw(15417698598606677168), + Fp::const_from_raw(7447949755934804788), + Fp::const_from_raw(8017843736725863212), + Fp::const_from_raw(1003688007091182795), + Fp::const_from_raw(8935767355090348282), + Fp::const_from_raw(793319158990348431), + Fp::const_from_raw(4437923789992338287), + Fp::const_from_raw(7869978205237541489), + Fp::const_from_raw(9039403419111053092), + Fp::const_from_raw(3845065612997771849), + Fp::const_from_raw(15179573672801872590), + Fp::const_from_raw(2879645310341005490), + Fp::const_from_raw(4421001170561580576), + Fp::const_from_raw(7614461260369642079), + Fp::const_from_raw(10869617590371203777), + Fp::const_from_raw(4582902440098948914), +]; +*/ diff --git a/crypto/src/hash/rescue_prime/utils.rs b/crypto/src/hash/rescue_prime/utils.rs new file mode 100644 index 000000000..7af8900d4 --- /dev/null +++ b/crypto/src/hash/rescue_prime/utils.rs @@ -0,0 +1,74 @@ +use super::Fp; + +pub fn bytes_to_field_elements(input: &[u8]) -> Vec { + input + .chunks(7) + .map(|chunk| { + let mut buf = [0u8; 8]; + buf[..chunk.len()].copy_from_slice(chunk); + if chunk.len() < 7 { + buf[chunk.len()] = 1; + } + let value = u64::from_le_bytes(buf); + Fp::from(value) + }) + .collect() +} + +pub fn ntt(input: &[Fp], omega: Fp) -> Vec { + (0..input.len()) + .map(|i| { + input.iter().enumerate().fold(Fp::zero(), |acc, (j, val)| { + acc + *val * omega.pow((i * j) as u64) + }) + }) + .collect() +} + +pub fn intt(input: &[Fp], omega_inv: Fp) -> Vec { + let inv_n = Fp::from(input.len() as u64).inv().unwrap(); + ntt(input, omega_inv) + .into_iter() + .map(|val| val * inv_n) + .collect() +} + +pub fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { + let n = lhs.len(); + if n <= 32 { + let mut result = vec![Fp::zero(); 2 * n - 1]; + lhs.iter().enumerate().for_each(|(i, &lhs_val)| { + rhs.iter().enumerate().for_each(|(j, &rhs_val)| { + result[i + j] = result[i + j] + lhs_val * rhs_val; + }); + }); + return result; + } + + let half = n / 2; + let (lhs_low, lhs_high) = lhs.split_at(half); + let (rhs_low, rhs_high) = rhs.split_at(half); + + let z0 = karatsuba(lhs_low, rhs_low); + let z2 = karatsuba(lhs_high, rhs_high); + + let lhs_sum: Vec = lhs_low.iter().zip(lhs_high).map(|(a, b)| *a + *b).collect(); + let rhs_sum: Vec = rhs_low.iter().zip(rhs_high).map(|(a, b)| *a + *b).collect(); + + let z1 = karatsuba(&lhs_sum, &rhs_sum); + + let mut result = vec![Fp::zero(); 2 * n - 1]; + + z0.iter().enumerate().for_each(|(i, &val)| result[i] = val); + z2.iter() + .enumerate() + .for_each(|(i, &val)| result[i + 2 * half] = val); + + z1.iter().enumerate().for_each(|(i, &val)| { + result[i + half] += val + - z0.get(i).cloned().unwrap_or(Fp::zero()) + - z2.get(i).cloned().unwrap_or(Fp::zero()); + }); + + result +} From d5b9c91797ee0faaa313adc082c99967ad7fe761 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Tue, 15 Oct 2024 18:58:38 -0300 Subject: [PATCH 08/17] more refactor --- crypto/Cargo.toml | 1 - crypto/src/hash/rescue_prime/mod.rs | 604 ++++++++------------- crypto/src/hash/rescue_prime/parameters.rs | 573 ++++++------------- crypto/src/hash/rescue_prime/utils.rs | 2 +- 4 files changed, 390 insertions(+), 790 deletions(-) diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index f761b8def..463fedee6 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -11,7 +11,6 @@ license.workspace = true lambdaworks-math = { workspace = true, features = ["alloc"] } sha3 = { version = "0.10", default-features = false } sha2 = { version = "0.10", default-features = false } -lazy_static = "1.5.0" # Optional serde = { version = "1.0", default-features = false, features = [ "derive", diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index 7fb019e59..e96a2b954 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -28,11 +28,11 @@ pub struct RescuePrimeOptimized, + round_constants: [Fp; 168], mds_matrix: Vec>, mds_vector: Vec, - alpha: u64, - alpha_inv: u64, + //alpha: u64, + //alpha_inv: u64, mds_method: MdsMethod, } @@ -59,187 +59,15 @@ impl let mds_vector = Self::get_mds_vector(m); let mds_matrix = Self::generate_circulant_matrix(&mds_vector); - let round_constants = ROUND_CONSTANTS.clone(); - /*let round_constants = vec![ - Fp::from(5789762306288267392u64), - Fp::from(6522564764413701783u64), - Fp::from(17809893479458208203u64), - Fp::from(107145243989736508u64), - Fp::from(6388978042437517382u64), - Fp::from(15844067734406016715u64), - Fp::from(9975000513555218239u64), - Fp::from(3344984123768313364u64), - Fp::from(9959189626657347191u64), - Fp::from(12960773468763563665u64), - Fp::from(9602914297752488475u64), - Fp::from(16657542370200465908u64), - Fp::from(6077062762357204287u64), - Fp::from(15277620170502011191u64), - Fp::from(5358738125714196705u64), - Fp::from(14233283787297595718u64), - Fp::from(13792579614346651365u64), - Fp::from(11614812331536767105u64), - Fp::from(14871063686742261166u64), - Fp::from(10148237148793043499u64), - Fp::from(4457428952329675767u64), - Fp::from(15590786458219172475u64), - Fp::from(10063319113072092615u64), - Fp::from(14200078843431360086u64), - Fp::from(12987190162843096997u64), - Fp::from(653957632802705281u64), - Fp::from(4441654670647621225u64), - Fp::from(4038207883745915761u64), - Fp::from(5613464648874830118u64), - Fp::from(13222989726778338773u64), - Fp::from(3037761201230264149u64), - Fp::from(16683759727265180203u64), - Fp::from(8337364536491240715u64), - Fp::from(3227397518293416448u64), - Fp::from(8110510111539674682u64), - Fp::from(2872078294163232137u64), - Fp::from(6202948458916099932u64), - Fp::from(17690140365333231091u64), - Fp::from(3595001575307484651u64), - Fp::from(373995945117666487u64), - Fp::from(1235734395091296013u64), - Fp::from(14172757457833931602u64), - Fp::from(707573103686350224u64), - Fp::from(15453217512188187135u64), - Fp::from(219777875004506018u64), - Fp::from(17876696346199469008u64), - Fp::from(17731621626449383378u64), - Fp::from(2897136237748376248u64), - Fp::from(18072785500942327487u64), - Fp::from(6200974112677013481u64), - Fp::from(17682092219085884187u64), - Fp::from(10599526828986756440u64), - Fp::from(975003873302957338u64), - Fp::from(8264241093196931281u64), - Fp::from(10065763900435475170u64), - Fp::from(2181131744534710197u64), - Fp::from(6317303992309418647u64), - Fp::from(1401440938888741532u64), - Fp::from(8884468225181997494u64), - Fp::from(13066900325715521532u64), - Fp::from(8023374565629191455u64), - Fp::from(15013690343205953430u64), - Fp::from(4485500052507912973u64), - Fp::from(12489737547229155153u64), - Fp::from(9500452585969030576u64), - Fp::from(2054001340201038870u64), - Fp::from(12420704059284934186u64), - Fp::from(355990932618543755u64), - Fp::from(9071225051243523860u64), - Fp::from(12766199826003448536u64), - Fp::from(9045979173463556963u64), - Fp::from(12934431667190679898u64), - Fp::from(5674685213610121970u64), - Fp::from(5759084860419474071u64), - Fp::from(13943282657648897737u64), - Fp::from(1352748651966375394u64), - Fp::from(17110913224029905221u64), - Fp::from(1003883795902368422u64), - Fp::from(4141870621881018291u64), - Fp::from(8121410972417424656u64), - Fp::from(14300518605864919529u64), - Fp::from(13712227150607670181u64), - Fp::from(17021852944633065291u64), - Fp::from(6252096473787587650u64), - Fp::from(18389244934624494276u64), - Fp::from(16731736864863925227u64), - Fp::from(4440209734760478192u64), - Fp::from(17208448209698888938u64), - Fp::from(8739495587021565984u64), - Fp::from(17000774922218161967u64), - Fp::from(13533282547195532087u64), - Fp::from(525402848358706231u64), - Fp::from(16987541523062161972u64), - Fp::from(5466806524462797102u64), - Fp::from(14512769585918244983u64), - Fp::from(10973956031244051118u64), - Fp::from(4887609836208846458u64), - Fp::from(3027115137917284492u64), - Fp::from(9595098600469470675u64), - Fp::from(10528569829048484079u64), - Fp::from(7864689113198939815u64), - Fp::from(17533723827845969040u64), - Fp::from(5781638039037710951u64), - Fp::from(17024078752430719006u64), - Fp::from(109659393484013511u64), - Fp::from(7158933660534805869u64), - Fp::from(2955076958026921730u64), - Fp::from(7433723648458773977u64), - Fp::from(6982293561042362913u64), - Fp::from(14065426295947720331u64), - Fp::from(16451845770444974180u64), - Fp::from(7139138592091306727u64), - Fp::from(9012006439959783127u64), - Fp::from(14619614108529063361u64), - Fp::from(1394813199588124371u64), - Fp::from(4635111139507788575u64), - Fp::from(16217473952264203365u64), - Fp::from(10782018226466330683u64), - Fp::from(6844229992533662050u64), - Fp::from(7446486531695178711u64), - Fp::from(16308865189192447297u64), - Fp::from(11977192855656444890u64), - Fp::from(12532242556065780287u64), - Fp::from(14594890931430968898u64), - Fp::from(7291784239689209784u64), - Fp::from(5514718540551361949u64), - Fp::from(10025733853830934803u64), - Fp::from(7293794580341021693u64), - Fp::from(6728552937464861756u64), - Fp::from(6332385040983343262u64), - Fp::from(13277683694236792804u64), - Fp::from(2600778905124452676u64), - Fp::from(3736792340494631448u64), - Fp::from(577852220195055341u64), - Fp::from(6689998335515779805u64), - Fp::from(13886063479078013492u64), - Fp::from(14358505101923202168u64), - Fp::from(7744142531772274164u64), - Fp::from(16135070735728404443u64), - Fp::from(12290902521256031137u64), - Fp::from(12059913662657709804u64), - Fp::from(16456018495793751911u64), - Fp::from(4571485474751953524u64), - Fp::from(17200392109565783176u64), - Fp::from(7123075680859040534u64), - Fp::from(1034205548717903090u64), - Fp::from(7717824418247931797u64), - Fp::from(3019070937878604058u64), - Fp::from(11403792746066867460u64), - Fp::from(10280580802233112374u64), - Fp::from(337153209462421218u64), - Fp::from(13333398568519923717u64), - Fp::from(3596153696935337464u64), - Fp::from(8104208463525993784u64), - Fp::from(14345062289456085693u64), - Fp::from(17036731477169661256u64), - Fp::from(17130398059294018733u64), - Fp::from(519782857322261988u64), - Fp::from(9625384390925085478u64), - Fp::from(1664893052631119222u64), - Fp::from(7629576092524553570u64), - Fp::from(3485239601103661425u64), - Fp::from(9755891797164033838u64), - Fp::from(15218148195153269027u64), - Fp::from(16460604813734957368u64), - Fp::from(9643968136937729763u64), - Fp::from(3611348709641382851u64), - Fp::from(18256379591337759196u64), - ];*/ + let round_constants = ROUND_CONSTANTS; Self { m, capacity, rate, - round_constants: round_constants, + round_constants, mds_matrix, mds_vector, - alpha: ALPHA, - alpha_inv: ALPHA_INV, mds_method, } } @@ -266,16 +94,31 @@ impl _ => panic!("Unsupported state size"), } } - - fn generate_circulant_matrix(mds_vector: &[Fp]) -> Vec> { - let m = mds_vector.len(); - let mut mds_matrix = vec![vec![Fp::zero(); m]; m]; - for i in 0..m { - for j in 0..m { - mds_matrix[i][j] = mds_vector[(j + m - i) % m].clone(); + /* + fn generate_circulant_matrix(mds_vector: &[Fp]) -> Vec> { + let m = mds_vector.len(); + let mut mds_matrix = vec![vec![Fp::zero(); m]; m]; + for i in 0..m { + for j in 0..m { + mds_matrix[i][j] = mds_vector[(j + m - i) % m]; + } } + mds_matrix } - mds_matrix + */ + fn generate_circulant_matrix(mds_vector: &[Fp]) -> Vec> { + let m = mds_vector.len(); + (0..m) + .map(|i| { + mds_vector + .iter() + .cycle() + .skip(m - i) + .take(m) + .cloned() + .collect::>() + }) + .collect() } pub fn apply_sbox(state: &mut [Fp]) { @@ -287,14 +130,15 @@ impl fn mds_matrix_vector_multiplication(&self, state: &[Fp]) -> Vec { let m = state.len(); let mut new_state = vec![Fp::zero(); m]; - for i in 0..m { - for j in 0..m { - new_state[i] = new_state[i] + self.mds_matrix[i][j] * state[j]; + + for (i, new_value) in new_state.iter_mut().enumerate() { + for (j, state_value) in state.iter().enumerate() { + *new_value += self.mds_matrix[i][j] * state_value; } } + new_state } - fn mds_ntt(&self, state: &[Fp]) -> Vec { let m = state.len(); let omega = match m { @@ -304,7 +148,7 @@ impl }; let mds_ntt = ntt(&self.mds_vector, omega); - let state_rev: Vec = std::iter::once(state[0].clone()) + let state_rev: Vec = std::iter::once(state[0]) .chain(state[1..].iter().rev().cloned()) .collect(); let state_ntt = ntt(&state_rev, omega); @@ -317,25 +161,27 @@ impl let omega_inv = omega.inv().unwrap(); let result = intt(&product_ntt, omega_inv); - std::iter::once(result[0].clone()) + std::iter::once(result[0]) .chain(result[1..].iter().rev().cloned()) .collect() } fn mds_karatsuba(&self, state: &[Fp]) -> Vec { let m = state.len(); - let mds_rev: Vec = std::iter::once(self.mds_vector[0].clone()) + let mds_rev: Vec = std::iter::once(self.mds_vector[0]) .chain(self.mds_vector[1..].iter().rev().cloned()) .collect(); let conv = karatsuba(&mds_rev, state); let mut result = vec![Fp::zero(); m]; - for i in 0..m { - result[i] = conv[i].clone(); - } + result[..m].copy_from_slice(&conv[..m]); + //for i in 0..m { + // result[i] = conv[i].clone(); + //} + for i in m..conv.len() { - result[i - m] = result[i - m] + conv[i].clone(); + result[i - m] += conv[i]; } result @@ -355,7 +201,7 @@ impl let round_constants = &self.round_constants; for j in 0..m { - state[j] = state[j] + round_constants[round * 2 * m + j]; + state[j] += round_constants[round * 2 * m + j]; } } @@ -364,7 +210,7 @@ impl let round_constants = &self.round_constants; for j in 0..m { - state[j] = state[j] + round_constants[round * 2 * m + m + j]; + state[j] += round_constants[round * 2 * m + m + j] } } @@ -397,9 +243,10 @@ impl for i in 0..num_full_chunks { let chunk = &input_sequence[i * rate..(i + 1) * rate]; - for j in 0..rate { - state[capacity + j] = chunk[j]; - } + state[capacity..(rate + capacity)].copy_from_slice(&chunk[..rate]); + //for j in 0..rate { + // state[capacity + j] = chunk[j]; + //} self.permutation(&mut state); } @@ -412,9 +259,10 @@ impl } last_chunk[last_chunk_size] = Fp::one(); - for j in 0..rate { - state[capacity + j] = last_chunk[j]; - } + state[capacity..(rate + capacity)].copy_from_slice(&last_chunk[..rate]); + //for j in 0..rate { + // state[capacity + j] = last_chunk[j]; + //} self.permutation(&mut state); } @@ -431,135 +279,130 @@ impl #[cfg(test)] mod tests { use super::*; - use lazy_static::lazy_static; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; - lazy_static! { - pub static ref EXPECTED: Vec> = vec![ - vec![ - Fp::from(1502364727743950833u64), - Fp::from(5880949717274681448u64), - Fp::from(162790463902224431u64), - Fp::from(6901340476773664264u64), - ], - vec![ - Fp::from(7478710183745780580u64), - Fp::from(3308077307559720969u64), - Fp::from(3383561985796182409u64), - Fp::from(17205078494700259815u64), - ], - vec![ - Fp::from(17439912364295172999u64), - Fp::from(17979156346142712171u64), - Fp::from(8280795511427637894u64), - Fp::from(9349844417834368814u64), - ], - vec![ - Fp::from(5105868198472766874u64), - Fp::from(13090564195691924742u64), - Fp::from(1058904296915798891u64), - Fp::from(18379501748825152268u64), - ], - vec![ - Fp::from(9133662113608941286u64), - Fp::from(12096627591905525991u64), - Fp::from(14963426595993304047u64), - Fp::from(13290205840019973377u64), - ], - vec![ - Fp::from(3134262397541159485u64), - Fp::from(10106105871979362399u64), - Fp::from(138768814855329459u64), - Fp::from(15044809212457404677u64), - ], - vec![ - Fp::from(162696376578462826u64), - Fp::from(4991300494838863586u64), - Fp::from(660346084748120605u64), - Fp::from(13179389528641752698u64), - ], - vec![ - Fp::from(2242391899857912644u64), - Fp::from(12689382052053305418u64), - Fp::from(235236990017815546u64), - Fp::from(5046143039268215739u64), - ], - vec![ - Fp::from(9585630502158073976u64), - Fp::from(1310051013427303477u64), - Fp::from(7491921222636097758u64), - Fp::from(9417501558995216762u64), - ], - vec![ - Fp::from(1994394001720334744u64), - Fp::from(10866209900885216467u64), - Fp::from(13836092831163031683u64), - Fp::from(10814636682252756697u64), - ], - vec![ - Fp::from(17486854790732826405u64), - Fp::from(17376549265955727562u64), - Fp::from(2371059831956435003u64), - Fp::from(17585704935858006533u64), - ], - vec![ - Fp::from(11368277489137713825u64), - Fp::from(3906270146963049287u64), - Fp::from(10236262408213059745u64), - Fp::from(78552867005814007u64), - ], - vec![ - Fp::from(17899847381280262181u64), - Fp::from(14717912805498651446u64), - Fp::from(10769146203951775298u64), - Fp::from(2774289833490417856u64), - ], - vec![ - Fp::from(3794717687462954368u64), - Fp::from(4386865643074822822u64), - Fp::from(8854162840275334305u64), - Fp::from(7129983987107225269u64), - ], - vec![ - Fp::from(7244773535611633983u64), - Fp::from(19359923075859320u64), - Fp::from(10898655967774994333u64), - Fp::from(9319339563065736480u64), - ], - vec![ - Fp::from(4935426252518736883u64), - Fp::from(12584230452580950419u64), - Fp::from(8762518969632303998u64), - Fp::from(18159875708229758073u64), - ], - vec![ - Fp::from(14871230873837295931u64), - Fp::from(11225255908868362971u64), - Fp::from(18100987641405432308u64), - Fp::from(1559244340089644233u64), - ], - vec![ - Fp::from(8348203744950016968u64), - Fp::from(4041411241960726733u64), - Fp::from(17584743399305468057u64), - Fp::from(16836952610803537051u64), - ], - vec![ - Fp::from(16139797453633030050u64), - Fp::from(1090233424040889412u64), - Fp::from(10770255347785669036u64), - Fp::from(16982398877290254028u64), - ], - ]; - } + pub const EXPECTED: [[Fp; 4]; 19] = [ + [ + Fp::const_from_raw(1502364727743950833u64), + Fp::const_from_raw(5880949717274681448u64), + Fp::const_from_raw(162790463902224431u64), + Fp::const_from_raw(6901340476773664264u64), + ], + [ + Fp::const_from_raw(7478710183745780580u64), + Fp::const_from_raw(3308077307559720969u64), + Fp::const_from_raw(3383561985796182409u64), + Fp::const_from_raw(17205078494700259815u64), + ], + [ + Fp::const_from_raw(17439912364295172999u64), + Fp::const_from_raw(17979156346142712171u64), + Fp::const_from_raw(8280795511427637894u64), + Fp::const_from_raw(9349844417834368814u64), + ], + [ + Fp::const_from_raw(5105868198472766874u64), + Fp::const_from_raw(13090564195691924742u64), + Fp::const_from_raw(1058904296915798891u64), + Fp::const_from_raw(18379501748825152268u64), + ], + [ + Fp::const_from_raw(9133662113608941286u64), + Fp::const_from_raw(12096627591905525991u64), + Fp::const_from_raw(14963426595993304047u64), + Fp::const_from_raw(13290205840019973377u64), + ], + [ + Fp::const_from_raw(3134262397541159485u64), + Fp::const_from_raw(10106105871979362399u64), + Fp::const_from_raw(138768814855329459u64), + Fp::const_from_raw(15044809212457404677u64), + ], + [ + Fp::const_from_raw(162696376578462826u64), + Fp::const_from_raw(4991300494838863586u64), + Fp::const_from_raw(660346084748120605u64), + Fp::const_from_raw(13179389528641752698u64), + ], + [ + Fp::const_from_raw(2242391899857912644u64), + Fp::const_from_raw(12689382052053305418u64), + Fp::const_from_raw(235236990017815546u64), + Fp::const_from_raw(5046143039268215739u64), + ], + [ + Fp::const_from_raw(9585630502158073976u64), + Fp::const_from_raw(1310051013427303477u64), + Fp::const_from_raw(7491921222636097758u64), + Fp::const_from_raw(9417501558995216762u64), + ], + [ + Fp::const_from_raw(1994394001720334744u64), + Fp::const_from_raw(10866209900885216467u64), + Fp::const_from_raw(13836092831163031683u64), + Fp::const_from_raw(10814636682252756697u64), + ], + [ + Fp::const_from_raw(17486854790732826405u64), + Fp::const_from_raw(17376549265955727562u64), + Fp::const_from_raw(2371059831956435003u64), + Fp::const_from_raw(17585704935858006533u64), + ], + [ + Fp::const_from_raw(11368277489137713825u64), + Fp::const_from_raw(3906270146963049287u64), + Fp::const_from_raw(10236262408213059745u64), + Fp::const_from_raw(78552867005814007u64), + ], + [ + Fp::const_from_raw(17899847381280262181u64), + Fp::const_from_raw(14717912805498651446u64), + Fp::const_from_raw(10769146203951775298u64), + Fp::const_from_raw(2774289833490417856u64), + ], + [ + Fp::const_from_raw(3794717687462954368u64), + Fp::const_from_raw(4386865643074822822u64), + Fp::const_from_raw(8854162840275334305u64), + Fp::const_from_raw(7129983987107225269u64), + ], + [ + Fp::const_from_raw(7244773535611633983u64), + Fp::const_from_raw(19359923075859320u64), + Fp::const_from_raw(10898655967774994333u64), + Fp::const_from_raw(9319339563065736480u64), + ], + [ + Fp::const_from_raw(4935426252518736883u64), + Fp::const_from_raw(12584230452580950419u64), + Fp::const_from_raw(8762518969632303998u64), + Fp::const_from_raw(18159875708229758073u64), + ], + [ + Fp::const_from_raw(14871230873837295931u64), + Fp::const_from_raw(11225255908868362971u64), + Fp::const_from_raw(18100987641405432308u64), + Fp::const_from_raw(1559244340089644233u64), + ], + [ + Fp::const_from_raw(8348203744950016968u64), + Fp::const_from_raw(4041411241960726733u64), + Fp::const_from_raw(17584743399305468057u64), + Fp::const_from_raw(16836952610803537051u64), + ], + [ + Fp::const_from_raw(16139797453633030050u64), + Fp::const_from_raw(1090233424040889412u64), + Fp::const_from_raw(10770255347785669036u64), + Fp::const_from_raw(16982398877290254028u64), + ], + ]; - // Utility function to generate random field elements fn rand_field_element(rng: &mut R) -> Fp { Fp::from(rng.gen::()) } - // Test for S-box operation #[test] fn test_apply_sbox() { let mut rng = StdRng::seed_from_u64(1); @@ -575,7 +418,6 @@ mod tests { assert_eq!(expected, state); } - // Test for inverse S-box operation #[test] fn test_apply_inverse_sbox() { let mut rng = StdRng::seed_from_u64(2); @@ -591,7 +433,6 @@ mod tests { assert_eq!(expected, state); } - // Test for MDS matrix multiplication #[test] fn test_mds_matrix_multiplication() { let mut rng = StdRng::seed_from_u64(3); @@ -607,7 +448,6 @@ mod tests { assert_eq!(expected_state, computed_state); } - // Test for NTT-based MDS matrix multiplication #[test] fn test_mds_ntt() { let mut rng = StdRng::seed_from_u64(4); @@ -623,7 +463,6 @@ mod tests { assert_eq!(expected_state, computed_state); } - // Test for Karatsuba-based MDS matrix multiplication #[test] fn test_mds_karatsuba() { let mut rng = StdRng::seed_from_u64(5); @@ -639,7 +478,6 @@ mod tests { assert_eq!(expected_state, computed_state); } - // Test for round constant addition in permutation function #[test] fn test_add_round_constants() { let mut rng = StdRng::seed_from_u64(6); @@ -692,8 +530,7 @@ mod tests { let input_sequence: Vec = (0..8).map(Fp::from).collect(); let hash_output = rescue.hash(&input_sequence); - // Verify the squeezed output - assert_eq!(hash_output.len(), 4); // Half the rate (rate = 8) + assert_eq!(hash_output.len(), 4); } #[test] @@ -702,14 +539,13 @@ mod tests { let input_sequence: Vec = (0..16).map(Fp::from).collect(); // Two chunks of size 8 let hash_output = rescue.hash(&input_sequence); - // Verify the squeezed output - assert_eq!(hash_output.len(), 4); // Half the rate (rate = 8) + assert_eq!(hash_output.len(), 4); } #[test] fn test_hash_with_padding() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let input_sequence: Vec = (0..5).map(Fp::from).collect(); // Input smaller than rate + let input_sequence: Vec = (0..5).map(Fp::from).collect(); let hash_output = rescue.hash(&input_sequence); assert_eq!(hash_output.len(), 4); @@ -754,27 +590,23 @@ mod tests { zeroes.push(Fp::zero()); } } - // Test for hash function with byte input #[test] fn test_hash_bytes() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); let input_bytes = b"Rescue Prime Optimized"; let hash_output = rescue.hash_bytes(input_bytes); - // Verify the squeezed output - assert_eq!(hash_output.len(), 4); // Half the rate (rate = 8) + assert_eq!(hash_output.len(), 4); } - // Test for round constants instantiation #[test] fn test_instantiate_round_constants() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); let round_constants = &rescue.round_constants; - assert_eq!(round_constants.len(), 2 * 12 * 7); // 2 * m * NUM_FULL_ROUNDS + assert_eq!(round_constants.len(), 2 * 12 * 7); } - // Test for MDS methods consistency #[test] fn test_mds_methods_consistency() { let rescue_matrix = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); @@ -800,46 +632,27 @@ mod tests { assert_eq!(hash_matrix, hash_ntt); assert_eq!(hash_ntt, hash_karatsuba); } - - #[test] - fn generate_test_vectors() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let elements = vec![ - Fp::from(0u64), - Fp::from(1u64), - Fp::from(2u64), - Fp::from(3u64), - Fp::from(4u64), - Fp::from(5u64), - Fp::from(6u64), - Fp::from(7u64), - Fp::from(8u64), - Fp::from(9u64), - Fp::from(10u64), - Fp::from(11u64), - Fp::from(12u64), - Fp::from(13u64), - Fp::from(14u64), - Fp::from(15u64), - Fp::from(16u64), - Fp::from(17u64), - Fp::from(18u64), - ]; - - println!("let expected_hashes = vec!["); - for i in 0..elements.len() { - let input = &elements[..=i]; - let hash_output = rescue.hash(input); - - print!(" vec!["); - for value in &hash_output { - print!("Fp::from({}u64), ", value.value()); - } - println!("],"); + /* + Test used to generate the expected hashes for the test vectors (they are the same as in the Polygon Miden implementation) + #[test] + fn generate_test_vectors() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let elements: Vec = (0..19).map(Fp::from).collect(); + + println!("let expected_hashes = vec!["); + elements.iter().enumerate().for_each(|(i, _)| { + let input = elements.iter().take(i + 1); // Tomar el prefijo hasta i + 1 + let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); + + print!(" vec!["); + hash_output.iter().for_each(|value| { + print!("Fp::from({}u64), ", value.value()); + }); + println!("],"); + }); + println!("];"); } - println!("];"); - } - + */ #[test] fn test_print_round_constants() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); @@ -849,17 +662,35 @@ mod tests { println!("Constant {}: Fp::from({}u64)", i, constant.value()); } - assert_eq!(rescue.round_constants.len(), 2 * rescue.m * 7); // 2 * m * NUM_FULL_ROUNDS + assert_eq!(rescue.round_constants.len(), 2 * rescue.m * 7); } + /* + #[test] + fn test_hash_vectors() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let elements: Vec = (0..19).map(Fp::from).collect(); + for (i, expected) in EXPECTED.iter().enumerate() { + let input = &elements[..=i]; // Tomar el prefijo hasta i + let hash_output = rescue.hash(input); + + assert_eq!( + hash_output, + *expected, + "Hash mismatch for input length {}", + i + 1 + ); + } + } + */ #[test] fn test_hash_vectors() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); let elements: Vec = (0..19).map(Fp::from).collect(); - for (i, expected) in EXPECTED.iter().enumerate() { - let input = &elements[..=i]; // Tomar el prefijo hasta i - let hash_output = rescue.hash(input); + EXPECTED.iter().enumerate().for_each(|(i, expected)| { + let input = elements.iter().take(i + 1); + let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); assert_eq!( hash_output, @@ -867,14 +698,13 @@ mod tests { "Hash mismatch for input length {}", i + 1 ); - } + }); } - #[test] fn test_hash_example_and_print() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Ntt); - let input = b"Boquita campeon del mundo!"; + let input = b"Hello there"; let hash_result = rescue.hash_bytes(input); diff --git a/crypto/src/hash/rescue_prime/parameters.rs b/crypto/src/hash/rescue_prime/parameters.rs index 8e205ed2b..19bf29718 100644 --- a/crypto/src/hash/rescue_prime/parameters.rs +++ b/crypto/src/hash/rescue_prime/parameters.rs @@ -1,406 +1,177 @@ use super::Fp; -use lazy_static::lazy_static; pub const ALPHA: u64 = 7; pub const ALPHA_INV: u64 = 10540996611094048183; -//pub const P: u64 = 18446744069414584321; // p = 2^64 - 2^32 + 1 -lazy_static! { - pub static ref ROUND_CONSTANTS: Vec = vec![ - Fp::from(5789762306288267392u64), - Fp::from(6522564764413701783u64), - Fp::from(17809893479458208203u64), - Fp::from(107145243989736508u64), - Fp::from(6388978042437517382u64), - Fp::from(15844067734406016715u64), - Fp::from(9975000513555218239u64), - Fp::from(3344984123768313364u64), - Fp::from(9959189626657347191u64), - Fp::from(12960773468763563665u64), - Fp::from(9602914297752488475u64), - Fp::from(16657542370200465908u64), - Fp::from(6077062762357204287u64), - Fp::from(15277620170502011191u64), - Fp::from(5358738125714196705u64), - Fp::from(14233283787297595718u64), - Fp::from(13792579614346651365u64), - Fp::from(11614812331536767105u64), - Fp::from(14871063686742261166u64), - Fp::from(10148237148793043499u64), - Fp::from(4457428952329675767u64), - Fp::from(15590786458219172475u64), - Fp::from(10063319113072092615u64), - Fp::from(14200078843431360086u64), - Fp::from(12987190162843096997u64), - Fp::from(653957632802705281u64), - Fp::from(4441654670647621225u64), - Fp::from(4038207883745915761u64), - Fp::from(5613464648874830118u64), - Fp::from(13222989726778338773u64), - Fp::from(3037761201230264149u64), - Fp::from(16683759727265180203u64), - Fp::from(8337364536491240715u64), - Fp::from(3227397518293416448u64), - Fp::from(8110510111539674682u64), - Fp::from(2872078294163232137u64), - Fp::from(6202948458916099932u64), - Fp::from(17690140365333231091u64), - Fp::from(3595001575307484651u64), - Fp::from(373995945117666487u64), - Fp::from(1235734395091296013u64), - Fp::from(14172757457833931602u64), - Fp::from(707573103686350224u64), - Fp::from(15453217512188187135u64), - Fp::from(219777875004506018u64), - Fp::from(17876696346199469008u64), - Fp::from(17731621626449383378u64), - Fp::from(2897136237748376248u64), - Fp::from(18072785500942327487u64), - Fp::from(6200974112677013481u64), - Fp::from(17682092219085884187u64), - Fp::from(10599526828986756440u64), - Fp::from(975003873302957338u64), - Fp::from(8264241093196931281u64), - Fp::from(10065763900435475170u64), - Fp::from(2181131744534710197u64), - Fp::from(6317303992309418647u64), - Fp::from(1401440938888741532u64), - Fp::from(8884468225181997494u64), - Fp::from(13066900325715521532u64), - Fp::from(8023374565629191455u64), - Fp::from(15013690343205953430u64), - Fp::from(4485500052507912973u64), - Fp::from(12489737547229155153u64), - Fp::from(9500452585969030576u64), - Fp::from(2054001340201038870u64), - Fp::from(12420704059284934186u64), - Fp::from(355990932618543755u64), - Fp::from(9071225051243523860u64), - Fp::from(12766199826003448536u64), - Fp::from(9045979173463556963u64), - Fp::from(12934431667190679898u64), - Fp::from(5674685213610121970u64), - Fp::from(5759084860419474071u64), - Fp::from(13943282657648897737u64), - Fp::from(1352748651966375394u64), - Fp::from(17110913224029905221u64), - Fp::from(1003883795902368422u64), - Fp::from(4141870621881018291u64), - Fp::from(8121410972417424656u64), - Fp::from(14300518605864919529u64), - Fp::from(13712227150607670181u64), - Fp::from(17021852944633065291u64), - Fp::from(6252096473787587650u64), - Fp::from(18389244934624494276u64), - Fp::from(16731736864863925227u64), - Fp::from(4440209734760478192u64), - Fp::from(17208448209698888938u64), - Fp::from(8739495587021565984u64), - Fp::from(17000774922218161967u64), - Fp::from(13533282547195532087u64), - Fp::from(525402848358706231u64), - Fp::from(16987541523062161972u64), - Fp::from(5466806524462797102u64), - Fp::from(14512769585918244983u64), - Fp::from(10973956031244051118u64), - Fp::from(4887609836208846458u64), - Fp::from(3027115137917284492u64), - Fp::from(9595098600469470675u64), - Fp::from(10528569829048484079u64), - Fp::from(7864689113198939815u64), - Fp::from(17533723827845969040u64), - Fp::from(5781638039037710951u64), - Fp::from(17024078752430719006u64), - Fp::from(109659393484013511u64), - Fp::from(7158933660534805869u64), - Fp::from(2955076958026921730u64), - Fp::from(7433723648458773977u64), - Fp::from(6982293561042362913u64), - Fp::from(14065426295947720331u64), - Fp::from(16451845770444974180u64), - Fp::from(7139138592091306727u64), - Fp::from(9012006439959783127u64), - Fp::from(14619614108529063361u64), - Fp::from(1394813199588124371u64), - Fp::from(4635111139507788575u64), - Fp::from(16217473952264203365u64), - Fp::from(10782018226466330683u64), - Fp::from(6844229992533662050u64), - Fp::from(7446486531695178711u64), - Fp::from(16308865189192447297u64), - Fp::from(11977192855656444890u64), - Fp::from(12532242556065780287u64), - Fp::from(14594890931430968898u64), - Fp::from(7291784239689209784u64), - Fp::from(5514718540551361949u64), - Fp::from(10025733853830934803u64), - Fp::from(7293794580341021693u64), - Fp::from(6728552937464861756u64), - Fp::from(6332385040983343262u64), - Fp::from(13277683694236792804u64), - Fp::from(2600778905124452676u64), - Fp::from(3736792340494631448u64), - Fp::from(577852220195055341u64), - Fp::from(6689998335515779805u64), - Fp::from(13886063479078013492u64), - Fp::from(14358505101923202168u64), - Fp::from(7744142531772274164u64), - Fp::from(16135070735728404443u64), - Fp::from(12290902521256031137u64), - Fp::from(12059913662657709804u64), - Fp::from(16456018495793751911u64), - Fp::from(4571485474751953524u64), - Fp::from(17200392109565783176u64), - Fp::from(7123075680859040534u64), - Fp::from(1034205548717903090u64), - Fp::from(7717824418247931797u64), - Fp::from(3019070937878604058u64), - Fp::from(11403792746066867460u64), - Fp::from(10280580802233112374u64), - Fp::from(337153209462421218u64), - Fp::from(13333398568519923717u64), - Fp::from(3596153696935337464u64), - Fp::from(8104208463525993784u64), - Fp::from(14345062289456085693u64), - Fp::from(17036731477169661256u64), - Fp::from(17130398059294018733u64), - Fp::from(519782857322261988u64), - Fp::from(9625384390925085478u64), - Fp::from(1664893052631119222u64), - Fp::from(7629576092524553570u64), - Fp::from(3485239601103661425u64), - Fp::from(9755891797164033838u64), - Fp::from(15218148195153269027u64), - Fp::from(16460604813734957368u64), - Fp::from(9643968136937729763u64), - Fp::from(3611348709641382851u64), - Fp::from(18256379591337759196u64), - ]; -} -/* -pub const ROUND_CONSTANTS_HEX: [Fp; 224] = [ - Fp::const_from_raw(1965335827333385572), - Fp::const_from_raw(13386940263093285890), - Fp::const_from_raw(2676433512518024499), - Fp::const_from_raw(3265387569419834752), - Fp::const_from_raw(1983410871005483133), - Fp::const_from_raw(9697282293408698131), - Fp::const_from_raw(1272774544215511539), - Fp::const_from_raw(8206289606243220511), - Fp::const_from_raw(1290391036756663400), - Fp::const_from_raw(18219831014774660739), - Fp::const_from_raw(9691367064095402927), - Fp::const_from_raw(1323942862844130786), - Fp::const_from_raw(15151407902520044968), - Fp::const_from_raw(3367241195349533752), - Fp::const_from_raw(4045613938354522492), - Fp::const_from_raw(8414577515806306591), - Fp::const_from_raw(12735791373473705278), - Fp::const_from_raw(3301196190123345788), - Fp::const_from_raw(4934538150586227609), - Fp::const_from_raw(3817643842607407527), - Fp::const_from_raw(13416431558822898318), - Fp::const_from_raw(5832629091408730901), - Fp::const_from_raw(3362368740314001033), - Fp::const_from_raw(11092906639494490385), - Fp::const_from_raw(6071859273097876791), - Fp::const_from_raw(10161425034618716356), - Fp::const_from_raw(7152209120756903545), - Fp::const_from_raw(16380870469663741149), - Fp::const_from_raw(3952136951542576078), - Fp::const_from_raw(17537441052343611097), - Fp::const_from_raw(11551242553047556263), - Fp::const_from_raw(10106900133850428740), - Fp::const_from_raw(11416650542216810040), - Fp::const_from_raw(11422270812969046329), - Fp::const_from_raw(8866991719313052084), - Fp::const_from_raw(11055863001411088108), - Fp::const_from_raw(6180770262849183127), - Fp::const_from_raw(15065904341621422463), - Fp::const_from_raw(6379231142859676194), - Fp::const_from_raw(12898133478008807755), - Fp::const_from_raw(17022976567648776965), - Fp::const_from_raw(9092326911543756291), - Fp::const_from_raw(6030122978628466915), - Fp::const_from_raw(9597034755157312926), - Fp::const_from_raw(994741965321505508), - Fp::const_from_raw(7556490651023083151), - Fp::const_from_raw(13471961853484783473), - Fp::const_from_raw(5530500298270693480), - Fp::const_from_raw(3138602747749119790), - Fp::const_from_raw(14959768162492908516), - Fp::const_from_raw(9134218270579160311), - Fp::const_from_raw(11526344086740032769), - Fp::const_from_raw(18056157006815181954), - Fp::const_from_raw(6800589288408907691), - Fp::const_from_raw(15936640138392473876), - Fp::const_from_raw(2300163192580995689), - Fp::const_from_raw(4526841916921293676), - Fp::const_from_raw(7195881155996340935), - Fp::const_from_raw(2785483023916634674), - Fp::const_from_raw(15081468567893261932), - Fp::const_from_raw(6614707290651872269), - Fp::const_from_raw(13681365294828420351), - Fp::const_from_raw(10664658542323360702), - Fp::const_from_raw(10084964797450915045), - Fp::const_from_raw(4845198022119750202), - Fp::const_from_raw(2607866667643628253), - Fp::const_from_raw(5208104371714885253), - Fp::const_from_raw(12959011109386888563), - Fp::const_from_raw(4000466944391262442), - Fp::const_from_raw(17728719744160665330), - Fp::const_from_raw(7150641948246037689), - Fp::const_from_raw(9776810486328380322), - Fp::const_from_raw(8402715679168885485), - Fp::const_from_raw(3121448252217290414), - Fp::const_from_raw(17436789549778885163), - Fp::const_from_raw(15165907014487612788), - Fp::const_from_raw(11269595316481578714), - Fp::const_from_raw(9914651255870961898), - Fp::const_from_raw(12689101348845299684), - Fp::const_from_raw(11975655653136929369), - Fp::const_from_raw(7372192115875804252), - Fp::const_from_raw(374526648312709133), - Fp::const_from_raw(5985220408386061330), - Fp::const_from_raw(7185802228951619536), - Fp::const_from_raw(1399294693953396201), - Fp::const_from_raw(3261364014951657316), - Fp::const_from_raw(12077409443637692420), - Fp::const_from_raw(9673650825325087603), - Fp::const_from_raw(5569045552142119082), - Fp::const_from_raw(17617312550416673451), - Fp::const_from_raw(6211450796053144311), - Fp::const_from_raw(11274862073326008409), - Fp::const_from_raw(18367233290057731659), - Fp::const_from_raw(13198876392118957255), - Fp::const_from_raw(13272050586507026767), - Fp::const_from_raw(13010781901687851463), - Fp::const_from_raw(11176896862794321170), - Fp::const_from_raw(6638609153583434674), - Fp::const_from_raw(14505835809704498565), - Fp::const_from_raw(17581684280975726513), - Fp::const_from_raw(699795237352602006), - Fp::const_from_raw(9944038704239459812), - Fp::const_from_raw(8047212797227008956), - Fp::const_from_raw(1395744870455664103), - Fp::const_from_raw(18357515964980248812), - Fp::const_from_raw(9097466431298056431), - Fp::const_from_raw(14710664890151992774), - Fp::const_from_raw(6629781383077611287), - Fp::const_from_raw(17573797615501516970), - Fp::const_from_raw(12347664633647440814), - Fp::const_from_raw(11021709264172808686), - Fp::const_from_raw(10955032358008028206), - Fp::const_from_raw(12827014260928926472), - Fp::const_from_raw(14274600229400487385), - Fp::const_from_raw(12031986599882032134), - Fp::const_from_raw(16154104676212634613), - Fp::const_from_raw(18132152994017433356), - Fp::const_from_raw(15441239634310983499), - Fp::const_from_raw(10976597099491887044), - Fp::const_from_raw(3707145841124002094), - Fp::const_from_raw(8720928559638383045), - Fp::const_from_raw(16336200500310468906), - Fp::const_from_raw(6210805750383775651), - Fp::const_from_raw(7719884621977079797), - Fp::const_from_raw(11449042012956416425), - Fp::const_from_raw(9075619080551251971), - Fp::const_from_raw(617668424765806231), - Fp::const_from_raw(12270348236411784037), - Fp::const_from_raw(6186113401837024523), - Fp::const_from_raw(15458192282022704662), - Fp::const_from_raw(3533646002027882636), - Fp::const_from_raw(7323750725122298699), - Fp::const_from_raw(17370102587019252090), - Fp::const_from_raw(1740987243995377904), - Fp::const_from_raw(10219908189144498973), - Fp::const_from_raw(1822464913426161699), - Fp::const_from_raw(13340330593340428766), - Fp::const_from_raw(11476413915876641735), - Fp::const_from_raw(10301877462024259119), - Fp::const_from_raw(17003473479205724655), - Fp::const_from_raw(10899885430087119072), - Fp::const_from_raw(2161571014943847810), - Fp::const_from_raw(10337649388059569402), - Fp::const_from_raw(1627927149280118935), - Fp::const_from_raw(981019442244479500), - Fp::const_from_raw(8080861373146567887), - Fp::const_from_raw(8033636340692269807), - Fp::const_from_raw(1747076424940820198), - Fp::const_from_raw(15430102639810276278), - Fp::const_from_raw(9286420248392647962), - Fp::const_from_raw(11497964697936588530), - Fp::const_from_raw(17639509337065865628), - Fp::const_from_raw(2160917583540985983), - Fp::const_from_raw(6735220140815683510), - Fp::const_from_raw(6183237619116523957), - Fp::const_from_raw(13347893983048485379), - Fp::const_from_raw(4087545433624195113), - Fp::const_from_raw(11701648626105993864), - Fp::const_from_raw(11913677089736238784), - Fp::const_from_raw(271004950317860287), - Fp::const_from_raw(11794070108002091165), - Fp::const_from_raw(15639064309077629849), - Fp::const_from_raw(16481734838884572560), - Fp::const_from_raw(3932918848577657311), - Fp::const_from_raw(16327200574281469287), - Fp::const_from_raw(7060041503065075033), - Fp::const_from_raw(4892761442718320741), - Fp::const_from_raw(8255275116206368067), - Fp::const_from_raw(14957838536671021552), - Fp::const_from_raw(14493715972468567436), - Fp::const_from_raw(7463718209809697261), - Fp::const_from_raw(3440982266989812843), - Fp::const_from_raw(2354199421703013492), - Fp::const_from_raw(2321628279578256047), - Fp::const_from_raw(3746041501354899488), - Fp::const_from_raw(11186576936873825301), - Fp::const_from_raw(15218587616061641074), - Fp::const_from_raw(11844784525417523222), - Fp::const_from_raw(7998727848169056055), - Fp::const_from_raw(7948968711630609066), - Fp::const_from_raw(11805042600408037937), - Fp::const_from_raw(18172588443872800894), - Fp::const_from_raw(13092373363317372568), - Fp::const_from_raw(2169983441195298580), - Fp::const_from_raw(1499680808057735775), - Fp::const_from_raw(7077486803310915643), - Fp::const_from_raw(743612288630452727), - Fp::const_from_raw(11665426394426065172), - Fp::const_from_raw(15533499373769144802), - Fp::const_from_raw(14249183160150274240), - Fp::const_from_raw(13792290235996127743), - Fp::const_from_raw(4995017088228886738), - Fp::const_from_raw(9763845271226970122), - Fp::const_from_raw(1727820159257625458), - Fp::const_from_raw(9681902124347643227), - Fp::const_from_raw(11327574568051933160), - Fp::const_from_raw(10627429556158481577), - Fp::const_from_raw(13984143774797145216), - Fp::const_from_raw(17082059622058840713), - Fp::const_from_raw(16264233536802058333), - Fp::const_from_raw(10077962488096645822), - Fp::const_from_raw(5057253598123536060), - Fp::const_from_raw(2301672207952647376), - Fp::const_from_raw(17506877517896521554), - Fp::const_from_raw(14583366393971011156), - Fp::const_from_raw(6226877164823354372), - Fp::const_from_raw(2260055134098203623), - Fp::const_from_raw(12945296184826522120), - Fp::const_from_raw(15417698598606677168), - Fp::const_from_raw(7447949755934804788), - Fp::const_from_raw(8017843736725863212), - Fp::const_from_raw(1003688007091182795), - Fp::const_from_raw(8935767355090348282), - Fp::const_from_raw(793319158990348431), - Fp::const_from_raw(4437923789992338287), - Fp::const_from_raw(7869978205237541489), - Fp::const_from_raw(9039403419111053092), - Fp::const_from_raw(3845065612997771849), - Fp::const_from_raw(15179573672801872590), - Fp::const_from_raw(2879645310341005490), - Fp::const_from_raw(4421001170561580576), - Fp::const_from_raw(7614461260369642079), - Fp::const_from_raw(10869617590371203777), - Fp::const_from_raw(4582902440098948914), +// Constants for the Rescue hash function, these were obtained using the paper implementation in sage +// https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation + +pub const ROUND_CONSTANTS: [Fp; 168] = [ + Fp::const_from_raw(5789762306288267392), + Fp::const_from_raw(6522564764413701783), + Fp::const_from_raw(17809893479458208203), + Fp::const_from_raw(107145243989736508), + Fp::const_from_raw(6388978042437517382), + Fp::const_from_raw(15844067734406016715), + Fp::const_from_raw(9975000513555218239), + Fp::const_from_raw(3344984123768313364), + Fp::const_from_raw(9959189626657347191), + Fp::const_from_raw(12960773468763563665), + Fp::const_from_raw(9602914297752488475), + Fp::const_from_raw(16657542370200465908), + Fp::const_from_raw(6077062762357204287), + Fp::const_from_raw(15277620170502011191), + Fp::const_from_raw(5358738125714196705), + Fp::const_from_raw(14233283787297595718), + Fp::const_from_raw(13792579614346651365), + Fp::const_from_raw(11614812331536767105), + Fp::const_from_raw(14871063686742261166), + Fp::const_from_raw(10148237148793043499), + Fp::const_from_raw(4457428952329675767), + Fp::const_from_raw(15590786458219172475), + Fp::const_from_raw(10063319113072092615), + Fp::const_from_raw(14200078843431360086), + Fp::const_from_raw(12987190162843096997), + Fp::const_from_raw(653957632802705281), + Fp::const_from_raw(4441654670647621225), + Fp::const_from_raw(4038207883745915761), + Fp::const_from_raw(5613464648874830118), + Fp::const_from_raw(13222989726778338773), + Fp::const_from_raw(3037761201230264149), + Fp::const_from_raw(16683759727265180203), + Fp::const_from_raw(8337364536491240715), + Fp::const_from_raw(3227397518293416448), + Fp::const_from_raw(8110510111539674682), + Fp::const_from_raw(2872078294163232137), + Fp::const_from_raw(6202948458916099932), + Fp::const_from_raw(17690140365333231091), + Fp::const_from_raw(3595001575307484651), + Fp::const_from_raw(373995945117666487), + Fp::const_from_raw(1235734395091296013), + Fp::const_from_raw(14172757457833931602), + Fp::const_from_raw(707573103686350224), + Fp::const_from_raw(15453217512188187135), + Fp::const_from_raw(219777875004506018), + Fp::const_from_raw(17876696346199469008), + Fp::const_from_raw(17731621626449383378), + Fp::const_from_raw(2897136237748376248), + Fp::const_from_raw(18072785500942327487), + Fp::const_from_raw(6200974112677013481), + Fp::const_from_raw(17682092219085884187), + Fp::const_from_raw(10599526828986756440), + Fp::const_from_raw(975003873302957338), + Fp::const_from_raw(8264241093196931281), + Fp::const_from_raw(10065763900435475170), + Fp::const_from_raw(2181131744534710197), + Fp::const_from_raw(6317303992309418647), + Fp::const_from_raw(1401440938888741532), + Fp::const_from_raw(8884468225181997494), + Fp::const_from_raw(13066900325715521532), + Fp::const_from_raw(8023374565629191455), + Fp::const_from_raw(15013690343205953430), + Fp::const_from_raw(4485500052507912973), + Fp::const_from_raw(12489737547229155153), + Fp::const_from_raw(9500452585969030576), + Fp::const_from_raw(2054001340201038870), + Fp::const_from_raw(12420704059284934186), + Fp::const_from_raw(355990932618543755), + Fp::const_from_raw(9071225051243523860), + Fp::const_from_raw(12766199826003448536), + Fp::const_from_raw(9045979173463556963), + Fp::const_from_raw(12934431667190679898), + Fp::const_from_raw(5674685213610121970), + Fp::const_from_raw(5759084860419474071), + Fp::const_from_raw(13943282657648897737), + Fp::const_from_raw(1352748651966375394), + Fp::const_from_raw(17110913224029905221), + Fp::const_from_raw(1003883795902368422), + Fp::const_from_raw(4141870621881018291), + Fp::const_from_raw(8121410972417424656), + Fp::const_from_raw(14300518605864919529), + Fp::const_from_raw(13712227150607670181), + Fp::const_from_raw(17021852944633065291), + Fp::const_from_raw(6252096473787587650), + Fp::const_from_raw(18389244934624494276), + Fp::const_from_raw(16731736864863925227), + Fp::const_from_raw(4440209734760478192), + Fp::const_from_raw(17208448209698888938), + Fp::const_from_raw(8739495587021565984), + Fp::const_from_raw(17000774922218161967), + Fp::const_from_raw(13533282547195532087), + Fp::const_from_raw(525402848358706231), + Fp::const_from_raw(16987541523062161972), + Fp::const_from_raw(5466806524462797102), + Fp::const_from_raw(14512769585918244983), + Fp::const_from_raw(10973956031244051118), + Fp::const_from_raw(4887609836208846458), + Fp::const_from_raw(3027115137917284492), + Fp::const_from_raw(9595098600469470675), + Fp::const_from_raw(10528569829048484079), + Fp::const_from_raw(7864689113198939815), + Fp::const_from_raw(17533723827845969040), + Fp::const_from_raw(5781638039037710951), + Fp::const_from_raw(17024078752430719006), + Fp::const_from_raw(109659393484013511), + Fp::const_from_raw(7158933660534805869), + Fp::const_from_raw(2955076958026921730), + Fp::const_from_raw(7433723648458773977), + Fp::const_from_raw(6982293561042362913), + Fp::const_from_raw(14065426295947720331), + Fp::const_from_raw(16451845770444974180), + Fp::const_from_raw(7139138592091306727), + Fp::const_from_raw(9012006439959783127), + Fp::const_from_raw(14619614108529063361), + Fp::const_from_raw(1394813199588124371), + Fp::const_from_raw(4635111139507788575), + Fp::const_from_raw(16217473952264203365), + Fp::const_from_raw(10782018226466330683), + Fp::const_from_raw(6844229992533662050), + Fp::const_from_raw(7446486531695178711), + Fp::const_from_raw(16308865189192447297), + Fp::const_from_raw(11977192855656444890), + Fp::const_from_raw(12532242556065780287), + Fp::const_from_raw(14594890931430968898), + Fp::const_from_raw(7291784239689209784), + Fp::const_from_raw(5514718540551361949), + Fp::const_from_raw(10025733853830934803), + Fp::const_from_raw(7293794580341021693), + Fp::const_from_raw(6728552937464861756), + Fp::const_from_raw(6332385040983343262), + Fp::const_from_raw(13277683694236792804), + Fp::const_from_raw(2600778905124452676), + Fp::const_from_raw(3736792340494631448), + Fp::const_from_raw(577852220195055341), + Fp::const_from_raw(6689998335515779805), + Fp::const_from_raw(13886063479078013492), + Fp::const_from_raw(14358505101923202168), + Fp::const_from_raw(7744142531772274164), + Fp::const_from_raw(16135070735728404443), + Fp::const_from_raw(12290902521256031137), + Fp::const_from_raw(12059913662657709804), + Fp::const_from_raw(16456018495793751911), + Fp::const_from_raw(4571485474751953524), + Fp::const_from_raw(17200392109565783176), + Fp::const_from_raw(7123075680859040534), + Fp::const_from_raw(1034205548717903090), + Fp::const_from_raw(7717824418247931797), + Fp::const_from_raw(3019070937878604058), + Fp::const_from_raw(11403792746066867460), + Fp::const_from_raw(10280580802233112374), + Fp::const_from_raw(337153209462421218), + Fp::const_from_raw(13333398568519923717), + Fp::const_from_raw(3596153696935337464), + Fp::const_from_raw(8104208463525993784), + Fp::const_from_raw(14345062289456085693), + Fp::const_from_raw(17036731477169661256), + Fp::const_from_raw(17130398059294018733), + Fp::const_from_raw(519782857322261988), + Fp::const_from_raw(9625384390925085478), + Fp::const_from_raw(1664893052631119222), + Fp::const_from_raw(7629576092524553570), + Fp::const_from_raw(3485239601103661425), + Fp::const_from_raw(9755891797164033838), + Fp::const_from_raw(15218148195153269027), + Fp::const_from_raw(16460604813734957368), + Fp::const_from_raw(9643968136937729763), + Fp::const_from_raw(3611348709641382851), + Fp::const_from_raw(18256379591337759196), ]; -*/ diff --git a/crypto/src/hash/rescue_prime/utils.rs b/crypto/src/hash/rescue_prime/utils.rs index 7af8900d4..cba786900 100644 --- a/crypto/src/hash/rescue_prime/utils.rs +++ b/crypto/src/hash/rescue_prime/utils.rs @@ -39,7 +39,7 @@ pub fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { let mut result = vec![Fp::zero(); 2 * n - 1]; lhs.iter().enumerate().for_each(|(i, &lhs_val)| { rhs.iter().enumerate().for_each(|(j, &rhs_val)| { - result[i + j] = result[i + j] + lhs_val * rhs_val; + result[i + j] += lhs_val * rhs_val; }); }); return result; From 2a0338faccc5085bb6a35ed5db4648cf1ffc8f4c Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 16 Oct 2024 11:57:48 -0300 Subject: [PATCH 09/17] added precomputed values for 160 security level --- crypto/src/hash/rescue_prime/mod.rs | 252 ++++++++++++++------- crypto/src/hash/rescue_prime/parameters.rs | 241 +++++++++++++++++++- 2 files changed, 410 insertions(+), 83 deletions(-) diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index e96a2b954..eac4de6db 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -1,12 +1,5 @@ use lambdaworks_math::field::element::FieldElement; use lambdaworks_math::field::fields::u64_goldilocks_field::Goldilocks64Field; -//use lazy_static::lazy_static; -//use std::io::Read; - -//use sha3::{ -// digest::{ExtendableOutput, Update}, -// Shake256, -//}; mod parameters; mod utils; @@ -28,11 +21,9 @@ pub struct RescuePrimeOptimized, mds_matrix: Vec>, mds_vector: Vec, - //alpha: u64, - //alpha_inv: u64, mds_method: MdsMethod, } @@ -59,13 +50,17 @@ impl let mds_vector = Self::get_mds_vector(m); let mds_matrix = Self::generate_circulant_matrix(&mds_vector); - let round_constants = ROUND_CONSTANTS; + let round_constants = match SECURITY_LEVEL { + 128 => get_round_constants(SecurityLevel::Sec128), + 160 => get_round_constants(SecurityLevel::Sec160), + _ => panic!("Unsupported security level"), + }; Self { m, capacity, rate, - round_constants, + round_constants: round_constants.to_vec().try_into().unwrap(), mds_matrix, mds_vector, mds_method, @@ -94,18 +89,7 @@ impl _ => panic!("Unsupported state size"), } } - /* - fn generate_circulant_matrix(mds_vector: &[Fp]) -> Vec> { - let m = mds_vector.len(); - let mut mds_matrix = vec![vec![Fp::zero(); m]; m]; - for i in 0..m { - for j in 0..m { - mds_matrix[i][j] = mds_vector[(j + m - i) % m]; - } - } - mds_matrix - } - */ + fn generate_circulant_matrix(mds_vector: &[Fp]) -> Vec> { let m = mds_vector.len(); (0..m) @@ -176,10 +160,6 @@ impl let mut result = vec![Fp::zero(); m]; result[..m].copy_from_slice(&conv[..m]); - //for i in 0..m { - // result[i] = conv[i].clone(); - //} - for i in m..conv.len() { result[i - m] += conv[i]; } @@ -199,7 +179,6 @@ impl fn add_round_constants(&self, state: &mut [Fp], round: usize) { let m = self.m; let round_constants = &self.round_constants; - for j in 0..m { state[j] += round_constants[round * 2 * m + j]; } @@ -208,7 +187,6 @@ impl fn add_round_constants_second(&self, state: &mut [Fp], round: usize) { let m = self.m; let round_constants = &self.round_constants; - for j in 0..m { state[j] += round_constants[round * 2 * m + m + j] } @@ -216,7 +194,6 @@ impl pub fn permutation(&self, state: &mut [Fp]) { let num_rounds = NUM_FULL_ROUNDS; - for round in 0..num_rounds { self.apply_mds(state); self.add_round_constants(state, round); @@ -231,39 +208,25 @@ impl let m = self.m; let capacity = self.capacity; let rate = self.rate; - let mut state = vec![Fp::zero(); m]; let input_len = input_sequence.len(); - if input_len % rate != 0 { state[0] = Fp::one(); } - let num_full_chunks = input_len / rate; - for i in 0..num_full_chunks { let chunk = &input_sequence[i * rate..(i + 1) * rate]; state[capacity..(rate + capacity)].copy_from_slice(&chunk[..rate]); - //for j in 0..rate { - // state[capacity + j] = chunk[j]; - //} self.permutation(&mut state); } - let last_chunk_size = input_len % rate; - if last_chunk_size != 0 { let mut last_chunk = vec![Fp::zero(); rate]; for j in 0..last_chunk_size { last_chunk[j] = input_sequence[num_full_chunks * rate + j]; } last_chunk[last_chunk_size] = Fp::one(); - state[capacity..(rate + capacity)].copy_from_slice(&last_chunk[..rate]); - //for j in 0..rate { - // state[capacity + j] = last_chunk[j]; - //} - self.permutation(&mut state); } @@ -282,7 +245,9 @@ mod tests { use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; - pub const EXPECTED: [[Fp; 4]; 19] = [ + // Values obtained using the Sage implementation in + // https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation + pub const EXPECTED_128: [[Fp; 4]; 19] = [ [ Fp::const_from_raw(1502364727743950833u64), Fp::const_from_raw(5880949717274681448u64), @@ -399,6 +364,141 @@ mod tests { ], ]; + pub const EXPECTED_160: [[Fp; 5]; 19] = [ + [ + Fp::const_from_raw(4766737105427868572), + Fp::const_from_raw(7538777753317835226), + Fp::const_from_raw(13644171984579649606), + Fp::const_from_raw(6748107971891460622), + Fp::const_from_raw(3480072938342119934), + ], + [ + Fp::const_from_raw(6277287777617382937), + Fp::const_from_raw(5688033921803605355), + Fp::const_from_raw(1104978478612014217), + Fp::const_from_raw(973672476085279574), + Fp::const_from_raw(7883652116413797779), + ], + [ + Fp::const_from_raw(3071553803427093579), + Fp::const_from_raw(12239501990998925662), + Fp::const_from_raw(14411295652479845526), + Fp::const_from_raw(5735407824213194294), + Fp::const_from_raw(6714816738691504270), + ], + [ + Fp::const_from_raw(4455998568145007624), + Fp::const_from_raw(18218360213084301612), + Fp::const_from_raw(8963555484142424669), + Fp::const_from_raw(13451196299356019287), + Fp::const_from_raw(660967320761434775), + ], + [ + Fp::const_from_raw(7894041400531553560), + Fp::const_from_raw(3138084719322472990), + Fp::const_from_raw(15017675162298246509), + Fp::const_from_raw(12340633143623038238), + Fp::const_from_raw(3710158928968726190), + ], + [ + Fp::const_from_raw(18345924309197503617), + Fp::const_from_raw(6448668044176965096), + Fp::const_from_raw(5891298758878861437), + Fp::const_from_raw(18404292940273103487), + Fp::const_from_raw(399715742058360811), + ], + [ + Fp::const_from_raw(4293522863608749708), + Fp::const_from_raw(11352999694211746044), + Fp::const_from_raw(15850245073570756600), + Fp::const_from_raw(1206950096837096206), + Fp::const_from_raw(6945598368659615878), + ], + [ + Fp::const_from_raw(1339949574743034442), + Fp::const_from_raw(5967452101017112419), + Fp::const_from_raw(824612579975542151), + Fp::const_from_raw(3327557828938393394), + Fp::const_from_raw(14113149399665697150), + ], + [ + Fp::const_from_raw(3540904694808418824), + Fp::const_from_raw(5951416386790014715), + Fp::const_from_raw(13859113410786779774), + Fp::const_from_raw(17205554479494520251), + Fp::const_from_raw(7359323608260195110), + ], + [ + Fp::const_from_raw(7504301802792161339), + Fp::const_from_raw(12879743137663115497), + Fp::const_from_raw(17245986604042562042), + Fp::const_from_raw(8175050867418132561), + Fp::const_from_raw(1063965910664731268), + ], + [ + Fp::const_from_raw(18267475461736255602), + Fp::const_from_raw(4481864641736940956), + Fp::const_from_raw(11260039501101148638), + Fp::const_from_raw(7529970948767692955), + Fp::const_from_raw(4177810888704753150), + ], + [ + Fp::const_from_raw(16604116128892623566), + Fp::const_from_raw(1520851983040290492), + Fp::const_from_raw(9361704524730297620), + Fp::const_from_raw(7447748879766268839), + Fp::const_from_raw(10834422028571028806), + ], + [ + Fp::const_from_raw(243957224918814907), + Fp::const_from_raw(9966149007214472697), + Fp::const_from_raw(18130816682404489504), + Fp::const_from_raw(3814760895598122151), + Fp::const_from_raw(862573500652233787), + ], + [ + Fp::const_from_raw(13414343823130474877), + Fp::const_from_raw(1002887112060795246), + Fp::const_from_raw(16685735965176892618), + Fp::const_from_raw(16172309857128312555), + Fp::const_from_raw(5158081519803147178), + ], + [ + Fp::const_from_raw(14614132925482133961), + Fp::const_from_raw(7618082792229868740), + Fp::const_from_raw(1881720834768448253), + Fp::const_from_raw(11508391877383996679), + Fp::const_from_raw(5348386073072413261), + ], + [ + Fp::const_from_raw(6268111131988518030), + Fp::const_from_raw(17920308297240232909), + Fp::const_from_raw(17719152474870950965), + Fp::const_from_raw(14857432101092580778), + Fp::const_from_raw(5708937553833180778), + ], + [ + Fp::const_from_raw(11597726741964198121), + Fp::const_from_raw(1568026444559423552), + Fp::const_from_raw(3233218961458461983), + Fp::const_from_raw(9700509409081014876), + Fp::const_from_raw(7989061413164577390), + ], + [ + Fp::const_from_raw(11180580619692834182), + Fp::const_from_raw(16871004730930134181), + Fp::const_from_raw(17810700669516829599), + Fp::const_from_raw(13679692060051982328), + Fp::const_from_raw(10386085719330760064), + ], + [ + Fp::const_from_raw(6222872143719551583), + Fp::const_from_raw(3842704143974291265), + Fp::const_from_raw(18311432727968603639), + Fp::const_from_raw(12278517700025439333), + Fp::const_from_raw(7011953052853282225), + ], + ]; fn rand_field_element(rng: &mut R) -> Fp { Fp::from(rng.gen::()) } @@ -547,31 +647,27 @@ mod tests { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); let input_sequence: Vec = (0..5).map(Fp::from).collect(); let hash_output = rescue.hash(&input_sequence); - assert_eq!(hash_output.len(), 4); } #[test] + // test ported from https://github.com/0xPolygonMiden/crypto/blob/main/src/hash/rescue/rpo/tests.rs fn hash_padding() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); let input1 = vec![1u8, 2, 3]; let input2 = vec![1u8, 2, 3, 0]; - let hash1 = rescue.hash_bytes(&input1); let hash2 = rescue.hash_bytes(&input2); - assert_ne!(hash1, hash2); let input1 = vec![1_u8, 2, 3, 4, 5, 6]; let input2 = vec![1_u8, 2, 3, 4, 5, 6, 0]; - let hash1 = rescue.hash_bytes(&input1); let hash2 = rescue.hash_bytes(&input2); assert_ne!(hash1, hash2); let input1 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0]; let input2 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0]; - let hash1 = rescue.hash_bytes(&input1); let hash2 = rescue.hash_bytes(&input2); assert_ne!(hash1, hash2); @@ -599,14 +695,6 @@ mod tests { assert_eq!(hash_output.len(), 4); } - #[test] - fn test_instantiate_round_constants() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - - let round_constants = &rescue.round_constants; - assert_eq!(round_constants.len(), 2 * 12 * 7); - } - #[test] fn test_mds_methods_consistency() { let rescue_matrix = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); @@ -632,11 +720,12 @@ mod tests { assert_eq!(hash_matrix, hash_ntt); assert_eq!(hash_ntt, hash_karatsuba); } + /* - Test used to generate the expected hashes for the test vectors (they are the same as in the Polygon Miden implementation) + //Test used to generate the expected hashes for the test vectors (they are the same as in the Polygon Miden implementation) #[test] fn generate_test_vectors() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + let rescue = RescuePrimeOptimized::<160, 7>::new(MdsMethod::MatrixMultiplication); let elements: Vec = (0..19).map(Fp::from).collect(); println!("let expected_hashes = vec!["); @@ -652,7 +741,7 @@ mod tests { }); println!("];"); } - */ + */ #[test] fn test_print_round_constants() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); @@ -664,31 +753,30 @@ mod tests { assert_eq!(rescue.round_constants.len(), 2 * rescue.m * 7); } - /* - #[test] - fn test_hash_vectors() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let elements: Vec = (0..19).map(Fp::from).collect(); - - for (i, expected) in EXPECTED.iter().enumerate() { - let input = &elements[..=i]; // Tomar el prefijo hasta i - let hash_output = rescue.hash(input); - assert_eq!( - hash_output, - *expected, - "Hash mismatch for input length {}", - i + 1 - ); - } - } - */ #[test] fn test_hash_vectors() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); let elements: Vec = (0..19).map(Fp::from).collect(); - EXPECTED.iter().enumerate().for_each(|(i, expected)| { + EXPECTED_128.iter().enumerate().for_each(|(i, expected)| { + let input = elements.iter().take(i + 1); + let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); + + assert_eq!( + hash_output, + *expected, + "Hash mismatch for input length {}", + i + 1 + ); + }); + } + #[test] + fn test_hash_vector_2() { + let rescue = RescuePrimeOptimized::<160, 7>::new(MdsMethod::MatrixMultiplication); + let elements: Vec = (0..19).map(Fp::from).collect(); + + EXPECTED_160.iter().enumerate().for_each(|(i, expected)| { let input = elements.iter().take(i + 1); let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); @@ -721,5 +809,5 @@ mod tests { println!(); assert_eq!(hash_result.len(), 4); } - // this gives the same in Polygon Miden + // Same result obtained in Miden Crypto } diff --git a/crypto/src/hash/rescue_prime/parameters.rs b/crypto/src/hash/rescue_prime/parameters.rs index 19bf29718..a06b988df 100644 --- a/crypto/src/hash/rescue_prime/parameters.rs +++ b/crypto/src/hash/rescue_prime/parameters.rs @@ -5,7 +5,7 @@ pub const ALPHA_INV: u64 = 10540996611094048183; // Constants for the Rescue hash function, these were obtained using the paper implementation in sage // https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation -pub const ROUND_CONSTANTS: [Fp; 168] = [ +pub const ROUND_CONSTANTS_128: [Fp; 168] = [ Fp::const_from_raw(5789762306288267392), Fp::const_from_raw(6522564764413701783), Fp::const_from_raw(17809893479458208203), @@ -175,3 +175,242 @@ pub const ROUND_CONSTANTS: [Fp; 168] = [ Fp::const_from_raw(3611348709641382851), Fp::const_from_raw(18256379591337759196), ]; + +pub const ROUND_CONSTANTS_160: [Fp; 224] = [ + Fp::const_from_raw(1965335827333385572), + Fp::const_from_raw(13386940263093285890), + Fp::const_from_raw(2676433512518024499), + Fp::const_from_raw(3265387569419834752), + Fp::const_from_raw(1983410871005483133), + Fp::const_from_raw(9697282293408698131), + Fp::const_from_raw(1272774544215511539), + Fp::const_from_raw(8206289606243220511), + Fp::const_from_raw(1290391036756663400), + Fp::const_from_raw(18219831014774660739), + Fp::const_from_raw(9691367064095402927), + Fp::const_from_raw(1323942862844130786), + Fp::const_from_raw(15151407902520044968), + Fp::const_from_raw(3367241195349533752), + Fp::const_from_raw(4045613938354522492), + Fp::const_from_raw(8414577515806306591), + Fp::const_from_raw(12735791373473705278), + Fp::const_from_raw(3301196190123345788), + Fp::const_from_raw(4934538150586227609), + Fp::const_from_raw(3817643842607407527), + Fp::const_from_raw(13416431558822898318), + Fp::const_from_raw(5832629091408730901), + Fp::const_from_raw(3362368740314001033), + Fp::const_from_raw(11092906639494490385), + Fp::const_from_raw(6071859273097876791), + Fp::const_from_raw(10161425034618716356), + Fp::const_from_raw(7152209120756903545), + Fp::const_from_raw(16380870469663741149), + Fp::const_from_raw(3952136951542576078), + Fp::const_from_raw(17537441052343611097), + Fp::const_from_raw(11551242553047556263), + Fp::const_from_raw(10106900133850428740), + Fp::const_from_raw(11416650542216810040), + Fp::const_from_raw(11422270812969046329), + Fp::const_from_raw(8866991719313052084), + Fp::const_from_raw(11055863001411088108), + Fp::const_from_raw(6180770262849183127), + Fp::const_from_raw(15065904341621422463), + Fp::const_from_raw(6379231142859676194), + Fp::const_from_raw(12898133478008807755), + Fp::const_from_raw(17022976567648776965), + Fp::const_from_raw(9092326911543756291), + Fp::const_from_raw(6030122978628466915), + Fp::const_from_raw(9597034755157312926), + Fp::const_from_raw(994741965321505508), + Fp::const_from_raw(7556490651023083151), + Fp::const_from_raw(13471961853484783473), + Fp::const_from_raw(5530500298270693480), + Fp::const_from_raw(3138602747749119790), + Fp::const_from_raw(14959768162492908516), + Fp::const_from_raw(9134218270579160311), + Fp::const_from_raw(11526344086740032769), + Fp::const_from_raw(18056157006815181954), + Fp::const_from_raw(6800589288408907691), + Fp::const_from_raw(15936640138392473876), + Fp::const_from_raw(2300163192580995689), + Fp::const_from_raw(4526841916921293676), + Fp::const_from_raw(7195881155996340935), + Fp::const_from_raw(2785483023916634674), + Fp::const_from_raw(15081468567893261932), + Fp::const_from_raw(6614707290651872269), + Fp::const_from_raw(13681365294828420351), + Fp::const_from_raw(10664658542323360702), + Fp::const_from_raw(10084964797450915045), + Fp::const_from_raw(4845198022119750202), + Fp::const_from_raw(2607866667643628253), + Fp::const_from_raw(5208104371714885253), + Fp::const_from_raw(12959011109386888563), + Fp::const_from_raw(4000466944391262442), + Fp::const_from_raw(17728719744160665330), + Fp::const_from_raw(7150641948246037689), + Fp::const_from_raw(9776810486328380322), + Fp::const_from_raw(8402715679168885485), + Fp::const_from_raw(3121448252217290414), + Fp::const_from_raw(17436789549778885163), + Fp::const_from_raw(15165907014487612788), + Fp::const_from_raw(11269595316481578714), + Fp::const_from_raw(9914651255870961898), + Fp::const_from_raw(12689101348845299684), + Fp::const_from_raw(11975655653136929369), + Fp::const_from_raw(7372192115875804252), + Fp::const_from_raw(374526648312709133), + Fp::const_from_raw(5985220408386061330), + Fp::const_from_raw(7185802228951619536), + Fp::const_from_raw(1399294693953396201), + Fp::const_from_raw(3261364014951657316), + Fp::const_from_raw(12077409443637692420), + Fp::const_from_raw(9673650825325087603), + Fp::const_from_raw(5569045552142119082), + Fp::const_from_raw(17617312550416673451), + Fp::const_from_raw(6211450796053144311), + Fp::const_from_raw(11274862073326008409), + Fp::const_from_raw(18367233290057731659), + Fp::const_from_raw(13198876392118957255), + Fp::const_from_raw(13272050586507026767), + Fp::const_from_raw(13010781901687851463), + Fp::const_from_raw(11176896862794321170), + Fp::const_from_raw(6638609153583434674), + Fp::const_from_raw(14505835809704498565), + Fp::const_from_raw(17581684280975726513), + Fp::const_from_raw(699795237352602006), + Fp::const_from_raw(9944038704239459812), + Fp::const_from_raw(8047212797227008956), + Fp::const_from_raw(1395744870455664103), + Fp::const_from_raw(18357515964980248812), + Fp::const_from_raw(9097466431298056431), + Fp::const_from_raw(14710664890151992774), + Fp::const_from_raw(6629781383077611287), + Fp::const_from_raw(17573797615501516970), + Fp::const_from_raw(12347664633647440814), + Fp::const_from_raw(11021709264172808686), + Fp::const_from_raw(10955032358008028206), + Fp::const_from_raw(12827014260928926472), + Fp::const_from_raw(14274600229400487385), + Fp::const_from_raw(12031986599882032134), + Fp::const_from_raw(16154104676212634613), + Fp::const_from_raw(18132152994017433356), + Fp::const_from_raw(15441239634310983499), + Fp::const_from_raw(10976597099491887044), + Fp::const_from_raw(3707145841124002094), + Fp::const_from_raw(8720928559638383045), + Fp::const_from_raw(16336200500310468906), + Fp::const_from_raw(6210805750383775651), + Fp::const_from_raw(7719884621977079797), + Fp::const_from_raw(11449042012956416425), + Fp::const_from_raw(9075619080551251971), + Fp::const_from_raw(617668424765806231), + Fp::const_from_raw(12270348236411784037), + Fp::const_from_raw(6186113401837024523), + Fp::const_from_raw(15458192282022704662), + Fp::const_from_raw(3533646002027882636), + Fp::const_from_raw(7323750725122298699), + Fp::const_from_raw(17370102587019252090), + Fp::const_from_raw(1740987243995377904), + Fp::const_from_raw(10219908189144498973), + Fp::const_from_raw(1822464913426161699), + Fp::const_from_raw(13340330593340428766), + Fp::const_from_raw(11476413915876641735), + Fp::const_from_raw(10301877462024259119), + Fp::const_from_raw(17003473479205724655), + Fp::const_from_raw(10899885430087119072), + Fp::const_from_raw(2161571014943847810), + Fp::const_from_raw(10337649388059569402), + Fp::const_from_raw(1627927149280118935), + Fp::const_from_raw(981019442244479500), + Fp::const_from_raw(8080861373146567887), + Fp::const_from_raw(8033636340692269807), + Fp::const_from_raw(1747076424940820198), + Fp::const_from_raw(15430102639810276278), + Fp::const_from_raw(9286420248392647962), + Fp::const_from_raw(11497964697936588530), + Fp::const_from_raw(17639509337065865628), + Fp::const_from_raw(2160917583540985983), + Fp::const_from_raw(6735220140815683510), + Fp::const_from_raw(6183237619116523957), + Fp::const_from_raw(13347893983048485379), + Fp::const_from_raw(4087545433624195113), + Fp::const_from_raw(11701648626105993864), + Fp::const_from_raw(11913677089736238784), + Fp::const_from_raw(271004950317860287), + Fp::const_from_raw(11794070108002091165), + Fp::const_from_raw(15639064309077629849), + Fp::const_from_raw(16481734838884572560), + Fp::const_from_raw(3932918848577657311), + Fp::const_from_raw(16327200574281469287), + Fp::const_from_raw(7060041503065075033), + Fp::const_from_raw(4892761442718320741), + Fp::const_from_raw(8255275116206368067), + Fp::const_from_raw(14957838536671021552), + Fp::const_from_raw(14493715972468567436), + Fp::const_from_raw(7463718209809697261), + Fp::const_from_raw(3440982266989812843), + Fp::const_from_raw(2354199421703013492), + Fp::const_from_raw(2321628279578256047), + Fp::const_from_raw(3746041501354899488), + Fp::const_from_raw(11186576936873825301), + Fp::const_from_raw(15218587616061641074), + Fp::const_from_raw(11844784525417523222), + Fp::const_from_raw(7998727848169056055), + Fp::const_from_raw(7948968711630609066), + Fp::const_from_raw(11805042600408037937), + Fp::const_from_raw(18172588443872800894), + Fp::const_from_raw(13092373363317372568), + Fp::const_from_raw(2169983441195298580), + Fp::const_from_raw(1499680808057735775), + Fp::const_from_raw(7077486803310915643), + Fp::const_from_raw(743612288630452727), + Fp::const_from_raw(11665426394426065172), + Fp::const_from_raw(15533499373769144802), + Fp::const_from_raw(14249183160150274240), + Fp::const_from_raw(13792290235996127743), + Fp::const_from_raw(4995017088228886738), + Fp::const_from_raw(9763845271226970122), + Fp::const_from_raw(1727820159257625458), + Fp::const_from_raw(9681902124347643227), + Fp::const_from_raw(11327574568051933160), + Fp::const_from_raw(10627429556158481577), + Fp::const_from_raw(13984143774797145216), + Fp::const_from_raw(17082059622058840713), + Fp::const_from_raw(16264233536802058333), + Fp::const_from_raw(10077962488096645822), + Fp::const_from_raw(5057253598123536060), + Fp::const_from_raw(2301672207952647376), + Fp::const_from_raw(17506877517896521554), + Fp::const_from_raw(14583366393971011156), + Fp::const_from_raw(6226877164823354372), + Fp::const_from_raw(2260055134098203623), + Fp::const_from_raw(12945296184826522120), + Fp::const_from_raw(15417698598606677168), + Fp::const_from_raw(7447949755934804788), + Fp::const_from_raw(8017843736725863212), + Fp::const_from_raw(1003688007091182795), + Fp::const_from_raw(8935767355090348282), + Fp::const_from_raw(793319158990348431), + Fp::const_from_raw(4437923789992338287), + Fp::const_from_raw(7869978205237541489), + Fp::const_from_raw(9039403419111053092), + Fp::const_from_raw(3845065612997771849), + Fp::const_from_raw(15179573672801872590), + Fp::const_from_raw(2879645310341005490), + Fp::const_from_raw(4421001170561580576), + Fp::const_from_raw(7614461260369642079), + Fp::const_from_raw(10869617590371203777), + Fp::const_from_raw(4582902440098948914), +]; + +pub enum SecurityLevel { + Sec128, + Sec160, +} + +pub fn get_round_constants(level: SecurityLevel) -> &'static [Fp] { + match level { + SecurityLevel::Sec128 => &ROUND_CONSTANTS_128, + SecurityLevel::Sec160 => &ROUND_CONSTANTS_160, + } +} From ca9d36b6eb0705814adcb1282b4974e4998edaa0 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 16 Oct 2024 15:01:58 -0300 Subject: [PATCH 10/17] fix typos --- crypto/src/hash/rescue_prime/mod.rs | 2 +- crypto/src/hash/rescue_prime/parameters.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index eac4de6db..a66317880 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -730,7 +730,7 @@ mod tests { println!("let expected_hashes = vec!["); elements.iter().enumerate().for_each(|(i, _)| { - let input = elements.iter().take(i + 1); // Tomar el prefijo hasta i + 1 + let input = elements.iter().take(i + 1); let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); print!(" vec!["); diff --git a/crypto/src/hash/rescue_prime/parameters.rs b/crypto/src/hash/rescue_prime/parameters.rs index a06b988df..4c8b9ffae 100644 --- a/crypto/src/hash/rescue_prime/parameters.rs +++ b/crypto/src/hash/rescue_prime/parameters.rs @@ -2,7 +2,7 @@ use super::Fp; pub const ALPHA: u64 = 7; pub const ALPHA_INV: u64 = 10540996611094048183; -// Constants for the Rescue hash function, these were obtained using the paper implementation in sage +// Constants obtained using the paper implementation in Sage // https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation pub const ROUND_CONSTANTS_128: [Fp; 168] = [ From cabe504e5214e06b53ec52c08f974e12c55e2bf9 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 16 Oct 2024 15:23:48 -0300 Subject: [PATCH 11/17] fix clippy and wasm --- crypto/src/hash/rescue_prime/mod.rs | 10 ++++++---- crypto/src/hash/rescue_prime/utils.rs | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index a66317880..ec459ac4a 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -1,3 +1,5 @@ +use crate::alloc::vec::Vec; +use core::iter; use lambdaworks_math::field::element::FieldElement; use lambdaworks_math::field::fields::u64_goldilocks_field::Goldilocks64Field; @@ -60,7 +62,7 @@ impl m, capacity, rate, - round_constants: round_constants.to_vec().try_into().unwrap(), + round_constants: round_constants.to_vec(), mds_matrix, mds_vector, mds_method, @@ -132,7 +134,7 @@ impl }; let mds_ntt = ntt(&self.mds_vector, omega); - let state_rev: Vec = std::iter::once(state[0]) + let state_rev: Vec = iter::once(state[0]) .chain(state[1..].iter().rev().cloned()) .collect(); let state_ntt = ntt(&state_rev, omega); @@ -145,14 +147,14 @@ impl let omega_inv = omega.inv().unwrap(); let result = intt(&product_ntt, omega_inv); - std::iter::once(result[0]) + iter::once(result[0]) .chain(result[1..].iter().rev().cloned()) .collect() } fn mds_karatsuba(&self, state: &[Fp]) -> Vec { let m = state.len(); - let mds_rev: Vec = std::iter::once(self.mds_vector[0]) + let mds_rev: Vec = iter::once(self.mds_vector[0]) .chain(self.mds_vector[1..].iter().rev().cloned()) .collect(); diff --git a/crypto/src/hash/rescue_prime/utils.rs b/crypto/src/hash/rescue_prime/utils.rs index cba786900..04b386921 100644 --- a/crypto/src/hash/rescue_prime/utils.rs +++ b/crypto/src/hash/rescue_prime/utils.rs @@ -1,4 +1,5 @@ use super::Fp; +use alloc::vec::Vec; pub fn bytes_to_field_elements(input: &[u8]) -> Vec { input From fd293b22db6d132f777afe63e2a61bcf2fbcf5bd Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 16 Oct 2024 15:35:18 -0300 Subject: [PATCH 12/17] add std feature when needed --- crypto/src/hash/rescue_prime/mod.rs | 59 +++++++++++++++-------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index ec459ac4a..160340f63 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -674,7 +674,7 @@ mod tests { let hash2 = rescue.hash_bytes(&input2); assert_ne!(hash1, hash2); } - + #[cfg(feature = "std")] #[test] fn sponge_zeroes_collision() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); @@ -724,38 +724,38 @@ mod tests { } /* - //Test used to generate the expected hashes for the test vectors (they are the same as in the Polygon Miden implementation) - #[test] - fn generate_test_vectors() { - let rescue = RescuePrimeOptimized::<160, 7>::new(MdsMethod::MatrixMultiplication); - let elements: Vec = (0..19).map(Fp::from).collect(); - - println!("let expected_hashes = vec!["); - elements.iter().enumerate().for_each(|(i, _)| { - let input = elements.iter().take(i + 1); - let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); - - print!(" vec!["); - hash_output.iter().for_each(|value| { - print!("Fp::from({}u64), ", value.value()); + //Test used to generate the expected hashes for the test vectors (they are the same as in the Polygon Miden implementation) + #[test] + fn generate_test_vectors() { + let rescue = RescuePrimeOptimized::<160, 7>::new(MdsMethod::MatrixMultiplication); + let elements: Vec = (0..19).map(Fp::from).collect(); + + println!("let expected_hashes = vec!["); + elements.iter().enumerate().for_each(|(i, _)| { + let input = elements.iter().take(i + 1); + let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); + + print!(" vec!["); + hash_output.iter().for_each(|value| { + print!("Fp::from({}u64), ", value.value()); + }); + println!("],"); }); - println!("],"); - }); - println!("];"); - } - */ - #[test] - fn test_print_round_constants() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + println!("];"); + } - println!("Round constants:"); - for (i, constant) in rescue.round_constants.iter().enumerate() { - println!("Constant {}: Fp::from({}u64)", i, constant.value()); - } + #[test] + fn test_print_round_constants() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - assert_eq!(rescue.round_constants.len(), 2 * rescue.m * 7); - } + println!("Round constants:"); + for (i, constant) in rescue.round_constants.iter().enumerate() { + println!("Constant {}: Fp::from({}u64)", i, constant.value()); + } + assert_eq!(rescue.round_constants.len(), 2 * rescue.m * 7); + } + */ #[test] fn test_hash_vectors() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); @@ -790,6 +790,7 @@ mod tests { ); }); } + #[cfg(feature = "std")] #[test] fn test_hash_example_and_print() { let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Ntt); From cbd20dd178e489097dd359519a63f478ef09b78d Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Sun, 20 Oct 2024 22:08:07 -0300 Subject: [PATCH 13/17] save work, refactor and solved some comments --- crypto/Cargo.toml | 2 +- crypto/src/hash/rescue_prime/mds_method.rs | 10 + crypto/src/hash/rescue_prime/mod.rs | 818 +--------------- crypto/src/hash/rescue_prime/parameters.rs | 534 +++++++++++ .../rescue_prime/rescue_prime_optimized.rs | 882 ++++++++++++++++++ crypto/src/hash/rescue_prime/utils.rs | 14 +- 6 files changed, 1447 insertions(+), 813 deletions(-) create mode 100644 crypto/src/hash/rescue_prime/mds_method.rs create mode 100644 crypto/src/hash/rescue_prime/rescue_prime_optimized.rs diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index 463fedee6..45ec2caa6 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -23,7 +23,7 @@ criterion = "0.4" iai-callgrind.workspace = true rand = "0.8.5" rand_chacha = "0.3.1" -proptest = "1.5.0" + [features] default = ["asm", "std"] asm = ["sha3/asm"] diff --git a/crypto/src/hash/rescue_prime/mds_method.rs b/crypto/src/hash/rescue_prime/mds_method.rs new file mode 100644 index 000000000..6fd19909a --- /dev/null +++ b/crypto/src/hash/rescue_prime/mds_method.rs @@ -0,0 +1,10 @@ +#[derive(Clone)] +#[allow(dead_code)] +pub enum MdsMethod { + /// Use standard matrix multiplication. + MatrixMultiplication, + /// Use Number Theoretic Transform for multiplication. + Ntt, + /// Use Karatsuba algorithm for multiplication. + Karatsuba, +} diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index 160340f63..35a76a580 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -1,816 +1,12 @@ -use crate::alloc::vec::Vec; -use core::iter; -use lambdaworks_math::field::element::FieldElement; -use lambdaworks_math::field::fields::u64_goldilocks_field::Goldilocks64Field; - +mod mds_method; mod parameters; +mod rescue_prime_optimized; mod utils; -use parameters::*; -use utils::*; - -type Fp = FieldElement; - -#[derive(Clone)] -#[allow(dead_code)] -enum MdsMethod { - MatrixMultiplication, - Ntt, - Karatsuba, -} -#[warn(dead_code)] -pub struct RescuePrimeOptimized { - m: usize, - capacity: usize, - rate: usize, - round_constants: Vec, - mds_matrix: Vec>, - mds_vector: Vec, - mds_method: MdsMethod, -} - -impl Default - for RescuePrimeOptimized -{ - fn default() -> Self { - Self::new(MdsMethod::MatrixMultiplication) - } -} - -impl - RescuePrimeOptimized -{ - fn new(mds_method: MdsMethod) -> Self { - assert!(SECURITY_LEVEL == 128 || SECURITY_LEVEL == 160); - - let (m, capacity) = if SECURITY_LEVEL == 128 { - (12, 4) - } else { - (16, 6) - }; - let rate = m - capacity; - - let mds_vector = Self::get_mds_vector(m); - let mds_matrix = Self::generate_circulant_matrix(&mds_vector); - let round_constants = match SECURITY_LEVEL { - 128 => get_round_constants(SecurityLevel::Sec128), - 160 => get_round_constants(SecurityLevel::Sec160), - _ => panic!("Unsupported security level"), - }; - - Self { - m, - capacity, - rate, - round_constants: round_constants.to_vec(), - mds_matrix, - mds_vector, - mds_method, - } - } - - pub fn apply_inverse_sbox(state: &mut [Fp]) { - for x in state.iter_mut() { - *x = x.pow(ALPHA_INV); - } - } - - fn get_mds_vector(m: usize) -> Vec { - match m { - 12 => vec![7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8] - .into_iter() - .map(Fp::from) - .collect(), - 16 => vec![ - 256, 2, 1073741824, 2048, 16777216, 128, 8, 16, 524288, 4194304, 1, 268435456, 1, - 1024, 2, 8192, - ] - .into_iter() - .map(Fp::from) - .collect(), - _ => panic!("Unsupported state size"), - } - } - - fn generate_circulant_matrix(mds_vector: &[Fp]) -> Vec> { - let m = mds_vector.len(); - (0..m) - .map(|i| { - mds_vector - .iter() - .cycle() - .skip(m - i) - .take(m) - .cloned() - .collect::>() - }) - .collect() - } - - pub fn apply_sbox(state: &mut [Fp]) { - for x in state.iter_mut() { - *x = x.pow(ALPHA); - } - } - - fn mds_matrix_vector_multiplication(&self, state: &[Fp]) -> Vec { - let m = state.len(); - let mut new_state = vec![Fp::zero(); m]; - - for (i, new_value) in new_state.iter_mut().enumerate() { - for (j, state_value) in state.iter().enumerate() { - *new_value += self.mds_matrix[i][j] * state_value; - } - } - - new_state - } - fn mds_ntt(&self, state: &[Fp]) -> Vec { - let m = state.len(); - let omega = match m { - 12 => Fp::from(281474976645120u64), - 16 => Fp::from(17293822564807737345u64), - _ => panic!("Unsupported state size for NTT"), - }; - - let mds_ntt = ntt(&self.mds_vector, omega); - let state_rev: Vec = iter::once(state[0]) - .chain(state[1..].iter().rev().cloned()) - .collect(); - let state_ntt = ntt(&state_rev, omega); - - let mut product_ntt = vec![Fp::zero(); m]; - for i in 0..m { - product_ntt[i] = mds_ntt[i] * state_ntt[i]; - } - - let omega_inv = omega.inv().unwrap(); - let result = intt(&product_ntt, omega_inv); - - iter::once(result[0]) - .chain(result[1..].iter().rev().cloned()) - .collect() - } - - fn mds_karatsuba(&self, state: &[Fp]) -> Vec { - let m = state.len(); - let mds_rev: Vec = iter::once(self.mds_vector[0]) - .chain(self.mds_vector[1..].iter().rev().cloned()) - .collect(); - - let conv = karatsuba(&mds_rev, state); - - let mut result = vec![Fp::zero(); m]; - result[..m].copy_from_slice(&conv[..m]); - for i in m..conv.len() { - result[i - m] += conv[i]; - } - - result - } - - fn apply_mds(&self, state: &mut [Fp]) { - let new_state = match self.mds_method { - MdsMethod::MatrixMultiplication => self.mds_matrix_vector_multiplication(state), - MdsMethod::Ntt => self.mds_ntt(state), - MdsMethod::Karatsuba => self.mds_karatsuba(state), - }; - state.copy_from_slice(&new_state); - } - - fn add_round_constants(&self, state: &mut [Fp], round: usize) { - let m = self.m; - let round_constants = &self.round_constants; - for j in 0..m { - state[j] += round_constants[round * 2 * m + j]; - } - } - - fn add_round_constants_second(&self, state: &mut [Fp], round: usize) { - let m = self.m; - let round_constants = &self.round_constants; - for j in 0..m { - state[j] += round_constants[round * 2 * m + m + j] - } - } - - pub fn permutation(&self, state: &mut [Fp]) { - let num_rounds = NUM_FULL_ROUNDS; - for round in 0..num_rounds { - self.apply_mds(state); - self.add_round_constants(state, round); - Self::apply_sbox(state); - self.apply_mds(state); - self.add_round_constants_second(state, round); - Self::apply_inverse_sbox(state); - } - } - - pub fn hash(&self, input_sequence: &[Fp]) -> Vec { - let m = self.m; - let capacity = self.capacity; - let rate = self.rate; - let mut state = vec![Fp::zero(); m]; - let input_len = input_sequence.len(); - if input_len % rate != 0 { - state[0] = Fp::one(); - } - let num_full_chunks = input_len / rate; - for i in 0..num_full_chunks { - let chunk = &input_sequence[i * rate..(i + 1) * rate]; - state[capacity..(rate + capacity)].copy_from_slice(&chunk[..rate]); - self.permutation(&mut state); - } - let last_chunk_size = input_len % rate; - if last_chunk_size != 0 { - let mut last_chunk = vec![Fp::zero(); rate]; - for j in 0..last_chunk_size { - last_chunk[j] = input_sequence[num_full_chunks * rate + j]; - } - last_chunk[last_chunk_size] = Fp::one(); - state[capacity..(rate + capacity)].copy_from_slice(&last_chunk[..rate]); - self.permutation(&mut state); - } - - state[capacity..capacity + rate / 2].to_vec() - } - - pub fn hash_bytes(&self, input: &[u8]) -> Vec { - let field_elements = bytes_to_field_elements(input); - self.hash(&field_elements) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use rand::rngs::StdRng; - use rand::{Rng, SeedableRng}; - - // Values obtained using the Sage implementation in - // https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation - pub const EXPECTED_128: [[Fp; 4]; 19] = [ - [ - Fp::const_from_raw(1502364727743950833u64), - Fp::const_from_raw(5880949717274681448u64), - Fp::const_from_raw(162790463902224431u64), - Fp::const_from_raw(6901340476773664264u64), - ], - [ - Fp::const_from_raw(7478710183745780580u64), - Fp::const_from_raw(3308077307559720969u64), - Fp::const_from_raw(3383561985796182409u64), - Fp::const_from_raw(17205078494700259815u64), - ], - [ - Fp::const_from_raw(17439912364295172999u64), - Fp::const_from_raw(17979156346142712171u64), - Fp::const_from_raw(8280795511427637894u64), - Fp::const_from_raw(9349844417834368814u64), - ], - [ - Fp::const_from_raw(5105868198472766874u64), - Fp::const_from_raw(13090564195691924742u64), - Fp::const_from_raw(1058904296915798891u64), - Fp::const_from_raw(18379501748825152268u64), - ], - [ - Fp::const_from_raw(9133662113608941286u64), - Fp::const_from_raw(12096627591905525991u64), - Fp::const_from_raw(14963426595993304047u64), - Fp::const_from_raw(13290205840019973377u64), - ], - [ - Fp::const_from_raw(3134262397541159485u64), - Fp::const_from_raw(10106105871979362399u64), - Fp::const_from_raw(138768814855329459u64), - Fp::const_from_raw(15044809212457404677u64), - ], - [ - Fp::const_from_raw(162696376578462826u64), - Fp::const_from_raw(4991300494838863586u64), - Fp::const_from_raw(660346084748120605u64), - Fp::const_from_raw(13179389528641752698u64), - ], - [ - Fp::const_from_raw(2242391899857912644u64), - Fp::const_from_raw(12689382052053305418u64), - Fp::const_from_raw(235236990017815546u64), - Fp::const_from_raw(5046143039268215739u64), - ], - [ - Fp::const_from_raw(9585630502158073976u64), - Fp::const_from_raw(1310051013427303477u64), - Fp::const_from_raw(7491921222636097758u64), - Fp::const_from_raw(9417501558995216762u64), - ], - [ - Fp::const_from_raw(1994394001720334744u64), - Fp::const_from_raw(10866209900885216467u64), - Fp::const_from_raw(13836092831163031683u64), - Fp::const_from_raw(10814636682252756697u64), - ], - [ - Fp::const_from_raw(17486854790732826405u64), - Fp::const_from_raw(17376549265955727562u64), - Fp::const_from_raw(2371059831956435003u64), - Fp::const_from_raw(17585704935858006533u64), - ], - [ - Fp::const_from_raw(11368277489137713825u64), - Fp::const_from_raw(3906270146963049287u64), - Fp::const_from_raw(10236262408213059745u64), - Fp::const_from_raw(78552867005814007u64), - ], - [ - Fp::const_from_raw(17899847381280262181u64), - Fp::const_from_raw(14717912805498651446u64), - Fp::const_from_raw(10769146203951775298u64), - Fp::const_from_raw(2774289833490417856u64), - ], - [ - Fp::const_from_raw(3794717687462954368u64), - Fp::const_from_raw(4386865643074822822u64), - Fp::const_from_raw(8854162840275334305u64), - Fp::const_from_raw(7129983987107225269u64), - ], - [ - Fp::const_from_raw(7244773535611633983u64), - Fp::const_from_raw(19359923075859320u64), - Fp::const_from_raw(10898655967774994333u64), - Fp::const_from_raw(9319339563065736480u64), - ], - [ - Fp::const_from_raw(4935426252518736883u64), - Fp::const_from_raw(12584230452580950419u64), - Fp::const_from_raw(8762518969632303998u64), - Fp::const_from_raw(18159875708229758073u64), - ], - [ - Fp::const_from_raw(14871230873837295931u64), - Fp::const_from_raw(11225255908868362971u64), - Fp::const_from_raw(18100987641405432308u64), - Fp::const_from_raw(1559244340089644233u64), - ], - [ - Fp::const_from_raw(8348203744950016968u64), - Fp::const_from_raw(4041411241960726733u64), - Fp::const_from_raw(17584743399305468057u64), - Fp::const_from_raw(16836952610803537051u64), - ], - [ - Fp::const_from_raw(16139797453633030050u64), - Fp::const_from_raw(1090233424040889412u64), - Fp::const_from_raw(10770255347785669036u64), - Fp::const_from_raw(16982398877290254028u64), - ], - ]; - - pub const EXPECTED_160: [[Fp; 5]; 19] = [ - [ - Fp::const_from_raw(4766737105427868572), - Fp::const_from_raw(7538777753317835226), - Fp::const_from_raw(13644171984579649606), - Fp::const_from_raw(6748107971891460622), - Fp::const_from_raw(3480072938342119934), - ], - [ - Fp::const_from_raw(6277287777617382937), - Fp::const_from_raw(5688033921803605355), - Fp::const_from_raw(1104978478612014217), - Fp::const_from_raw(973672476085279574), - Fp::const_from_raw(7883652116413797779), - ], - [ - Fp::const_from_raw(3071553803427093579), - Fp::const_from_raw(12239501990998925662), - Fp::const_from_raw(14411295652479845526), - Fp::const_from_raw(5735407824213194294), - Fp::const_from_raw(6714816738691504270), - ], - [ - Fp::const_from_raw(4455998568145007624), - Fp::const_from_raw(18218360213084301612), - Fp::const_from_raw(8963555484142424669), - Fp::const_from_raw(13451196299356019287), - Fp::const_from_raw(660967320761434775), - ], - [ - Fp::const_from_raw(7894041400531553560), - Fp::const_from_raw(3138084719322472990), - Fp::const_from_raw(15017675162298246509), - Fp::const_from_raw(12340633143623038238), - Fp::const_from_raw(3710158928968726190), - ], - [ - Fp::const_from_raw(18345924309197503617), - Fp::const_from_raw(6448668044176965096), - Fp::const_from_raw(5891298758878861437), - Fp::const_from_raw(18404292940273103487), - Fp::const_from_raw(399715742058360811), - ], - [ - Fp::const_from_raw(4293522863608749708), - Fp::const_from_raw(11352999694211746044), - Fp::const_from_raw(15850245073570756600), - Fp::const_from_raw(1206950096837096206), - Fp::const_from_raw(6945598368659615878), - ], - [ - Fp::const_from_raw(1339949574743034442), - Fp::const_from_raw(5967452101017112419), - Fp::const_from_raw(824612579975542151), - Fp::const_from_raw(3327557828938393394), - Fp::const_from_raw(14113149399665697150), - ], - [ - Fp::const_from_raw(3540904694808418824), - Fp::const_from_raw(5951416386790014715), - Fp::const_from_raw(13859113410786779774), - Fp::const_from_raw(17205554479494520251), - Fp::const_from_raw(7359323608260195110), - ], - [ - Fp::const_from_raw(7504301802792161339), - Fp::const_from_raw(12879743137663115497), - Fp::const_from_raw(17245986604042562042), - Fp::const_from_raw(8175050867418132561), - Fp::const_from_raw(1063965910664731268), - ], - [ - Fp::const_from_raw(18267475461736255602), - Fp::const_from_raw(4481864641736940956), - Fp::const_from_raw(11260039501101148638), - Fp::const_from_raw(7529970948767692955), - Fp::const_from_raw(4177810888704753150), - ], - [ - Fp::const_from_raw(16604116128892623566), - Fp::const_from_raw(1520851983040290492), - Fp::const_from_raw(9361704524730297620), - Fp::const_from_raw(7447748879766268839), - Fp::const_from_raw(10834422028571028806), - ], - [ - Fp::const_from_raw(243957224918814907), - Fp::const_from_raw(9966149007214472697), - Fp::const_from_raw(18130816682404489504), - Fp::const_from_raw(3814760895598122151), - Fp::const_from_raw(862573500652233787), - ], - [ - Fp::const_from_raw(13414343823130474877), - Fp::const_from_raw(1002887112060795246), - Fp::const_from_raw(16685735965176892618), - Fp::const_from_raw(16172309857128312555), - Fp::const_from_raw(5158081519803147178), - ], - [ - Fp::const_from_raw(14614132925482133961), - Fp::const_from_raw(7618082792229868740), - Fp::const_from_raw(1881720834768448253), - Fp::const_from_raw(11508391877383996679), - Fp::const_from_raw(5348386073072413261), - ], - [ - Fp::const_from_raw(6268111131988518030), - Fp::const_from_raw(17920308297240232909), - Fp::const_from_raw(17719152474870950965), - Fp::const_from_raw(14857432101092580778), - Fp::const_from_raw(5708937553833180778), - ], - [ - Fp::const_from_raw(11597726741964198121), - Fp::const_from_raw(1568026444559423552), - Fp::const_from_raw(3233218961458461983), - Fp::const_from_raw(9700509409081014876), - Fp::const_from_raw(7989061413164577390), - ], - [ - Fp::const_from_raw(11180580619692834182), - Fp::const_from_raw(16871004730930134181), - Fp::const_from_raw(17810700669516829599), - Fp::const_from_raw(13679692060051982328), - Fp::const_from_raw(10386085719330760064), - ], - [ - Fp::const_from_raw(6222872143719551583), - Fp::const_from_raw(3842704143974291265), - Fp::const_from_raw(18311432727968603639), - Fp::const_from_raw(12278517700025439333), - Fp::const_from_raw(7011953052853282225), - ], - ]; - fn rand_field_element(rng: &mut R) -> Fp { - Fp::from(rng.gen::()) - } - - #[test] - fn test_apply_sbox() { - let mut rng = StdRng::seed_from_u64(1); - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let mut state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let mut expected = state.clone(); - expected.iter_mut().for_each(|v| *v = v.pow(ALPHA)); - - RescuePrimeOptimized::<128, 7>::apply_sbox(&mut state); - assert_eq!(expected, state); - } - - #[test] - fn test_apply_inverse_sbox() { - let mut rng = StdRng::seed_from_u64(2); - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let mut state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let mut expected = state.clone(); - expected.iter_mut().for_each(|v| *v = v.pow(ALPHA_INV)); - - RescuePrimeOptimized::<128, 7>::apply_inverse_sbox(&mut state); - assert_eq!(expected, state); - } - - #[test] - fn test_mds_matrix_multiplication() { - let mut rng = StdRng::seed_from_u64(3); - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let expected_state = rescue.mds_matrix_vector_multiplication(&state); - let mut computed_state = state.clone(); - rescue.apply_mds(&mut computed_state); - - assert_eq!(expected_state, computed_state); - } +pub use mds_method::MdsMethod; +pub use rescue_prime_optimized::RescuePrimeOptimized; - #[test] - fn test_mds_ntt() { - let mut rng = StdRng::seed_from_u64(4); - let rescue_ntt = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Ntt); - let state: Vec = (0..rescue_ntt.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let expected_state = rescue_ntt.mds_ntt(&state); - let mut computed_state = state.clone(); - rescue_ntt.apply_mds(&mut computed_state); - - assert_eq!(expected_state, computed_state); - } - - #[test] - fn test_mds_karatsuba() { - let mut rng = StdRng::seed_from_u64(5); - let rescue_karatsuba = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Karatsuba); - let state: Vec = (0..rescue_karatsuba.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let expected_state = rescue_karatsuba.mds_karatsuba(&state); - let mut computed_state = state.clone(); - rescue_karatsuba.apply_mds(&mut computed_state); - - assert_eq!(expected_state, computed_state); - } - - #[test] - fn test_add_round_constants() { - let mut rng = StdRng::seed_from_u64(6); - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let mut state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let round = 0; - let expected_state = state - .iter() - .enumerate() - .map(|(i, &x)| x + rescue.round_constants[round * 2 * rescue.m + i]) - .collect::>(); - - rescue.add_round_constants(&mut state, round); - - assert_eq!(expected_state, state); - } - - #[test] - fn test_permutation() { - let mut rng = StdRng::seed_from_u64(7); - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let mut state: Vec = (0..rescue.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let expected_state = { - let mut temp_state = state.clone(); - for round in 0..7 { - rescue.apply_mds(&mut temp_state); - rescue.add_round_constants(&mut temp_state, round); - RescuePrimeOptimized::<128, 7>::apply_sbox(&mut temp_state); - rescue.apply_mds(&mut temp_state); - rescue.add_round_constants_second(&mut temp_state, round); - RescuePrimeOptimized::<128, 7>::apply_inverse_sbox(&mut temp_state); - } - temp_state - }; - - rescue.permutation(&mut state); - - assert_eq!(expected_state, state); - } - - #[test] - fn test_hash_single_chunk() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let input_sequence: Vec = (0..8).map(Fp::from).collect(); - let hash_output = rescue.hash(&input_sequence); - - assert_eq!(hash_output.len(), 4); - } - - #[test] - fn test_hash_multiple_chunks() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let input_sequence: Vec = (0..16).map(Fp::from).collect(); // Two chunks of size 8 - let hash_output = rescue.hash(&input_sequence); - - assert_eq!(hash_output.len(), 4); - } - - #[test] - fn test_hash_with_padding() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let input_sequence: Vec = (0..5).map(Fp::from).collect(); - let hash_output = rescue.hash(&input_sequence); - assert_eq!(hash_output.len(), 4); - } - #[test] - // test ported from https://github.com/0xPolygonMiden/crypto/blob/main/src/hash/rescue/rpo/tests.rs - fn hash_padding() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - - let input1 = vec![1u8, 2, 3]; - let input2 = vec![1u8, 2, 3, 0]; - let hash1 = rescue.hash_bytes(&input1); - let hash2 = rescue.hash_bytes(&input2); - assert_ne!(hash1, hash2); - - let input1 = vec![1_u8, 2, 3, 4, 5, 6]; - let input2 = vec![1_u8, 2, 3, 4, 5, 6, 0]; - let hash1 = rescue.hash_bytes(&input1); - let hash2 = rescue.hash_bytes(&input2); - assert_ne!(hash1, hash2); - - let input1 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0]; - let input2 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0]; - let hash1 = rescue.hash_bytes(&input1); - let hash2 = rescue.hash_bytes(&input2); - assert_ne!(hash1, hash2); - } - #[cfg(feature = "std")] - #[test] - fn sponge_zeroes_collision() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - - let mut zeroes = Vec::new(); - let mut hashes = std::collections::HashSet::new(); - - for _ in 0..255 { - let hash = rescue.hash(&zeroes); - assert!(hashes.insert(hash)); - zeroes.push(Fp::zero()); - } - } - #[test] - fn test_hash_bytes() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let input_bytes = b"Rescue Prime Optimized"; - let hash_output = rescue.hash_bytes(input_bytes); - - assert_eq!(hash_output.len(), 4); - } - - #[test] - fn test_mds_methods_consistency() { - let rescue_matrix = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let rescue_ntt = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Ntt); - let rescue_karatsuba = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Karatsuba); - - let input = vec![ - Fp::from(1u64), - Fp::from(2u64), - Fp::from(3u64), - Fp::from(4u64), - Fp::from(5u64), - Fp::from(6u64), - Fp::from(7u64), - Fp::from(8u64), - Fp::from(9u64), - ]; - - let hash_matrix = rescue_matrix.hash(&input); - let hash_ntt = rescue_ntt.hash(&input); - let hash_karatsuba = rescue_karatsuba.hash(&input); - - assert_eq!(hash_matrix, hash_ntt); - assert_eq!(hash_ntt, hash_karatsuba); - } - - /* - //Test used to generate the expected hashes for the test vectors (they are the same as in the Polygon Miden implementation) - #[test] - fn generate_test_vectors() { - let rescue = RescuePrimeOptimized::<160, 7>::new(MdsMethod::MatrixMultiplication); - let elements: Vec = (0..19).map(Fp::from).collect(); - - println!("let expected_hashes = vec!["); - elements.iter().enumerate().for_each(|(i, _)| { - let input = elements.iter().take(i + 1); - let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); - - print!(" vec!["); - hash_output.iter().for_each(|value| { - print!("Fp::from({}u64), ", value.value()); - }); - println!("],"); - }); - println!("];"); - } - - #[test] - fn test_print_round_constants() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - - println!("Round constants:"); - for (i, constant) in rescue.round_constants.iter().enumerate() { - println!("Constant {}: Fp::from({}u64)", i, constant.value()); - } - - assert_eq!(rescue.round_constants.len(), 2 * rescue.m * 7); - } - */ - #[test] - fn test_hash_vectors() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - let elements: Vec = (0..19).map(Fp::from).collect(); - - EXPECTED_128.iter().enumerate().for_each(|(i, expected)| { - let input = elements.iter().take(i + 1); - let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); - - assert_eq!( - hash_output, - *expected, - "Hash mismatch for input length {}", - i + 1 - ); - }); - } - #[test] - fn test_hash_vector_2() { - let rescue = RescuePrimeOptimized::<160, 7>::new(MdsMethod::MatrixMultiplication); - let elements: Vec = (0..19).map(Fp::from).collect(); - - EXPECTED_160.iter().enumerate().for_each(|(i, expected)| { - let input = elements.iter().take(i + 1); - let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); - - assert_eq!( - hash_output, - *expected, - "Hash mismatch for input length {}", - i + 1 - ); - }); - } - #[cfg(feature = "std")] - #[test] - fn test_hash_example_and_print() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::Ntt); - - let input = b"Hello there"; - - let hash_result = rescue.hash_bytes(input); - - println!("Input: {:?}", input); - println!("Hash result:"); - for (i, value) in hash_result.iter().enumerate() { - println!(" {}: {}", i, value); - } +use lambdaworks_math::field::element::FieldElement; +use lambdaworks_math::field::fields::u64_goldilocks_field::Goldilocks64Field; - println!("Hash as u64 values:"); - for value in hash_result.iter() { - print!("{}, ", value.value()); - } - println!(); - assert_eq!(hash_result.len(), 4); - } - // Same result obtained in Miden Crypto -} +pub type Fp = FieldElement; diff --git a/crypto/src/hash/rescue_prime/parameters.rs b/crypto/src/hash/rescue_prime/parameters.rs index 4c8b9ffae..a10391604 100644 --- a/crypto/src/hash/rescue_prime/parameters.rs +++ b/crypto/src/hash/rescue_prime/parameters.rs @@ -5,6 +5,21 @@ pub const ALPHA_INV: u64 = 10540996611094048183; // Constants obtained using the paper implementation in Sage // https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation +// Constants for the 128-bit security level +pub const MDS_VECTOR_128: [Fp; 12] = [ + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), +]; pub const ROUND_CONSTANTS_128: [Fp; 168] = [ Fp::const_from_raw(5789762306288267392), Fp::const_from_raw(6522564764413701783), @@ -176,6 +191,198 @@ pub const ROUND_CONSTANTS_128: [Fp; 168] = [ Fp::const_from_raw(18256379591337759196), ]; +pub const MDS_MATRIX_128: [[Fp; 12]; 12] = [ + [ + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + ], + [ + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + ], + [ + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + ], + [ + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + ], + [ + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + ], + [ + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + ], + [ + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + ], + [ + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + ], + [ + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + ], + [ + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + Fp::const_from_raw(8), + ], + [ + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + Fp::const_from_raw(23), + ], + [ + Fp::const_from_raw(23), + Fp::const_from_raw(8), + Fp::const_from_raw(26), + Fp::const_from_raw(13), + Fp::const_from_raw(10), + Fp::const_from_raw(9), + Fp::const_from_raw(7), + Fp::const_from_raw(6), + Fp::const_from_raw(22), + Fp::const_from_raw(21), + Fp::const_from_raw(8), + Fp::const_from_raw(7), + ], +]; + +// Constants for the 160-bit security level + +pub const MDS_VECTOR_160: [Fp; 16] = [ + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), +]; + pub const ROUND_CONSTANTS_160: [Fp; 224] = [ Fp::const_from_raw(1965335827333385572), Fp::const_from_raw(13386940263093285890), @@ -403,14 +610,341 @@ pub const ROUND_CONSTANTS_160: [Fp; 224] = [ Fp::const_from_raw(4582902440098948914), ]; +pub const MDS_MATRIX_160: [[Fp; 16]; 16] = [ + [ + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + ], + [ + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + ], + [ + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + ], + [ + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + ], + [ + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + ], + [ + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + ], + [ + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + ], + [ + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + ], + [ + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + ], + [ + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + ], + [ + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + ], + [ + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + ], + [ + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + ], + [ + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + ], + [ + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + Fp::const_from_raw(2), + ], + [ + Fp::const_from_raw(2), + Fp::const_from_raw(1073741824), + Fp::const_from_raw(2048), + Fp::const_from_raw(16777216), + Fp::const_from_raw(128), + Fp::const_from_raw(8), + Fp::const_from_raw(16), + Fp::const_from_raw(524288), + Fp::const_from_raw(4194304), + Fp::const_from_raw(1), + Fp::const_from_raw(268435456), + Fp::const_from_raw(1), + Fp::const_from_raw(1024), + Fp::const_from_raw(2), + Fp::const_from_raw(8192), + Fp::const_from_raw(256), + ], +]; + pub enum SecurityLevel { Sec128, Sec160, } +// Here if I use [Fp] i get error abpout two return values +// if pub fn get_round_constants(level: SecurityLevel) -> &'static [Fp] { match level { SecurityLevel::Sec128 => &ROUND_CONSTANTS_128, SecurityLevel::Sec160 => &ROUND_CONSTANTS_160, } } + +pub enum MdsVector { + Mds128([Fp; 12]), + Mds160([Fp; 16]), +} + +pub fn get_mds_vector(level: SecurityLevel) -> MdsVector { + match level { + SecurityLevel::Sec128 => MdsVector::Mds128(MDS_VECTOR_128), + SecurityLevel::Sec160 => MdsVector::Mds160(MDS_VECTOR_160), + } +} + +#[allow(clippy::large_enum_variant)] +pub enum MdsMatrix { + Mds128([[Fp; 12]; 12]), + Mds160([[Fp; 16]; 16]), +} + +pub fn get_mds_matrix(level: SecurityLevel) -> MdsMatrix { + match level { + SecurityLevel::Sec128 => MdsMatrix::Mds128(MDS_MATRIX_128), + SecurityLevel::Sec160 => MdsMatrix::Mds160(MDS_MATRIX_160), + } +} + +impl MdsVector { + pub fn as_slice(&self) -> &[Fp] { + match self { + MdsVector::Mds128(ref vec) => vec, + MdsVector::Mds160(ref vec) => vec, + } + } +} diff --git a/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs new file mode 100644 index 000000000..d3dc9b0f9 --- /dev/null +++ b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs @@ -0,0 +1,882 @@ +use super::parameters::*; +use super::utils::*; +use super::Fp; +use super::MdsMethod; +use crate::alloc::vec::Vec; +use core::iter; +use lambdaworks_math::field::errors::FieldError; + +/// Implementation of the Rescue Prime Optimized hash function. + +/// # Type Parameters +/// - `SECURITY_LEVEL`: Security level in bits (e.g., 128 or 160). +/// - `NUM_FULL_ROUNDS`: Number of full rounds in the permutation. This is set to 7 in the reference implementation for the best performance. +const NUM_FULL_ROUNDS: usize = 7; + +pub struct RescuePrimeOptimized { + /// Security level of the hash function. + //security_level: SecurityLevel, + /// State width of the hash function. + m: usize, + /// Capacity of the sponge. + capacity: usize, + /// Rate of the sponge. + rate: usize, + /// Precomputed round constants used in the permutation. + round_constants: Vec, + /// MDS matrix used in the permutation. + mds_matrix: Vec>, + /// MDS vector used for optimizing matrix multiplication. + mds_vector: MdsVector, + /// Method used for applying the MDS matrix. + mds_method: MdsMethod, +} + +impl Default for RescuePrimeOptimized { + fn default() -> Self { + Self::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication).unwrap() + } +} + +impl RescuePrimeOptimized { + /// Creates a new instance of `RescuePrimeOptimized` with the specified MDS method. + pub fn new(security_level: SecurityLevel, mds_method: MdsMethod) -> Result { + let (m, capacity) = match security_level { + SecurityLevel::Sec128 => (12, 4), + SecurityLevel::Sec160 => (16, 6), + }; + let rate = m - capacity; + let (mds_matrix, round_constants, mds_vector) = match security_level { + SecurityLevel::Sec128 => ( + get_mds_matrix(SecurityLevel::Sec128), + get_round_constants(SecurityLevel::Sec128), + get_mds_vector(SecurityLevel::Sec128), + ), + SecurityLevel::Sec160 => ( + get_mds_matrix(SecurityLevel::Sec160), + get_round_constants(SecurityLevel::Sec160), + get_mds_vector(SecurityLevel::Sec160), + ), + }; + Ok(Self { + // security_level, + m, + capacity, + rate, + round_constants: round_constants.to_vec(), + mds_matrix: match mds_matrix { + MdsMatrix::Mds128(matrix) => matrix.iter().map(|&row| row.to_vec()).collect(), + MdsMatrix::Mds160(matrix) => matrix.iter().map(|&row| row.to_vec()).collect(), + }, + mds_vector, + mds_method, + }) + } + + /// Applies the inverse S-box to the state. + pub fn apply_inverse_sbox(state: &mut [Fp]) { + for x in state.iter_mut() { + *x = x.pow(ALPHA_INV); + } + } + /* + /// Gets the MDS vector based on the state size. + fn get_mds_vector(m: usize) -> Result, &'static str> { + match m { + 12 => { + let arr: [Fp; 12] = [7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8].map(Fp::from); + Ok(arr.into()) + } + 16 => { + let arr: [Fp; 16] = [ + 256, 2, 1073741824, 2048, 16777216, 128, 8, 16, 524288, 4194304, 1, 268435456, + 1, 1024, 2, 8192, + ] + .map(Fp::from); + Ok(arr.into()) + } + _ => Err("Unsupported state size"), + } + } + */ + /// Applies the S-box to the state. + pub fn apply_sbox(state: &mut [Fp]) { + for x in state.iter_mut() { + *x = x.pow(ALPHA); + } + } + + /// Performs MDS matrix-vector multiplication. + fn mds_matrix_vector_multiplication(&self, state: &[Fp]) -> Vec { + let m = state.len(); + let mut new_state = vec![Fp::zero(); m]; + + for (i, new_value) in new_state.iter_mut().enumerate() { + for (j, state_value) in state.iter().enumerate() { + *new_value += self.mds_matrix[i][j] * state_value; + } + } + + new_state + } + + /// Performs MDS using Number Theoretic Transform. + fn mds_ntt(&self, state: &[Fp]) -> Result, FieldError> { + let m = state.len(); + let omega = if m == 12 { + Fp::from(281474976645120u64) + } else { + Fp::from(17293822564807737345u64) + }; + let mds_vector = self.mds_vector.as_slice(); + + let mds_ntt = ntt(mds_vector, omega); + let state_rev: Vec = iter::once(state[0]) + .chain(state[1..].iter().rev().cloned()) + .collect(); + let state_ntt = ntt(&state_rev, omega); + + let mut product_ntt = vec![Fp::zero(); m]; + for i in 0..m { + product_ntt[i] = mds_ntt[i] * state_ntt[i]; + } + + let omega_inv = omega.inv()?; + let result = intt(&product_ntt, omega_inv)?; + + Ok(iter::once(result[0]) + .chain(result[1..].iter().rev().cloned()) + .collect()) + } + + /// Performs MDS using the Karatsuba algorithm. + fn mds_karatsuba(&self, state: &[Fp]) -> Vec { + let m = state.len(); + let mds_vector = self.mds_vector.as_slice(); + let mds_rev: Vec = iter::once(mds_vector[0]) + .chain(mds_vector[1..].iter().rev().cloned()) + .collect(); + + let conv = karatsuba(&mds_rev, state); + + let mut result = vec![Fp::zero(); m]; + result[..m].copy_from_slice(&conv[..m]); + for i in m..conv.len() { + result[i - m] += conv[i]; + } + + result + } + + /// Applies the MDS transformation to the state. + fn apply_mds(&self, state: &mut [Fp]) -> Result<(), FieldError> { + let new_state = match self.mds_method { + MdsMethod::MatrixMultiplication => self.mds_matrix_vector_multiplication(state), + MdsMethod::Ntt => self.mds_ntt(state)?, + MdsMethod::Karatsuba => self.mds_karatsuba(state), + }; + state.copy_from_slice(&new_state); + Ok(()) + } + + /// Adds the round constants to the state. + fn add_round_constants(&self, state: &mut [Fp], round: usize) { + let m = self.m; + let round_constants = &self.round_constants[round * 2 * m..]; + + state + .iter_mut() + .zip(round_constants.iter()) + .take(m) + .for_each(|(state_elem, &constant)| { + *state_elem += constant; + }); + } + + /// Adds the second set of round constants to the state. + fn add_round_constants_second(&self, state: &mut [Fp], round: usize) { + let m = self.m; + let round_constants = &self.round_constants[round * 2 * m + m..]; + + state + .iter_mut() + .zip(round_constants.iter()) + .take(m) + .for_each(|(state_elem, &constant)| { + *state_elem += constant; + }); + } + + /// Performs the full permutation on the state. + pub fn permutation(&self, state: &mut [Fp]) { + let num_rounds = NUM_FULL_ROUNDS; + for round in 0..num_rounds { + self.apply_mds(state); + self.add_round_constants(state, round); + Self::apply_sbox(state); + self.apply_mds(state); + self.add_round_constants_second(state, round); + Self::apply_inverse_sbox(state); + } + } + + /// Hashes an input sequence of field elements. + pub fn hash(&self, input_sequence: &[Fp]) -> Vec { + //let m = self.m; + //let capacity = self.capacity; + //let rate = self.rate; + // Is it a bad practice to do thtath let.. = self...? + let mut state = vec![Fp::zero(); self.m]; + let input_len = input_sequence.len(); + if input_len % self.rate != 0 { + state[0] = Fp::one(); + } + let num_full_chunks = input_len / self.rate; + for i in 0..num_full_chunks { + let chunk = &input_sequence[i * self.rate..(i + 1) * self.rate]; + state[self.capacity..(self.rate + self.capacity)].copy_from_slice(&chunk[..self.rate]); + self.permutation(&mut state); + } + let last_chunk_size = input_len % self.rate; + if last_chunk_size != 0 { + let mut last_chunk = vec![Fp::zero(); self.rate]; + for j in 0..last_chunk_size { + last_chunk[j] = input_sequence[num_full_chunks * self.rate + j]; + } + last_chunk[last_chunk_size] = Fp::one(); + state[self.capacity..(self.rate + self.capacity)] + .copy_from_slice(&last_chunk[..self.rate]); + self.permutation(&mut state); + } + + state[self.capacity..self.capacity + self.rate / 2].to_vec() + } + + /// Hashes an input sequence of bytes. + pub fn hash_bytes(&self, input: &[u8]) -> Vec { + let field_elements = bytes_to_field_elements(input); + self.hash(&field_elements) + } +} +#[cfg(test)] +mod tests { + use super::*; + use rand::rngs::StdRng; + use rand::{Rng, SeedableRng}; + + // Values obtained using the Sage implementation in + // https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation + pub const EXPECTED_128: [[Fp; 4]; 19] = [ + [ + Fp::const_from_raw(1502364727743950833u64), + Fp::const_from_raw(5880949717274681448u64), + Fp::const_from_raw(162790463902224431u64), + Fp::const_from_raw(6901340476773664264u64), + ], + [ + Fp::const_from_raw(7478710183745780580u64), + Fp::const_from_raw(3308077307559720969u64), + Fp::const_from_raw(3383561985796182409u64), + Fp::const_from_raw(17205078494700259815u64), + ], + [ + Fp::const_from_raw(17439912364295172999u64), + Fp::const_from_raw(17979156346142712171u64), + Fp::const_from_raw(8280795511427637894u64), + Fp::const_from_raw(9349844417834368814u64), + ], + [ + Fp::const_from_raw(5105868198472766874u64), + Fp::const_from_raw(13090564195691924742u64), + Fp::const_from_raw(1058904296915798891u64), + Fp::const_from_raw(18379501748825152268u64), + ], + [ + Fp::const_from_raw(9133662113608941286u64), + Fp::const_from_raw(12096627591905525991u64), + Fp::const_from_raw(14963426595993304047u64), + Fp::const_from_raw(13290205840019973377u64), + ], + [ + Fp::const_from_raw(3134262397541159485u64), + Fp::const_from_raw(10106105871979362399u64), + Fp::const_from_raw(138768814855329459u64), + Fp::const_from_raw(15044809212457404677u64), + ], + [ + Fp::const_from_raw(162696376578462826u64), + Fp::const_from_raw(4991300494838863586u64), + Fp::const_from_raw(660346084748120605u64), + Fp::const_from_raw(13179389528641752698u64), + ], + [ + Fp::const_from_raw(2242391899857912644u64), + Fp::const_from_raw(12689382052053305418u64), + Fp::const_from_raw(235236990017815546u64), + Fp::const_from_raw(5046143039268215739u64), + ], + [ + Fp::const_from_raw(9585630502158073976u64), + Fp::const_from_raw(1310051013427303477u64), + Fp::const_from_raw(7491921222636097758u64), + Fp::const_from_raw(9417501558995216762u64), + ], + [ + Fp::const_from_raw(1994394001720334744u64), + Fp::const_from_raw(10866209900885216467u64), + Fp::const_from_raw(13836092831163031683u64), + Fp::const_from_raw(10814636682252756697u64), + ], + [ + Fp::const_from_raw(17486854790732826405u64), + Fp::const_from_raw(17376549265955727562u64), + Fp::const_from_raw(2371059831956435003u64), + Fp::const_from_raw(17585704935858006533u64), + ], + [ + Fp::const_from_raw(11368277489137713825u64), + Fp::const_from_raw(3906270146963049287u64), + Fp::const_from_raw(10236262408213059745u64), + Fp::const_from_raw(78552867005814007u64), + ], + [ + Fp::const_from_raw(17899847381280262181u64), + Fp::const_from_raw(14717912805498651446u64), + Fp::const_from_raw(10769146203951775298u64), + Fp::const_from_raw(2774289833490417856u64), + ], + [ + Fp::const_from_raw(3794717687462954368u64), + Fp::const_from_raw(4386865643074822822u64), + Fp::const_from_raw(8854162840275334305u64), + Fp::const_from_raw(7129983987107225269u64), + ], + [ + Fp::const_from_raw(7244773535611633983u64), + Fp::const_from_raw(19359923075859320u64), + Fp::const_from_raw(10898655967774994333u64), + Fp::const_from_raw(9319339563065736480u64), + ], + [ + Fp::const_from_raw(4935426252518736883u64), + Fp::const_from_raw(12584230452580950419u64), + Fp::const_from_raw(8762518969632303998u64), + Fp::const_from_raw(18159875708229758073u64), + ], + [ + Fp::const_from_raw(14871230873837295931u64), + Fp::const_from_raw(11225255908868362971u64), + Fp::const_from_raw(18100987641405432308u64), + Fp::const_from_raw(1559244340089644233u64), + ], + [ + Fp::const_from_raw(8348203744950016968u64), + Fp::const_from_raw(4041411241960726733u64), + Fp::const_from_raw(17584743399305468057u64), + Fp::const_from_raw(16836952610803537051u64), + ], + [ + Fp::const_from_raw(16139797453633030050u64), + Fp::const_from_raw(1090233424040889412u64), + Fp::const_from_raw(10770255347785669036u64), + Fp::const_from_raw(16982398877290254028u64), + ], + ]; + + pub const EXPECTED_160: [[Fp; 5]; 19] = [ + [ + Fp::const_from_raw(4766737105427868572), + Fp::const_from_raw(7538777753317835226), + Fp::const_from_raw(13644171984579649606), + Fp::const_from_raw(6748107971891460622), + Fp::const_from_raw(3480072938342119934), + ], + [ + Fp::const_from_raw(6277287777617382937), + Fp::const_from_raw(5688033921803605355), + Fp::const_from_raw(1104978478612014217), + Fp::const_from_raw(973672476085279574), + Fp::const_from_raw(7883652116413797779), + ], + [ + Fp::const_from_raw(3071553803427093579), + Fp::const_from_raw(12239501990998925662), + Fp::const_from_raw(14411295652479845526), + Fp::const_from_raw(5735407824213194294), + Fp::const_from_raw(6714816738691504270), + ], + [ + Fp::const_from_raw(4455998568145007624), + Fp::const_from_raw(18218360213084301612), + Fp::const_from_raw(8963555484142424669), + Fp::const_from_raw(13451196299356019287), + Fp::const_from_raw(660967320761434775), + ], + [ + Fp::const_from_raw(7894041400531553560), + Fp::const_from_raw(3138084719322472990), + Fp::const_from_raw(15017675162298246509), + Fp::const_from_raw(12340633143623038238), + Fp::const_from_raw(3710158928968726190), + ], + [ + Fp::const_from_raw(18345924309197503617), + Fp::const_from_raw(6448668044176965096), + Fp::const_from_raw(5891298758878861437), + Fp::const_from_raw(18404292940273103487), + Fp::const_from_raw(399715742058360811), + ], + [ + Fp::const_from_raw(4293522863608749708), + Fp::const_from_raw(11352999694211746044), + Fp::const_from_raw(15850245073570756600), + Fp::const_from_raw(1206950096837096206), + Fp::const_from_raw(6945598368659615878), + ], + [ + Fp::const_from_raw(1339949574743034442), + Fp::const_from_raw(5967452101017112419), + Fp::const_from_raw(824612579975542151), + Fp::const_from_raw(3327557828938393394), + Fp::const_from_raw(14113149399665697150), + ], + [ + Fp::const_from_raw(3540904694808418824), + Fp::const_from_raw(5951416386790014715), + Fp::const_from_raw(13859113410786779774), + Fp::const_from_raw(17205554479494520251), + Fp::const_from_raw(7359323608260195110), + ], + [ + Fp::const_from_raw(7504301802792161339), + Fp::const_from_raw(12879743137663115497), + Fp::const_from_raw(17245986604042562042), + Fp::const_from_raw(8175050867418132561), + Fp::const_from_raw(1063965910664731268), + ], + [ + Fp::const_from_raw(18267475461736255602), + Fp::const_from_raw(4481864641736940956), + Fp::const_from_raw(11260039501101148638), + Fp::const_from_raw(7529970948767692955), + Fp::const_from_raw(4177810888704753150), + ], + [ + Fp::const_from_raw(16604116128892623566), + Fp::const_from_raw(1520851983040290492), + Fp::const_from_raw(9361704524730297620), + Fp::const_from_raw(7447748879766268839), + Fp::const_from_raw(10834422028571028806), + ], + [ + Fp::const_from_raw(243957224918814907), + Fp::const_from_raw(9966149007214472697), + Fp::const_from_raw(18130816682404489504), + Fp::const_from_raw(3814760895598122151), + Fp::const_from_raw(862573500652233787), + ], + [ + Fp::const_from_raw(13414343823130474877), + Fp::const_from_raw(1002887112060795246), + Fp::const_from_raw(16685735965176892618), + Fp::const_from_raw(16172309857128312555), + Fp::const_from_raw(5158081519803147178), + ], + [ + Fp::const_from_raw(14614132925482133961), + Fp::const_from_raw(7618082792229868740), + Fp::const_from_raw(1881720834768448253), + Fp::const_from_raw(11508391877383996679), + Fp::const_from_raw(5348386073072413261), + ], + [ + Fp::const_from_raw(6268111131988518030), + Fp::const_from_raw(17920308297240232909), + Fp::const_from_raw(17719152474870950965), + Fp::const_from_raw(14857432101092580778), + Fp::const_from_raw(5708937553833180778), + ], + [ + Fp::const_from_raw(11597726741964198121), + Fp::const_from_raw(1568026444559423552), + Fp::const_from_raw(3233218961458461983), + Fp::const_from_raw(9700509409081014876), + Fp::const_from_raw(7989061413164577390), + ], + [ + Fp::const_from_raw(11180580619692834182), + Fp::const_from_raw(16871004730930134181), + Fp::const_from_raw(17810700669516829599), + Fp::const_from_raw(13679692060051982328), + Fp::const_from_raw(10386085719330760064), + ], + [ + Fp::const_from_raw(6222872143719551583), + Fp::const_from_raw(3842704143974291265), + Fp::const_from_raw(18311432727968603639), + Fp::const_from_raw(12278517700025439333), + Fp::const_from_raw(7011953052853282225), + ], + ]; + fn rand_field_element(rng: &mut R) -> Fp { + Fp::from(rng.gen::()) + } + + #[test] + fn test_apply_sbox() { + let mut rng = StdRng::seed_from_u64(1); + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let mut state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let mut expected = state.clone(); + expected.iter_mut().for_each(|v| *v = v.pow(ALPHA)); + + RescuePrimeOptimized::apply_sbox(&mut state); + assert_eq!(expected, state); + } + + #[test] + fn test_apply_inverse_sbox() { + let mut rng = StdRng::seed_from_u64(2); + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let mut state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let mut expected = state.clone(); + expected.iter_mut().for_each(|v| *v = v.pow(ALPHA_INV)); + + RescuePrimeOptimized::apply_inverse_sbox(&mut state); + assert_eq!(expected, state); + } + + #[test] + fn test_mds_matrix_multiplication() { + let mut rng = StdRng::seed_from_u64(3); + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let expected_state = rescue.mds_matrix_vector_multiplication(&state); + let mut computed_state = state.clone(); + rescue.apply_mds(&mut computed_state); + + assert_eq!(expected_state, computed_state); + } + /* + #[test] + fn test_mds_ntt() { + let mut rng = StdRng::seed_from_u64(4); + let rescue_ntt = RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Ntt).unwrap(); + let state: Vec = (0..rescue_ntt.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let expected_state = rescue_ntt.mds_ntt(&state); + let mut computed_state = state.clone(); + rescue_ntt.apply_mds(&mut computed_state); + + assert_eq!(expected_state, computed_state); + } + */ + #[test] + fn test_mds_karatsuba() { + let mut rng = StdRng::seed_from_u64(5); + let rescue_karatsuba = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Karatsuba).unwrap(); + let state: Vec = (0..rescue_karatsuba.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let expected_state = rescue_karatsuba.mds_karatsuba(&state); + let mut computed_state = state.clone(); + rescue_karatsuba.apply_mds(&mut computed_state); + + assert_eq!(expected_state, computed_state); + } + + #[test] + fn test_add_round_constants() { + let mut rng = StdRng::seed_from_u64(6); + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let mut state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let round = 0; + let expected_state = state + .iter() + .enumerate() + .map(|(i, &x)| x + rescue.round_constants[round * 2 * rescue.m + i]) + .collect::>(); + + rescue.add_round_constants(&mut state, round); + + assert_eq!(expected_state, state); + } + + #[test] + fn test_permutation() { + let mut rng = StdRng::seed_from_u64(7); + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let mut state: Vec = (0..rescue.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let expected_state = { + let mut temp_state = state.clone(); + for round in 0..7 { + rescue.apply_mds(&mut temp_state); + rescue.add_round_constants(&mut temp_state, round); + RescuePrimeOptimized::apply_sbox(&mut temp_state); + rescue.apply_mds(&mut temp_state); + rescue.add_round_constants_second(&mut temp_state, round); + RescuePrimeOptimized::apply_inverse_sbox(&mut temp_state); + } + temp_state + }; + + rescue.permutation(&mut state); + + assert_eq!(expected_state, state); + } + + #[test] + fn test_hash_single_chunk() { + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let input_sequence: Vec = (0..8).map(Fp::from).collect(); + let hash_output = rescue.hash(&input_sequence); + + assert_eq!(hash_output.len(), 4); + } + + #[test] + fn test_hash_multiple_chunks() { + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let input_sequence: Vec = (0..16).map(Fp::from).collect(); // Two chunks of size 8 + let hash_output = rescue.hash(&input_sequence); + + assert_eq!(hash_output.len(), 4); + } + + #[test] + fn test_hash_with_padding() { + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let input_sequence: Vec = (0..5).map(Fp::from).collect(); + let hash_output = rescue.hash(&input_sequence); + assert_eq!(hash_output.len(), 4); + } + #[test] + // test ported from https://github.com/0xPolygonMiden/crypto/blob/main/src/hash/rescue/rpo/tests.rs + fn hash_padding() { + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + + let input1 = vec![1u8, 2, 3]; + let input2 = vec![1u8, 2, 3, 0]; + let hash1 = rescue.hash_bytes(&input1); + let hash2 = rescue.hash_bytes(&input2); + assert_ne!(hash1, hash2); + + let input1 = vec![1_u8, 2, 3, 4, 5, 6]; + let input2 = vec![1_u8, 2, 3, 4, 5, 6, 0]; + let hash1 = rescue.hash_bytes(&input1); + let hash2 = rescue.hash_bytes(&input2); + assert_ne!(hash1, hash2); + + let input1 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0]; + let input2 = vec![1_u8, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0]; + let hash1 = rescue.hash_bytes(&input1); + let hash2 = rescue.hash_bytes(&input2); + assert_ne!(hash1, hash2); + } + #[cfg(feature = "std")] + #[test] + fn sponge_zeroes_collision() { + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + + let mut zeroes = Vec::new(); + let mut hashes = std::collections::HashSet::new(); + + for _ in 0..255 { + let hash = rescue.hash(&zeroes); + assert!(hashes.insert(hash)); + zeroes.push(Fp::zero()); + } + } + #[test] + fn test_hash_bytes() { + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let input_bytes = b"Rescue Prime Optimized"; + let hash_output = rescue.hash_bytes(input_bytes); + + assert_eq!(hash_output.len(), 4); + } + + #[test] + fn test_mds_methods_consistency() { + let rescue_matrix = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let rescue_ntt = RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Ntt).unwrap(); + let rescue_karatsuba = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Karatsuba).unwrap(); + + let input = vec![ + Fp::from(1u64), + Fp::from(2u64), + Fp::from(3u64), + Fp::from(4u64), + Fp::from(5u64), + Fp::from(6u64), + Fp::from(7u64), + Fp::from(8u64), + Fp::from(9u64), + ]; + + let hash_matrix = rescue_matrix.hash(&input); + let hash_ntt = rescue_ntt.hash(&input); + let hash_karatsuba = rescue_karatsuba.hash(&input); + + assert_eq!(hash_matrix, hash_ntt); + assert_eq!(hash_ntt, hash_karatsuba); + } + + /* + //Test used to generate the expected hashes for the test vectors (they are the same as in the Polygon Miden implementation) + #[test] + fn generate_test_vectors() { + let rescue = RescuePrimeOptimized::<160, 7>::new(MdsMethod::MatrixMultiplication); + let elements: Vec = (0..19).map(Fp::from).collect(); + + println!("let expected_hashes = vec!["); + elements.iter().enumerate().for_each(|(i, _)| { + let input = elements.iter().take(i + 1); + let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); + + print!(" vec!["); + hash_output.iter().for_each(|value| { + print!("Fp::from({}u64), ", value.value()); + }); + println!("],"); + }); + println!("];"); + } + + #[test] + fn test_print_round_constants() { + let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); + + println!("Round constants:"); + for (i, constant) in rescue.round_constants.iter().enumerate() { + println!("Constant {}: Fp::from({}u64)", i, constant.value()); + } + + assert_eq!(rescue.round_constants.len(), 2 * rescue.m * 7); + } + */ + #[test] + fn test_hash_vectors() { + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) + .unwrap(); + let elements: Vec = (0..19).map(Fp::from).collect(); + + EXPECTED_128.iter().enumerate().for_each(|(i, expected)| { + let input = elements.iter().take(i + 1); + let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); + + assert_eq!( + hash_output, + *expected, + "Hash mismatch for input length {}", + i + 1 + ); + }); + } + #[test] + fn test_hash_vector_2() { + let rescue = + RescuePrimeOptimized::new(SecurityLevel::Sec160, MdsMethod::MatrixMultiplication) + .unwrap(); + let elements: Vec = (0..19).map(Fp::from).collect(); + + EXPECTED_160.iter().enumerate().for_each(|(i, expected)| { + let input = elements.iter().take(i + 1); + let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); + + assert_eq!( + hash_output, + *expected, + "Hash mismatch for input length {}", + i + 1 + ); + }); + } + #[cfg(feature = "std")] + #[test] + fn test_hash_example_and_print() { + let rescue = RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Ntt).unwrap(); + + let input = b"Hello there"; + + let hash_result = rescue.hash_bytes(input); + + println!("Input: {:?}", input); + println!("Hash result:"); + for (i, value) in hash_result.iter().enumerate() { + println!(" {}: {}", i, value); + } + + println!("Hash as u64 values:"); + for value in hash_result.iter() { + print!("{}, ", value.value()); + } + println!(); + //assert_eq!(hash_result.len(), 4); + } + // Same result obtained in Miden Crypto + /* + #[test] + fn test_print_circulant_matrix() { + let m = 12; // or 16 depending on which one you want to print + let mds_vector = RescuePrimeOptimized::<128, 7>::get_mds_vector(m); + let circulant_matrix = + RescuePrimeOptimized::<128, 7>::generate_circulant_matrix(&mds_vector); + + println!("Circulant Matrix:"); + for row in circulant_matrix.iter() { + for element in row.iter() { + print!("{:?}, ", element); + } + println!(); + } + // Ensure the matrix dimensions are correct for testing + assert_eq!(circulant_matrix.len(), m); + assert_eq!(circulant_matrix[0].len(), m); + } */ +} diff --git a/crypto/src/hash/rescue_prime/utils.rs b/crypto/src/hash/rescue_prime/utils.rs index 04b386921..a1220b40b 100644 --- a/crypto/src/hash/rescue_prime/utils.rs +++ b/crypto/src/hash/rescue_prime/utils.rs @@ -1,5 +1,9 @@ use super::Fp; use alloc::vec::Vec; +use lambdaworks_math::field::errors::FieldError; + +// Auxiliary algorithms based on the reference implementation in Sage +// https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation pub fn bytes_to_field_elements(input: &[u8]) -> Vec { input @@ -25,7 +29,7 @@ pub fn ntt(input: &[Fp], omega: Fp) -> Vec { }) .collect() } - +/* pub fn intt(input: &[Fp], omega_inv: Fp) -> Vec { let inv_n = Fp::from(input.len() as u64).inv().unwrap(); ntt(input, omega_inv) @@ -33,7 +37,15 @@ pub fn intt(input: &[Fp], omega_inv: Fp) -> Vec { .map(|val| val * inv_n) .collect() } + */ +pub fn intt(input: &[Fp], omega_inv: Fp) -> Result, FieldError> { + let n = input.len() as u64; + let inv_n = Fp::from(n).inv()?; + let transformed = ntt(input, omega_inv); + Ok(transformed.into_iter().map(|val| val * inv_n).collect()) +} +// TO DO: Solve Ivan's comment about not unwrappint and return Result pub fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { let n = lhs.len(); if n <= 32 { From 0e4c45f9d24f7b5f5ed3b8673edc47a79cfc7cd9 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 23 Oct 2024 10:13:07 -0300 Subject: [PATCH 14/17] save work --- .../src/hash/rescue_prime/rescue_prime_optimized.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs index d3dc9b0f9..c119ac8bb 100644 --- a/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs +++ b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs @@ -211,10 +211,10 @@ impl RescuePrimeOptimized { pub fn permutation(&self, state: &mut [Fp]) { let num_rounds = NUM_FULL_ROUNDS; for round in 0..num_rounds { - self.apply_mds(state); + let _ = self.apply_mds(state); self.add_round_constants(state, round); Self::apply_sbox(state); - self.apply_mds(state); + let _ = self.apply_mds(state); self.add_round_constants_second(state, round); Self::apply_inverse_sbox(state); } @@ -568,7 +568,7 @@ mod tests { let expected_state = rescue.mds_matrix_vector_multiplication(&state); let mut computed_state = state.clone(); - rescue.apply_mds(&mut computed_state); + let _ = rescue.apply_mds(&mut computed_state); assert_eq!(expected_state, computed_state); } @@ -599,7 +599,7 @@ mod tests { let expected_state = rescue_karatsuba.mds_karatsuba(&state); let mut computed_state = state.clone(); - rescue_karatsuba.apply_mds(&mut computed_state); + let _ = rescue_karatsuba.apply_mds(&mut computed_state); assert_eq!(expected_state, computed_state); } @@ -639,10 +639,10 @@ mod tests { let expected_state = { let mut temp_state = state.clone(); for round in 0..7 { - rescue.apply_mds(&mut temp_state); + let _ = rescue.apply_mds(&mut temp_state); rescue.add_round_constants(&mut temp_state, round); RescuePrimeOptimized::apply_sbox(&mut temp_state); - rescue.apply_mds(&mut temp_state); + let _ = rescue.apply_mds(&mut temp_state); rescue.add_round_constants_second(&mut temp_state, round); RescuePrimeOptimized::apply_inverse_sbox(&mut temp_state); } From da7ab0ea98ffe3609c553629730c58fac9675f81 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 23 Oct 2024 16:06:48 -0300 Subject: [PATCH 15/17] move Mds enum and removed commented code --- crypto/src/hash/rescue_prime/mds_method.rs | 10 -- crypto/src/hash/rescue_prime/mod.rs | 3 +- crypto/src/hash/rescue_prime/parameters.rs | 2 - .../rescue_prime/rescue_prime_optimized.rs | 162 ++++++------------ 4 files changed, 54 insertions(+), 123 deletions(-) delete mode 100644 crypto/src/hash/rescue_prime/mds_method.rs diff --git a/crypto/src/hash/rescue_prime/mds_method.rs b/crypto/src/hash/rescue_prime/mds_method.rs deleted file mode 100644 index 6fd19909a..000000000 --- a/crypto/src/hash/rescue_prime/mds_method.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[derive(Clone)] -#[allow(dead_code)] -pub enum MdsMethod { - /// Use standard matrix multiplication. - MatrixMultiplication, - /// Use Number Theoretic Transform for multiplication. - Ntt, - /// Use Karatsuba algorithm for multiplication. - Karatsuba, -} diff --git a/crypto/src/hash/rescue_prime/mod.rs b/crypto/src/hash/rescue_prime/mod.rs index 35a76a580..c68be4b8a 100644 --- a/crypto/src/hash/rescue_prime/mod.rs +++ b/crypto/src/hash/rescue_prime/mod.rs @@ -1,9 +1,8 @@ -mod mds_method; mod parameters; mod rescue_prime_optimized; mod utils; -pub use mds_method::MdsMethod; +pub use rescue_prime_optimized::MdsMethod; pub use rescue_prime_optimized::RescuePrimeOptimized; use lambdaworks_math::field::element::FieldElement; diff --git a/crypto/src/hash/rescue_prime/parameters.rs b/crypto/src/hash/rescue_prime/parameters.rs index a10391604..763709d7a 100644 --- a/crypto/src/hash/rescue_prime/parameters.rs +++ b/crypto/src/hash/rescue_prime/parameters.rs @@ -906,8 +906,6 @@ pub enum SecurityLevel { Sec160, } -// Here if I use [Fp] i get error abpout two return values -// if pub fn get_round_constants(level: SecurityLevel) -> &'static [Fp] { match level { SecurityLevel::Sec128 => &ROUND_CONSTANTS_128, diff --git a/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs index c119ac8bb..d542e4b0c 100644 --- a/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs +++ b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs @@ -1,21 +1,33 @@ use super::parameters::*; use super::utils::*; use super::Fp; -use super::MdsMethod; use crate::alloc::vec::Vec; use core::iter; use lambdaworks_math::field::errors::FieldError; -/// Implementation of the Rescue Prime Optimized hash function. - -/// # Type Parameters -/// - `SECURITY_LEVEL`: Security level in bits (e.g., 128 or 160). -/// - `NUM_FULL_ROUNDS`: Number of full rounds in the permutation. This is set to 7 in the reference implementation for the best performance. +// Implementation of the Rescue Prime Optimized hash function. +// https://eprint.iacr.org/2022/1577 +// https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation +// It supports two security levels: 128-bit and 160-bit. Depending on the security level chosen +// the integer parameters are set accordingly. + +// For the Security level (λ) of 128 bits we have: +// Number of rounds (N): 7 +// State size (m): 12 +// Rate (r): 8 +// Capacity (c): 4 + +// For the Security level (λ) of 160 bits we have: +// Number of rounds (N): 7 +// State size (m): 16 +// Rate (r): 10 +// Capacity (c): 6 + +// In the paper, the authors use a number of rounds equal to 7 as a trade-off between security and performance. +// The number of rounds can be increased to 8 or 9 to achieve a higher level of security at the cost of performance. const NUM_FULL_ROUNDS: usize = 7; pub struct RescuePrimeOptimized { - /// Security level of the hash function. - //security_level: SecurityLevel, /// State width of the hash function. m: usize, /// Capacity of the sponge. @@ -23,7 +35,7 @@ pub struct RescuePrimeOptimized { /// Rate of the sponge. rate: usize, /// Precomputed round constants used in the permutation. - round_constants: Vec, + round_constants: &'static [Fp], /// MDS matrix used in the permutation. mds_matrix: Vec>, /// MDS vector used for optimizing matrix multiplication. @@ -39,7 +51,7 @@ impl Default for RescuePrimeOptimized { } impl RescuePrimeOptimized { - /// Creates a new instance of `RescuePrimeOptimized` with the specified MDS method. + /// Creates a new instance of `RescuePrimeOptimized` with corresponding Security level and the specified MDS method. pub fn new(security_level: SecurityLevel, mds_method: MdsMethod) -> Result { let (m, capacity) = match security_level { SecurityLevel::Sec128 => (12, 4), @@ -59,11 +71,10 @@ impl RescuePrimeOptimized { ), }; Ok(Self { - // security_level, m, capacity, rate, - round_constants: round_constants.to_vec(), + round_constants, mds_matrix: match mds_matrix { MdsMatrix::Mds128(matrix) => matrix.iter().map(|&row| row.to_vec()).collect(), MdsMatrix::Mds160(matrix) => matrix.iter().map(|&row| row.to_vec()).collect(), @@ -79,26 +90,7 @@ impl RescuePrimeOptimized { *x = x.pow(ALPHA_INV); } } - /* - /// Gets the MDS vector based on the state size. - fn get_mds_vector(m: usize) -> Result, &'static str> { - match m { - 12 => { - let arr: [Fp; 12] = [7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8].map(Fp::from); - Ok(arr.into()) - } - 16 => { - let arr: [Fp; 16] = [ - 256, 2, 1073741824, 2048, 16777216, 128, 8, 16, 524288, 4194304, 1, 268435456, - 1, 1024, 2, 8192, - ] - .map(Fp::from); - Ok(arr.into()) - } - _ => Err("Unsupported state size"), - } - } - */ + /// Applies the S-box to the state. pub fn apply_sbox(state: &mut [Fp]) { for x in state.iter_mut() { @@ -222,10 +214,6 @@ impl RescuePrimeOptimized { /// Hashes an input sequence of field elements. pub fn hash(&self, input_sequence: &[Fp]) -> Vec { - //let m = self.m; - //let capacity = self.capacity; - //let rate = self.rate; - // Is it a bad practice to do thtath let.. = self...? let mut state = vec![Fp::zero(); self.m]; let input_len = input_sequence.len(); if input_len % self.rate != 0 { @@ -258,13 +246,22 @@ impl RescuePrimeOptimized { self.hash(&field_elements) } } +#[derive(Clone)] +pub enum MdsMethod { + /// Use standard matrix multiplication. + MatrixMultiplication, + /// Use Number Theoretic Transform for multiplication. + Ntt, + /// Use Karatsuba algorithm for multiplication. + Karatsuba, +} #[cfg(test)] mod tests { use super::*; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; - // Values obtained using the Sage implementation in + // Values obtained from the Sage implemenstation in // https://github.com/ASDiscreteMathematics/rpo/tree/master/reference_implementation pub const EXPECTED_128: [[Fp; 4]; 19] = [ [ @@ -572,22 +569,22 @@ mod tests { assert_eq!(expected_state, computed_state); } - /* - #[test] - fn test_mds_ntt() { - let mut rng = StdRng::seed_from_u64(4); - let rescue_ntt = RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Ntt).unwrap(); - let state: Vec = (0..rescue_ntt.m) - .map(|_| rand_field_element(&mut rng)) - .collect(); - - let expected_state = rescue_ntt.mds_ntt(&state); - let mut computed_state = state.clone(); - rescue_ntt.apply_mds(&mut computed_state); - - assert_eq!(expected_state, computed_state); - } - */ + + #[test] + fn test_mds_ntt() { + let mut rng = StdRng::seed_from_u64(4); + let rescue_ntt = RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::Ntt).unwrap(); + let state: Vec = (0..rescue_ntt.m) + .map(|_| rand_field_element(&mut rng)) + .collect(); + + let expected_state = rescue_ntt.mds_ntt(&state).unwrap(); + let mut computed_state = state.clone(); + let _ = rescue_ntt.apply_mds(&mut computed_state); + + assert_eq!(expected_state, computed_state); + } + #[test] fn test_mds_karatsuba() { let mut rng = StdRng::seed_from_u64(5); @@ -766,41 +763,8 @@ mod tests { assert_eq!(hash_ntt, hash_karatsuba); } - /* - //Test used to generate the expected hashes for the test vectors (they are the same as in the Polygon Miden implementation) - #[test] - fn generate_test_vectors() { - let rescue = RescuePrimeOptimized::<160, 7>::new(MdsMethod::MatrixMultiplication); - let elements: Vec = (0..19).map(Fp::from).collect(); - - println!("let expected_hashes = vec!["); - elements.iter().enumerate().for_each(|(i, _)| { - let input = elements.iter().take(i + 1); - let hash_output = rescue.hash(input.cloned().collect::>().as_slice()); - - print!(" vec!["); - hash_output.iter().for_each(|value| { - print!("Fp::from({}u64), ", value.value()); - }); - println!("],"); - }); - println!("];"); - } - - #[test] - fn test_print_round_constants() { - let rescue = RescuePrimeOptimized::<128, 7>::new(MdsMethod::MatrixMultiplication); - - println!("Round constants:"); - for (i, constant) in rescue.round_constants.iter().enumerate() { - println!("Constant {}: Fp::from({}u64)", i, constant.value()); - } - - assert_eq!(rescue.round_constants.len(), 2 * rescue.m * 7); - } - */ #[test] - fn test_hash_vectors() { + fn test_hash_vectors_128() { let rescue = RescuePrimeOptimized::new(SecurityLevel::Sec128, MdsMethod::MatrixMultiplication) .unwrap(); @@ -819,7 +783,7 @@ mod tests { }); } #[test] - fn test_hash_vector_2() { + fn test_hash_vector_160() { let rescue = RescuePrimeOptimized::new(SecurityLevel::Sec160, MdsMethod::MatrixMultiplication) .unwrap(); @@ -857,26 +821,6 @@ mod tests { print!("{}, ", value.value()); } println!(); - //assert_eq!(hash_result.len(), 4); + assert_eq!(hash_result.len(), 4); } - // Same result obtained in Miden Crypto - /* - #[test] - fn test_print_circulant_matrix() { - let m = 12; // or 16 depending on which one you want to print - let mds_vector = RescuePrimeOptimized::<128, 7>::get_mds_vector(m); - let circulant_matrix = - RescuePrimeOptimized::<128, 7>::generate_circulant_matrix(&mds_vector); - - println!("Circulant Matrix:"); - for row in circulant_matrix.iter() { - for element in row.iter() { - print!("{:?}, ", element); - } - println!(); - } - // Ensure the matrix dimensions are correct for testing - assert_eq!(circulant_matrix.len(), m); - assert_eq!(circulant_matrix[0].len(), m); - } */ } From 8ee7116dfb2ee0847b6165f086f9f94e2257a265 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Wed, 30 Oct 2024 11:17:30 -0300 Subject: [PATCH 16/17] remove unnecessary match --- crypto/src/hash/rescue_prime/parameters.rs | 19 +++++++++++++++-- .../rescue_prime/rescue_prime_optimized.rs | 21 +++++++------------ crypto/src/hash/rescue_prime/utils.rs | 11 +--------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/crypto/src/hash/rescue_prime/parameters.rs b/crypto/src/hash/rescue_prime/parameters.rs index 763709d7a..d091d8962 100644 --- a/crypto/src/hash/rescue_prime/parameters.rs +++ b/crypto/src/hash/rescue_prime/parameters.rs @@ -901,12 +901,27 @@ pub const MDS_MATRIX_160: [[Fp; 16]; 16] = [ ], ]; +#[derive(Clone)] pub enum SecurityLevel { Sec128, Sec160, } -pub fn get_round_constants(level: SecurityLevel) -> &'static [Fp] { +pub fn get_state_size(security_level: &SecurityLevel) -> usize { + match security_level { + SecurityLevel::Sec128 => 12, + SecurityLevel::Sec160 => 16, + } +} + +pub fn get_capacity(security_level: &SecurityLevel) -> usize { + match security_level { + SecurityLevel::Sec128 => 4, + SecurityLevel::Sec160 => 6, + } +} + +pub fn get_round_constants(level: &SecurityLevel) -> &'static [Fp] { match level { SecurityLevel::Sec128 => &ROUND_CONSTANTS_128, SecurityLevel::Sec160 => &ROUND_CONSTANTS_160, @@ -931,7 +946,7 @@ pub enum MdsMatrix { Mds160([[Fp; 16]; 16]), } -pub fn get_mds_matrix(level: SecurityLevel) -> MdsMatrix { +pub fn get_mds_matrix(level: &SecurityLevel) -> MdsMatrix { match level { SecurityLevel::Sec128 => MdsMatrix::Mds128(MDS_MATRIX_128), SecurityLevel::Sec160 => MdsMatrix::Mds160(MDS_MATRIX_160), diff --git a/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs index d542e4b0c..f9172a571 100644 --- a/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs +++ b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs @@ -53,23 +53,16 @@ impl Default for RescuePrimeOptimized { impl RescuePrimeOptimized { /// Creates a new instance of `RescuePrimeOptimized` with corresponding Security level and the specified MDS method. pub fn new(security_level: SecurityLevel, mds_method: MdsMethod) -> Result { - let (m, capacity) = match security_level { + /*let (m, capacity) = match security_level { SecurityLevel::Sec128 => (12, 4), SecurityLevel::Sec160 => (16, 6), - }; + };*/ + let m = get_state_size(&security_level); + let capacity = get_capacity(&security_level); let rate = m - capacity; - let (mds_matrix, round_constants, mds_vector) = match security_level { - SecurityLevel::Sec128 => ( - get_mds_matrix(SecurityLevel::Sec128), - get_round_constants(SecurityLevel::Sec128), - get_mds_vector(SecurityLevel::Sec128), - ), - SecurityLevel::Sec160 => ( - get_mds_matrix(SecurityLevel::Sec160), - get_round_constants(SecurityLevel::Sec160), - get_mds_vector(SecurityLevel::Sec160), - ), - }; + let mds_matrix = get_mds_matrix(&security_level); + let round_constants = get_round_constants(&security_level); + let mds_vector = get_mds_vector(security_level); Ok(Self { m, capacity, diff --git a/crypto/src/hash/rescue_prime/utils.rs b/crypto/src/hash/rescue_prime/utils.rs index a1220b40b..ba17341bc 100644 --- a/crypto/src/hash/rescue_prime/utils.rs +++ b/crypto/src/hash/rescue_prime/utils.rs @@ -29,15 +29,7 @@ pub fn ntt(input: &[Fp], omega: Fp) -> Vec { }) .collect() } -/* -pub fn intt(input: &[Fp], omega_inv: Fp) -> Vec { - let inv_n = Fp::from(input.len() as u64).inv().unwrap(); - ntt(input, omega_inv) - .into_iter() - .map(|val| val * inv_n) - .collect() -} - */ + pub fn intt(input: &[Fp], omega_inv: Fp) -> Result, FieldError> { let n = input.len() as u64; let inv_n = Fp::from(n).inv()?; @@ -45,7 +37,6 @@ pub fn intt(input: &[Fp], omega_inv: Fp) -> Result, FieldError> { Ok(transformed.into_iter().map(|val| val * inv_n).collect()) } -// TO DO: Solve Ivan's comment about not unwrappint and return Result pub fn karatsuba(lhs: &[Fp], rhs: &[Fp]) -> Vec { let n = lhs.len(); if n <= 32 { From c385cbb31727575c7a04c683e7de266cf2945237 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Mon, 4 Nov 2024 12:04:12 -0300 Subject: [PATCH 17/17] removed commented code --- crypto/src/hash/rescue_prime/rescue_prime_optimized.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs index f9172a571..6ecf86f2b 100644 --- a/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs +++ b/crypto/src/hash/rescue_prime/rescue_prime_optimized.rs @@ -53,10 +53,6 @@ impl Default for RescuePrimeOptimized { impl RescuePrimeOptimized { /// Creates a new instance of `RescuePrimeOptimized` with corresponding Security level and the specified MDS method. pub fn new(security_level: SecurityLevel, mds_method: MdsMethod) -> Result { - /*let (m, capacity) = match security_level { - SecurityLevel::Sec128 => (12, 4), - SecurityLevel::Sec160 => (16, 6), - };*/ let m = get_state_size(&security_level); let capacity = get_capacity(&security_level); let rate = m - capacity;