poseidon_primitives/poseidon/primitives/
p128pow5t3.rs

1use std::marker::PhantomData;
2
3use ff::PrimeField;
4
5use super::{Mds, Spec};
6
7/// The trait required for fields can handle a pow5 sbox, 3 field, 2 rate permutation
8pub trait P128Pow5T3Constants: PrimeField {
9    fn partial_rounds() -> usize {
10        56
11    }
12    fn round_constants() -> Vec<[Self; 3]>;
13    fn mds() -> Mds<Self, 3>;
14    fn mds_inv() -> Mds<Self, 3>;
15}
16
17/// Poseidon-128 using the $x^5$ S-box, with a width of 3 field elements, and the
18/// standard number of rounds for 128-bit security "with margin".
19///
20/// The standard specification for this set of parameters (on either of the Pasta
21/// fields) uses $R_F = 8, R_P = 56$. This is conveniently an even number of
22/// partial rounds, making it easier to construct a Halo 2 circuit.
23#[derive(Debug)]
24pub struct P128Pow5T3<C> {
25    _marker: PhantomData<C>,
26}
27
28impl<Fp: P128Pow5T3Constants> Spec<Fp, 3, 2> for P128Pow5T3<Fp> {
29    fn full_rounds() -> usize {
30        8
31    }
32
33    fn partial_rounds() -> usize {
34        Fp::partial_rounds()
35    }
36
37    fn sbox(val: Fp) -> Fp {
38        val.pow_vartime([5])
39    }
40
41    fn secure_mds() -> usize {
42        unimplemented!()
43    }
44
45    fn constants() -> (Vec<[Fp; 3]>, Mds<Fp, 3>, Mds<Fp, 3>) {
46        (Fp::round_constants(), Fp::mds(), Fp::mds_inv())
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use std::marker::PhantomData;
53
54    use ff::{FromUniformBytes, PrimeField};
55
56    use super::super::pasta::{fp, test_vectors, Fp};
57    use crate::poseidon::primitives::{permute, ConstantLength, Hash, Spec};
58
59    /// The same Poseidon specification as poseidon::P128Pow5T3, but constructed
60    /// such that its constants will be generated at runtime.
61    #[derive(Debug)]
62    pub struct P128Pow5T3Gen<F, const SECURE_MDS: usize>(PhantomData<F>);
63
64    type P128Pow5T3Pasta = super::P128Pow5T3<Fp>;
65
66    impl<F: PrimeField, const SECURE_MDS: usize> P128Pow5T3Gen<F, SECURE_MDS> {
67        pub fn new() -> Self {
68            P128Pow5T3Gen(PhantomData::default())
69        }
70    }
71
72    impl<F: PrimeField, const SECURE_MDS: usize> Spec<F, 3, 2> for P128Pow5T3Gen<F, SECURE_MDS> {
73        fn full_rounds() -> usize {
74            8
75        }
76
77        fn partial_rounds() -> usize {
78            56
79        }
80
81        fn sbox(val: F) -> F {
82            val.pow_vartime([5])
83        }
84
85        fn secure_mds() -> usize {
86            SECURE_MDS
87        }
88    }
89
90    #[test]
91    fn verify_constants() {
92        fn verify_constants_helper<F: FromUniformBytes<64> + Ord>(
93            expected_round_constants: [[F; 3]; 64],
94            expected_mds: [[F; 3]; 3],
95            expected_mds_inv: [[F; 3]; 3],
96        ) {
97            let (round_constants, mds, mds_inv) = P128Pow5T3Gen::<F, 0>::constants();
98
99            for (actual, expected) in round_constants
100                .iter()
101                .flatten()
102                .zip(expected_round_constants.iter().flatten())
103            {
104                assert_eq!(actual, expected);
105            }
106
107            for (actual, expected) in mds.iter().flatten().zip(expected_mds.iter().flatten()) {
108                assert_eq!(actual, expected);
109            }
110
111            for (actual, expected) in mds_inv
112                .iter()
113                .flatten()
114                .zip(expected_mds_inv.iter().flatten())
115            {
116                assert_eq!(actual, expected);
117            }
118        }
119
120        verify_constants_helper(fp::ROUND_CONSTANTS, fp::MDS, fp::MDS_INV);
121        //verify_constants_helper(fq::ROUND_CONSTANTS, fq::MDS, fq::MDS_INV);
122    }
123
124    #[test]
125    fn test_against_reference() {
126        {
127            // <https://github.com/daira/pasta-hadeshash>, using parameters from
128            // `generate_parameters_grain.sage 1 0 255 3 8 56 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001`.
129            // The test vector is generated by `sage poseidonperm_x5_pallas_3.sage --rust`
130
131            let mut input = [
132                Fp::from_raw([
133                    0x0000_0000_0000_0000,
134                    0x0000_0000_0000_0000,
135                    0x0000_0000_0000_0000,
136                    0x0000_0000_0000_0000,
137                ]),
138                Fp::from_raw([
139                    0x0000_0000_0000_0001,
140                    0x0000_0000_0000_0000,
141                    0x0000_0000_0000_0000,
142                    0x0000_0000_0000_0000,
143                ]),
144                Fp::from_raw([
145                    0x0000_0000_0000_0002,
146                    0x0000_0000_0000_0000,
147                    0x0000_0000_0000_0000,
148                    0x0000_0000_0000_0000,
149                ]),
150            ];
151
152            let expected_output = [
153                Fp::from_raw([
154                    0xaeb1_bc02_4aec_a456,
155                    0xf7e6_9a71_d0b6_42a0,
156                    0x94ef_b364_f966_240f,
157                    0x2a52_6acd_0b64_b453,
158                ]),
159                Fp::from_raw([
160                    0x012a_3e96_28e5_b82a,
161                    0xdcd4_2e7f_bed9_dafe,
162                    0x76ff_7dae_343d_5512,
163                    0x13c5_d156_8b4a_a430,
164                ]),
165                Fp::from_raw([
166                    0x3590_29a1_d34e_9ddd,
167                    0xf7cf_dfe1_bda4_2c7b,
168                    0x256f_cd59_7984_561a,
169                    0x0a49_c868_c697_6544,
170                ]),
171            ];
172
173            permute::<Fp, P128Pow5T3Gen<Fp, 0>, 3, 2>(&mut input, &fp::MDS, &fp::ROUND_CONSTANTS);
174            assert_eq!(input, expected_output);
175        }
176
177        /*        {
178                    // <https://github.com/daira/pasta-hadeshash>, using parameters from
179                    // `generate_parameters_grain.sage 1 0 255 3 8 56 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001`.
180                    // The test vector is generated by `sage poseidonperm_x5_vesta_3.sage --rust`
181
182                    let mut input = [
183                        Fq::from_raw([
184                            0x0000_0000_0000_0000,
185                            0x0000_0000_0000_0000,
186                            0x0000_0000_0000_0000,
187                            0x0000_0000_0000_0000,
188                        ]),
189                        Fq::from_raw([
190                            0x0000_0000_0000_0001,
191                            0x0000_0000_0000_0000,
192                            0x0000_0000_0000_0000,
193                            0x0000_0000_0000_0000,
194                        ]),
195                        Fq::from_raw([
196                            0x0000_0000_0000_0002,
197                            0x0000_0000_0000_0000,
198                            0x0000_0000_0000_0000,
199                            0x0000_0000_0000_0000,
200                        ]),
201                    ];
202
203                    let expected_output = [
204                        Fq::from_raw([
205                            0x0eb0_8ea8_13be_be59,
206                            0x4d43_d197_3dd3_36c6,
207                            0xeddd_74f2_2f8f_2ff7,
208                            0x315a_1f4c_db94_2f7c,
209                        ]),
210                        Fq::from_raw([
211                            0xf9f1_26e6_1ea1_65f1,
212                            0x413e_e0eb_7bbd_2198,
213                            0x642a_dee0_dd13_aa48,
214                            0x3be4_75f2_d764_2bde,
215                        ]),
216                        Fq::from_raw([
217                            0x14d5_4237_2a7b_a0d9,
218                            0x5019_bfd4_e042_3fa0,
219                            0x117f_db24_20d8_ea60,
220                            0x25ab_8aec_e953_7168,
221                        ]),
222                    ];
223
224                    permute::<Fq, P128Pow5T3Gen<Fq, 0>, 3, 2>(&mut input, &fq::MDS, &fq::ROUND_CONSTANTS);
225                    assert_eq!(input, expected_output);
226                }
227        */
228    }
229
230    #[test]
231    fn permute_test_vectors() {
232        {
233            let (round_constants, mds, _) = P128Pow5T3Pasta::constants();
234
235            for tv in test_vectors::fp::permute() {
236                let mut state = [
237                    Fp::from_repr(tv.initial_state[0]).unwrap(),
238                    Fp::from_repr(tv.initial_state[1]).unwrap(),
239                    Fp::from_repr(tv.initial_state[2]).unwrap(),
240                ];
241
242                permute::<Fp, P128Pow5T3Pasta, 3, 2>(&mut state, &mds, &round_constants);
243
244                for (expected, actual) in tv.final_state.iter().zip(state.iter()) {
245                    assert_eq!(&actual.to_repr(), expected);
246                }
247            }
248        }
249        /*
250                {
251                    let (round_constants, mds, _) = super::P128Pow5T3::constants();
252
253                    for tv in crate::poseidon::primitives::test_vectors::fq::permute() {
254                        let mut state = [
255                            Fq::from_repr(tv.initial_state[0]).unwrap(),
256                            Fq::from_repr(tv.initial_state[1]).unwrap(),
257                            Fq::from_repr(tv.initial_state[2]).unwrap(),
258                        ];
259
260                        permute::<Fq, super::P128Pow5T3, 3, 2>(&mut state, &mds, &round_constants);
261
262                        for (expected, actual) in tv.final_state.iter().zip(state.iter()) {
263                            assert_eq!(&actual.to_repr(), expected);
264                        }
265                    }
266                }
267        */
268    }
269
270    #[test]
271    fn hash_test_vectors() {
272        for tv in test_vectors::fp::hash() {
273            let message = [
274                Fp::from_repr(tv.input[0]).unwrap(),
275                Fp::from_repr(tv.input[1]).unwrap(),
276            ];
277
278            let result = Hash::<_, P128Pow5T3Pasta, ConstantLength<2>, 3, 2>::init().hash(message);
279
280            assert_eq!(result.to_repr(), tv.output);
281        }
282
283        /*        for tv in crate::poseidon::primitives::test_vectors::fq::hash() {
284                    let message = [
285                        Fq::from_repr(tv.input[0]).unwrap(),
286                        Fq::from_repr(tv.input[1]).unwrap(),
287                    ];
288
289                    let result =
290                        Hash::<_, super::P128Pow5T3, ConstantLength<2>, 3, 2>::init().hash(message);
291
292                    assert_eq!(result.to_repr(), tv.output);
293                }
294        */
295    }
296}