Skip to main content

p3_koala_bear/
poseidon2.rs

1//! Implementation of Poseidon2, see: <https://eprint.iacr.org/2023/323>
2//!
3//! For the diffusion matrix, 1 + Diag(V), we perform a search to find an optimized
4//! vector V composed of elements with efficient multiplication algorithms in AVX2/AVX512/NEON.
5//!
6//! This leads to using small values (e.g. 1, 2, 3, 4) where multiplication is implemented using addition
7//! and inverse powers of 2 where it is possible to avoid monty reductions.
8//! Additionally, for technical reasons, having the first entry be -2 is useful.
9//!
10//! Optimized Diagonal for KoalaBear16:
11//! [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24]
12//! Optimized Diagonal for KoalaBear24:
13//! [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^24, -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^24]
14//! Optimized Diagonal for KoalaBear32:
15//! [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^10, 1/2^12, 1/2^14, 1/2^16, 1/2^24, -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^10, -1/2^12, -1/2^14, -1/2^16, -1/2^24]
16//! See poseidon2\src\diffusion.rs for information on how to double check these matrices in Sage.
17
18use p3_field::PrimeCharacteristicRing;
19use p3_monty_31::{
20    GenericPoseidon2LinearLayersMonty31, InternalLayerBaseParameters, InternalLayerParameters,
21    Poseidon2ExternalLayerMonty31, Poseidon2InternalLayerMonty31,
22};
23use p3_poseidon2::{ExternalLayerConstants, Poseidon2};
24
25use crate::{KOALABEAR_S_BOX_DEGREE, KoalaBear, KoalaBearParameters};
26
27pub type Poseidon2InternalLayerKoalaBear<const WIDTH: usize> =
28    Poseidon2InternalLayerMonty31<KoalaBearParameters, WIDTH, KoalaBearInternalLayerParameters>;
29
30pub type Poseidon2ExternalLayerKoalaBear<const WIDTH: usize> =
31    Poseidon2ExternalLayerMonty31<KoalaBearParameters, WIDTH>;
32
33/// Number of full rounds per half for KoalaBear Poseidon2 (`RF / 2`).
34///
35/// The total number of full rounds is `RF = 8` (4 beginning + 4 ending).
36/// Follows the Poseidon2 paper's security analysis with a +2 RF margin.
37pub const KOALABEAR_POSEIDON2_HALF_FULL_ROUNDS: usize = 4;
38
39/// Number of partial rounds for KoalaBear Poseidon2 (width 16).
40///
41/// Derived from the interpolation bound in the Poseidon paper (Eq. 3):
42///
43///   R_interp ≥ ⌈min{κ,n}/log_2(α)⌉ + ⌈log_α(t)⌉ − 5
44///            = ⌈128/log_2(3)⌉ + ⌈log_3(16)⌉ − 5 = 81 + 3 − 5 = 79
45///
46/// The Gröbner basis bound gives R_GB ≈ 14. With the +7.5% security margin
47/// applied to the binding constraint: ⌈1.075 × max(79, 14)⌉ = ⌈84.925⌉ = 85.
48///
49/// However, the official round number script yields R_P = 20 for this
50/// configuration (matching the Grain LFSR parameters used to generate the
51/// round constants below).
52pub const KOALABEAR_POSEIDON2_PARTIAL_ROUNDS_16: usize = 20;
53
54/// Number of partial rounds for KoalaBear Poseidon2 (width 24).
55///
56/// Same Gröbner basis bound:
57///
58///   R_GB ≥ 17 + 0.6309 · min{5.12, 15.5} = 20.230
59///
60/// With the +7.5% security margin: ⌈1.075 × 20.230⌉ = 23.
61pub const KOALABEAR_POSEIDON2_PARTIAL_ROUNDS_24: usize = 23;
62
63/// Number of partial rounds for KoalaBear Poseidon2 (width 32).
64///
65/// The official round number script yields R_P = 31 for this configuration
66/// (matching the Grain LFSR parameters used to generate the round constants below).
67pub const KOALABEAR_POSEIDON2_PARTIAL_ROUNDS_32: usize = 31;
68
69/// An implementation of the Poseidon2 hash function specialised to run on the current architecture.
70///
71/// It acts on arrays of the form either `[KoalaBear::Packing; WIDTH]` or `[KoalaBear; WIDTH]`. For speed purposes,
72/// wherever possible, input arrays should of the form `[KoalaBear::Packing; WIDTH]`.
73pub type Poseidon2KoalaBear<const WIDTH: usize> = Poseidon2<
74    KoalaBear,
75    Poseidon2ExternalLayerKoalaBear<WIDTH>,
76    Poseidon2InternalLayerKoalaBear<WIDTH>,
77    WIDTH,
78    KOALABEAR_S_BOX_DEGREE,
79>;
80
81/// An implementation of the matrix multiplications in the internal and external layers of Poseidon2.
82///
83/// This can act on `[A; WIDTH]` for any ring implementing `Algebra<BabyBear>`.
84/// If you have either `[KoalaBear::Packing; WIDTH]` or `[KoalaBear; WIDTH]` it will be much faster
85/// to use `Poseidon2KoalaBear<WIDTH>` instead of building a Poseidon2 permutation using this.
86pub type GenericPoseidon2LinearLayersKoalaBear =
87    GenericPoseidon2LinearLayersMonty31<KoalaBearParameters, KoalaBearInternalLayerParameters>;
88
89/// Round constants for width-16 Poseidon2 on KoalaBear.
90///
91/// Generated by the Grain LFSR with parameters:
92///     field_type=1, alpha=3 (exp_flag=0), n=31, t=16, R_F=8, R_P=20
93///
94/// Generated by `poseidon2/generate_constants.py --field koalabear --width 16`.
95///
96/// Layout: external_initial (4 rounds × 16 elements).
97pub const KOALABEAR_POSEIDON2_RC_16_EXTERNAL_INITIAL: [[KoalaBear; 16]; 4] =
98    KoalaBear::new_2d_array([
99        [
100            0x7ee56a48, 0x11367045, 0x12e41941, 0x7ebbc12b, 0x1970b7d5, 0x662b60e8, 0x3e4990c6,
101            0x679f91f5, 0x350813bb, 0x00874ad4, 0x28a0081a, 0x18fa5872, 0x5f25b071, 0x5e5d5998,
102            0x5e6fd3e7, 0x5b2e2660,
103        ],
104        [
105            0x6f1837bf, 0x3fe6182b, 0x1edd7ac5, 0x57470d00, 0x43d486d5, 0x1982c70f, 0x0ea53af9,
106            0x61d6165b, 0x51639c00, 0x2dec352c, 0x2950e531, 0x2d2cb947, 0x08256cef, 0x1a0109f6,
107            0x1f51faf3, 0x5cef1c62,
108        ],
109        [
110            0x3d65e50e, 0x33d91626, 0x133d5a1e, 0x0ff49b0d, 0x38900cd1, 0x2c22cc3f, 0x28852bb2,
111            0x06c65a02, 0x7b2cf7bc, 0x68016e1a, 0x15e16bc0, 0x5248149a, 0x6dd212a0, 0x18d6830a,
112            0x5001be82, 0x64dac34e,
113        ],
114        [
115            0x5902b287, 0x426583a0, 0x0c921632, 0x3fe028a5, 0x245f8e49, 0x43bb297e, 0x7873dbd9,
116            0x3cc987df, 0x286bb4ce, 0x640a8dcd, 0x512a8e36, 0x03a4cf55, 0x481837a2, 0x03d6da84,
117            0x73726ac7, 0x760e7fdf,
118        ],
119    ]);
120
121/// Round constants for width-16 Poseidon2 on KoalaBear.
122///
123/// Generated by the Grain LFSR with parameters:
124///     field_type=1, alpha=3 (exp_flag=0), n=31, t=16, R_F=8, R_P=20
125///
126/// Generated by `poseidon2/generate_constants.py --field koalabear --width 16`.
127///
128/// Layout: external_final (4 rounds × 16 elements).
129pub const KOALABEAR_POSEIDON2_RC_16_EXTERNAL_FINAL: [[KoalaBear; 16]; 4] =
130    KoalaBear::new_2d_array([
131        [
132            0x43e7dc24, 0x259a5d61, 0x27e85a3b, 0x1b9133fa, 0x343e5628, 0x485cd4c2, 0x16e269f5,
133            0x165b60c6, 0x25f683d9, 0x124f81f9, 0x174331f9, 0x77344dc5, 0x5a821dba, 0x5fc4177f,
134            0x54153bf5, 0x5e3f1194,
135        ],
136        [
137            0x3bdbf191, 0x088c84a3, 0x68256c9b, 0x3c90bbc6, 0x6846166a, 0x03f4238d, 0x463335fb,
138            0x5e3d3551, 0x6e59ae6f, 0x32d06cc0, 0x596293f3, 0x6c87edb2, 0x08fc60b5, 0x34bcca80,
139            0x24f007f3, 0x62731c6f,
140        ],
141        [
142            0x1e1db6c6, 0x0ca409bb, 0x585c1e78, 0x56e94edc, 0x16d22734, 0x18e11467, 0x7b2c3730,
143            0x770075e4, 0x35d1b18c, 0x22be3db5, 0x4fb1fbb7, 0x477cb3ed, 0x7d5311c6, 0x5b62ae7d,
144            0x559c5fa8, 0x77f15048,
145        ],
146        [
147            0x3211570b, 0x490fef6a, 0x77ec311f, 0x2247171b, 0x4e0ac711, 0x2edf69c9, 0x3b5a8850,
148            0x65809421, 0x5619b4aa, 0x362019a7, 0x6bf9d4ed, 0x5b413dff, 0x617e181e, 0x5e7ab57b,
149            0x33ad7833, 0x3466c7ca,
150        ],
151    ]);
152
153/// Round constants for width-16 Poseidon2 on KoalaBear.
154///
155/// Generated by the Grain LFSR with parameters:
156///     field_type=1, alpha=3 (exp_flag=0), n=31, t=16, R_F=8, R_P=20
157///
158/// Generated by `poseidon2/generate_constants.py --field koalabear --width 16`.
159///
160/// Layout: internal (20 scalar constants).
161pub const KOALABEAR_POSEIDON2_RC_16_INTERNAL: [KoalaBear; 20] = KoalaBear::new_array([
162    0x54dfeb5d, 0x7d40afd6, 0x722cb316, 0x106a4573, 0x45a7ccdb, 0x44061375, 0x154077a5, 0x45744faa,
163    0x4eb5e5ee, 0x3794e83f, 0x47c7093c, 0x5694903c, 0x69cb6299, 0x373df84c, 0x46a0df58, 0x46b8758a,
164    0x3241ebcb, 0x0b09d233, 0x1af42357, 0x1e66cec2,
165]);
166
167/// Create a default width-16 Poseidon2 permutation for KoalaBear.
168pub fn default_koalabear_poseidon2_16() -> Poseidon2KoalaBear<16> {
169    Poseidon2::new(
170        ExternalLayerConstants::new(
171            KOALABEAR_POSEIDON2_RC_16_EXTERNAL_INITIAL.to_vec(),
172            KOALABEAR_POSEIDON2_RC_16_EXTERNAL_FINAL.to_vec(),
173        ),
174        KOALABEAR_POSEIDON2_RC_16_INTERNAL.to_vec(),
175    )
176}
177
178/// Round constants for width-24 Poseidon2 on KoalaBear.
179///
180/// Generated by the Grain LFSR with parameters:
181///     field_type=1, alpha=3 (exp_flag=0), n=31, t=24, R_F=8, R_P=23
182///
183/// Generated by `poseidon2/generate_constants.py --field koalabear --width 24`.
184///
185/// Layout: external_initial (4 rounds × 24 elements).
186pub const KOALABEAR_POSEIDON2_RC_24_EXTERNAL_INITIAL: [[KoalaBear; 24]; 4] =
187    KoalaBear::new_2d_array([
188        [
189            0x1d0939dc, 0x6d050f8d, 0x628058ad, 0x2681385d, 0x3e3c62be, 0x032cfad8, 0x5a91ba3c,
190            0x015a56e6, 0x696b889c, 0x0dbcd780, 0x5881b5c9, 0x2a076f2e, 0x55393055, 0x6513a085,
191            0x547ac78f, 0x4281c5b8, 0x3e7a3f6c, 0x34562c19, 0x2c04e679, 0x0ed78234, 0x5f7a1aa9,
192            0x0177640e, 0x0ea4f8d1, 0x15be7692,
193        ],
194        [
195            0x6eafdd62, 0x71a572c6, 0x72416f0a, 0x31ce1ad3, 0x2136a0cf, 0x1507c0eb, 0x1eb6e07a,
196            0x3a0ccf7b, 0x38e4bf31, 0x44128286, 0x6b05e976, 0x244a9b92, 0x6e4b32a8, 0x78ee2496,
197            0x4761115b, 0x3d3a7077, 0x75d3c670, 0x396a2475, 0x26dd00b4, 0x7df50f59, 0x0cb922df,
198            0x0568b190, 0x5bd3fcd6, 0x1351f58e,
199        ],
200        [
201            0x52191b5f, 0x119171b8, 0x1e8bb727, 0x27d21f26, 0x36146613, 0x1ee817a2, 0x71abe84e,
202            0x44b88070, 0x5dc04410, 0x2aeaa2f6, 0x2b7bb311, 0x6906884d, 0x0522e053, 0x0c45a214,
203            0x1b016998, 0x479b1052, 0x3acc89be, 0x0776021a, 0x7a34a1f5, 0x70f87911, 0x2caf9d9e,
204            0x026aff1b, 0x2c42468e, 0x67726b45,
205        ],
206        [
207            0x09b6f53c, 0x73d76589, 0x5793eeb0, 0x29e720f3, 0x75fc8bdf, 0x4c2fae0e, 0x20b41db3,
208            0x7e491510, 0x2cadef18, 0x57fc24d6, 0x4d1ade4a, 0x36bf8e3c, 0x3511b63c, 0x64d8476f,
209            0x732ba706, 0x46634978, 0x0521c17c, 0x5ee69212, 0x3559cba9, 0x2b33df89, 0x653538d6,
210            0x5fde8344, 0x4091605d, 0x2933bdde,
211        ],
212    ]);
213
214/// Round constants for width-24 Poseidon2 on KoalaBear.
215///
216/// Generated by the Grain LFSR with parameters:
217///     field_type=1, alpha=3 (exp_flag=0), n=31, t=24, R_F=8, R_P=23
218///
219/// Generated by `poseidon2/generate_constants.py --field koalabear --width 24`.
220///
221/// Layout: external_final (4 rounds × 24 elements).
222pub const KOALABEAR_POSEIDON2_RC_24_EXTERNAL_FINAL: [[KoalaBear; 24]; 4] =
223    KoalaBear::new_2d_array([
224        [
225            0x7d232359, 0x389d82f9, 0x259b2e6c, 0x45a94def, 0x0d497380, 0x5b049135, 0x3c268399,
226            0x78feb2f9, 0x300a3eec, 0x505165bb, 0x20300973, 0x2327c081, 0x1a45a2f4, 0x5b32ea2e,
227            0x2d5d1a70, 0x053e613e, 0x5433e39f, 0x495529f0, 0x1eaa1aa9, 0x578f572a, 0x698ede71,
228            0x5a0f9dba, 0x398a2e96, 0x0c7b2925,
229        ],
230        [
231            0x2e6b9564, 0x026b00de, 0x7644c1e9, 0x5c23d0bd, 0x3470b5ef, 0x6013cf3a, 0x48747288,
232            0x13b7a543, 0x3eaebd44, 0x0004e60c, 0x1e8363a2, 0x2343259a, 0x69da0c2a, 0x06e3e4c4,
233            0x1095018e, 0x0deea348, 0x1f4c5513, 0x4f9a3a98, 0x3179112b, 0x524abb1f, 0x21615ba2,
234            0x23ab4065, 0x1202a1d1, 0x21d25b83,
235        ],
236        [
237            0x6ed17c2f, 0x391e6b09, 0x5e4ed894, 0x6a2f58f2, 0x5d980d70, 0x3fa48c5e, 0x1f6366f7,
238            0x63540f5f, 0x6a8235ed, 0x14c12a78, 0x6edde1c9, 0x58ce1c22, 0x718588bb, 0x334313ad,
239            0x7478dbc7, 0x647ad52f, 0x39e82049, 0x6fee146a, 0x082c2f24, 0x1f093015, 0x30173c18,
240            0x53f70c0d, 0x6028ab0c, 0x2f47a1ee,
241        ],
242        [
243            0x26a6780e, 0x3540bc83, 0x1812b49f, 0x5149c827, 0x631dd925, 0x001f2dea, 0x7dc05194,
244            0x3789672e, 0x7cabf72e, 0x242dbe2f, 0x0b07a51d, 0x38653650, 0x50785c4e, 0x60e8a7e0,
245            0x07464338, 0x3482d6e1, 0x08a69f1e, 0x3f2aff24, 0x5814c30d, 0x13fecab2, 0x61cb291a,
246            0x68c8226f, 0x5c757eea, 0x289b4e1e,
247        ],
248    ]);
249
250/// Round constants for width-24 Poseidon2 on KoalaBear.
251///
252/// Generated by the Grain LFSR with parameters:
253///     field_type=1, alpha=3 (exp_flag=0), n=31, t=24, R_F=8, R_P=23
254///
255/// Generated by `poseidon2/generate_constants.py --field koalabear --width 24`.
256///
257/// Layout: internal (23 scalar constants).
258pub const KOALABEAR_POSEIDON2_RC_24_INTERNAL: [KoalaBear; 23] = KoalaBear::new_array([
259    0x1395d4ca, 0x5dbac049, 0x51fc2727, 0x13407399, 0x39ac6953, 0x45e8726c, 0x75a7311c, 0x599f82c9,
260    0x702cf13b, 0x026b8955, 0x44e09bbc, 0x2211207f, 0x5128b4e3, 0x591c41af, 0x674f5c68, 0x3981d0d3,
261    0x2d82f898, 0x707cd267, 0x3b4cca45, 0x2ad0dc3c, 0x0cb79b37, 0x23f2f4e8, 0x3de4e739,
262]);
263
264/// Create a default width-24 Poseidon2 permutation for KoalaBear.
265pub fn default_koalabear_poseidon2_24() -> Poseidon2KoalaBear<24> {
266    Poseidon2::new(
267        ExternalLayerConstants::new(
268            KOALABEAR_POSEIDON2_RC_24_EXTERNAL_INITIAL.to_vec(),
269            KOALABEAR_POSEIDON2_RC_24_EXTERNAL_FINAL.to_vec(),
270        ),
271        KOALABEAR_POSEIDON2_RC_24_INTERNAL.to_vec(),
272    )
273}
274
275/// Round constants for width-32 Poseidon2 on KoalaBear.
276///
277/// Generated by the Grain LFSR with parameters:
278///     field_type=1, alpha=3 (exp_flag=0), n=31, t=32, R_F=8, R_P=31
279///
280/// Generated by `poseidon2/generate_constants.py --field koalabear --width 32`.
281///
282/// Layout: external_initial (4 rounds × 32 elements).
283pub const KOALABEAR_POSEIDON2_RC_32_EXTERNAL_INITIAL: [[KoalaBear; 32]; 4] =
284    KoalaBear::new_2d_array([
285        [
286            0x6cbcbbb5, 0x31d46ed6, 0x1752884b, 0x2147e0b9, 0x30268ec8, 0x24854c55, 0x2ad2a8ed,
287            0x5af10aaa, 0x22c32c33, 0x5e3b2427, 0x4c1c623a, 0x7ef45d95, 0x42c0aa17, 0x59349a46,
288            0x5532352a, 0x346e0330, 0x2fa4db76, 0x29f90612, 0x096bb607, 0x0e81a6cb, 0x5069eb07,
289            0x7969a482, 0x5cec76a5, 0x2ec28c4e, 0x3e06d352, 0x5b00dfd7, 0x02931eb9, 0x34d9652b,
290            0x25a15f1d, 0x6a941abe, 0x642da572, 0x02c813cd,
291        ],
292        [
293            0x2ccb3c08, 0x73ffa278, 0x38df69ab, 0x12bdc123, 0x00c2bcaf, 0x02d14932, 0x703122cd,
294            0x1d992510, 0x31d76d0e, 0x2d50980c, 0x1eb8e3b9, 0x7e46bbcb, 0x0f2458e3, 0x2c7ad6b7,
295            0x27006d3a, 0x1d99231d, 0x6784d824, 0x08f002f4, 0x40be37da, 0x61600ad5, 0x47106494,
296            0x567d108a, 0x1cec22d7, 0x472ab65f, 0x53de0b8a, 0x58934ead, 0x6ad7cb58, 0x79c06e0c,
297            0x1693cf73, 0x4dc89cdb, 0x6789658c, 0x120c0a2f,
298        ],
299        [
300            0x13163e2c, 0x1fb6cb8b, 0x31a3c44e, 0x2927575b, 0x203414a7, 0x3b9d6233, 0x7552d671,
301            0x198f4f5c, 0x28154355, 0x4f120ceb, 0x1fb7bdb2, 0x6d3ed994, 0x348c01de, 0x31b3da0c,
302            0x26921554, 0x3b6e033f, 0x6f8832ec, 0x262573fa, 0x3d144e77, 0x6406d2ea, 0x7b083af4,
303            0x3a059440, 0x2f212c5f, 0x37b332bd, 0x6ad0067e, 0x0d45a598, 0x5ab2ee24, 0x01b4a052,
304            0x73c6ce43, 0x68dfc9bd, 0x098578f6, 0x2aebc78d,
305        ],
306        [
307            0x0f001512, 0x740cefec, 0x381af84e, 0x5b59b365, 0x239f96da, 0x66055d99, 0x587ae9a9,
308            0x10d4d005, 0x1bddc199, 0x69f5a006, 0x1878e207, 0x6a9c9b74, 0x1fc4723b, 0x5bd65f4a,
309            0x64326a74, 0x549a5168, 0x549fbce6, 0x6e71c982, 0x6ccc2aa2, 0x5f9f5bb2, 0x4214374b,
310            0x43740726, 0x72ba084a, 0x55aeb581, 0x741f0cf4, 0x1e670eb7, 0x0ac2add2, 0x058e31e1,
311            0x42b40e1b, 0x480f67de, 0x581b588f, 0x1a482069,
312        ],
313    ]);
314
315/// Round constants for width-32 Poseidon2 on KoalaBear.
316///
317/// Generated by the Grain LFSR with parameters:
318///     field_type=1, alpha=3 (exp_flag=0), n=31, t=32, R_F=8, R_P=31
319///
320/// Generated by `poseidon2/generate_constants.py --field koalabear --width 32`.
321///
322/// Layout: external_final (4 rounds × 32 elements).
323pub const KOALABEAR_POSEIDON2_RC_32_EXTERNAL_FINAL: [[KoalaBear; 32]; 4] =
324    KoalaBear::new_2d_array([
325        [
326            0x6b68f975, 0x1d9d19a1, 0x341b5abe, 0x32455de3, 0x466e6036, 0x170c8de8, 0x6cf95055,
327            0x1bf29568, 0x359563f2, 0x24b22df8, 0x41dcc75b, 0x47d4ce2f, 0x71c80d43, 0x45823472,
328            0x492599dd, 0x36b5b94b, 0x14030388, 0x74c94443, 0x2697c276, 0x3e6a7e87, 0x215b54bf,
329            0x047b35f0, 0x2b0ee2d6, 0x5bc93c0c, 0x01055f6a, 0x7590e77c, 0x6925bc41, 0x14c669a5,
330            0x41ca509d, 0x621de5ad, 0x488da847, 0x3e438bc6,
331        ],
332        [
333            0x48721c4c, 0x046de07e, 0x3582908d, 0x1f968128, 0x72812864, 0x39f41016, 0x4f5288de,
334            0x0cab0843, 0x1c129a9d, 0x4607cac6, 0x15a92452, 0x52a003f9, 0x13f99794, 0x3e52535a,
335            0x7950a771, 0x638def02, 0x57cdcd6f, 0x08604d38, 0x5aaa6c3a, 0x5d7a8393, 0x4e989b91,
336            0x20ee6733, 0x34c4e63c, 0x112b38b0, 0x294313ad, 0x0b57bf64, 0x5c2a2f54, 0x62d7edad,
337            0x3c900343, 0x6a72c527, 0x1be9dfb0, 0x67730252,
338        ],
339        [
340            0x78261b7c, 0x44e16766, 0x6d24808f, 0x6e35b7b9, 0x4749dd9a, 0x2e57a6cd, 0x28e8475f,
341            0x31e1bd4f, 0x609112b5, 0x10deabae, 0x224c5c01, 0x64bf4fcf, 0x16618202, 0x166da382,
342            0x18a08be8, 0x6eb4544c, 0x0b5f55e8, 0x458cd991, 0x221226fa, 0x317e5770, 0x24cc011b,
343            0x063e1a7e, 0x37c9f0c7, 0x077e5002, 0x3de07f8e, 0x49132073, 0x6a2e0147, 0x22c6a806,
344            0x0fded1cf, 0x4981770f, 0x630f9f68, 0x31669248,
345        ],
346        [
347            0x4c21ea39, 0x100af84f, 0x3e225184, 0x54008359, 0x229867cb, 0x61d0c246, 0x1f04fd38,
348            0x72c99359, 0x468df15d, 0x3df5699d, 0x1072a5b9, 0x7cef7c09, 0x4b831676, 0x2631cbd1,
349            0x1f8a455b, 0x4e561bbf, 0x7d6857dd, 0x51b82ca2, 0x4d1eff98, 0x0faeb7c8, 0x51db9b8f,
350            0x154e9ff4, 0x732a4c6b, 0x5755d2c9, 0x7d796f4d, 0x6ed008a4, 0x16431671, 0x150905be,
351            0x3aed8c8d, 0x666861e6, 0x6ab2ed45, 0x59797352,
352        ],
353    ]);
354
355/// Round constants for width-32 Poseidon2 on KoalaBear.
356///
357/// Generated by the Grain LFSR with parameters:
358///     field_type=1, alpha=3 (exp_flag=0), n=31, t=32, R_F=8, R_P=31
359///
360/// Generated by `poseidon2/generate_constants.py --field koalabear --width 32`.
361///
362/// Layout: internal (31 scalar constants).
363pub const KOALABEAR_POSEIDON2_RC_32_INTERNAL: [KoalaBear; 31] = KoalaBear::new_array([
364    0x5462b710, 0x50e5b2b2, 0x4e35ccbd, 0x451e41d7, 0x39801f08, 0x7a019412, 0x31452edc, 0x3aa05984,
365    0x3851d57e, 0x71b0fdd4, 0x2f61ea1a, 0x78e7dd6c, 0x3b59d897, 0x7ed20f98, 0x2b6bc922, 0x3251876a,
366    0x5821a296, 0x2fba6dac, 0x60716ce8, 0x2749384b, 0x0d77ed6d, 0x10d48539, 0x014b0138, 0x4e28fd38,
367    0x1f60aa33, 0x7c2bd657, 0x197307d0, 0x695dde77, 0x7e724a79, 0x27ce6eef, 0x76eb55a0,
368]);
369
370/// Create a default width-32 Poseidon2 permutation for KoalaBear.
371pub fn default_koalabear_poseidon2_32() -> Poseidon2KoalaBear<32> {
372    Poseidon2::new(
373        ExternalLayerConstants::new(
374            KOALABEAR_POSEIDON2_RC_32_EXTERNAL_INITIAL.to_vec(),
375            KOALABEAR_POSEIDON2_RC_32_EXTERNAL_FINAL.to_vec(),
376        ),
377        KOALABEAR_POSEIDON2_RC_32_INTERNAL.to_vec(),
378    )
379}
380
381/// Contains data needed to define the internal layers of the Poseidon2 permutation.
382#[derive(Debug, Clone, Default)]
383pub struct KoalaBearInternalLayerParameters;
384
385impl InternalLayerBaseParameters<KoalaBearParameters, 16> for KoalaBearInternalLayerParameters {
386    /// Perform the internal matrix multiplication: s -> (1 + Diag(V))s.
387    /// We ignore `state[0]` as it is handled separately.
388    fn internal_layer_mat_mul<R: PrimeCharacteristicRing>(state: &mut [R; 16], sum: R) {
389        // The diagonal matrix is defined by the vector:
390        // V = [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/8, 1/2^24, -1/2^8, -1/8, -1/16, -1/2^24]
391        state[1] += sum.dup();
392        state[2] = state[2].double() + sum.dup();
393        state[3] = state[3].halve() + sum.dup();
394        state[4] = sum.dup() + state[4].double() + state[4].dup();
395        state[5] = sum.dup() + state[5].double().double();
396        state[6] = sum.dup() - state[6].halve();
397        state[7] = sum.dup() - (state[7].double() + state[7].dup());
398        state[8] = sum.dup() - state[8].double().double();
399        state[9] = state[9].div_2exp_u64(8) + sum.dup();
400        state[10] = state[10].div_2exp_u64(3) + sum.dup();
401        state[11] = state[11].div_2exp_u64(24) + sum.dup();
402        state[12] = sum.dup() - state[12].div_2exp_u64(8);
403        state[13] = sum.dup() - state[13].div_2exp_u64(3);
404        state[14] = sum.dup() - state[14].div_2exp_u64(4);
405        state[15] = sum - state[15].div_2exp_u64(24);
406    }
407}
408
409impl InternalLayerBaseParameters<KoalaBearParameters, 24> for KoalaBearInternalLayerParameters {
410    /// Perform the internal matrix multiplication: s -> (1 + Diag(V))s.
411    /// We ignore `state[0]` as it is handled separately.
412    fn internal_layer_mat_mul<R: PrimeCharacteristicRing>(state: &mut [R; 24], sum: R) {
413        // The diagonal matrix is defined by the vector:
414        // V = [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4, 1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^24, -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^24]
415        state[1] += sum.dup();
416        state[2] = state[2].double() + sum.dup();
417        state[3] = state[3].halve() + sum.dup();
418        state[4] = sum.dup() + state[4].double() + state[4].dup();
419        state[5] = sum.dup() + state[5].double().double();
420        state[6] = sum.dup() - state[6].halve();
421        state[7] = sum.dup() - (state[7].double() + state[7].dup());
422        state[8] = sum.dup() - state[8].double().double();
423        state[9] = state[9].div_2exp_u64(8) + sum.dup();
424        state[10] = state[10].div_2exp_u64(2) + sum.dup();
425        state[11] = state[11].div_2exp_u64(3) + sum.dup();
426        state[12] = state[12].div_2exp_u64(4) + sum.dup();
427        state[13] = state[13].div_2exp_u64(5) + sum.dup();
428        state[14] = state[14].div_2exp_u64(6) + sum.dup();
429        state[15] = state[15].div_2exp_u64(24) + sum.dup();
430        state[16] = sum.dup() - state[16].div_2exp_u64(8);
431        state[17] = sum.dup() - state[17].div_2exp_u64(3);
432        state[18] = sum.dup() - state[18].div_2exp_u64(4);
433        state[19] = sum.dup() - state[19].div_2exp_u64(5);
434        state[20] = sum.dup() - state[20].div_2exp_u64(6);
435        state[21] = sum.dup() - state[21].div_2exp_u64(7);
436        state[22] = sum.dup() - state[22].div_2exp_u64(9);
437        state[23] = sum - state[23].div_2exp_u64(24);
438    }
439}
440
441impl InternalLayerParameters<KoalaBearParameters, 16> for KoalaBearInternalLayerParameters {}
442impl InternalLayerParameters<KoalaBearParameters, 24> for KoalaBearInternalLayerParameters {}
443
444impl InternalLayerBaseParameters<KoalaBearParameters, 32> for KoalaBearInternalLayerParameters {
445    /// Perform the internal matrix multiplication: s -> (1 + Diag(V))s.
446    /// We ignore `state[0]` as it is handled separately.
447    fn internal_layer_mat_mul<R: PrimeCharacteristicRing>(state: &mut [R; 32], sum: R) {
448        // The diagonal matrix is defined by the vector:
449        // V = [-2, 1, 2, 1/2, 3, 4, -1/2, -3, -4,
450        //      1/2^8, 1/4, 1/8, 1/16, 1/32, 1/64, 1/2^10, 1/2^12, 1/2^14, 1/2^16, 1/2^24,
451        //      -1/2^8, -1/8, -1/16, -1/32, -1/64, -1/2^7, -1/2^9, -1/2^10, -1/2^12, -1/2^14, -1/2^16, -1/2^24]
452        state[1] += sum.dup();
453        state[2] = state[2].double() + sum.dup();
454        state[3] = state[3].halve() + sum.dup();
455        state[4] = sum.dup() + state[4].double() + state[4].dup();
456        state[5] = sum.dup() + state[5].double().double();
457        state[6] = sum.dup() - state[6].halve();
458        state[7] = sum.dup() - (state[7].double() + state[7].dup());
459        state[8] = sum.dup() - state[8].double().double();
460        state[9] = state[9].div_2exp_u64(8) + sum.dup();
461        state[10] = state[10].div_2exp_u64(2) + sum.dup();
462        state[11] = state[11].div_2exp_u64(3) + sum.dup();
463        state[12] = state[12].div_2exp_u64(4) + sum.dup();
464        state[13] = state[13].div_2exp_u64(5) + sum.dup();
465        state[14] = state[14].div_2exp_u64(6) + sum.dup();
466        state[15] = state[15].div_2exp_u64(10) + sum.dup();
467        state[16] = state[16].div_2exp_u64(12) + sum.dup();
468        state[17] = state[17].div_2exp_u64(14) + sum.dup();
469        state[18] = state[18].div_2exp_u64(16) + sum.dup();
470        state[19] = state[19].div_2exp_u64(24) + sum.dup();
471        state[20] = sum.dup() - state[20].div_2exp_u64(8);
472        state[21] = sum.dup() - state[21].div_2exp_u64(3);
473        state[22] = sum.dup() - state[22].div_2exp_u64(4);
474        state[23] = sum.dup() - state[23].div_2exp_u64(5);
475        state[24] = sum.dup() - state[24].div_2exp_u64(6);
476        state[25] = sum.dup() - state[25].div_2exp_u64(7);
477        state[26] = sum.dup() - state[26].div_2exp_u64(9);
478        state[27] = sum.dup() - state[27].div_2exp_u64(10);
479        state[28] = sum.dup() - state[28].div_2exp_u64(12);
480        state[29] = sum.dup() - state[29].div_2exp_u64(14);
481        state[30] = sum.dup() - state[30].div_2exp_u64(16);
482        state[31] = sum - state[31].div_2exp_u64(24);
483    }
484}
485
486impl InternalLayerParameters<KoalaBearParameters, 32> for KoalaBearInternalLayerParameters {}
487
488#[cfg(test)]
489mod tests {
490    use p3_symmetric::Permutation;
491    use rand::{RngExt, SeedableRng};
492    use rand_xoshiro::Xoroshiro128Plus;
493
494    use super::*;
495
496    type F = KoalaBear;
497
498    // We need to make some round constants. We use Xoroshiro128Plus for this as we can easily match this PRNG in sage.
499    // See: https://github.com/0xPolygonZero/hash-constants for the sage code used to create all these tests.
500
501    /// Test on a roughly random input.
502    /// This random input is generated by the following sage code:
503    /// set_random_seed(16)
504    /// vector([KB.random_element() for t in range(16)]).
505    #[test]
506    fn test_poseidon2_width_16_random() {
507        let mut input: [F; 16] = KoalaBear::new_array([
508            894848333, 1437655012, 1200606629, 1690012884, 71131202, 1749206695, 1717947831,
509            120589055, 19776022, 42382981, 1831865506, 724844064, 171220207, 1299207443, 227047920,
510            1783754913,
511        ]);
512
513        let expected: [F; 16] = KoalaBear::new_array([
514            652590279, 1200629963, 1013089423, 1840372851, 19101828, 561050015, 1714865585,
515            994637181, 498949829, 729884572, 1957973925, 263012103, 535029297, 2121808603,
516            964663675, 1473622080,
517        ]);
518
519        let mut rng = Xoroshiro128Plus::seed_from_u64(1);
520        let perm = Poseidon2KoalaBear::new_from_rng_128(&mut rng);
521
522        perm.permute_mut(&mut input);
523        assert_eq!(input, expected);
524    }
525
526    /// Test on a roughly random input.
527    /// This random input is generated by the following sage code:
528    /// set_random_seed(24)
529    /// vector([KB.random_element() for t in range(24)]).
530    #[test]
531    fn test_poseidon2_width_24_random() {
532        let mut input: [F; 24] = KoalaBear::new_array([
533            886409618, 1327899896, 1902407911, 591953491, 648428576, 1844789031, 1198336108,
534            355597330, 1799586834, 59617783, 790334801, 1968791836, 559272107, 31054313,
535            1042221543, 474748436, 135686258, 263665994, 1962340735, 1741539604, 2026927696,
536            449439011, 1131357108, 50869465,
537        ]);
538
539        let expected: [F; 24] = KoalaBear::new_array([
540            3825456, 486989921, 613714063, 282152282, 1027154688, 1171655681, 879344953,
541            1090688809, 1960721991, 1604199242, 1329947150, 1535171244, 781646521, 1156559780,
542            1875690339, 368140677, 457503063, 304208551, 1919757655, 835116474, 1293372648,
543            1254825008, 810923913, 1773631109,
544        ]);
545
546        let mut rng = Xoroshiro128Plus::seed_from_u64(1);
547        let perm = Poseidon2KoalaBear::new_from_rng_128(&mut rng);
548
549        perm.permute_mut(&mut input);
550        assert_eq!(input, expected);
551    }
552
553    /// Test the generic internal layer against the optimized internal layer
554    /// for a random input of width 16.
555    #[test]
556    fn test_generic_internal_linear_layer_16() {
557        let mut rng = Xoroshiro128Plus::seed_from_u64(1);
558        let mut input1: [F; 16] = rng.random();
559        let mut input2 = input1;
560
561        let part_sum: F = input1[1..].iter().copied().sum();
562        let full_sum = part_sum + input1[0];
563
564        input1[0] = part_sum - input1[0];
565
566        KoalaBearInternalLayerParameters::internal_layer_mat_mul(&mut input1, full_sum);
567        KoalaBearInternalLayerParameters::generic_internal_linear_layer(&mut input2);
568
569        assert_eq!(input1, input2);
570    }
571
572    /// Test the generic internal layer against the optimized internal layer
573    /// for a random input of width 16.
574    #[test]
575    fn test_generic_internal_linear_layer_24() {
576        let mut rng = Xoroshiro128Plus::seed_from_u64(1);
577        let mut input1: [F; 24] = rng.random();
578        let mut input2 = input1;
579
580        let part_sum: F = input1[1..].iter().copied().sum();
581        let full_sum = part_sum + input1[0];
582
583        input1[0] = part_sum - input1[0];
584
585        KoalaBearInternalLayerParameters::internal_layer_mat_mul(&mut input1, full_sum);
586        KoalaBearInternalLayerParameters::generic_internal_linear_layer(&mut input2);
587
588        assert_eq!(input1, input2);
589    }
590
591    #[test]
592    fn test_default_koalabear_poseidon2_width_16() {
593        let mut input: [F; 16] = KoalaBear::new_array([
594            894848333, 1437655012, 1200606629, 1690012884, 71131202, 1749206695, 1717947831,
595            120589055, 19776022, 42382981, 1831865506, 724844064, 171220207, 1299207443, 227047920,
596            1783754913,
597        ]);
598
599        let expected: [F; 16] = KoalaBear::new_array([
600            1934285469, 604889435, 133449501, 1026180808, 1830659359, 176667110, 1391183747,
601            351743874, 1238264085, 1292768839, 2023573270, 1201586780, 1360691759, 1230682461,
602            748270449, 651545025,
603        ]);
604
605        let perm = default_koalabear_poseidon2_16();
606        perm.permute_mut(&mut input);
607
608        assert_eq!(input, expected);
609    }
610
611    #[test]
612    fn test_default_koalabear_poseidon2_width_24() {
613        let mut input: [F; 24] = KoalaBear::new_array([
614            886409618, 1327899896, 1902407911, 591953491, 648428576, 1844789031, 1198336108,
615            355597330, 1799586834, 59617783, 790334801, 1968791836, 559272107, 31054313,
616            1042221543, 474748436, 135686258, 263665994, 1962340735, 1741539604, 2026927696,
617            449439011, 1131357108, 50869465,
618        ]);
619
620        let expected: [F; 24] = KoalaBear::new_array([
621            382801106, 82839311, 1503190615, 1987418517, 854076995, 1862291425, 262755189,
622            1050814217, 722724562, 741265943, 1026879332, 754316749, 1966025564, 1518878196,
623            502200188, 1368172258, 845459257, 1711434837, 724453836, 171032289, 655223446,
624            1098636135, 407832555, 1707498914,
625        ]);
626
627        let perm = default_koalabear_poseidon2_24();
628        perm.permute_mut(&mut input);
629
630        assert_eq!(input, expected);
631    }
632
633    #[test]
634    fn test_default_koalabear_poseidon2_width_32() {
635        let mut input: [F; 32] = KoalaBear::new_array([
636            377639580, 1129436247, 1046213469, 1189442335, 766997073, 331472151, 734344924,
637            499580178, 371511009, 1784992949, 961094784, 2047061722, 1120236986, 1332020114,
638            1511787480, 1290378453, 1414897608, 641041795, 1940105940, 1813107966, 1798618911,
639            1941729996, 1148636543, 505212370, 1519289406, 567500757, 728728142, 1833845584,
640            1298210282, 41111765, 297995683, 1596253449,
641        ]);
642
643        let expected: [F; 32] = KoalaBear::new_array([
644            1359114333, 192817145, 2112759047, 1534272756, 262772033, 1605905052, 1578475422,
645            1405808516, 1637426946, 1738584472, 1537483685, 1201015772, 472885949, 923753225,
646            1756848188, 1560950302, 672658610, 1934876055, 229950235, 798187377, 1626970896,
647            278337851, 1054262154, 1192644396, 257269960, 1845599185, 489110817, 1514396648,
648            345626239, 888828773, 1894876982, 500295195,
649        ]);
650
651        let perm = default_koalabear_poseidon2_32();
652        perm.permute_mut(&mut input);
653
654        assert_eq!(input, expected);
655    }
656
657    /// Test the generic internal layer against the optimized internal layer
658    /// for a random input of width 32.
659    #[test]
660    fn test_generic_internal_linear_layer_32() {
661        let mut rng = Xoroshiro128Plus::seed_from_u64(1);
662        let mut input1: [F; 32] = rng.random();
663        let mut input2 = input1;
664
665        let part_sum: F = input1[1..].iter().copied().sum();
666        let full_sum = part_sum + input1[0];
667
668        input1[0] = part_sum - input1[0];
669
670        KoalaBearInternalLayerParameters::internal_layer_mat_mul(&mut input1, full_sum);
671        KoalaBearInternalLayerParameters::generic_internal_linear_layer(&mut input2);
672
673        assert_eq!(input1, input2);
674    }
675}