jf_rescue/
lib.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the Jellyfish library.
3
4// You should have received a copy of the MIT License
5// along with the Jellyfish library. If not, see <https://mit-license.org/>.
6
7//! This module implements Rescue hash function over the following fields
8//! - bls12_377 base field
9//! - ed_on_bls12_377 base field
10//! - ed_on_bls12_381 base field
11//! - ed_on_bn254 base field
12//!
13//! It also has place holders for
14//! - bls12_381 base field
15//! - bn254 base field
16//! - bw6_761 base field
17//!
18//! Those three place holders should never be used.
19
20#![cfg_attr(not(feature = "std"), no_std)]
21#![deny(missing_docs)]
22#[cfg(test)]
23extern crate std;
24
25#[cfg(any(not(feature = "std"), target_has_atomic = "ptr"))]
26#[doc(hidden)]
27extern crate alloc;
28
29pub mod commitment;
30pub mod crhf;
31#[cfg(feature = "gadgets")]
32pub mod gadgets;
33pub mod prf;
34mod rescue_constants;
35pub mod sponge;
36
37use ark_crypto_primitives::sponge::Absorb;
38use ark_ff::{PrimeField, Zero};
39use ark_std::{string::String, vec, vec::Vec};
40use displaydoc::Display;
41
42/// Rescue error type
43#[derive(Debug, Display, Eq, PartialEq)]
44pub enum RescueError {
45    /// Bad parameter in function call, {0}
46    ParameterError(String),
47}
48
49impl ark_std::error::Error for RescueError {}
50
51/// The state size of rescue hash.
52pub const STATE_SIZE: usize = 4;
53/// The rate of the sponge used in RescueCRHF.
54pub const CRHF_RATE: usize = 3;
55
56/// The # of rounds of rescue hash.
57// In the paper, to derive ROUND:
58//  sage: m = 4
59//  sage: for N in range (13):
60//  ....:     t = m*N*3+3+2
61//  ....:     b = m*N + 3
62//  ....:     sec = factorial(t)/factorial(b)/factorial(t-b)
63//  ....:     print (N, RR(log(sec^2,2)))
64//
65// for alpha = 5, (i.e., BLS12-381 and BN254)
66//      10 224.672644456021
67//      11 246.589942930803
68//      12 268.516687541633
69// set ROUND = 12, we have 134 bits security
70//
71// for alpha = 11, (i.e. BLS12-377) we have l1 =
72//      7 227.364142668101
73//      8 258.421493926570
74//      9 289.491120346551
75//      10 320.571247089962
76//      11 351.660410749737
77//      12 382.757409540148
78// The smallest possible round number will be max(10, l1), which
79// means round = 10 gives 160 bits security
80//
81// There is also the script from
82//  https://github.com/EspressoSystems/Marvellous
83//
84// For unknown reasons, for all alpha >=5, the ROUND number is taken as if alpha
85// = 5. This parameter choice does not seem to be optimal
86//
87//  if (self.alpha == 3):
88//      self.Nb = max(10, 2*ceil((1.0 * security_level + 2) / (4*m)))
89//  elif (self.alpha == 5):
90//      self.Nb = max(10, 2*ceil((1.0 * security_level + 3) / (5.5*m)))
91//  else :
92//      self.Nb = max(10, 2*ceil((1.0 * security_level + 3) / (5.5*m)))
93//  # where m = 4
94//
95// For conservative purpose, we are setting ROUNDS = 12 for now.
96// We may consider to use ROUNDS = 10 for BLS12-377 (alpha = 11) in futures.
97pub const ROUNDS: usize = 12;
98
99/// This trait defines constants that are used for rescue hash functions.
100pub trait RescueParameter: PrimeField + Absorb {
101    /// parameter A, a.k.a., alpha
102    const A: u64;
103    /// parameter A^-1
104    const A_INV: &'static [u64];
105    /// MDS matrix
106    const MDS_LE: [[&'static [u8]; STATE_SIZE]; STATE_SIZE];
107    /// Initial vector.
108    const INIT_VEC_LE: [&'static [u8]; STATE_SIZE];
109    /// Injected keys for each round.
110    const KEY_INJECTION_LE: [[&'static [u8]; 4]; 2 * ROUNDS];
111    /// Permutation keys.
112    const PERMUTATION_ROUND_KEYS: [[&'static [u8]; 4]; 25];
113}
114
115#[derive(Clone, Debug, Eq, PartialEq, Copy, Default)]
116/// Data type for rescue prp inputs, keys and internal data
117pub struct RescueVector<F> {
118    pub(crate) vec: [F; STATE_SIZE],
119}
120
121// Public functions
122impl<F: PrimeField> RescueVector<F> {
123    /// zero vector
124    pub fn zero() -> RescueVector<F> {
125        RescueVector {
126            vec: [F::zero(); STATE_SIZE],
127        }
128    }
129
130    /// Return vector of the field elements
131    /// WARNING: may expose the internal state.
132    pub fn elems(&self) -> Vec<F> {
133        self.vec.to_vec()
134    }
135
136    /// Perform a linear transform of the vector.
137    /// Function needs to be public for circuits generation..
138    pub fn linear(&mut self, matrix: &RescueMatrix<F>, vector: &RescueVector<F>) {
139        let mut aux = matrix.mul_vec(self);
140        aux.add_assign(vector);
141        *self = aux
142    }
143}
144
145// Private functions
146impl<F: PrimeField> RescueVector<F> {
147    fn from_elems_le_bytes(e0: &[u8], e1: &[u8], e2: &[u8], e3: &[u8]) -> RescueVector<F> {
148        RescueVector {
149            vec: [
150                F::from_le_bytes_mod_order(e0),
151                F::from_le_bytes_mod_order(e1),
152                F::from_le_bytes_mod_order(e2),
153                F::from_le_bytes_mod_order(e3),
154            ],
155        }
156    }
157
158    fn pow(&mut self, exp: &[u64]) {
159        self.vec.iter_mut().for_each(|elem| {
160            *elem = elem.pow(exp);
161        });
162    }
163
164    fn add_assign(&mut self, vector: &RescueVector<F>) {
165        for (a, b) in self.vec.iter_mut().zip(vector.vec.iter()) {
166            a.add_assign(b);
167        }
168    }
169
170    fn add(&self, vector: &RescueVector<F>) -> RescueVector<F> {
171        let mut aux = *self;
172        aux.add_assign(vector);
173        aux
174    }
175
176    fn add_assign_elems(&mut self, elems: &[F]) {
177        self.vec
178            .iter_mut()
179            .zip(elems.iter())
180            .for_each(|(a, b)| a.add_assign(b));
181    }
182
183    fn dot_product(&self, vector: &RescueVector<F>) -> F {
184        let mut r = F::zero();
185        for (a, b) in self.vec.iter().zip(vector.vec.iter()) {
186            r.add_assign(&a.mul(b));
187        }
188        r
189    }
190}
191
192impl<F: RescueParameter> RescueVector<F> {
193    /// Helper function to compute f(M,x,c) = Mx^a + c.
194    /// Function needs to be public for circuits generation..
195    pub fn non_linear(&mut self, matrix: &RescueMatrix<F>, vector: &RescueVector<F>) {
196        let mut self_aux = *self;
197        self_aux.pow(&[F::A]);
198        let mut aux = matrix.mul_vec(&self_aux);
199        aux.add_assign(vector);
200        *self = aux;
201    }
202}
203
204impl<F: Copy> From<&[F]> for RescueVector<F> {
205    fn from(field_elems: &[F]) -> RescueVector<F> {
206        assert_eq!(field_elems.len(), STATE_SIZE);
207        RescueVector {
208            vec: [
209                field_elems[0],
210                field_elems[1],
211                field_elems[2],
212                field_elems[3],
213            ],
214        }
215    }
216}
217
218impl<F: Copy> From<&[F; STATE_SIZE]> for RescueVector<F> {
219    fn from(field_elems: &[F; STATE_SIZE]) -> RescueVector<F> {
220        RescueVector { vec: *field_elems }
221    }
222}
223
224/// A matrix that consists of `STATE_SIZE` number of rescue vectors.
225#[derive(Debug, Clone)]
226pub struct RescueMatrix<F> {
227    matrix: [RescueVector<F>; STATE_SIZE],
228}
229
230impl<F: PrimeField> From<&[RescueVector<F>; STATE_SIZE]> for RescueMatrix<F> {
231    fn from(vectors: &[RescueVector<F>; STATE_SIZE]) -> Self {
232        Self { matrix: *vectors }
233    }
234}
235
236impl<F: PrimeField> RescueMatrix<F> {
237    fn mul_vec(&self, vector: &RescueVector<F>) -> RescueVector<F> {
238        let mut result = [F::zero(); STATE_SIZE];
239        self.matrix
240            .iter()
241            .enumerate()
242            .for_each(|(i, row)| result[i] = row.dot_product(vector));
243        RescueVector { vec: result }
244    }
245
246    /// Accessing the i-th vector of the matrix.    
247    /// Function needs to be public for circuits generation..
248    /// WARNING: may expose the internal state.
249    pub fn vec(&self, i: usize) -> RescueVector<F> {
250        self.matrix[i]
251    }
252
253    /// Check if the matrix is empty.
254    pub fn is_empty(&self) -> bool {
255        self.matrix.is_empty()
256    }
257
258    /// Return the number of columns of the matrix.
259    pub fn len(&self) -> usize {
260        self.matrix.len()
261    }
262}
263
264// Rescue Pseudorandom Permutation (PRP) implementation for the BLS12_381 Scalar
265// field with 4 elements as key and input size. From the PRP it derives 3 hash
266// functions: 1. Sponge construction with arbitrary input and output length
267// 2. Sponge construction with input length multiple of the RATE (3) (no padding
268// needed) 3. 3 to 1 hashing (same construction as 1 and 2, but limiting the
269// input to 3 and output to 1
270//
271
272#[derive(Debug, Clone)]
273#[allow(clippy::upper_case_acronyms)]
274/// Rescue pseudo-random permutation (PRP) instance
275pub struct PRP<F> {
276    mds: RescueMatrix<F>,      // rescue permutation MDS matrix
277    init_vec: RescueVector<F>, // rescue permutation initial constants
278    key_injection: Vec<RescueVector<F>>, /* rescue permutation key injection constants to compute
279                                * round keys */
280}
281
282impl<F: RescueParameter> Default for PRP<F> {
283    fn default() -> Self {
284        let mut key_injection = Vec::with_capacity(2 * ROUNDS);
285        for bytes in F::KEY_INJECTION_LE.iter() {
286            key_injection.push(RescueVector::from_elems_le_bytes(
287                bytes[0], bytes[1], bytes[2], bytes[3],
288            ));
289        }
290        PRP {
291            mds: RescueMatrix::from(&[
292                RescueVector::from_elems_le_bytes(
293                    F::MDS_LE[0][0],
294                    F::MDS_LE[0][1],
295                    F::MDS_LE[0][2],
296                    F::MDS_LE[0][3],
297                ),
298                RescueVector::from_elems_le_bytes(
299                    F::MDS_LE[1][0],
300                    F::MDS_LE[1][1],
301                    F::MDS_LE[1][2],
302                    F::MDS_LE[1][3],
303                ),
304                RescueVector::from_elems_le_bytes(
305                    F::MDS_LE[2][0],
306                    F::MDS_LE[2][1],
307                    F::MDS_LE[2][2],
308                    F::MDS_LE[2][3],
309                ),
310                RescueVector::from_elems_le_bytes(
311                    F::MDS_LE[3][0],
312                    F::MDS_LE[3][1],
313                    F::MDS_LE[3][2],
314                    F::MDS_LE[3][3],
315                ),
316            ]),
317            init_vec: RescueVector::from_elems_le_bytes(
318                F::INIT_VEC_LE[0],
319                F::INIT_VEC_LE[1],
320                F::INIT_VEC_LE[2],
321                F::INIT_VEC_LE[3],
322            ),
323            key_injection,
324        }
325    }
326}
327
328impl<F: RescueParameter> PRP<F> {
329    /// Rescue pseudorandom permutation for Bls12381 scalars vectors of size 4
330    /// without key scheduled keys (scheduling occurs online)
331    pub fn prp(&self, key: &RescueVector<F>, input: &RescueVector<F>) -> RescueVector<F> {
332        let round_keys = self.key_schedule(key);
333        self.prp_with_round_keys(round_keys.as_slice(), input)
334    }
335
336    /// Rescue pseudorandom permutation for Bls12381 scalars vectors of size 4
337    /// using scheduled keys
338    pub fn prp_with_round_keys(
339        &self,
340        round_keys: &[RescueVector<F>],
341        input: &RescueVector<F>,
342    ) -> RescueVector<F> {
343        assert_eq!(round_keys.len(), 2 * ROUNDS + 1);
344        let mut perm_state = input.add(&round_keys[0]);
345        round_keys[1..].iter().enumerate().for_each(|(round, key)| {
346            if (round % 2).is_zero() {
347                perm_state.pow(F::A_INV);
348            } else {
349                perm_state.pow(&[F::A]);
350            }
351            perm_state.linear(&self.mds, key)
352        });
353        perm_state
354    }
355
356    /// Key scheduling for rescue based PRP for Bls12_381 scalars vector of size
357    /// 4
358    pub fn key_schedule(&self, key: &RescueVector<F>) -> Vec<RescueVector<F>> {
359        let mut aux = key.add(&self.init_vec);
360        let mut round_keys = vec![aux];
361        (0..2 * ROUNDS).for_each(|i| {
362            let exp = if (i % 2).is_zero() { F::A_INV } else { &[F::A] };
363            aux.pow(exp);
364            aux.linear(&self.mds, &self.key_injection[i]);
365            round_keys.push(aux);
366        });
367        round_keys
368    }
369
370    /// Return a pointer to the mds matrix.
371    /// Does not expose secret states.
372    #[inline]
373    pub fn mds_matrix_ref(&self) -> &RescueMatrix<F> {
374        &self.mds
375    }
376
377    /// Return a pointer to the key injection vectors.
378    /// Function needs to be public for circuits generation..
379    /// WARNING!!! May expose secret state if keys are supposed to be secret.
380    #[inline]
381    pub fn key_injection_vec_ref(&self) -> &[RescueVector<F>] {
382        &self.key_injection
383    }
384
385    /// Return a pointer to the initial vectors.
386    /// Does not expose secret states.
387    #[inline]
388    pub fn init_vec_ref(&self) -> &RescueVector<F> {
389        &self.init_vec
390    }
391}
392
393/// Instance of a unkeyed cryptographic permutation to be used for instantiation
394/// hashing, pseudo-random function, and other cryptographic primitives
395#[derive(Debug, Clone)]
396pub struct Permutation<F> {
397    rescue_prp: PRP<F>,
398    round_keys: Vec<RescueVector<F>>,
399}
400
401impl<F: RescueParameter> From<PRP<F>> for Permutation<F> {
402    fn from(rescue: PRP<F>) -> Self {
403        let mut keys: Vec<RescueVector<F>> = Vec::with_capacity(2 * ROUNDS + 1);
404        for key in F::PERMUTATION_ROUND_KEYS.iter() {
405            keys.push(RescueVector::from_elems_le_bytes(
406                key[0], key[1], key[2], key[3],
407            ))
408        }
409        Permutation {
410            rescue_prp: rescue,
411            round_keys: keys,
412        }
413    }
414}
415
416impl<F: RescueParameter> Default for Permutation<F> {
417    fn default() -> Self {
418        Permutation::from(PRP::default())
419    }
420}
421
422impl<F: RescueParameter> Permutation<F> {
423    /// Return a pointer to the round key.
424    /// Does not expose secret states.
425    #[inline]
426    pub fn round_keys_ref(&self) -> &[RescueVector<F>] {
427        self.round_keys.as_slice()
428    }
429
430    /// Return a pointer to the mds matrix.
431    /// Does not expose secret states.
432    #[inline]
433    pub fn mds_matrix_ref(&self) -> &RescueMatrix<F> {
434        self.rescue_prp.mds_matrix_ref()
435    }
436    /// Compute the permutation on RescueVector `input`
437    pub fn eval(&self, input: &RescueVector<F>) -> RescueVector<F> {
438        self.rescue_prp
439            .prp_with_round_keys(self.round_keys.as_slice(), input)
440    }
441}
442
443#[cfg(test)]
444mod test_prp {
445    use crate::{RescueVector, PRP};
446    use ark_bls12_377::Fq as Fq377;
447    use ark_bn254::Fq as Fq254;
448    use ark_ed_on_bls12_377::Fq as Fr377;
449    use ark_ed_on_bls12_381::Fq as Fr381;
450    use ark_ed_on_bn254::Fq as Fr254;
451
452    // hash output on vector [0, 0, 0, 0]
453    // this value is cross checked with sage script
454    // rescue761.Sponge([0,0,0,0], 4)
455    const OUTPUT761: [[u8; 48]; 4] = [
456        [
457            0x37, 0xBE, 0x12, 0x7E, 0xDF, 0x9C, 0xBF, 0xCE, 0x78, 0xE1, 0x4F, 0xEB, 0x69, 0xAC,
458            0x89, 0x53, 0xE7, 0xC4, 0x8D, 0x89, 0x90, 0x77, 0x64, 0x0D, 0xD0, 0x87, 0x42, 0xDD,
459            0x1F, 0x98, 0x30, 0xC8, 0x0F, 0x12, 0x6D, 0x7A, 0x49, 0xD3, 0x22, 0x2E, 0x12, 0xBA,
460            0x5B, 0x0E, 0x29, 0xB7, 0x2C, 0x01,
461        ],
462        [
463            0x68, 0xFE, 0x2E, 0x95, 0x57, 0xDA, 0x2E, 0x36, 0xEC, 0xC1, 0xC5, 0x8A, 0x19, 0x50,
464            0xD7, 0xBE, 0x11, 0x00, 0x3D, 0x5B, 0xAA, 0x8C, 0xF8, 0x45, 0x6F, 0xDC, 0xE4, 0x1F,
465            0xF0, 0x35, 0xC7, 0x62, 0x6A, 0xC2, 0x33, 0xE7, 0x98, 0x9F, 0x26, 0x2A, 0x6E, 0x89,
466            0xD5, 0x43, 0x21, 0xF8, 0x67, 0x01,
467        ],
468        [
469            0x84, 0xB4, 0x93, 0x04, 0x3B, 0x23, 0x3A, 0x1B, 0x43, 0xC3, 0x61, 0x61, 0x1B, 0xA0,
470            0x59, 0xFB, 0x2E, 0x88, 0x76, 0x62, 0x28, 0xBB, 0x32, 0x6F, 0x27, 0x1C, 0xA9, 0xCA,
471            0x60, 0xC1, 0xE0, 0x7A, 0x7D, 0x37, 0x2F, 0x95, 0x75, 0xDD, 0x37, 0x2A, 0x70, 0xD1,
472            0xE4, 0x55, 0xDB, 0x50, 0x2F, 0x00,
473        ],
474        [
475            0x4E, 0x01, 0x9E, 0x8A, 0x7F, 0x6F, 0x3B, 0xDE, 0x7F, 0xF5, 0x58, 0x0B, 0x1A, 0x34,
476            0x95, 0x8D, 0xBC, 0x94, 0x88, 0xD8, 0x5D, 0x25, 0x7A, 0xB0, 0xCC, 0x72, 0xFE, 0x36,
477            0xC3, 0x13, 0xCB, 0x1B, 0x7A, 0x69, 0xCF, 0xCC, 0xAB, 0x2B, 0x55, 0x11, 0x1E, 0xC5,
478            0x7C, 0xFC, 0x47, 0x7D, 0x9D, 0x01,
479        ],
480    ];
481
482    // hash output on vector [0, 0, 0, 0]
483    // this value is cross checked with sage script
484    // rescue381.Sponge([0,0,0,0], 4)
485    const OUTPUT381: [[u8; 32]; 4] = [
486        [
487            0x12, 0x53, 0x24, 0x66, 0x84, 0xA2, 0x4D, 0x2B, 0xC7, 0x28, 0x3E, 0x0F, 0x80, 0xDF,
488            0x1A, 0xC3, 0x5B, 0xA1, 0xA9, 0x5B, 0x46, 0x60, 0xBD, 0xED, 0xA6, 0xD1, 0x43, 0xB7,
489            0x60, 0xCA, 0x59, 0x0D,
490        ],
491        [
492            0x1B, 0xBE, 0xAB, 0x6C, 0xAB, 0x62, 0xB7, 0xAB, 0x19, 0xDF, 0xFF, 0x4D, 0x73, 0xB5,
493            0x78, 0x30, 0x72, 0xC0, 0xC6, 0xDA, 0x1F, 0x10, 0xAD, 0xD1, 0x28, 0x65, 0xB4, 0x94,
494            0x6F, 0xAC, 0xE5, 0x4B,
495        ],
496        [
497            0x07, 0x86, 0xBD, 0x9A, 0xB3, 0x35, 0x96, 0x22, 0xF0, 0xE5, 0xEA, 0xCC, 0x9C, 0x79,
498            0x89, 0x1F, 0x9D, 0x1D, 0x43, 0x44, 0xCC, 0xA9, 0x9A, 0xB0, 0x0E, 0xC0, 0x57, 0x6B,
499            0x07, 0xF8, 0x53, 0x06,
500        ],
501        [
502            0x9C, 0x23, 0x34, 0xB3, 0x0A, 0xCD, 0x94, 0x11, 0x49, 0xC0, 0x9D, 0x90, 0x7E, 0x7E,
503            0xC8, 0x51, 0x42, 0xD3, 0xCD, 0x5D, 0x05, 0x13, 0x31, 0x66, 0x4D, 0x36, 0x98, 0xCE,
504            0xAC, 0x44, 0x5C, 0x60,
505        ],
506    ];
507    // this value is cross checked with sage script
508    // rescue377.Sponge([0,0,0,0], 4)
509    const OUTPUT377: [[u8; 32]; 4] = [
510        [
511            0x65, 0xF2, 0xF2, 0x74, 0x15, 0x7A, 0x5A, 0xB5, 0xE0, 0x86, 0x46, 0x9D, 0xAE, 0x27,
512            0x29, 0xE0, 0x08, 0x39, 0x0D, 0xA6, 0x44, 0x5E, 0x20, 0x76, 0x23, 0x42, 0xDA, 0xF0,
513            0x49, 0xA3, 0x51, 0x02,
514        ],
515        [
516            0x67, 0xB5, 0x6A, 0xBA, 0x4B, 0xB8, 0x0F, 0xE2, 0xFC, 0x3D, 0x7E, 0xFC, 0x70, 0xCA,
517            0x3D, 0x1D, 0xAC, 0xDD, 0xEA, 0x62, 0x81, 0xD7, 0x08, 0x0B, 0x38, 0x5F, 0x0A, 0x68,
518            0xEC, 0xED, 0x53, 0x02,
519        ],
520        [
521            0x10, 0xC5, 0xA0, 0xA1, 0x8E, 0x8D, 0xBC, 0xAD, 0x99, 0xC3, 0xB4, 0xE9, 0x22, 0xC9,
522            0xB1, 0xCF, 0x35, 0x46, 0xE3, 0x52, 0x99, 0x5B, 0xBE, 0x6E, 0x08, 0xFF, 0x4B, 0x2F,
523            0xCE, 0xF0, 0xCB, 0x0A,
524        ],
525        [
526            0x33, 0xB0, 0xD0, 0x58, 0xE9, 0x25, 0x15, 0xB2, 0x8A, 0x9D, 0x16, 0x04, 0xEB, 0x26,
527            0xC4, 0x0E, 0x3F, 0xBF, 0xCF, 0x49, 0x20, 0xA8, 0x89, 0xE2, 0x16, 0x2D, 0x76, 0x19,
528            0xDF, 0x01, 0x02, 0x09,
529        ],
530    ];
531
532    // this value is cross checked with sage script
533    // rescue254.Sponge([0,0,0,0], 4)
534    const OUTPUT254: [[u8; 32]; 4] = [
535        [
536            0xDD, 0xE7, 0x55, 0x8E, 0x14, 0xF9, 0x4C, 0xEE, 0x9F, 0xCC, 0xB2, 0x02, 0xFC, 0x0E,
537            0x54, 0x21, 0xF2, 0xAA, 0xB8, 0x48, 0x05, 0xDB, 0x9B, 0x7A, 0xD2, 0x36, 0xA5, 0xF1,
538            0x49, 0x77, 0xB4, 0x17,
539        ],
540        [
541            0x43, 0x5F, 0x99, 0x3C, 0xB7, 0xB3, 0x84, 0x74, 0x4E, 0x80, 0x83, 0xFF, 0x73, 0x20,
542            0x07, 0xD9, 0x7B, 0xEC, 0x4B, 0x90, 0x48, 0x1D, 0xFD, 0x72, 0x4C, 0xF0, 0xA5, 0x7C,
543            0xDC, 0x68, 0xC0, 0x25,
544        ],
545        [
546            0x2C, 0x7B, 0x21, 0x09, 0x9D, 0x10, 0xE9, 0x5C, 0x36, 0x3E, 0x6D, 0x20, 0x28, 0xBB,
547            0xDB, 0x1E, 0xED, 0xF4, 0x22, 0x9B, 0x3A, 0xEE, 0x1E, 0x6F, 0x89, 0x13, 0x3D, 0x1E,
548            0x4C, 0xA0, 0xA6, 0x23,
549        ],
550        [
551            0x25, 0x9B, 0x47, 0xA2, 0x29, 0xFD, 0xC1, 0x08, 0xA9, 0xD1, 0x44, 0x71, 0x15, 0x8A,
552            0x5A, 0x1A, 0x55, 0x5B, 0x88, 0xAE, 0xD6, 0xF6, 0x57, 0xD3, 0x33, 0x07, 0xE1, 0x5B,
553            0x71, 0x5F, 0x12, 0x25,
554        ],
555    ];
556
557    // this value is cross checked with sage script
558    // rescue254.Sponge([0,0,0,0], 4)
559    const OUTPUTFQ254: [[u8; 32]; 4] = [
560        [
561            0xC9, 0xAC, 0x42, 0x08, 0x04, 0x18, 0xFC, 0x10, 0xA4, 0x41, 0xF4, 0xF9, 0x06, 0x2B,
562            0xE2, 0x87, 0xB7, 0x7A, 0xC4, 0x65, 0x8C, 0xE0, 0xE9, 0x78, 0xB0, 0x48, 0x39, 0xB6,
563            0x96, 0x9B, 0x60, 0x1B,
564        ],
565        [
566            0x00, 0xC2, 0xF9, 0x07, 0x54, 0x25, 0xF2, 0xC6, 0x75, 0x21, 0x01, 0x14, 0xF6, 0xD1,
567            0xAF, 0xE1, 0x0D, 0xD4, 0xFC, 0xEC, 0x15, 0xF3, 0x7D, 0x9A, 0x91, 0x26, 0x51, 0xDE,
568            0xC8, 0x8A, 0x19, 0x09,
569        ],
570        [
571            0x78, 0xDB, 0xB7, 0xEA, 0xD4, 0x35, 0x5E, 0xED, 0xAE, 0x14, 0xD6, 0xB1, 0xE8, 0x0D,
572            0xB4, 0xA7, 0x29, 0x9B, 0xBA, 0x6F, 0xBD, 0xA4, 0xEB, 0x52, 0x7D, 0xD5, 0x9B, 0x03,
573            0x17, 0x83, 0x06, 0x1D,
574        ],
575        [
576            0x72, 0xC6, 0xB8, 0xF9, 0x9E, 0xF3, 0xDA, 0x20, 0xED, 0x3D, 0xE4, 0x39, 0x87, 0x28,
577            0xE9, 0x25, 0x0D, 0x8D, 0x57, 0xCE, 0xEE, 0xCA, 0x35, 0xFB, 0x8E, 0x7E, 0xE3, 0x32,
578            0xDA, 0x03, 0x3F, 0x1B,
579        ],
580    ];
581
582    #[test]
583    fn test_rescue_perm_on_0_vec() {
584        test_rescue_perm_on_0_vec_fq254();
585        test_rescue_perm_on_0_vec_254();
586        test_rescue_perm_on_0_vec_377();
587        test_rescue_perm_on_0_vec_381();
588        test_rescue_perm_on_0_vec_761();
589    }
590
591    fn test_rescue_perm_on_0_vec_fq254() {
592        let rescue = PRP::<Fq254>::default();
593        let key = RescueVector::zero();
594        let input = RescueVector::zero();
595        let expected = RescueVector::from_elems_le_bytes(
596            &OUTPUTFQ254[0],
597            &OUTPUTFQ254[1],
598            &OUTPUTFQ254[2],
599            &OUTPUTFQ254[3],
600        );
601        let real_output = rescue.prp(&key, &input);
602        let round_keys = rescue.key_schedule(&key);
603
604        let real_output_with_round_keys = rescue.prp_with_round_keys(&round_keys, &input);
605        assert_eq!(real_output, real_output_with_round_keys);
606        assert_eq!(real_output, expected);
607    }
608
609    fn test_rescue_perm_on_0_vec_254() {
610        let rescue = PRP::<Fr254>::default();
611        let key = RescueVector::zero();
612        let input = RescueVector::zero();
613        let expected = RescueVector::from_elems_le_bytes(
614            &OUTPUT254[0],
615            &OUTPUT254[1],
616            &OUTPUT254[2],
617            &OUTPUT254[3],
618        );
619        let real_output = rescue.prp(&key, &input);
620        let round_keys = rescue.key_schedule(&key);
621        let real_output_with_round_keys = rescue.prp_with_round_keys(&round_keys, &input);
622        assert_eq!(real_output, real_output_with_round_keys);
623        assert_eq!(real_output, expected);
624    }
625
626    fn test_rescue_perm_on_0_vec_381() {
627        let rescue = PRP::<Fr381>::default();
628        let key = RescueVector::zero();
629        let input = RescueVector::zero();
630        let expected = RescueVector::from_elems_le_bytes(
631            &OUTPUT381[0],
632            &OUTPUT381[1],
633            &OUTPUT381[2],
634            &OUTPUT381[3],
635        );
636        let real_output = rescue.prp(&key, &input);
637        let round_keys = rescue.key_schedule(&key);
638        let real_output_with_round_keys = rescue.prp_with_round_keys(&round_keys, &input);
639
640        assert_eq!(real_output, real_output_with_round_keys);
641        assert_eq!(real_output, expected);
642    }
643
644    fn test_rescue_perm_on_0_vec_377() {
645        let rescue = PRP::<Fr377>::default();
646        let key = RescueVector::zero();
647        let input = RescueVector::zero();
648        let expected = RescueVector::from_elems_le_bytes(
649            &OUTPUT377[0],
650            &OUTPUT377[1],
651            &OUTPUT377[2],
652            &OUTPUT377[3],
653        );
654        let real_output = rescue.prp(&key, &input);
655        let round_keys = rescue.key_schedule(&key);
656        let real_output_with_round_keys = rescue.prp_with_round_keys(&round_keys, &input);
657        assert_eq!(real_output, real_output_with_round_keys);
658        assert_eq!(real_output, expected);
659    }
660
661    fn test_rescue_perm_on_0_vec_761() {
662        let rescue = PRP::<Fq377>::default();
663        let key = RescueVector::zero();
664        let input = RescueVector::zero();
665        let expected = RescueVector::from_elems_le_bytes(
666            &OUTPUT761[0],
667            &OUTPUT761[1],
668            &OUTPUT761[2],
669            &OUTPUT761[3],
670        );
671        let real_output = rescue.prp(&key, &input);
672        let round_keys = rescue.key_schedule(&key);
673        let real_output_with_round_keys = rescue.prp_with_round_keys(&round_keys, &input);
674        assert_eq!(real_output, real_output_with_round_keys);
675        assert_eq!(real_output, expected);
676    }
677
678    // printing vectors as hex bytes little endian
679    // use ark_ff::{BigInteger, PrimeField};
680    // use ark_std::{format, println, string::String, vec::Vec};
681    // #[test]
682    // fn print() {
683    //     let rescue_hash = PRP::<Fq254>::default();
684    //     println!("KeySchedule:");
685    //     let keys = rescue_hash.key_schedule(&RescueVector::zero());
686    //     println!("[");
687    //     for key in keys {
688    //         for elem in key.vec.iter() {
689    //             let str: Vec<String> = elem
690    //                 .into_bigint()
691    //                 .to_bytes_le()
692    //                 .iter()
693    //                 .map(|b| format!("0x{:02X},", b))
694    //                 .collect();
695    //             println!("&[{}],", str.join(" "));
696    //         }
697    //         println!("],[");
698    //     }
699    // }
700}
701
702#[cfg(test)]
703mod test_permutation {
704    use crate::{crhf::RescueCRHF, Permutation, RescueParameter, RescueVector, PRP};
705    use ark_bls12_377::Fq as Fq377;
706    use ark_bn254::Fq as Fq254;
707    use ark_ed_on_bls12_377::Fq as Fr377;
708    use ark_ed_on_bls12_381::Fq as Fr381;
709    use ark_ed_on_bn254::Fq as Fr254;
710    use ark_ff::PrimeField;
711    use ark_std::{vec, Zero};
712
713    #[test]
714    fn test_round_keys() {
715        test_round_keys_helper::<Fq254>();
716        test_round_keys_helper::<Fr254>();
717        test_round_keys_helper::<Fr377>();
718        test_round_keys_helper::<Fr381>();
719        test_round_keys_helper::<Fq377>();
720    }
721
722    fn test_round_keys_helper<F: RescueParameter>() {
723        let rescue_perm = PRP::<F>::default();
724        let rescue_hash = Permutation::default();
725        let zero = RescueVector::zero();
726        let keys2 = rescue_perm.key_schedule(&zero);
727
728        // // the following code is used to dump the key schedule to screen
729        // // in a sage friendly format
730        // for e in keys2.iter() {
731        //     for f in e.vec.iter() {
732        //         ark_std::println!("permutation_round_key.append({})",
733        // f.into_bigint());     }
734        // }
735        // assert!(false);
736
737        assert_eq!(rescue_hash.round_keys, keys2);
738    }
739
740    // hash output on vector [0, 0, 0, 0]
741    // this value is cross checked with sage script
742    // first three vectors of rescue761.Sponge([0,0,0,0], 4)
743    const OUTPUT761: [[u8; 48]; 3] = [
744        [
745            0x37, 0xBE, 0x12, 0x7E, 0xDF, 0x9C, 0xBF, 0xCE, 0x78, 0xE1, 0x4F, 0xEB, 0x69, 0xAC,
746            0x89, 0x53, 0xE7, 0xC4, 0x8D, 0x89, 0x90, 0x77, 0x64, 0x0D, 0xD0, 0x87, 0x42, 0xDD,
747            0x1F, 0x98, 0x30, 0xC8, 0x0F, 0x12, 0x6D, 0x7A, 0x49, 0xD3, 0x22, 0x2E, 0x12, 0xBA,
748            0x5B, 0x0E, 0x29, 0xB7, 0x2C, 0x01,
749        ],
750        [
751            0x68, 0xFE, 0x2E, 0x95, 0x57, 0xDA, 0x2E, 0x36, 0xEC, 0xC1, 0xC5, 0x8A, 0x19, 0x50,
752            0xD7, 0xBE, 0x11, 0x00, 0x3D, 0x5B, 0xAA, 0x8C, 0xF8, 0x45, 0x6F, 0xDC, 0xE4, 0x1F,
753            0xF0, 0x35, 0xC7, 0x62, 0x6A, 0xC2, 0x33, 0xE7, 0x98, 0x9F, 0x26, 0x2A, 0x6E, 0x89,
754            0xD5, 0x43, 0x21, 0xF8, 0x67, 0x01,
755        ],
756        [
757            0x84, 0xB4, 0x93, 0x04, 0x3B, 0x23, 0x3A, 0x1B, 0x43, 0xC3, 0x61, 0x61, 0x1B, 0xA0,
758            0x59, 0xFB, 0x2E, 0x88, 0x76, 0x62, 0x28, 0xBB, 0x32, 0x6F, 0x27, 0x1C, 0xA9, 0xCA,
759            0x60, 0xC1, 0xE0, 0x7A, 0x7D, 0x37, 0x2F, 0x95, 0x75, 0xDD, 0x37, 0x2A, 0x70, 0xD1,
760            0xE4, 0x55, 0xDB, 0x50, 0x2F, 0x00,
761        ],
762    ];
763
764    // hash output on vector [0, 0, 0, 0]
765    // this value is cross checked with sage script
766    // first three vectors of rescue254.Sponge([0,0,0,0], 4)
767    const OUTPUT254: [[u8; 32]; 3] = [
768        [
769            0xDD, 0xE7, 0x55, 0x8E, 0x14, 0xF9, 0x4C, 0xEE, 0x9F, 0xCC, 0xB2, 0x02, 0xFC, 0x0E,
770            0x54, 0x21, 0xF2, 0xAA, 0xB8, 0x48, 0x05, 0xDB, 0x9B, 0x7A, 0xD2, 0x36, 0xA5, 0xF1,
771            0x49, 0x77, 0xB4, 0x17,
772        ],
773        [
774            0x43, 0x5F, 0x99, 0x3C, 0xB7, 0xB3, 0x84, 0x74, 0x4E, 0x80, 0x83, 0xFF, 0x73, 0x20,
775            0x07, 0xD9, 0x7B, 0xEC, 0x4B, 0x90, 0x48, 0x1D, 0xFD, 0x72, 0x4C, 0xF0, 0xA5, 0x7C,
776            0xDC, 0x68, 0xC0, 0x25,
777        ],
778        [
779            0x2C, 0x7B, 0x21, 0x09, 0x9D, 0x10, 0xE9, 0x5C, 0x36, 0x3E, 0x6D, 0x20, 0x28, 0xBB,
780            0xDB, 0x1E, 0xED, 0xF4, 0x22, 0x9B, 0x3A, 0xEE, 0x1E, 0x6F, 0x89, 0x13, 0x3D, 0x1E,
781            0x4C, 0xA0, 0xA6, 0x23,
782        ],
783    ];
784    // hash output on vector [0, 0, 0, 0]
785    // this value is cross checked with sage script
786    // first three vectors of rescue377.Sponge([0,0,0,0], 4)
787    const OUTPUT377: [[u8; 32]; 3] = [
788        [
789            0x65, 0xF2, 0xF2, 0x74, 0x15, 0x7A, 0x5A, 0xB5, 0xE0, 0x86, 0x46, 0x9D, 0xAE, 0x27,
790            0x29, 0xE0, 0x08, 0x39, 0x0D, 0xA6, 0x44, 0x5E, 0x20, 0x76, 0x23, 0x42, 0xDA, 0xF0,
791            0x49, 0xA3, 0x51, 0x02,
792        ],
793        [
794            0x67, 0xB5, 0x6A, 0xBA, 0x4B, 0xB8, 0x0F, 0xE2, 0xFC, 0x3D, 0x7E, 0xFC, 0x70, 0xCA,
795            0x3D, 0x1D, 0xAC, 0xDD, 0xEA, 0x62, 0x81, 0xD7, 0x08, 0x0B, 0x38, 0x5F, 0x0A, 0x68,
796            0xEC, 0xED, 0x53, 0x02,
797        ],
798        [
799            0x10, 0xC5, 0xA0, 0xA1, 0x8E, 0x8D, 0xBC, 0xAD, 0x99, 0xC3, 0xB4, 0xE9, 0x22, 0xC9,
800            0xB1, 0xCF, 0x35, 0x46, 0xE3, 0x52, 0x99, 0x5B, 0xBE, 0x6E, 0x08, 0xFF, 0x4B, 0x2F,
801            0xCE, 0xF0, 0xCB, 0x0A,
802        ],
803    ];
804
805    // hash output on vector [0, 0, 0, 0]
806    // this value is cross checked with sage script
807    // first three vectors of rescue381.Sponge([0,0,0,0], 4)
808    const OUTPUT381: [[u8; 32]; 3] = [
809        [
810            0x12, 0x53, 0x24, 0x66, 0x84, 0xA2, 0x4D, 0x2B, 0xC7, 0x28, 0x3E, 0x0F, 0x80, 0xDF,
811            0x1A, 0xC3, 0x5B, 0xA1, 0xA9, 0x5B, 0x46, 0x60, 0xBD, 0xED, 0xA6, 0xD1, 0x43, 0xB7,
812            0x60, 0xCA, 0x59, 0x0D,
813        ],
814        [
815            0x1B, 0xBE, 0xAB, 0x6C, 0xAB, 0x62, 0xB7, 0xAB, 0x19, 0xDF, 0xFF, 0x4D, 0x73, 0xB5,
816            0x78, 0x30, 0x72, 0xC0, 0xC6, 0xDA, 0x1F, 0x10, 0xAD, 0xD1, 0x28, 0x65, 0xB4, 0x94,
817            0x6F, 0xAC, 0xE5, 0x4B,
818        ],
819        [
820            0x07, 0x86, 0xBD, 0x9A, 0xB3, 0x35, 0x96, 0x22, 0xF0, 0xE5, 0xEA, 0xCC, 0x9C, 0x79,
821            0x89, 0x1F, 0x9D, 0x1D, 0x43, 0x44, 0xCC, 0xA9, 0x9A, 0xB0, 0x0E, 0xC0, 0x57, 0x6B,
822            0x07, 0xF8, 0x53, 0x06,
823        ],
824    ];
825
826    // this value is cross checked with sage script
827    // rescue254.Sponge([0,0,0,0], 4)
828    const OUTPUTFQ254: [[u8; 32]; 4] = [
829        [
830            0xC9, 0xAC, 0x42, 0x08, 0x04, 0x18, 0xFC, 0x10, 0xA4, 0x41, 0xF4, 0xF9, 0x06, 0x2B,
831            0xE2, 0x87, 0xB7, 0x7A, 0xC4, 0x65, 0x8C, 0xE0, 0xE9, 0x78, 0xB0, 0x48, 0x39, 0xB6,
832            0x96, 0x9B, 0x60, 0x1B,
833        ],
834        [
835            0x00, 0xC2, 0xF9, 0x07, 0x54, 0x25, 0xF2, 0xC6, 0x75, 0x21, 0x01, 0x14, 0xF6, 0xD1,
836            0xAF, 0xE1, 0x0D, 0xD4, 0xFC, 0xEC, 0x15, 0xF3, 0x7D, 0x9A, 0x91, 0x26, 0x51, 0xDE,
837            0xC8, 0x8A, 0x19, 0x09,
838        ],
839        [
840            0x78, 0xDB, 0xB7, 0xEA, 0xD4, 0x35, 0x5E, 0xED, 0xAE, 0x14, 0xD6, 0xB1, 0xE8, 0x0D,
841            0xB4, 0xA7, 0x29, 0x9B, 0xBA, 0x6F, 0xBD, 0xA4, 0xEB, 0x52, 0x7D, 0xD5, 0x9B, 0x03,
842            0x17, 0x83, 0x06, 0x1D,
843        ],
844        [
845            0x72, 0xC6, 0xB8, 0xF9, 0x9E, 0xF3, 0xDA, 0x20, 0xED, 0x3D, 0xE4, 0x39, 0x87, 0x28,
846            0xE9, 0x25, 0x0D, 0x8D, 0x57, 0xCE, 0xEE, 0xCA, 0x35, 0xFB, 0x8E, 0x7E, 0xE3, 0x32,
847            0xDA, 0x03, 0x3F, 0x1B,
848        ],
849    ];
850
851    #[test]
852    fn test_sponge() {
853        test_sponge_helper::<Fq254>();
854        test_sponge_helper::<Fr254>();
855        test_sponge_helper::<Fr377>();
856        test_sponge_helper::<Fr381>();
857        test_sponge_helper::<Fq377>();
858    }
859
860    fn test_sponge_helper<F: RescueParameter>() {
861        let rescue_prp = PRP::default();
862        let mut prng = jf_utils::test_rng();
863        let e0 = F::rand(&mut prng);
864        let e1 = F::rand(&mut prng);
865        let e2 = F::rand(&mut prng);
866        let e3 = F::rand(&mut prng);
867        let e4 = F::rand(&mut prng);
868        let e5 = F::rand(&mut prng);
869
870        let input = [e0, e1, e2, e3, e4, e5];
871
872        let output = RescueCRHF::<F>::sponge_no_padding(&input, 1).unwrap()[0];
873
874        let zero = RescueVector::zero();
875        let mut state = RescueVector {
876            vec: [input[0], input[1], input[2], F::zero()],
877        };
878        state = rescue_prp.prp(&zero, &state);
879        state.add_assign_elems(&input[3..6]);
880        state = rescue_prp.prp(&zero, &state);
881        assert_eq!(output, state.vec[0]);
882    }
883
884    #[test]
885    fn test_rescue_hash_on_0_vec() {
886        test_rescue_hash_on_0_vec_fq254();
887        test_rescue_hash_on_0_vec_254();
888        test_rescue_hash_on_0_vec_377();
889        test_rescue_hash_on_0_vec_381();
890        test_rescue_hash_on_0_vec_761()
891    }
892
893    fn test_rescue_hash_on_0_vec_fq254() {
894        let input = [Fq254::zero(); 3];
895        let expected = vec![
896            Fq254::from_le_bytes_mod_order(&OUTPUTFQ254[0]),
897            Fq254::from_le_bytes_mod_order(&OUTPUTFQ254[1]),
898            Fq254::from_le_bytes_mod_order(&OUTPUTFQ254[2]),
899        ];
900        let real_output = RescueCRHF::sponge_no_padding(&input, 3).unwrap();
901        assert_eq!(real_output, expected);
902    }
903
904    fn test_rescue_hash_on_0_vec_254() {
905        let input = [Fr254::zero(); 3];
906        let expected = vec![
907            Fr254::from_le_bytes_mod_order(&OUTPUT254[0]),
908            Fr254::from_le_bytes_mod_order(&OUTPUT254[1]),
909            Fr254::from_le_bytes_mod_order(&OUTPUT254[2]),
910        ];
911        let real_output = RescueCRHF::sponge_no_padding(&input, 3).unwrap();
912        assert_eq!(real_output, expected);
913    }
914
915    fn test_rescue_hash_on_0_vec_377() {
916        let input = [Fr377::zero(); 3];
917        let expected = vec![
918            Fr377::from_le_bytes_mod_order(&OUTPUT377[0]),
919            Fr377::from_le_bytes_mod_order(&OUTPUT377[1]),
920            Fr377::from_le_bytes_mod_order(&OUTPUT377[2]),
921        ];
922        let real_output = RescueCRHF::sponge_no_padding(&input, 3).unwrap();
923        assert_eq!(real_output, expected);
924    }
925
926    fn test_rescue_hash_on_0_vec_381() {
927        let input = [Fr381::zero(); 3];
928        let expected = vec![
929            Fr381::from_le_bytes_mod_order(&OUTPUT381[0]),
930            Fr381::from_le_bytes_mod_order(&OUTPUT381[1]),
931            Fr381::from_le_bytes_mod_order(&OUTPUT381[2]),
932        ];
933        let real_output = RescueCRHF::sponge_no_padding(&input, 3).unwrap();
934        assert_eq!(real_output, expected);
935    }
936
937    fn test_rescue_hash_on_0_vec_761() {
938        let input = [Fq377::zero(); 3];
939        let expected = vec![
940            Fq377::from_le_bytes_mod_order(&OUTPUT761[0]),
941            Fq377::from_le_bytes_mod_order(&OUTPUT761[1]),
942            Fq377::from_le_bytes_mod_order(&OUTPUT761[2]),
943        ];
944        let real_output = RescueCRHF::sponge_no_padding(&input, 3).unwrap();
945        assert_eq!(real_output, expected);
946    }
947}