diff --git a/CHANGELOG.md b/CHANGELOG.md index fc413312f..f26efd87f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,10 +38,14 @@ and follow [semantic versioning](https://semver.org/) for our releases. - [#144](https://github.com/EspressoSystems/jellyfish/pull/144) (`jf-primitives`) Updated append-only merkle tree gadget with the latest MT API - [#119](https://github.com/EspressoSystems/jellyfish/pull/119) (all) Updated dependencies - Upgraded `criterion` from `0.3.1` to `0.4.0` +- [#146](https://github.com/EspressoSystems/jellyfish/pull/146) (`jf-primitives`) Refactored Rescue sponge API: + - Remove all `.*sponge.*` methods from `Permutation`. + - Introduce `RescueCRHF` which takes over `sponge_with_padding` and `sponge_no_padding` from `Permutation`. + - Introduce `RescuePRF` which takes over `full_state_keyed_sponge_with_padding` and `full_state_keyed_sponge_no_padding` from `Permutation`. - [#148](https://github.com/EspressoSystems/jellyfish/pull/148), [#156](https://github.com/EspressoSystems/jellyfish/pull/156) (`jf-primitives`) Refactored BLS Signature implementation - #148 Added trait bounds on associated types of `trait SignatureScheme` - #156 Improved BLS correctness and API compliance with IRTF standard with better doc - + ### Fixed - [#76](https://github.com/EspressoSystems/jellyfish/pull/76) (`jf-plonk`) Splitting polynomials are masked to ensure zero-knowledge of Plonk diff --git a/plonk/src/transcript/rescue.rs b/plonk/src/transcript/rescue.rs index 730d16d61..5c47046e3 100644 --- a/plonk/src/transcript/rescue.rs +++ b/plonk/src/transcript/rescue.rs @@ -17,7 +17,7 @@ use ark_ec::{ use ark_std::vec::Vec; use jf_primitives::{ pcs::prelude::Commitment, - rescue::{Permutation as RescueHash, RescueParameter, STATE_SIZE}, + rescue::{sponge::RescueCRHF, RescueParameter, STATE_SIZE}, }; use jf_relation::gadgets::ecc::{Point, SWToTEConParam}; use jf_utils::{bytes_to_field_elements, field_switching, fq_to_fr_with_mask}; @@ -176,10 +176,8 @@ where // 2. challenge = state[0] in Fr // 3. transcript = Vec::new() - let hasher = RescueHash::default(); - let input = [self.state.as_ref(), self.transcript.as_ref()].concat(); - let tmp = hasher.sponge_with_padding(&input, STATE_SIZE); + let tmp = RescueCRHF::sponge_with_padding(&input, STATE_SIZE); let challenge = fq_to_fr_with_mask::(&tmp[0]); self.state.copy_from_slice(&tmp); self.transcript = Vec::new(); diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 6c606d56b..3fd98b2c4 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -18,6 +18,7 @@ ark-ed-on-bn254 = "0.3.0" ark-ff = "0.3.0" ark-poly = "0.3.0" ark-serialize = "0.3.0" +ark-sponge = "0.3.0" ark-std = { version = "0.3.0", default-features = false } blst = "0.3.10" crypto_box = "0.8.1" diff --git a/primitives/src/circuit/commitment.rs b/primitives/src/circuit/commitment.rs index 8004b3629..5a04bd964 100644 --- a/primitives/src/circuit/commitment.rs +++ b/primitives/src/circuit/commitment.rs @@ -8,7 +8,7 @@ use crate::{ circuit::rescue::RescueGadget, - rescue::{RescueParameter, RATE}, + rescue::{RescueParameter, CRHF_RATE}, utils::pad_with, }; use ark_std::vec; @@ -33,7 +33,7 @@ where fn commit(&mut self, input: &[Variable], blinding: Variable) -> Result { let mut msg = vec![blinding]; msg.extend_from_slice(input); - pad_with(&mut msg, RATE, self.zero()); + pad_with(&mut msg, CRHF_RATE, self.zero()); Ok(self.rescue_sponge_no_padding(&msg, 1)?[0]) } } diff --git a/primitives/src/circuit/rescue/native.rs b/primitives/src/circuit/rescue/native.rs index 43829dbdc..07b8285b2 100644 --- a/primitives/src/circuit/rescue/native.rs +++ b/primitives/src/circuit/rescue/native.rs @@ -652,7 +652,8 @@ mod tests { use super::{RescueGadget, RescueHelperGadget, RescueStateVar}; use crate::rescue::{ - Permutation, RescueMatrix, RescueParameter, RescueVector, PRP, RATE, STATE_SIZE, + sponge::{RescueCRHF, RescuePRF}, + Permutation, RescueMatrix, RescueParameter, RescueVector, CRHF_RATE, PRP, STATE_SIZE, }; use ark_ed_on_bls12_377::Fq as FqEd377; use ark_ed_on_bls12_381::Fq as FqEd381; @@ -965,14 +966,13 @@ mod tests { let mut circuit = PlonkCircuit::new_turbo_plonk(); let mut prng = ark_std::test_rng(); - let data = (0..2 * RATE).map(|_| F::rand(&mut prng)).collect_vec(); + let data = (0..2 * CRHF_RATE).map(|_| F::rand(&mut prng)).collect_vec(); let data_vars = data .iter() .map(|&x| circuit.create_variable(x).unwrap()) .collect_vec(); - let rescue_perm = Permutation::default(); - let expected_sponge = rescue_perm.sponge_no_padding(&data, 1).unwrap()[0]; + let expected_sponge = RescueCRHF::sponge_no_padding(&data, 1).unwrap()[0]; let sponge_var = circuit .rescue_sponge_no_padding(data_vars.as_slice(), 1) .unwrap()[0]; @@ -987,7 +987,7 @@ mod tests { // If the data length is not a multiple of RATE==3 then an error is triggered let mut circuit = PlonkCircuit::::new_turbo_plonk(); - let size = 2 * RATE + 1; // Non multiple of RATE + let size = 2 * CRHF_RATE + 1; // Non multiple of RATE let data = (0..size).map(|_| F::rand(&mut prng)).collect_vec(); let data_vars = data .iter() @@ -1022,17 +1022,13 @@ mod tests { .rescue_sponge_no_padding(&input_var, output_len) .unwrap(); - let rescue_hash = Permutation::default(); - // Check consistency between inputs for i in 0..rate { assert_eq!(input_vec[i], circuit.witness(input_var[i]).unwrap()); } // Check consistency between outputs - let expected_hash = rescue_hash - .sponge_no_padding(&input_vec, output_len) - .unwrap(); + let expected_hash = RescueCRHF::sponge_no_padding(&input_vec, output_len).unwrap(); for (e, f) in out_var.iter().zip(expected_hash.iter()) { assert_eq!(*f, circuit.witness(*e).unwrap()); @@ -1087,15 +1083,13 @@ mod tests { .rescue_sponge_with_padding(&input_var, output_len) .unwrap(); - let rescue_hash = Permutation::default(); - // Check consistency between inputs for i in 0..input_len { assert_eq!(input_vec[i], circuit.witness(input_var[i]).unwrap()); } // Check consistency between outputs - let expected_hash = rescue_hash.sponge_with_padding(&input_vec, output_len); + let expected_hash = RescueCRHF::sponge_with_padding(&input_vec, output_len); for (&e, &f) in expected_hash.iter().zip(out_var.iter()) { assert_eq!(e, circuit.witness(f).unwrap()); @@ -1131,10 +1125,8 @@ mod tests { .map(|&x| circuit.create_variable(x).unwrap()) .collect_vec(); - let perm = Permutation::default(); - let expected_fsks_output = perm - .full_state_keyed_sponge_no_padding(&key, &data, 1) - .unwrap(); + let expected_fsks_output = + RescuePRF::full_state_keyed_sponge_no_padding(&key, &data, 1).unwrap(); let fsks_var = circuit .rescue_full_state_keyed_sponge_no_padding(key_var, &data_vars) diff --git a/primitives/src/circuit/rescue/non_native.rs b/primitives/src/circuit/rescue/non_native.rs index b73f7d983..9c496d057 100644 --- a/primitives/src/circuit/rescue/non_native.rs +++ b/primitives/src/circuit/rescue/non_native.rs @@ -736,7 +736,8 @@ mod tests { use super::{RescueNonNativeGadget, RescueNonNativeHelperGadget, RescueNonNativeStateVar}; use crate::rescue::{ - Permutation, RescueMatrix, RescueParameter, RescueVector, PRP, RATE, STATE_SIZE, + sponge::{RescueCRHF, RescuePRF}, + Permutation, RescueMatrix, RescueParameter, RescueVector, CRHF_RATE, PRP, STATE_SIZE, }; use ark_bls12_377::Fq as Fq377; use ark_ed_on_bls12_377::Fq as FqEd377; @@ -1063,7 +1064,7 @@ mod tests { let mut prng = ark_std::test_rng(); // setup the inputs - let data_t: Vec = (0..2 * RATE).map(|_| T::rand(&mut prng)).collect_vec(); + let data_t: Vec = (0..2 * CRHF_RATE).map(|_| T::rand(&mut prng)).collect_vec(); let data_f: Vec = data_t.iter().map(|x| field_switching(x)).collect(); let data_vars: Vec> = data_f .iter() @@ -1071,8 +1072,7 @@ mod tests { .collect(); // sponge no padding with output length 1 - let rescue_perm = Permutation::::default(); - let expected_sponge = rescue_perm.sponge_no_padding(&data_t, 1).unwrap()[0]; + let expected_sponge = RescueCRHF::sponge_no_padding(&data_t, 1).unwrap()[0]; let sponge_var = circuit .rescue_sponge_no_padding::(data_vars.as_slice(), 1) .unwrap()[0]; @@ -1091,8 +1091,7 @@ mod tests { // general sponge no padding for output_len in 1..max_output_len { - let rescue_perm = Permutation::::default(); - let expected_sponge = rescue_perm.sponge_no_padding(&data_t, output_len).unwrap(); + let expected_sponge = RescueCRHF::sponge_no_padding(&data_t, output_len).unwrap(); let sponge_var = circuit .rescue_sponge_no_padding::(data_vars.as_slice(), output_len) .unwrap(); @@ -1111,7 +1110,7 @@ mod tests { // If the data length is not a multiple of RATE==3 then an error is triggered let mut circuit = PlonkCircuit::::new_ultra_plonk(RANGE_BIT_LEN_FOR_TEST); - let size = 2 * RATE + 1; // Non multiple of RATE + let size = 2 * CRHF_RATE + 1; // Non multiple of RATE let data_t = (0..size).map(|_| T::rand(&mut prng)).collect_vec(); let data_f: Vec = data_t.iter().map(|x| field_switching(x)).collect(); let data_vars: Vec> = data_f @@ -1153,8 +1152,7 @@ mod tests { .map(|x| FpElemVar::new_from_field_element(&mut circuit, x, m, None).unwrap()) .collect(); - let rescue_perm = Permutation::::default(); - let expected_sponge = rescue_perm.sponge_with_padding(&data_t, 1); + let expected_sponge = RescueCRHF::sponge_with_padding(&data_t, 1); // sponge with padding let sponge_var = circuit @@ -1175,8 +1173,7 @@ mod tests { // sponge full with padding for output_len in 1..max_output_len { - let rescue_perm = Permutation::::default(); - let expected_sponge = rescue_perm.sponge_with_padding(&data_t, output_len); + let expected_sponge = RescueCRHF::sponge_with_padding(&data_t, output_len); let sponge_var = circuit .rescue_sponge_with_padding::(data_vars.as_slice(), output_len) @@ -1220,8 +1217,6 @@ mod tests { .rescue_sponge_no_padding::(&input_var, 1) .unwrap()[0]; - let rescue_hash = Permutation::::default(); - // Check consistency between inputs for i in 0..rate { assert_eq!(input_vec_f[i], input_var[i].witness(&circuit).unwrap()); @@ -1229,7 +1224,8 @@ mod tests { // Check consistency between outputs let expected_hash = - rescue_hash.hash_3_to_1(&[input_vec_t[0], input_vec_t[1], input_vec_t[2]]); + RescueCRHF::sponge_no_padding(&[input_vec_t[0], input_vec_t[1], input_vec_t[2]], 1) + .unwrap()[0]; assert_eq!( field_switching::(&expected_hash), out_var.witness(&circuit).unwrap() @@ -1271,10 +1267,8 @@ mod tests { }) .collect(); - let perm = Permutation::::default(); - let expected_fsks_output = perm - .full_state_keyed_sponge_no_padding(&key_t, &data_t, 1) - .unwrap(); + let expected_fsks_output = + RescuePRF::full_state_keyed_sponge_no_padding(&key_t, &data_t, 1).unwrap(); let fsks_var = circuit .rescue_full_state_keyed_sponge_no_padding::(key_var, &data_vars) diff --git a/primitives/src/commitment.rs b/primitives/src/commitment.rs index a754c1b72..ce34f41c5 100644 --- a/primitives/src/commitment.rs +++ b/primitives/src/commitment.rs @@ -6,9 +6,11 @@ //! Implements a rescue hash based commitment scheme. +use ark_std::marker::PhantomData; + use crate::{ errors::PrimitivesError, - rescue::{Permutation, RescueParameter, RATE}, + rescue::{sponge::RescueCRHF, RescueParameter, CRHF_RATE}, }; use ark_std::{format, string::String, vec}; use jf_utils::pad_with_zeros; @@ -16,8 +18,8 @@ use jf_utils::pad_with_zeros; #[derive(Default)] /// Commitment instance for user defined input size (in scalar elements) pub struct Commitment { - hash: Permutation, input_len: usize, + phantom_f: PhantomData, } impl Commitment { @@ -25,8 +27,8 @@ impl Commitment { pub fn new(input_len: usize) -> Commitment { assert!(input_len > 0, "input_len must be positive"); Commitment { - hash: Permutation::default(), input_len, + phantom_f: PhantomData, } } /// Commits to `input` slice using blinding `blind`. Return @@ -44,8 +46,8 @@ impl Commitment { let mut msg = vec![*blind]; msg.extend_from_slice(input); // Ok to pad with 0's since input length is fixed for the commitment instance - pad_with_zeros(&mut msg, RATE); - let result_vec = self.hash.sponge_no_padding(msg.as_slice(), 1)?; + pad_with_zeros(&mut msg, CRHF_RATE); + let result_vec = RescueCRHF::sponge_no_padding(msg.as_slice(), 1)?; Ok(result_vec[0]) } /// Verifies `commitment` against `input` and `blind`. diff --git a/primitives/src/merkle_tree/examples.rs b/primitives/src/merkle_tree/examples.rs index abe6f7ae9..52b567418 100644 --- a/primitives/src/merkle_tree/examples.rs +++ b/primitives/src/merkle_tree/examples.rs @@ -8,7 +8,7 @@ //! E.g. Sparse merkle tree with BigUInt index. use super::{append_only::MerkleTree, prelude::RescueHash, DigestAlgorithm, Element, Index}; -use crate::rescue::{Permutation, RescueParameter}; +use crate::rescue::{sponge::RescueCRHF, RescueParameter}; use ark_ff::Field; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; use sha3::{Digest, Sha3_256}; @@ -21,14 +21,12 @@ pub struct Interval(pub F, pub F); impl DigestAlgorithm, u64, F> for RescueHash { fn digest(data: &[F]) -> F { - let perm = Permutation::default(); - perm.sponge_no_padding(data, 1).unwrap()[0] + RescueCRHF::::sponge_no_padding(data, 1).unwrap()[0] } fn digest_leaf(pos: &u64, elem: &Interval) -> F { let data = [F::from(*pos), elem.0, elem.1]; - let perm = Permutation::default(); - perm.sponge_no_padding(&data, 1).unwrap()[0] + RescueCRHF::::sponge_no_padding(&data, 1).unwrap()[0] } } diff --git a/primitives/src/merkle_tree/prelude.rs b/primitives/src/merkle_tree/prelude.rs index 3c9f9ceb3..3ac50f67a 100644 --- a/primitives/src/merkle_tree/prelude.rs +++ b/primitives/src/merkle_tree/prelude.rs @@ -16,7 +16,7 @@ pub use crate::{ }, }; -use crate::rescue::{Permutation, RescueParameter}; +use crate::rescue::{sponge::RescueCRHF, RescueParameter}; use ark_std::marker::PhantomData; use num_bigint::BigUint; use typenum::U3; @@ -28,14 +28,12 @@ pub struct RescueHash { impl DigestAlgorithm for RescueHash { fn digest(data: &[F]) -> F { - let perm = Permutation::default(); - perm.sponge_no_padding(data, 1).unwrap()[0] + RescueCRHF::::sponge_no_padding(data, 1).unwrap()[0] } fn digest_leaf(pos: &u64, elem: &F) -> F { let data = [F::zero(), F::from(*pos), *elem]; - let perm = Permutation::default(); - perm.sponge_no_padding(&data, 1).unwrap()[0] + RescueCRHF::::sponge_no_padding(&data, 1).unwrap()[0] } } @@ -44,14 +42,12 @@ pub type RescueMerkleTree = MerkleTree, u64, U3, F>; impl DigestAlgorithm for RescueHash { fn digest(data: &[F]) -> F { - let perm = Permutation::default(); - perm.sponge_no_padding(data, 1).unwrap()[0] + RescueCRHF::::sponge_no_padding(data, 1).unwrap()[0] } fn digest_leaf(pos: &BigUint, elem: &F) -> F { let data = [F::zero(), F::from(pos.clone()), *elem]; - let perm = Permutation::default(); - perm.sponge_no_padding(&data, 1).unwrap()[0] + RescueCRHF::::sponge_no_padding(&data, 1).unwrap()[0] } } diff --git a/primitives/src/prf.rs b/primitives/src/prf.rs index ece6171e9..ffc2130fe 100644 --- a/primitives/src/prf.rs +++ b/primitives/src/prf.rs @@ -7,9 +7,11 @@ //! This module implements a pseudo random function that is derived from //! the rescue hash function. +use ark_std::marker::PhantomData; + use crate::{ errors::PrimitivesError, - rescue::{Permutation, RescueParameter, STATE_SIZE}, + rescue::{sponge::RescuePRF, RescueParameter, STATE_SIZE}, }; use ark_ff::PrimeField; use ark_serialize::*; @@ -24,7 +26,7 @@ pub struct PRF { pub input_len: usize, /// Length of the output. pub output_len: usize, - permutation: Permutation, + phantom_f: PhantomData, } #[derive( @@ -61,7 +63,7 @@ impl PRF { PRF { input_len, output_len, - permutation: Default::default(), + phantom_f: PhantomData, } } @@ -84,11 +86,9 @@ impl PRF { // Ok to pad with 0's since input length is fixed for the PRF instance let mut padded = input.to_owned(); pad_with_zeros(&mut padded, STATE_SIZE); - self.permutation - .full_state_keyed_sponge_no_padding(&key.0, &padded, self.output_len) - .map_err(|_| { - PrimitivesError::InternalError("Bug in PRF: bad padding for input".to_string()) - }) + RescuePRF::full_state_keyed_sponge_no_padding(&key.0, &padded, self.output_len).map_err( + |_| PrimitivesError::InternalError("Bug in PRF: bad padding for input".to_string()), + ) } } diff --git a/primitives/src/rescue/mod.rs b/primitives/src/rescue/mod.rs index b66e12ece..eaef29a2b 100644 --- a/primitives/src/rescue/mod.rs +++ b/primitives/src/rescue/mod.rs @@ -21,16 +21,16 @@ #![deny(warnings)] pub mod errors; mod rescue_constants; +pub mod sponge; use ark_ff::{PrimeField, Zero}; -use ark_std::{string::ToString, vec, vec::Vec}; -use errors::RescueError; -use jf_utils::pad_with_zeros; +use ark_sponge::Absorb; +use ark_std::{vec, vec::Vec}; /// The state size of rescue hash. pub const STATE_SIZE: usize = 4; -/// The rate of rescue hash. -pub const RATE: usize = 3; +/// The rate of the sponge used in RescueCRHF. +pub const CRHF_RATE: usize = 3; /// The # of rounds of rescue hash. // In the paper, to derive ROUND: @@ -76,7 +76,7 @@ pub const RATE: usize = 3; pub const ROUNDS: usize = 12; /// This trait defines constants that are used for rescue hash functions. -pub trait RescueParameter: PrimeField { +pub trait RescueParameter: PrimeField + Absorb { /// parameter A, a.k.a., alpha const A: u64; /// parameter A^-1 @@ -91,7 +91,7 @@ pub trait RescueParameter: PrimeField { const PERMUTATION_ROUND_KEYS: [[&'static [u8]; 4]; 25]; } -#[derive(Clone, Debug, Eq, PartialEq, Copy)] +#[derive(Clone, Debug, Eq, PartialEq, Copy, Default)] /// Data type for rescue prp inputs, keys and internal data pub struct RescueVector { pub(crate) vec: [F; STATE_SIZE], @@ -134,15 +134,6 @@ impl RescueVector { } } - fn pad_smaller_chunk(input: &[F]) -> RescueVector { - assert!(input.len() < 4); - let mut vec = Self::zero().vec; - for (i, elem) in input.iter().enumerate() { - vec[i] = *elem; - } - RescueVector { vec } - } - fn pow(&mut self, exp: &[u64]) { self.vec.iter_mut().for_each(|elem| { *elem = elem.pow(exp); @@ -162,7 +153,6 @@ impl RescueVector { } fn add_assign_elems(&mut self, elems: &[F]) { - assert_eq!(elems.len(), STATE_SIZE); self.vec .iter_mut() .zip(elems.iter()) @@ -381,6 +371,7 @@ impl PRP { /// Instance of a unkeyed cryptographic permutation to be used for instantiation /// hashing, pseudo-random function, and other cryptographic primitives +#[derive(Clone)] pub struct Permutation { rescue_prp: PRP, round_keys: Vec>, @@ -428,120 +419,6 @@ impl Permutation { } } -// Implement Sponge Hashing -impl Permutation { - /// Sponge hashing based on rescue permutation for Bls12_381 scalar field - /// for RATE 3 and CAPACITY 1. It allows unrestricted variable length - /// input and number of output elements - pub fn sponge_with_padding(&self, input: &[F], num_output: usize) -> Vec { - // Pad input as follows: append a One, then pad with 0 until length is multiple - // of RATE - let mut padded = input.to_vec(); - padded.push(F::one()); - pad_with_zeros(&mut padded, RATE); - self.sponge_no_padding(padded.as_slice(), num_output) - .expect("Bug in JF Primitives : bad padding of input for FSKS construction") - } - - /// Sponge hashing based on rescue permutation for Bls12_381 scalar field - /// for RATE 3 and CAPACITY 1. It allows input length multiple of the - /// RATE and variable output length - pub fn sponge_no_padding(&self, input: &[F], num_output: usize) -> Result, RescueError> { - if input.len() % RATE != 0 { - return Err(RescueError::ParameterError( - "Rescue sponge Error : input to sponge hashing function is not multiple of RATE." - .to_string(), - )); - } - // ABSORB PHASE - let mut state = RescueVector::zero(); - input.chunks_exact(RATE).into_iter().for_each(|chunk| { - let block = RescueVector::pad_smaller_chunk(chunk); - state.add_assign(&block); - state = self.eval(&state) - }); - - // SQUEEZE PHASE - let mut result = vec![]; - let mut remaining = num_output; - // extract current rate before calling PRP again - loop { - let extract = remaining.min(RATE); - result.extend_from_slice(&state.vec[0..extract]); - remaining -= extract; - if remaining == 0 { - break; - } - state = self.eval(&state) - } - Ok(result) - } - - /// Compute the 3-to-1 rescue based hash function - /// * `input` - input of size RATE - /// * `returns` - hash value (single field element) - pub fn hash_3_to_1(&self, input: &[F; RATE]) -> F { - let input = [input[0], input[1], input[2], F::zero()]; - let res_vec = self.eval(&RescueVector::from(&input)); - res_vec.elems()[0] - } -} - -impl Permutation { - /// Pseudorandom function for Bls12_381 scalar field. It allows unrestricted - /// variable length input and number of output elements - pub fn full_state_keyed_sponge_with_padding( - &self, - key: &F, - input: &[F], - num_outputs: usize, - ) -> Vec { - let mut padded_input = input.to_vec(); - padded_input.push(F::one()); - pad_with_zeros(&mut padded_input, STATE_SIZE); - self.full_state_keyed_sponge_no_padding(key, padded_input.as_slice(), num_outputs) - .expect("Bug in JF Primitives : bad padding of input for FSKS construction") - } - - /// Pseudorandom function for Bls12_381 scalar field. It allows unrestricted - /// variable length input and number of output elements. Return error if - /// input is not multiple of STATE_SIZE = 4 - pub fn full_state_keyed_sponge_no_padding( - &self, - key: &F, - input: &[F], - num_outputs: usize, - ) -> Result, RescueError> { - if input.len() % STATE_SIZE != 0 { - return Err(RescueError::ParameterError( - "Rescue FSKS PRF Error: input to prf function is not multiple of STATE_SIZE." - .to_string(), - )); - } - // ABSORB PHASE - let mut state = RescueVector::zero(); - state.vec[STATE_SIZE - 1] = *key; - input.chunks_exact(STATE_SIZE).for_each(|chunk| { - state.add_assign_elems(chunk); - state = self.eval(&state); - }); - // SQUEEZE PHASE - let mut result = vec![]; - let mut remaining = num_outputs; - // extract current rate before calling PRP again - loop { - let extract = remaining.min(RATE); - result.extend_from_slice(&state.vec[0..extract]); - remaining -= extract; - if remaining == 0 { - break; - } - state = self.eval(&state) - } - Ok(result) - } -} - #[cfg(test)] mod test_prp { use crate::rescue::{RescueVector, PRP}; @@ -751,7 +628,10 @@ mod test_prp { #[cfg(test)] mod test_permutation { - use crate::rescue::{Permutation, RescueParameter, RescueVector, PRP}; + use crate::rescue::{ + sponge::{RescueCRHF, RescuePRF}, + Permutation, RescueParameter, RescueVector, PRP, + }; use ark_bls12_377::Fq as Fq377; use ark_ed_on_bls12_377::Fq as Fr377; use ark_ed_on_bls12_381::Fq as Fr381; @@ -881,7 +761,6 @@ mod test_permutation { fn test_sponge_helper() { let rescue_prp = PRP::default(); - let rescue_permutation = Permutation::from(rescue_prp.clone()); let mut prng = ark_std::test_rng(); let e0 = F::rand(&mut prng); let e1 = F::rand(&mut prng); @@ -892,14 +771,14 @@ mod test_permutation { let input = [e0, e1, e2, e3, e4, e5]; - let output = rescue_permutation.sponge_no_padding(&input, 1).unwrap()[0]; + let output = RescueCRHF::::sponge_no_padding(&input, 1).unwrap()[0]; let zero = RescueVector::zero(); let mut state = RescueVector { vec: [input[0], input[1], input[2], F::zero()], }; state = rescue_prp.prp(&zero, &state); - state.add_assign(&RescueVector::pad_smaller_chunk(&input[3..6])); + state.add_assign_elems(&input[3..6]); state = rescue_prp.prp(&zero, &state); assert_eq!(output, state.vec[0]); } @@ -913,78 +792,49 @@ mod test_permutation { } fn test_rescue_hash_on_0_vec_254() { - let rescue = Permutation::default(); let input = [Fr254::zero(); 3]; let expected = vec![ Fr254::from_le_bytes_mod_order(&OUTPUT254[0]), Fr254::from_le_bytes_mod_order(&OUTPUT254[1]), Fr254::from_le_bytes_mod_order(&OUTPUT254[2]), ]; - let real_output = rescue.sponge_no_padding(&input, 3).unwrap(); + let real_output = RescueCRHF::sponge_no_padding(&input, 3).unwrap(); assert_eq!(real_output, expected); } fn test_rescue_hash_on_0_vec_377() { - let rescue = Permutation::default(); let input = [Fr377::zero(); 3]; let expected = vec![ Fr377::from_le_bytes_mod_order(&OUTPUT377[0]), Fr377::from_le_bytes_mod_order(&OUTPUT377[1]), Fr377::from_le_bytes_mod_order(&OUTPUT377[2]), ]; - let real_output = rescue.sponge_no_padding(&input, 3).unwrap(); + let real_output = RescueCRHF::sponge_no_padding(&input, 3).unwrap(); assert_eq!(real_output, expected); } fn test_rescue_hash_on_0_vec_381() { - let rescue = Permutation::default(); let input = [Fr381::zero(); 3]; let expected = vec![ Fr381::from_le_bytes_mod_order(&OUTPUT381[0]), Fr381::from_le_bytes_mod_order(&OUTPUT381[1]), Fr381::from_le_bytes_mod_order(&OUTPUT381[2]), ]; - let real_output = rescue.sponge_no_padding(&input, 3).unwrap(); + let real_output = RescueCRHF::sponge_no_padding(&input, 3).unwrap(); assert_eq!(real_output, expected); } fn test_rescue_hash_on_0_vec_761() { - let rescue = Permutation::default(); let input = [Fq377::zero(); 3]; let expected = vec![ Fq377::from_le_bytes_mod_order(&OUTPUT761[0]), Fq377::from_le_bytes_mod_order(&OUTPUT761[1]), Fq377::from_le_bytes_mod_order(&OUTPUT761[2]), ]; - let real_output = rescue.sponge_no_padding(&input, 3).unwrap(); + let real_output = RescueCRHF::sponge_no_padding(&input, 3).unwrap(); assert_eq!(real_output, expected); } - #[test] - fn test_sponge_no_padding_errors() { - test_sponge_no_padding_errors_helper::(); - test_sponge_no_padding_errors_helper::(); - test_sponge_no_padding_errors_helper::(); - test_sponge_no_padding_errors_helper::(); - } - fn test_sponge_no_padding_errors_helper() { - let rescue = Permutation::default(); - - let input = vec![F::from(9u64); 3]; - assert!(rescue.sponge_no_padding(input.as_slice(), 1).is_ok()); - let input = vec![F::from(9u64); 12]; - assert!(rescue.sponge_no_padding(input.as_slice(), 1).is_ok()); - - // test should panic because number of inputs is not multiple of 3 - let input = vec![F::from(9u64); 10]; - assert!(rescue.sponge_no_padding(input.as_slice(), 1).is_err()); - let input = vec![F::from(9u64)]; - assert!(rescue.sponge_no_padding(input.as_slice(), 1).is_err()); - - let input = vec![]; - assert!(rescue.sponge_no_padding(input.as_slice(), 1).is_ok()); - } - #[test] fn test_fsks_no_padding_errors() { test_fsks_no_padding_errors_helper::(); @@ -993,31 +843,20 @@ mod test_permutation { test_fsks_no_padding_errors_helper::(); } fn test_fsks_no_padding_errors_helper() { - let rescue = Permutation::default(); let key = F::rand(&mut ark_std::test_rng()); let input = vec![F::from(9u64); 4]; - assert!(rescue - .full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1) - .is_ok()); + assert!(RescuePRF::full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1).is_ok()); let input = vec![F::from(9u64); 12]; - assert!(rescue - .full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1) - .is_ok()); + assert!(RescuePRF::full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1).is_ok()); // test should panic because number of inputs is not multiple of 3 let input = vec![F::from(9u64); 10]; - assert!(rescue - .full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1) - .is_err()); + assert!(RescuePRF::full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1).is_err()); let input = vec![F::from(9u64)]; - assert!(rescue - .full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1) - .is_err()); + assert!(RescuePRF::full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1).is_err()); let input = vec![]; - assert!(rescue - .full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1) - .is_ok()); + assert!(RescuePRF::full_state_keyed_sponge_no_padding(&key, input.as_slice(), 1).is_ok()); } #[test] @@ -1028,83 +867,67 @@ mod test_permutation { test_variable_output_sponge_and_fsks_helper::(); } fn test_variable_output_sponge_and_fsks_helper() { - let rescue = Permutation::default(); let input = [F::zero(), F::one(), F::zero()]; - assert_eq!(rescue.sponge_with_padding(&input, 0).len(), 0); - assert_eq!(rescue.sponge_with_padding(&input, 1).len(), 1); - assert_eq!(rescue.sponge_with_padding(&input, 2).len(), 2); - assert_eq!(rescue.sponge_with_padding(&input, 3).len(), 3); - assert_eq!(rescue.sponge_with_padding(&input, 10).len(), 10); - - assert_eq!(rescue.sponge_no_padding(&input, 0).unwrap().len(), 0); - assert_eq!(rescue.sponge_no_padding(&input, 1).unwrap().len(), 1); - assert_eq!(rescue.sponge_no_padding(&input, 2).unwrap().len(), 2); - assert_eq!(rescue.sponge_no_padding(&input, 3).unwrap().len(), 3); - assert_eq!(rescue.sponge_no_padding(&input, 10).unwrap().len(), 10); + assert_eq!(RescueCRHF::sponge_with_padding(&input, 0).len(), 0); + assert_eq!(RescueCRHF::sponge_with_padding(&input, 1).len(), 1); + assert_eq!(RescueCRHF::sponge_with_padding(&input, 2).len(), 2); + assert_eq!(RescueCRHF::sponge_with_padding(&input, 3).len(), 3); + assert_eq!(RescueCRHF::sponge_with_padding(&input, 10).len(), 10); + + assert_eq!(RescueCRHF::sponge_no_padding(&input, 0).unwrap().len(), 0); + assert_eq!(RescueCRHF::sponge_no_padding(&input, 1).unwrap().len(), 1); + assert_eq!(RescueCRHF::sponge_no_padding(&input, 2).unwrap().len(), 2); + assert_eq!(RescueCRHF::sponge_no_padding(&input, 3).unwrap().len(), 3); + assert_eq!(RescueCRHF::sponge_no_padding(&input, 10).unwrap().len(), 10); let key = F::rand(&mut ark_std::test_rng()); let input = [F::zero(), F::one(), F::zero(), F::zero()]; assert_eq!( - rescue - .full_state_keyed_sponge_with_padding(&key, &input, 0) - .len(), + RescuePRF::full_state_keyed_sponge_with_padding(&key, &input, 0).len(), 0 ); assert_eq!( - rescue - .full_state_keyed_sponge_with_padding(&key, &input, 1) - .len(), + RescuePRF::full_state_keyed_sponge_with_padding(&key, &input, 1).len(), 1 ); assert_eq!( - rescue - .full_state_keyed_sponge_with_padding(&key, &input, 2) - .len(), + RescuePRF::full_state_keyed_sponge_with_padding(&key, &input, 2).len(), 2 ); assert_eq!( - rescue - .full_state_keyed_sponge_with_padding(&key, &input, 4) - .len(), + RescuePRF::full_state_keyed_sponge_with_padding(&key, &input, 4).len(), 4 ); assert_eq!( - rescue - .full_state_keyed_sponge_with_padding(&key, &input, 10) - .len(), + RescuePRF::full_state_keyed_sponge_with_padding(&key, &input, 10).len(), 10 ); assert_eq!( - rescue - .full_state_keyed_sponge_no_padding(&key, &input, 0) + RescuePRF::full_state_keyed_sponge_no_padding(&key, &input, 0) .unwrap() .len(), 0 ); assert_eq!( - rescue - .full_state_keyed_sponge_no_padding(&key, &input, 1) + RescuePRF::full_state_keyed_sponge_no_padding(&key, &input, 1) .unwrap() .len(), 1 ); assert_eq!( - rescue - .full_state_keyed_sponge_no_padding(&key, &input, 2) + RescuePRF::full_state_keyed_sponge_no_padding(&key, &input, 2) .unwrap() .len(), 2 ); assert_eq!( - rescue - .full_state_keyed_sponge_no_padding(&key, &input, 4) + RescuePRF::full_state_keyed_sponge_no_padding(&key, &input, 4) .unwrap() .len(), 4 ); assert_eq!( - rescue - .full_state_keyed_sponge_no_padding(&key, &input, 10) + RescuePRF::full_state_keyed_sponge_no_padding(&key, &input, 10) .unwrap() .len(), 10 diff --git a/primitives/src/rescue/sponge.rs b/primitives/src/rescue/sponge.rs new file mode 100644 index 000000000..4799ef1e6 --- /dev/null +++ b/primitives/src/rescue/sponge.rs @@ -0,0 +1,335 @@ +// Copyright (c) 2022 Espresso Systems (espressosys.com) +// This file is part of the Jellyfish library. + +// You should have received a copy of the MIT License +// along with the Jellyfish library. If not, see . + +//! This file contains the APIs wrappers for ark-sponge + +use ark_ff::PrimeField; +use ark_sponge::{ + Absorb, CryptographicSponge, FieldBasedCryptographicSponge, FieldElementSize, SpongeExt, +}; +use ark_std::{string::ToString, vec, vec::Vec}; +use jf_utils::pad_with_zeros; + +use super::{ + errors::RescueError, Permutation, RescueParameter, RescueVector, CRHF_RATE, STATE_SIZE, +}; + +#[derive(Clone, Default)] +/// A rescue hash function consists of a permutation function and +/// an internal state. +struct RescueSponge { + pub(crate) state: RescueVector, + pub(crate) permutation: Permutation, +} + +/// CRHF +pub struct RescueCRHF { + sponge: RescueSponge, +} + +/// PRF +pub struct RescuePRF { + sponge: RescueSponge, +} + +impl RescueCRHF { + /// Sponge hashing based on rescue permutation for RATE 3. It allows + /// unrestricted variable length input and returns a vector of + /// `num_outputs` elements. + pub fn sponge_with_padding(input: &[F], num_outputs: usize) -> Vec { + // Pad input as follows: append a One, then pad with 0 until length is multiple + // of RATE + let mut padded = input.to_vec(); + padded.push(F::one()); + pad_with_zeros(&mut padded, CRHF_RATE); + Self::sponge_no_padding(padded.as_slice(), num_outputs) + .expect("Bug in JF Primitives : bad padding of input for FSKS construction") + } + + /// Sponge hashing based on rescue permutation for RATE 3 and CAPACITY 1. It + /// allows inputs with length that is a multiple of `CRHF_RATE` and + /// returns a vector of `num_outputs` elements. + pub fn sponge_no_padding(input: &[F], num_output: usize) -> Result, RescueError> { + if input.len() % CRHF_RATE != 0 { + return Err(RescueError::ParameterError( + "Rescue sponge Error : input to sponge hashing function is not multiple of RATE." + .to_string(), + )); + } + // ABSORB PHASE + let mut r = Self { + sponge: RescueSponge::from_state(RescueVector::zero(), &Permutation::default()), + }; + r.sponge.absorb(&input); + + // SQUEEZE PHASE + Ok(r.sponge.squeeze_native_field_elements(num_output)) + } +} + +impl RescuePRF { + /// Pseudorandom function based on rescue permutation for RATE 4. It allows + /// unrestricted variable length input and returns a vector of + /// `num_outputs` elements. + pub fn full_state_keyed_sponge_with_padding( + key: &F, + input: &[F], + num_outputs: usize, + ) -> Vec { + let mut padded_input = input.to_vec(); + padded_input.push(F::one()); + pad_with_zeros(&mut padded_input, STATE_SIZE); + Self::full_state_keyed_sponge_no_padding(key, padded_input.as_slice(), num_outputs) + .expect("Bug in JF Primitives : bad padding of input for FSKS construction") + } + + /// Pseudorandom function based on rescue permutation for RATE 4. It allows + /// inputs with length that is a multiple of `STATE_SIZE` and returns a + /// vector of `num_outputs` elements. + pub fn full_state_keyed_sponge_no_padding( + key: &F, + input: &[F], + num_outputs: usize, + ) -> Result, RescueError> { + if input.len() % STATE_SIZE != 0 { + return Err(RescueError::ParameterError( + "Rescue FSKS PRF Error: input to prf function is not multiple of STATE_SIZE." + .to_string(), + )); + } + // ABSORB PHASE + let mut state = RescueVector::zero(); + state.vec[STATE_SIZE - 1] = *key; + let mut r = Self { + sponge: RescueSponge::from_state(state, &Permutation::default()), + }; + r.sponge.absorb(&input); + + // SQUEEZE PHASE + Ok(r.sponge.squeeze_native_field_elements(num_outputs)) + } +} + +impl SpongeExt for RescueSponge { + type State = RescueVector; + + fn from_state(state: Self::State, permutation: &Self::Parameters) -> Self { + Self { + state, + permutation: permutation.clone(), + } + } + + fn into_state(self) -> Self::State { + self.state + } +} + +impl CryptographicSponge + for RescueSponge +{ + /// Parameters used by the sponge. + type Parameters = Permutation; + + /// Initialize a new instance of the sponge. + fn new(permutation: &Self::Parameters) -> Self { + Self { + state: RescueVector::default(), + permutation: permutation.clone(), + } + } + + /// Absorb an input into the sponge. + /// This function will absorb the entire input, in chunks of `RATE`, + /// even if the input lenght is not a multiple of `RATE`. + fn absorb(&mut self, input: &impl Absorb) { + let input_field_elements = input.to_sponge_field_elements_as_vec(); + + // Absorb input. + input_field_elements + .chunks(RATE) + .into_iter() + .for_each(|chunk| { + self.state.add_assign_elems(chunk); + self.state = self.permutation.eval(&self.state) + }); + } + + /// WARNING! This trait method is unimplemented and should not be used. + /// Only use the `CryptographicSponge` for squeezing native field elements. + fn squeeze_bytes(&mut self, _num_bytes: usize) -> Vec { + unimplemented!("Currently we only support squeezing native field elements!") + } + + /// WARNING! This trait method is unimplemented and should not be used. + /// Only use the `CryptographicSponge` for squeezing native field elements. + fn squeeze_bits(&mut self, _num_bits: usize) -> Vec { + unimplemented!("Currently we only support squeezing native field elements!") + } + + /// WARNING! This trait method is unimplemented and should not be used. + /// Use `squeeze_native_field_elements` instead. + fn squeeze_field_elements_with_sizes( + &mut self, + _sizes: &[FieldElementSize], + ) -> Vec { + unimplemented!("Currently we only support squeezing native field elements!") + } + + /// WARNING! This trait method is unimplemented and should not be used. + /// Use `squeeze_native_field_elements` instead. + fn squeeze_field_elements(&mut self, _num_elements: usize) -> Vec { + unimplemented!("Currently we only support squeezing native field elements!") + } + + /// Creates a new sponge with applied domain separation. + fn fork(&self, domain: &[u8]) -> Self { + let mut new_sponge = self.clone(); + + let mut input = Absorb::to_sponge_bytes_as_vec(&domain.len()); + input.extend_from_slice(domain); + new_sponge.absorb(&input); + + new_sponge + } +} + +/// The interface for field-based cryptographic sponge. +/// `T` is the native field used by the cryptographic sponge implementation. +impl FieldBasedCryptographicSponge + for RescueSponge +{ + /// Squeeze `num_elements` field elements from the sponge. + fn squeeze_native_field_elements(&mut self, num_elements: usize) -> Vec { + // SQUEEZE PHASE + let mut result = vec![]; + let mut remaining = num_elements; + // extract current rate before calling PRP again + loop { + let extract = remaining.min(RATE); + result.extend_from_slice(&self.state.vec[0..extract]); + remaining -= extract; + if remaining == 0 { + break; + } + self.state = self.permutation.eval(&self.state) + } + result + } + + /// WARNING! This trait method is unimplemented and should not be used. + /// Use `squeeze_native_field_elements` instead. + fn squeeze_native_field_elements_with_sizes(&mut self, _sizes: &[FieldElementSize]) -> Vec { + unimplemented!("Currently we only support squeezing native field elements!") + } +} + +#[cfg(test)] +mod test { + use super::*; + use ark_bls12_381::Fr; + use ark_ff::{One, UniformRand}; + use ark_sponge::{ + absorb, collect_sponge_bytes, collect_sponge_field_elements, AbsorbWithLength, + }; + use ark_std::test_rng; + + fn assert_different_encodings(a: &A, b: &A) { + let bytes1 = a.to_sponge_bytes_as_vec(); + let bytes2 = b.to_sponge_bytes_as_vec(); + assert_ne!(bytes1, bytes2); + + let sponge_param = Permutation::default(); + let mut sponge1 = RescueSponge::::new(&sponge_param); + let mut sponge2 = RescueSponge::::new(&sponge_param); + + sponge1.absorb(&a); + sponge2.absorb(&b); + + assert_ne!( + sponge1.squeeze_native_field_elements(3), + sponge2.squeeze_native_field_elements(3) + ); + } + + #[test] + fn single_field_element() { + let mut rng = test_rng(); + let elem1 = Fr::rand(&mut rng); + let elem2 = elem1 + Fr::one(); + + assert_different_encodings::(&elem1, &elem2) + } + + #[test] + fn list_with_constant_size_element() { + let mut rng = test_rng(); + let lst1: Vec<_> = (0..1024 * 8).map(|_| Fr::rand(&mut rng)).collect(); + let mut lst2 = lst1.to_vec(); + lst2[3] += Fr::one(); + + assert_different_encodings::(&lst1, &lst2) + } + + struct VariableSizeList(Vec); + + impl Absorb for VariableSizeList { + fn to_sponge_bytes(&self, dest: &mut Vec) { + self.0.to_sponge_bytes_with_length(dest) + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + self.0.to_sponge_field_elements_with_length(dest) + } + } + + #[test] + fn list_with_nonconstant_size_element() { + let lst1 = vec![ + VariableSizeList(vec![1u8, 2, 3, 4]), + VariableSizeList(vec![5, 6]), + ]; + let lst2 = vec![ + VariableSizeList(vec![1u8, 2]), + VariableSizeList(vec![3, 4, 5, 6]), + ]; + + assert_different_encodings::(&lst1, &lst2); + } + + #[test] + fn test_macros() { + let sponge_param = Permutation::default(); + let mut sponge1 = RescueSponge::::new(&sponge_param); + sponge1.absorb(&vec![1u8, 2, 3, 4, 5, 6]); + sponge1.absorb(&Fr::from(114514u128)); + + let mut sponge2 = RescueSponge::::new(&sponge_param); + absorb!(&mut sponge2, vec![1u8, 2, 3, 4, 5, 6], Fr::from(114514u128)); + + let expected = sponge1.squeeze_native_field_elements(3); + let actual = sponge2.squeeze_native_field_elements(3); + + assert_eq!(actual, expected); + + let mut expected = Vec::new(); + vec![6u8, 5, 4, 3, 2, 1].to_sponge_bytes(&mut expected); + Fr::from(42u8).to_sponge_bytes(&mut expected); + + let actual = collect_sponge_bytes!(vec![6u8, 5, 4, 3, 2, 1], Fr::from(42u8)); + + assert_eq!(actual, expected); + + let mut expected: Vec = Vec::new(); + vec![6u8, 5, 4, 3, 2, 1].to_sponge_field_elements(&mut expected); + Fr::from(42u8).to_sponge_field_elements(&mut expected); + + let actual: Vec = + collect_sponge_field_elements!(vec![6u8, 5, 4, 3, 2, 1], Fr::from(42u8)); + + assert_eq!(actual, expected); + } +} diff --git a/primitives/src/signatures/schnorr.rs b/primitives/src/signatures/schnorr.rs index 368eef9e9..f955f2168 100644 --- a/primitives/src/signatures/schnorr.rs +++ b/primitives/src/signatures/schnorr.rs @@ -11,7 +11,7 @@ use super::SignatureScheme; use crate::{ constants::CS_ID_SCHNORR, errors::PrimitivesError, - rescue::{Permutation, RescueParameter}, + rescue::{sponge::RescueCRHF, RescueParameter}, utils::curve_cofactor, }; use ark_ec::{ @@ -294,15 +294,14 @@ where /// Signature function #[allow(non_snake_case)] pub fn sign>(&self, msg: &[F], csid: B) -> Signature

{ - let hash = Permutation::default(); // Do we want to remove the instance description? let instance_description = F::from_be_bytes_mod_order(csid.as_ref()); let mut msg_input = vec![instance_description, fr_to_fq::(&self.sk.0)]; msg_input.extend(msg.iter()); - let r = fq_to_fr::(&hash.sponge_with_padding(&msg_input, 1)[0]); + let r = fq_to_fr::(&RescueCRHF::sponge_with_padding(&msg_input, 1)[0]); let R = Group::mul(&GroupProjective::

::prime_subgroup_generator(), &r); - let c = self.vk.challenge(&hash, &R, msg, csid); + let c = self.vk.challenge(&R, msg, csid); let s = c * self.sk.0 + r; Signature { s, R } @@ -367,8 +366,7 @@ where } // restrictive cofactorless verification - let hash = Permutation::::default(); - let c = self.challenge(&hash, &sig.R, msg, csid); + let c = self.challenge(&sig.R, msg, csid); let base = GroupProjective::

::prime_subgroup_generator(); let x = Group::mul(&base, &sig.s); @@ -394,7 +392,6 @@ where #[allow(non_snake_case)] fn challenge>( &self, - hash: &Permutation, R: &GroupProjective

, msg: &[F], csid: B, @@ -414,7 +411,7 @@ where ] }; challenge_input.extend(msg); - let challenge_fq = hash.sponge_with_padding(&challenge_input, 1)[0]; + let challenge_fq = RescueCRHF::sponge_with_padding(&challenge_input, 1)[0]; // this masking will drop the last byte, and the resulting // challenge will be 248 bits