p3_symmetric/
sponge.rs

1use alloc::string::String;
2use core::marker::PhantomData;
3
4use itertools::Itertools;
5use p3_field::{reduce_32, Field, PrimeField, PrimeField32};
6
7use crate::hasher::CryptographicHasher;
8use crate::permutation::CryptographicPermutation;
9
10/// A padding-free, overwrite-mode sponge function.
11///
12/// `WIDTH` is the sponge's rate plus the sponge's capacity.
13#[derive(Copy, Clone, Debug)]
14pub struct PaddingFreeSponge<P, const WIDTH: usize, const RATE: usize, const OUT: usize> {
15    permutation: P,
16}
17
18impl<P, const WIDTH: usize, const RATE: usize, const OUT: usize>
19    PaddingFreeSponge<P, WIDTH, RATE, OUT>
20{
21    pub const fn new(permutation: P) -> Self {
22        Self { permutation }
23    }
24}
25
26impl<T, P, const WIDTH: usize, const RATE: usize, const OUT: usize> CryptographicHasher<T, [T; OUT]>
27    for PaddingFreeSponge<P, WIDTH, RATE, OUT>
28where
29    T: Default + Copy,
30    P: CryptographicPermutation<[T; WIDTH]>,
31{
32    fn hash_iter<I>(&self, input: I) -> [T; OUT]
33    where
34        I: IntoIterator<Item = T>,
35    {
36        // static_assert(RATE < WIDTH)
37        let mut state = [T::default(); WIDTH];
38        let mut input = input.into_iter();
39
40        // Itertools' chunks() is more convenient, but seems to add more overhead,
41        // hence the more manual loop.
42        'outer: loop {
43            for i in 0..RATE {
44                if let Some(x) = input.next() {
45                    state[i] = x;
46                } else {
47                    if i != 0 {
48                        self.permutation.permute_mut(&mut state);
49                    }
50                    break 'outer;
51                }
52            }
53            self.permutation.permute_mut(&mut state);
54        }
55
56        state[..OUT].try_into().unwrap()
57    }
58}
59
60/// A padding-free, overwrite-mode sponge function that operates natively over PF but accepts elements
61/// of F: PrimeField32.
62///
63/// `WIDTH` is the sponge's rate plus the sponge's capacity.
64#[derive(Clone, Debug)]
65pub struct MultiField32PaddingFreeSponge<
66    F,
67    PF,
68    P,
69    const WIDTH: usize,
70    const RATE: usize,
71    const OUT: usize,
72> {
73    permutation: P,
74    num_f_elms: usize,
75    _phantom: PhantomData<(F, PF)>,
76}
77
78impl<F, PF, P, const WIDTH: usize, const RATE: usize, const OUT: usize>
79    MultiField32PaddingFreeSponge<F, PF, P, WIDTH, RATE, OUT>
80where
81    F: PrimeField32,
82    PF: Field,
83{
84    pub fn new(permutation: P) -> Result<Self, String> {
85        if F::order() >= PF::order() {
86            return Err(String::from("F::order() must be less than PF::order()"));
87        }
88
89        let num_f_elms = PF::bits() / F::bits();
90        Ok(Self {
91            permutation,
92            num_f_elms,
93            _phantom: PhantomData,
94        })
95    }
96}
97
98impl<F, PF, P, const WIDTH: usize, const RATE: usize, const OUT: usize>
99    CryptographicHasher<F, [PF; OUT]> for MultiField32PaddingFreeSponge<F, PF, P, WIDTH, RATE, OUT>
100where
101    F: PrimeField32,
102    PF: PrimeField + Default + Copy,
103    P: CryptographicPermutation<[PF; WIDTH]>,
104{
105    fn hash_iter<I>(&self, input: I) -> [PF; OUT]
106    where
107        I: IntoIterator<Item = F>,
108    {
109        let mut state = [PF::default(); WIDTH];
110        for block_chunk in &input.into_iter().chunks(RATE) {
111            for (chunk_id, chunk) in (&block_chunk.chunks(self.num_f_elms))
112                .into_iter()
113                .enumerate()
114            {
115                state[chunk_id] = reduce_32(&chunk.collect_vec());
116            }
117            state = self.permutation.permute(state);
118        }
119
120        state[..OUT].try_into().unwrap()
121    }
122}