jf_poseidon2/
sponge.rs

1//! Poseidon2-based Cryptographic Sponge
2//!
3//! This module provides clean type aliases following the spongefish-poseidon
4//! pattern.
5
6use crate::{permutation::Poseidon2Permutation, Poseidon2Params};
7use ark_ff::PrimeField;
8use spongefish::Unit;
9
10/// Marker trait for the state of Poseidon2-based Cryptographic Sponge
11pub trait Poseidon2Sponge {}
12
13/// Implement the marker trait for our permutation state
14impl<F, const N: usize, const R: usize, P> Poseidon2Sponge for Poseidon2Permutation<F, N, R, P>
15where
16    F: PrimeField + Unit,
17    P: Poseidon2Params<F, N>,
18{
19}
20
21#[cfg(feature = "bls12-381")]
22/// Poseidon2 sponge types for BLS12-381 scalar field
23pub mod bls12_381 {
24    #![allow(dead_code)]
25    use super::*;
26    use crate::constants::bls12_381::*;
27    use ark_bls12_381::Fr;
28    use spongefish::duplex_sponge::DuplexSponge;
29    /// State of a sponge over BLS12-381 scalar field, state_size=2, rate=1.
30    pub type Poseidon2PermutationBlsN2R1 = Poseidon2Permutation<Fr, 2, 1, Poseidon2ParamsBls2>;
31    /// A sponge over BLS12-381 scalar field, state_size=2, rate=1.
32    pub type Poseidon2SpongeBlsN2R1 = DuplexSponge<Poseidon2PermutationBlsN2R1>;
33
34    /// State of a sponge over BLS12-381 scalar field, state_size=3, rate=1.
35    pub type Poseidon2PermutationBlsN3R1 = Poseidon2Permutation<Fr, 3, 1, Poseidon2ParamsBls3>;
36    /// A sponge over BLS12-381 scalar field, state_size=3, rate=1.
37    pub type Poseidon2SpongeBlsN3R1 = DuplexSponge<Poseidon2PermutationBlsN3R1>;
38
39    /// State of a sponge over BLS12-381 scalar field, state_size=3, rate=2.
40    pub type Poseidon2PermutationBlsN3R2 = Poseidon2Permutation<Fr, 3, 2, Poseidon2ParamsBls3>;
41    /// A sponge over BLS12-381 scalar field, state_size=3, rate=2.
42    pub type Poseidon2SpongeBlsN3R2 = DuplexSponge<Poseidon2PermutationBlsN3R2>;
43
44    #[test]
45    fn test_bls_sponge() {
46        use super::tests::test_sponge;
47        test_sponge::<Fr, Poseidon2SpongeBlsN2R1>();
48        test_sponge::<Fr, Poseidon2SpongeBlsN3R1>();
49        test_sponge::<Fr, Poseidon2SpongeBlsN3R2>();
50    }
51}
52
53#[cfg(feature = "bn254")]
54/// Poseidon2 sponge types for BN254 scalar field
55pub mod bn254 {
56    #![allow(dead_code)]
57    use super::*;
58    use crate::constants::bn254::*;
59    use ark_bn254::Fr;
60    use spongefish::duplex_sponge::DuplexSponge;
61    /// State of a sponge over BN254 scalar field, state_size=3, rate=1.
62    pub type Poseidon2PermutationBnN3R1 = Poseidon2Permutation<Fr, 3, 1, Poseidon2ParamsBn3>;
63    /// A sponge over BN254 scalar field, state_size=3, rate=1.
64    pub type Poseidon2SpongeBnN3R1 = DuplexSponge<Poseidon2PermutationBnN3R1>;
65
66    /// State of a sponge over BN254 scalar field, state_size=3, rate=2.
67    pub type Poseidon2PermutationBnN3R2 = Poseidon2Permutation<Fr, 3, 2, Poseidon2ParamsBn3>;
68    /// A sponge over BN254 scalar field, state_size=3, rate=2.
69    pub type Poseidon2SpongeBnN3R2 = DuplexSponge<Poseidon2PermutationBnN3R2>;
70
71    #[test]
72    fn test_bn_sponge() {
73        use super::tests::test_sponge;
74        test_sponge::<Fr, Poseidon2SpongeBnN3R1>();
75        test_sponge::<Fr, Poseidon2SpongeBnN3R2>();
76    }
77}
78
79#[cfg(test)]
80pub(crate) mod tests {
81    use super::*;
82    use ark_ff::BigInteger;
83    use ark_std::vec::Vec;
84    use spongefish::codecs::arkworks_algebra::*;
85
86    pub(crate) fn test_sponge<F: PrimeField + Unit, H: DuplexSpongeInterface<F>>() {
87        let io = DomainSeparator::<H, F>::new("test")
88            .absorb(1, "in")
89            .squeeze(2048, "out");
90
91        // prover transcript
92        let mut merlin = io.to_prover_state();
93        // prover first message (label: "in")
94        merlin.add_units(&[F::from(42u32)]).unwrap();
95
96        let mut merlin_challenges = [F::default(); 2048];
97        merlin.fill_challenge_units(&mut merlin_challenges).unwrap();
98
99        // verifier transcript
100        let mut arthur = io.to_verifier_state(merlin.narg_string());
101        // reading prover's first message labelled "in", since we don't need it, read
102        // into a one-time throw-away array
103        arthur.fill_next_units(&mut [F::default()]).unwrap();
104        let mut arthur_challenges = [F::default(); 2048];
105        arthur.fill_challenge_units(&mut arthur_challenges).unwrap();
106
107        // challenge computed from both sides should be the same
108        assert_eq!(merlin_challenges, arthur_challenges);
109
110        // Looking at byte distribution, whether it's close to uniform
111        let chal_bytes: Vec<u8> = merlin_challenges
112            .iter()
113            .flat_map(|c| c.into_bigint().to_bytes_le())
114            .collect();
115        let frequencies = (0u8..=255)
116            .map(|i| chal_bytes.iter().filter(|&&x| x == i).count())
117            .collect::<Vec<_>>();
118        // the expected frequency if it's uniformly random
119        let expected_mean = (F::MODULUS_BIT_SIZE / 8 * 2048 / 256) as usize;
120        assert!(
121            frequencies
122                .iter()
123                .all(|&x| x < expected_mean * 2 && x > expected_mean / 2),
124            "Counts for each value shouldn't be too far away from mean: {:?}",
125            frequencies
126        );
127    }
128}