Skip to main content

p3_poseidon2/
lib.rs

1//! The Poseidon2 permutation.
2//!
3//! This implementation was based upon the following resources:
4//! - `<https://github.com/HorizenLabs/poseidon2/blob/main/plain_implementations/src/poseidon2/poseidon2.rs>`
5//! - `<https://eprint.iacr.org/2023/323.pdf>`
6
7#![no_std]
8
9extern crate alloc;
10
11mod external;
12mod generic;
13mod internal;
14mod round_numbers;
15use alloc::vec::Vec;
16use core::marker::PhantomData;
17
18pub use external::*;
19pub use generic::*;
20pub use internal::*;
21use p3_field::{Algebra, InjectiveMonomial, PrimeField, PrimeField64};
22use p3_symmetric::{CryptographicPermutation, Permutation};
23use rand::distr::{Distribution, StandardUniform};
24use rand::{Rng, RngExt};
25pub use round_numbers::poseidon2_round_numbers_128;
26
27const SUPPORTED_WIDTHS: [usize; 9] = [2, 3, 4, 8, 12, 16, 20, 24, 32];
28
29/// The Poseidon2 permutation.
30#[derive(Clone, Debug)]
31pub struct Poseidon2<F, ExternalPerm, InternalPerm, const WIDTH: usize, const D: u64> {
32    /// The permutations used in External Rounds.
33    external_layer: ExternalPerm,
34
35    /// The permutation used in Internal Rounds.
36    internal_layer: InternalPerm,
37
38    _phantom: PhantomData<F>,
39}
40
41impl<F, ExternalPerm, InternalPerm, const WIDTH: usize, const D: u64>
42    Poseidon2<F, ExternalPerm, InternalPerm, WIDTH, D>
43where
44    F: PrimeField,
45    ExternalPerm: ExternalLayerConstructor<F, WIDTH>,
46    InternalPerm: InternalLayerConstructor<F>,
47{
48    /// Create a new Poseidon2 configuration.
49    /// This internally converts the given constants to the relevant packed versions.
50    pub fn new(
51        external_constants: ExternalLayerConstants<F, WIDTH>,
52        internal_constants: Vec<F>,
53    ) -> Self {
54        // Compile-time structural checks from the Poseidon2 specification.
55        const {
56            let mut i = 0;
57            let mut found = false;
58            while i < SUPPORTED_WIDTHS.len() {
59                if SUPPORTED_WIDTHS[i] == WIDTH {
60                    found = true;
61                }
62                i += 1;
63            }
64            // Section 6: t ∈ {2, 3, 4, ..., 24}.
65            assert!(
66                found,
67                "WIDTH must be one of the supported widths (paper Section 6)"
68            );
69            // Section 6: S-box(x) = x^d where d ≥ 3.
70            assert!(D >= 3, "Poseidon2 requires D >= 3 (paper Section 6)");
71        }
72
73        // Section 7.1: RF ≥ 6 for statistical attack resistance (differential, linear).
74        let rounds_f = external_constants.get_initial_constants().len()
75            + external_constants.get_terminal_constants().len();
76        assert!(
77            rounds_f >= 6,
78            "Poseidon2 requires rounds_f >= 6 (paper Section 7.1: statistical attacks)"
79        );
80
81        let external_layer = ExternalPerm::new_from_constants(external_constants);
82        let internal_layer = InternalPerm::new_from_constants(internal_constants);
83
84        Self {
85            external_layer,
86            internal_layer,
87            _phantom: PhantomData,
88        }
89    }
90
91    /// Create a new Poseidon2 configuration with random parameters.
92    pub fn new_from_rng<R: Rng>(rounds_f: usize, rounds_p: usize, rng: &mut R) -> Self
93    where
94        StandardUniform: Distribution<F> + Distribution<[F; WIDTH]>,
95    {
96        // Section 7.1: RF ≥ 6 for statistical attack resistance.
97        assert!(
98            rounds_f >= 6,
99            "Poseidon2 requires rounds_f >= 6 (paper Section 7.1: statistical attacks)"
100        );
101
102        let external_constants = ExternalLayerConstants::new_from_rng(rounds_f, rng);
103        let internal_constants = rng.sample_iter(StandardUniform).take(rounds_p).collect();
104
105        Self::new(external_constants, internal_constants)
106    }
107}
108
109impl<F, ExternalPerm, InternalPerm, const WIDTH: usize, const D: u64>
110    Poseidon2<F, ExternalPerm, InternalPerm, WIDTH, D>
111where
112    F: PrimeField64,
113    ExternalPerm: ExternalLayerConstructor<F, WIDTH>,
114    InternalPerm: InternalLayerConstructor<F>,
115{
116    /// Create a new Poseidon2 configuration with 128 bit security and random rounds constants.
117    ///
118    /// # Panics
119    /// This will panic if D and F::ORDER_U64 - 1 are not relatively prime.
120    /// This will panic if the optimal parameters for the given field and width have not been computed.
121    pub fn new_from_rng_128<R: Rng>(rng: &mut R) -> Self
122    where
123        StandardUniform: Distribution<F> + Distribution<[F; WIDTH]>,
124    {
125        let round_numbers = poseidon2_round_numbers_128::<F>(WIDTH, D);
126        let (rounds_f, rounds_p) = round_numbers.unwrap_or_else(|e| panic!("{e}"));
127        Self::new_from_rng(rounds_f, rounds_p, rng)
128    }
129}
130
131impl<F, A, ExternalPerm, InternalPerm, const WIDTH: usize, const D: u64> Permutation<[A; WIDTH]>
132    for Poseidon2<F, ExternalPerm, InternalPerm, WIDTH, D>
133where
134    F: PrimeField + InjectiveMonomial<D>,
135    A: Algebra<F> + Sync + InjectiveMonomial<D>,
136    ExternalPerm: ExternalLayer<A, WIDTH, D>,
137    InternalPerm: InternalLayer<A, WIDTH, D>,
138{
139    fn permute_mut(&self, state: &mut [A; WIDTH]) {
140        self.external_layer.permute_state_initial(state);
141        self.internal_layer.permute_state(state);
142        self.external_layer.permute_state_terminal(state);
143    }
144}
145
146impl<F, A, ExternalPerm, InternalPerm, const WIDTH: usize, const D: u64>
147    CryptographicPermutation<[A; WIDTH]> for Poseidon2<F, ExternalPerm, InternalPerm, WIDTH, D>
148where
149    F: PrimeField + InjectiveMonomial<D>,
150    A: Algebra<F> + Sync + InjectiveMonomial<D>,
151    ExternalPerm: ExternalLayer<A, WIDTH, D>,
152    InternalPerm: InternalLayer<A, WIDTH, D>,
153{
154}