jf_rescue/
crhf.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//! A rescue CRHF implementation
8
9use crate::{
10    sponge::RescueSponge, Permutation, RescueError, RescueParameter, RescueVector, CRHF_RATE,
11};
12use ark_crypto_primitives::sponge::{
13    CryptographicSponge, FieldBasedCryptographicSponge, SpongeExt,
14};
15use ark_std::{borrow::Borrow, marker::PhantomData, string::ToString, vec::Vec};
16use jf_crhf::CRHF;
17use jf_utils::pad_with_zeros;
18
19/// CRHF
20#[derive(Debug, Clone)]
21pub struct RescueCRHF<F: RescueParameter> {
22    sponge: RescueSponge<F, CRHF_RATE>,
23}
24
25impl<F: RescueParameter> RescueCRHF<F> {
26    /// Sponge hashing based on rescue permutation for RATE 3. It allows
27    /// unrestricted variable length input and returns a vector of
28    /// `num_outputs` elements.
29    ///
30    /// we use ["bit padding"-style][padding] where "1" is always appended, then
31    /// as many "0" as required are added for the overall length to be a
32    /// multiple of RATE
33    ///
34    /// [padding]: https://en.wikipedia.org/wiki/Padding_(cryptography)#Bit_padding
35    pub fn sponge_with_bit_padding(input: &[F], num_outputs: usize) -> Vec<F> {
36        let mut padded = input.to_vec();
37        padded.push(F::one());
38        pad_with_zeros(&mut padded, CRHF_RATE);
39        Self::sponge_no_padding(padded.as_slice(), num_outputs)
40            .expect("Bug in JF Primitives : bad padding of input for FSKS construction")
41    }
42
43    /// Similar to [`RescueCRHF::sponge_with_bit_padding`] except we use ["zero
44    /// padding"][padding] where as many "0" as required are added for the
45    /// overall length to be a multiple of RATE.
46    ///
47    /// [padding]: https://en.wikipedia.org/wiki/Padding_(cryptography)#Zero_padding
48    pub fn sponge_with_zero_padding(input: &[F], num_outputs: usize) -> Vec<F> {
49        let mut padded = input.to_vec();
50        pad_with_zeros(&mut padded, CRHF_RATE);
51        Self::sponge_no_padding(padded.as_slice(), num_outputs)
52            .expect("Bug in JF Primitives : bad padding of input for FSKS construction")
53    }
54
55    /// Sponge hashing based on rescue permutation for RATE 3 and CAPACITY 1. It
56    /// allows inputs with length that is a multiple of `CRHF_RATE` and
57    /// returns a vector of `num_outputs` elements.
58    pub fn sponge_no_padding(input: &[F], num_output: usize) -> Result<Vec<F>, RescueError> {
59        if input.len() % CRHF_RATE != 0 {
60            return Err(RescueError::ParameterError(
61                "Rescue sponge Error : input to sponge hashing function is not multiple of RATE."
62                    .to_string(),
63            ));
64        }
65        // ABSORB PHASE
66        let mut r = Self {
67            sponge: RescueSponge::from_state(RescueVector::zero(), &Permutation::default()),
68        };
69        r.sponge.absorb(&input);
70
71        // SQUEEZE PHASE
72        Ok(r.sponge.squeeze_native_field_elements(num_output))
73    }
74}
75
76#[derive(Debug, Clone)]
77/// A rescue-sponge-based CRHF with fixed-input size (if not multiple of 3 will
78/// get auto-padded) and variable-output size
79pub struct FixedLengthRescueCRHF<
80    F: RescueParameter,
81    const INPUT_LEN: usize,
82    const OUTPUT_LEN: usize,
83>(PhantomData<F>);
84
85impl<F: RescueParameter, const INPUT_LEN: usize, const OUTPUT_LEN: usize> CRHF
86    for FixedLengthRescueCRHF<F, INPUT_LEN, OUTPUT_LEN>
87{
88    type Input = [F; INPUT_LEN];
89    type Output = [F; OUTPUT_LEN];
90    type Error = RescueError;
91
92    /// ## Padding
93    /// if `input` length is not a multiple of `CRHF_RATE`, then it will be
94    /// padded. By default, we use "zero padding"-style where as many "0" as
95    /// required are added.
96    fn evaluate<T: Borrow<Self::Input>>(input: T) -> Result<Self::Output, Self::Error> {
97        let mut output = [F::zero(); OUTPUT_LEN];
98
99        let res = match INPUT_LEN % CRHF_RATE {
100            0 => RescueCRHF::<F>::sponge_no_padding(input.borrow(), OUTPUT_LEN)?,
101            _ => RescueCRHF::<F>::sponge_with_zero_padding(input.borrow(), OUTPUT_LEN),
102        };
103        if res.len() != OUTPUT_LEN {
104            return Err(RescueError::ParameterError(
105                "Unexpected rescue sponge return length".to_string(),
106            ));
107        }
108
109        output.copy_from_slice(&res[..]);
110        Ok(output)
111    }
112}
113
114#[derive(Debug, Clone)]
115/// A rescue-sponge-based CRHF with variable-input and variable-output size
116pub struct VariableLengthRescueCRHF<F: RescueParameter, const OUTPUT_LEN: usize>(PhantomData<F>);
117
118impl<F: RescueParameter, const OUTPUT_LEN: usize> CRHF for VariableLengthRescueCRHF<F, OUTPUT_LEN> {
119    type Input = Vec<F>;
120    type Output = [F; OUTPUT_LEN];
121    type Error = RescueError;
122
123    /// ## Padding
124    /// if `input` length is not a multiple of `CRHF_RATE`, then it will be
125    /// padded. By default, we use "bit padding"-style where "1" is always
126    /// appended, then as many "0" as required are added for the overall
127    /// length to be a multiple of `CRHF_RATE`.
128    fn evaluate<T: Borrow<Self::Input>>(input: T) -> Result<Self::Output, Self::Error> {
129        let mut output = [F::zero(); OUTPUT_LEN];
130        let res = RescueCRHF::<F>::sponge_with_bit_padding(input.borrow(), OUTPUT_LEN);
131        if res.len() != OUTPUT_LEN {
132            return Err(RescueError::ParameterError(
133                "Unexpected rescue sponge return length".to_string(),
134            ));
135        }
136        output.copy_from_slice(&res[..]);
137        Ok(output)
138    }
139}