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}