use franklin_crypto::bellman::Engine;
use crate::common::params::InnerHashParameters;
use crate::traits::{CustomGate, HashFamily, HashParams, Sbox};
use std::convert::TryInto;
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct RescueParams<E: Engine, const RATE: usize, const WIDTH: usize> {
pub(crate) allows_specialization: bool,
pub(crate) full_rounds: usize,
#[serde(serialize_with = "crate::serialize_vec_of_arrays")]
#[serde(deserialize_with = "crate::deserialize_vec_of_arrays")]
pub(crate) round_constants: Vec<[E::Fr; WIDTH]>,
#[serde(serialize_with = "crate::serialize_array_of_arrays")]
#[serde(deserialize_with = "crate::deserialize_array_of_arrays")]
pub(crate) mds_matrix: [[E::Fr; WIDTH]; WIDTH],
pub(crate) alpha: Sbox,
pub(crate) alpha_inv: Sbox,
pub(crate) custom_gate: CustomGate,
}
impl<E: Engine, const RATE: usize, const WIDTH: usize> PartialEq for RescueParams<E, RATE, WIDTH> {
fn eq(&self, other: &Self) -> bool {
self.hash_family() == other.hash_family()
}
}
impl<E: Engine, const RATE: usize, const WIDTH: usize> Default for RescueParams<E, RATE, WIDTH> {
fn default() -> Self {
let (params, alpha, alpha_inv) = compute_params::<E, RATE, WIDTH>();
Self {
allows_specialization: false,
full_rounds: params.full_rounds,
round_constants: params.round_constants().try_into().expect("round constants"),
mds_matrix: *params.mds_matrix(),
alpha: Sbox::Alpha(alpha),
alpha_inv: Sbox::AlphaInverse(alpha_inv, alpha),
custom_gate: CustomGate::None,
}
}
}
impl<E: Engine, const RATE: usize, const WIDTH: usize> HashParams<E, RATE, WIDTH> for RescueParams<E, RATE, WIDTH> {
#[inline]
fn allows_specialization(&self) -> bool {
self.allows_specialization
}
fn hash_family(&self) -> HashFamily {
HashFamily::Rescue
}
fn constants_of_round(&self, round: usize) -> &[E::Fr; WIDTH] {
&self.round_constants[round]
}
fn mds_matrix(&self) -> &[[E::Fr; WIDTH]; WIDTH] {
&self.mds_matrix
}
fn number_of_full_rounds(&self) -> usize {
self.full_rounds
}
fn number_of_partial_rounds(&self) -> usize {
unimplemented!("Rescue doesn't have partial rounds.")
}
fn alpha(&self) -> &Sbox {
&self.alpha
}
fn alpha_inv(&self) -> &Sbox {
&self.alpha_inv
}
fn optimized_mds_matrixes(&self) -> (&[[E::Fr; WIDTH]; WIDTH], &[[[E::Fr; WIDTH]; WIDTH]]) {
unimplemented!("Rescue doesn't use optimized matrixes")
}
fn optimized_round_constants(&self) -> &[[E::Fr; WIDTH]] {
unimplemented!("Rescue doesn't use optimized round constants")
}
fn custom_gate(&self) -> CustomGate {
self.custom_gate
}
fn use_custom_gate(&mut self, custom_gate: CustomGate) {
self.custom_gate = custom_gate;
}
fn specialized_affine_transformation_for_round(&self, state: &mut [E::Fr; WIDTH], round_constants: &[E::Fr; WIDTH]) {
debug_assert_eq!(WIDTH, 3);
debug_assert!(self.allows_specialization);
use franklin_crypto::bellman::Field;
let mut res0 = state[0];
res0.double();
res0.add_assign(&state[1]);
res0.add_assign(&state[2]);
res0.add_assign(&round_constants[0]);
let mut res1 = state[1];
res1.double();
res1.add_assign(&state[0]);
res1.add_assign(&state[2]);
res1.add_assign(&round_constants[1]);
let mut res2 = state[2];
res2.double();
res2.add_assign(&state[0]);
res2.add_assign(&state[1]);
res2.add_assign(&round_constants[2]);
state[0] = res0;
state[1] = res1;
state[2] = res2;
}
}
impl<E: Engine> RescueParams<E, 2, 3> {
pub fn specialized_for_num_rounds(num_rounds: usize, claimed_security_bits: usize) -> Self {
let (params, alpha, _alpha_inv, addition_chain) = mds_optimized_params_alpha_5::<E>(num_rounds, claimed_security_bits);
Self {
allows_specialization: true,
full_rounds: params.full_rounds,
round_constants: params.round_constants().try_into().expect("round constants"),
mds_matrix: *params.mds_matrix(),
alpha: Sbox::Alpha(alpha),
alpha_inv: Sbox::AddChain(addition_chain, alpha),
custom_gate: CustomGate::None,
}
}
}
pub(crate) fn compute_params<E: Engine, const RATE: usize, const WIDTH: usize>() -> (InnerHashParameters<E, RATE, WIDTH>, u64, Vec<u64>) {
let full_rounds = 8;
let security_level = 126;
let mut params = InnerHashParameters::new(security_level, full_rounds, 0);
let rounds_tag = b"Rescue_f";
let _mds_tag = b"ResM0003";
let total_number_of_rounds = 2 * full_rounds + 1;
params.compute_round_constants(total_number_of_rounds, rounds_tag);
params.compute_mds_matrix_for_rescue();
let alpha = 5u64;
let alpha_inv = crate::common::utils::compute_gcd_vec::<E>(alpha).expect("inverse of alpha");
(params, alpha, alpha_inv)
}
pub(crate) fn mds_optimized_params_alpha_5<E: Engine>(full_rounds: usize, claimed_security_bits: usize) -> (InnerHashParameters<E, 2, 3>, u64, Vec<u64>, Vec<crate::traits::Step>) {
let mut params = InnerHashParameters::new(claimed_security_bits, full_rounds, 0);
let rounds_tag = b"Rescue_f";
let total_number_of_rounds = 2 * full_rounds + 1;
params.compute_round_constants_with_prefixed_blake2s(total_number_of_rounds, rounds_tag);
params.set_circular_optimized_mds();
let alpha = 5;
let alpha_inv = crate::common::utils::compute_gcd_vec::<E>(alpha).expect("inverse of alpha");
let alpha_inv_as_biguint = crate::common::utils::compute_gcd_biguint::<E>(alpha).expect("inverse of alpha");
let addition_chain: Vec<_> = addchain::build_addition_chain(alpha_inv_as_biguint).into_iter().map(|el| crate::traits::Step::from(el)).collect();
(params, alpha, alpha_inv, addition_chain)
}
#[cfg(test)]
mod tests {
use crate::add_chain_pow_smallvec;
use super::*;
use franklin_crypto::bellman::pairing::bn256::{Bn256, Fr};
use franklin_crypto::bellman::{PrimeField, ScalarEngine};
use num_bigint::{BigInt, Sign};
#[test]
fn test_addition_chains() {
let mut rng = crate::rand::thread_rng();
let alpha = 5;
let alpha_inv_as_biguint = crate::common::utils::compute_gcd_biguint::<Bn256>(alpha).expect("inverse of alpha");
let addition_chain: Vec<_> = addchain::build_addition_chain(alpha_inv_as_biguint).into_iter().map(|el| crate::traits::Step::from(el)).collect();
use crate::rand::Rand;
dbg!(addition_chain.len());
assert!(addition_chain.len() <= 512);
use franklin_crypto::bellman::Field;
let x = Fr::rand(&mut rng);
let y = Fr::rand(&mut rng);
let z = Fr::rand(&mut rng);
let mut state = [x, y, z];
use crate::common::sbox::sbox_alpha_inv_via_add_chain;
sbox_alpha_inv_via_add_chain::<Bn256>(&addition_chain, &mut state);
}
}