jf_poseidon2/
crhf.rs

1//! Collision-resistant Hash Function (CRHF) based on Poseidon2 permutation
2
3use core::marker::PhantomData;
4
5use ark_ff::{Field, PrimeField};
6use ark_std::{borrow::Borrow, string::ToString, vec::Vec};
7use jf_crhf::CRHF;
8use nimue::{
9    hash::sponge::{DuplexSponge, Sponge},
10    DuplexHash, Unit,
11};
12
13use crate::{sponge::Poseidon2Sponge, Poseidon2Error};
14
15/// Sponge-based CRHF where the Sponge uses Poseidon2 permutation
16/// Input length is fixed: the actual input can be shorter, but will internally
17/// be zero-padded to `INPUT_SIZE`
18///
19/// Example:
20/// `FixedLenPoseidon2Hash<ark_bn254::Fr, Poseidon2SpongeStateBnN3R1, 6, 2>`
21#[derive(Clone)]
22pub struct FixedLenPoseidon2Hash<F, S, const INPUT_SIZE: usize, const OUTPUT_SIZE: usize>
23where
24    F: PrimeField + Unit,
25    S: Sponge<U = F> + Poseidon2Sponge,
26{
27    _field: PhantomData<F>,
28    _sponge: PhantomData<S>,
29}
30
31impl<F, S, const IN: usize, const OUT: usize> CRHF for FixedLenPoseidon2Hash<F, S, IN, OUT>
32where
33    F: PrimeField + Unit,
34    S: Sponge<U = F> + Poseidon2Sponge,
35{
36    type Input = [F]; // length should be <= IN
37    type Output = [F; OUT];
38    type Error = Poseidon2Error;
39
40    fn evaluate<T: Borrow<Self::Input>>(input: T) -> Result<Self::Output, Self::Error> {
41        let input = input.borrow();
42        if input.len() > IN {
43            return Err(Poseidon2Error::ParamErr("hash input too long".to_string()));
44        }
45
46        let mut padded = Vec::from(input);
47        zero_padding(&mut padded, IN);
48
49        let mut sponge = DuplexSponge::<S>::default();
50        sponge.absorb_unchecked(&padded);
51        let mut output = [F::default(); OUT];
52        sponge.squeeze_unchecked(&mut output);
53        Ok(output)
54    }
55}
56
57/// Sponge-based CRHF where the Sponge uses Poseidon2 permutation, with
58/// variable-length input
59#[derive(Debug, Clone)]
60pub struct VariableLenPoseidon2Hash<F, S, const OUTPUT_SIZE: usize>
61where
62    F: PrimeField + Unit,
63    S: Sponge<U = F>,
64{
65    _field: PhantomData<F>,
66    _sponge: PhantomData<S>,
67}
68
69impl<F, S, const OUT: usize> CRHF for VariableLenPoseidon2Hash<F, S, OUT>
70where
71    F: PrimeField + Unit,
72    S: Sponge<U = F>,
73{
74    type Input = [F];
75    type Output = [F; OUT];
76    type Error = Poseidon2Error;
77
78    fn evaluate<T: Borrow<Self::Input>>(input: T) -> Result<Self::Output, Self::Error> {
79        let mut padded = Vec::from(input.borrow());
80        bit_padding(&mut padded, S::R);
81
82        let mut sponge = DuplexSponge::<S>::default();
83        sponge.absorb_unchecked(&padded);
84        let mut output = [F::default(); OUT];
85        sponge.squeeze_unchecked(&mut output);
86        Ok(output)
87    }
88}
89
90// pad `data` with zeros until the length is the next multiple of `multiple`
91#[inline(always)]
92fn zero_padding<F: Field>(data: &mut Vec<F>, multiple: usize) {
93    data.resize(data.len().next_multiple_of(multiple), F::zero());
94}
95
96// pad `data` with "10..0" (always pad "1"), until the length is the next
97// multiple of `multiple`
98#[inline(always)]
99fn bit_padding<F: Field>(data: &mut Vec<F>, multiple: usize) {
100    data.push(F::one());
101    zero_padding(data, multiple);
102}