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