twenty_first/tip5/
mod.rs

1//! The arithmetization-oriented, cryptographic hash function Tip5.
2//!
3//! This module contains the reference implementation of [“The Tip5 Hash
4//! Function for Recursive STARKs”](https://eprint.iacr.org/2023/107.pdf), as
5//! well as an [AVX-512](https://en.wikipedia.org/wiki/AVX-512)-accelerated
6//! implementation, which subject to conditional compilation and compatible
7//! hardware.
8
9use std::hash::Hasher;
10
11use arbitrary::Arbitrary;
12pub use digest::Digest;
13use get_size2::GetSize;
14use itertools::Itertools;
15use num_traits::ConstOne;
16use num_traits::ConstZero;
17use serde::Deserialize;
18use serde::Serialize;
19
20use crate::math::x_field_element::EXTENSION_DEGREE;
21use crate::prelude::BFieldCodec;
22use crate::prelude::BFieldElement;
23use crate::prelude::XFieldElement;
24use crate::util_types::sponge::Domain;
25use crate::util_types::sponge::Sponge;
26
27pub const STATE_SIZE: usize = 16;
28pub const NUM_SPLIT_AND_LOOKUP: usize = 4;
29pub const LOG2_STATE_SIZE: usize = 4;
30pub const CAPACITY: usize = 6;
31pub const RATE: usize = 10;
32pub const NUM_ROUNDS: usize = 5;
33
34pub mod digest;
35
36#[cfg(all(
37    target_feature = "avx512ifma",
38    target_feature = "avx512f",
39    target_feature = "avx512bw",
40    target_feature = "avx512vbmi"
41))]
42mod avx512;
43#[cfg(test)]
44mod inverse;
45#[cfg(test)]
46mod naive;
47
48/// The lookup table with a high algebraic degree used in the TIP-5 permutation. To verify its
49/// correctness, see the test “lookup_table_is_correct.”
50pub const LOOKUP_TABLE: [u8; 256] = [
51    0, 7, 26, 63, 124, 215, 85, 254, 214, 228, 45, 185, 140, 173, 33, 240, 29, 177, 176, 32, 8,
52    110, 87, 202, 204, 99, 150, 106, 230, 14, 235, 128, 213, 239, 212, 138, 23, 130, 208, 6, 44,
53    71, 93, 116, 146, 189, 251, 81, 199, 97, 38, 28, 73, 179, 95, 84, 152, 48, 35, 119, 49, 88,
54    242, 3, 148, 169, 72, 120, 62, 161, 166, 83, 175, 191, 137, 19, 100, 129, 112, 55, 221, 102,
55    218, 61, 151, 237, 68, 164, 17, 147, 46, 234, 203, 216, 22, 141, 65, 57, 123, 12, 244, 54, 219,
56    231, 96, 77, 180, 154, 5, 253, 133, 165, 98, 195, 205, 134, 245, 30, 9, 188, 59, 142, 186, 197,
57    181, 144, 92, 31, 224, 163, 111, 74, 58, 69, 113, 196, 67, 246, 225, 10, 121, 50, 60, 157, 90,
58    122, 2, 250, 101, 75, 178, 159, 24, 36, 201, 11, 243, 132, 198, 190, 114, 233, 39, 52, 21, 209,
59    108, 238, 91, 187, 18, 104, 194, 37, 153, 34, 200, 143, 126, 155, 236, 118, 64, 80, 172, 89,
60    94, 193, 135, 183, 86, 107, 252, 13, 167, 206, 136, 220, 207, 103, 171, 160, 76, 182, 227, 217,
61    158, 56, 174, 4, 66, 109, 139, 162, 184, 211, 249, 47, 125, 232, 117, 43, 16, 42, 127, 20, 241,
62    25, 149, 105, 156, 51, 53, 168, 145, 247, 223, 79, 78, 226, 15, 222, 82, 115, 70, 210, 27, 41,
63    1, 170, 40, 131, 192, 229, 248, 255,
64];
65
66/// The round constants used in the Tip5 permutation. To verify their correctness, see the test
67/// “round_constants_are_correct.”
68pub const ROUND_CONSTANTS: [BFieldElement; NUM_ROUNDS * STATE_SIZE] = [
69    BFieldElement::new(13630775303355457758),
70    BFieldElement::new(16896927574093233874),
71    BFieldElement::new(10379449653650130495),
72    BFieldElement::new(1965408364413093495),
73    BFieldElement::new(15232538947090185111),
74    BFieldElement::new(15892634398091747074),
75    BFieldElement::new(3989134140024871768),
76    BFieldElement::new(2851411912127730865),
77    BFieldElement::new(8709136439293758776),
78    BFieldElement::new(3694858669662939734),
79    BFieldElement::new(12692440244315327141),
80    BFieldElement::new(10722316166358076749),
81    BFieldElement::new(12745429320441639448),
82    BFieldElement::new(17932424223723990421),
83    BFieldElement::new(7558102534867937463),
84    BFieldElement::new(15551047435855531404),
85    BFieldElement::new(17532528648579384106),
86    BFieldElement::new(5216785850422679555),
87    BFieldElement::new(15418071332095031847),
88    BFieldElement::new(11921929762955146258),
89    BFieldElement::new(9738718993677019874),
90    BFieldElement::new(3464580399432997147),
91    BFieldElement::new(13408434769117164050),
92    BFieldElement::new(264428218649616431),
93    BFieldElement::new(4436247869008081381),
94    BFieldElement::new(4063129435850804221),
95    BFieldElement::new(2865073155741120117),
96    BFieldElement::new(5749834437609765994),
97    BFieldElement::new(6804196764189408435),
98    BFieldElement::new(17060469201292988508),
99    BFieldElement::new(9475383556737206708),
100    BFieldElement::new(12876344085611465020),
101    BFieldElement::new(13835756199368269249),
102    BFieldElement::new(1648753455944344172),
103    BFieldElement::new(9836124473569258483),
104    BFieldElement::new(12867641597107932229),
105    BFieldElement::new(11254152636692960595),
106    BFieldElement::new(16550832737139861108),
107    BFieldElement::new(11861573970480733262),
108    BFieldElement::new(1256660473588673495),
109    BFieldElement::new(13879506000676455136),
110    BFieldElement::new(10564103842682358721),
111    BFieldElement::new(16142842524796397521),
112    BFieldElement::new(3287098591948630584),
113    BFieldElement::new(685911471061284805),
114    BFieldElement::new(5285298776918878023),
115    BFieldElement::new(18310953571768047354),
116    BFieldElement::new(3142266350630002035),
117    BFieldElement::new(549990724933663297),
118    BFieldElement::new(4901984846118077401),
119    BFieldElement::new(11458643033696775769),
120    BFieldElement::new(8706785264119212710),
121    BFieldElement::new(12521758138015724072),
122    BFieldElement::new(11877914062416978196),
123    BFieldElement::new(11333318251134523752),
124    BFieldElement::new(3933899631278608623),
125    BFieldElement::new(16635128972021157924),
126    BFieldElement::new(10291337173108950450),
127    BFieldElement::new(4142107155024199350),
128    BFieldElement::new(16973934533787743537),
129    BFieldElement::new(11068111539125175221),
130    BFieldElement::new(17546769694830203606),
131    BFieldElement::new(5315217744825068993),
132    BFieldElement::new(4609594252909613081),
133    BFieldElement::new(3350107164315270407),
134    BFieldElement::new(17715942834299349177),
135    BFieldElement::new(9600609149219873996),
136    BFieldElement::new(12894357635820003949),
137    BFieldElement::new(4597649658040514631),
138    BFieldElement::new(7735563950920491847),
139    BFieldElement::new(1663379455870887181),
140    BFieldElement::new(13889298103638829706),
141    BFieldElement::new(7375530351220884434),
142    BFieldElement::new(3502022433285269151),
143    BFieldElement::new(9231805330431056952),
144    BFieldElement::new(9252272755288523725),
145    BFieldElement::new(10014268662326746219),
146    BFieldElement::new(15565031632950843234),
147    BFieldElement::new(1209725273521819323),
148    BFieldElement::new(6024642864597845108),
149];
150
151/// The defining, first column of the (circulant) MDS matrix.
152/// Derived from the SHA-256 hash of the ASCII string “Tip5” by dividing the digest into 16-bit
153/// chunks.
154pub const MDS_MATRIX_FIRST_COLUMN: [i64; STATE_SIZE] = [
155    61402, 1108, 28750, 33823, 7454, 43244, 53865, 12034, 56951, 27521, 41351, 40901, 12021, 59689,
156    26798, 17845,
157];
158
159#[derive(
160    Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, GetSize, BFieldCodec, Arbitrary,
161)]
162#[repr(align(64))] // for SIMD, align along BFieldElement
163pub struct Tip5 {
164    pub state: [BFieldElement; STATE_SIZE],
165}
166
167#[cfg(not(all(
168    target_feature = "avx512ifma",
169    target_feature = "avx512f",
170    target_feature = "avx512bw",
171    target_feature = "avx512vbmi"
172)))]
173impl Tip5 {
174    #[inline(always)]
175    fn round(&mut self, round_index: usize) {
176        self.sbox_layer();
177        self.mds_generated();
178        for i in 0..STATE_SIZE {
179            self.state[i] += ROUND_CONSTANTS[round_index * STATE_SIZE + i];
180        }
181    }
182
183    #[inline(always)]
184    fn sbox_layer(&mut self) {
185        for i in 0..NUM_SPLIT_AND_LOOKUP {
186            Self::split_and_lookup(&mut self.state[i]);
187        }
188
189        for i in NUM_SPLIT_AND_LOOKUP..STATE_SIZE {
190            let sq = self.state[i] * self.state[i];
191            let qu = sq * sq;
192            self.state[i] *= sq * qu;
193        }
194    }
195
196    #[inline]
197    fn split_and_lookup(element: &mut BFieldElement) {
198        // let value = element.value();
199        let mut bytes = element.raw_bytes();
200
201        for i in 0..8 {
202            // bytes[i] = Self::offset_fermat_cube_map(bytes[i].into()) as u8;
203            bytes[i] = LOOKUP_TABLE[bytes[i] as usize];
204        }
205
206        *element = BFieldElement::from_raw_bytes(&bytes);
207    }
208
209    #[inline(always)]
210    fn mds_generated(&mut self) {
211        let mut lo: [u64; STATE_SIZE] = [0; STATE_SIZE];
212        let mut hi: [u64; STATE_SIZE] = [0; STATE_SIZE];
213        for i in 0..STATE_SIZE {
214            let b = self.state[i].raw_u64();
215            hi[i] = b >> 32;
216            lo[i] = b & 0xffff_ffff;
217        }
218
219        lo = Self::generated_function(lo);
220        hi = Self::generated_function(hi);
221
222        // In isolation, the following reduction modulo BFieldElement::P is
223        // buggy. Concretely, it can produce degenerate Montgomery
224        // representations, that is, `state[r].0` can be greater than or equal
225        // to BFieldElement::P. While there are many inputs for which this can
226        // happen, the easiest with which to trace the behavior manually is:
227        // lo[r] = 0x10;
228        // hi[r] = 0xf_ffff_fff0;
229        //
230        // These starting values lead to `s_hi = 0`, `s_lo = BFieldElement::P`.
231        // Since `s_hi` is 0, the `overflowing_add` does nothing, and the
232        // degenerate representation from `s_lo` is directly transferred to the
233        // state element.
234        //
235        // All that said, due to the specific context this method is always (!)
236        // used in, the bug does not propagate. In particular, this method is
237        // followed up with round-constant addition. Due to a quirk in the
238        // implementation of `BFieldElement::Add` and a property of the round
239        // constants, any degenerate representations are corrected.
240        //
241        // Below, you can find tests for the specific properties claimed. The
242        // doc-string of those tests mention the name of this method.
243        for r in 0..STATE_SIZE {
244            let s = (lo[r] >> 4) as u128 + ((hi[r] as u128) << 28);
245
246            let s_hi = (s >> 64) as u64;
247            let s_lo = s as u64;
248
249            let (res, over) = s_lo.overflowing_add(s_hi * 0xffff_ffff);
250
251            self.state[r] = BFieldElement::from_raw_u64(if over { res + 0xffff_ffff } else { res });
252        }
253    }
254
255    #[inline(always)]
256    fn generated_function(input: [u64; STATE_SIZE]) -> [u64; STATE_SIZE] {
257        let node_34 = input[0].wrapping_add(input[8]);
258        let node_38 = input[4].wrapping_add(input[12]);
259        let node_36 = input[2].wrapping_add(input[10]);
260        let node_40 = input[6].wrapping_add(input[14]);
261        let node_35 = input[1].wrapping_add(input[9]);
262        let node_39 = input[5].wrapping_add(input[13]);
263        let node_37 = input[3].wrapping_add(input[11]);
264        let node_41 = input[7].wrapping_add(input[15]);
265        let node_50 = node_34.wrapping_add(node_38);
266        let node_52 = node_36.wrapping_add(node_40);
267        let node_51 = node_35.wrapping_add(node_39);
268        let node_53 = node_37.wrapping_add(node_41);
269        let node_160 = input[0].wrapping_sub(input[8]);
270        let node_161 = input[1].wrapping_sub(input[9]);
271        let node_165 = input[5].wrapping_sub(input[13]);
272        let node_163 = input[3].wrapping_sub(input[11]);
273        let node_167 = input[7].wrapping_sub(input[15]);
274        let node_162 = input[2].wrapping_sub(input[10]);
275        let node_166 = input[6].wrapping_sub(input[14]);
276        let node_164 = input[4].wrapping_sub(input[12]);
277        let node_58 = node_50.wrapping_add(node_52);
278        let node_59 = node_51.wrapping_add(node_53);
279        let node_90 = node_34.wrapping_sub(node_38);
280        let node_91 = node_35.wrapping_sub(node_39);
281        let node_93 = node_37.wrapping_sub(node_41);
282        let node_92 = node_36.wrapping_sub(node_40);
283        let node_64 = node_58.wrapping_add(node_59).wrapping_mul(524757);
284        let node_67 = node_58.wrapping_sub(node_59).wrapping_mul(52427);
285        let node_71 = node_50.wrapping_sub(node_52);
286        let node_72 = node_51.wrapping_sub(node_53);
287        let node_177 = node_161.wrapping_add(node_165);
288        let node_179 = node_163.wrapping_add(node_167);
289        let node_178 = node_162.wrapping_add(node_166);
290        let node_176 = node_160.wrapping_add(node_164);
291        let node_69 = node_64.wrapping_add(node_67);
292        let node_397 = node_71
293            .wrapping_mul(18446744073709525744)
294            .wrapping_sub(node_72.wrapping_mul(53918));
295        let node_1857 = node_90.wrapping_mul(395512);
296        let node_99 = node_91.wrapping_add(node_93);
297        let node_1865 = node_91.wrapping_mul(18446744073709254400);
298        let node_1869 = node_93.wrapping_mul(179380);
299        let node_1873 = node_92.wrapping_mul(18446744073709509368);
300        let node_1879 = node_160.wrapping_mul(35608);
301        let node_185 = node_161.wrapping_add(node_163);
302        let node_1915 = node_161.wrapping_mul(18446744073709340312);
303        let node_1921 = node_163.wrapping_mul(18446744073709494992);
304        let node_1927 = node_162.wrapping_mul(18446744073709450808);
305        let node_228 = node_165.wrapping_add(node_167);
306        let node_1939 = node_165.wrapping_mul(18446744073709420056);
307        let node_1945 = node_167.wrapping_mul(18446744073709505128);
308        let node_1951 = node_166.wrapping_mul(216536);
309        let node_1957 = node_164.wrapping_mul(18446744073709515080);
310        let node_70 = node_64.wrapping_sub(node_67);
311        let node_702 = node_71
312            .wrapping_mul(53918)
313            .wrapping_add(node_72.wrapping_mul(18446744073709525744));
314        let node_1961 = node_90.wrapping_mul(18446744073709254400);
315        let node_1963 = node_91.wrapping_mul(395512);
316        let node_1965 = node_92.wrapping_mul(179380);
317        let node_1967 = node_93.wrapping_mul(18446744073709509368);
318        let node_1970 = node_160.wrapping_mul(18446744073709340312);
319        let node_1973 = node_161.wrapping_mul(35608);
320        let node_1982 = node_162.wrapping_mul(18446744073709494992);
321        let node_1985 = node_163.wrapping_mul(18446744073709450808);
322        let node_1988 = node_166.wrapping_mul(18446744073709505128);
323        let node_1991 = node_167.wrapping_mul(216536);
324        let node_1994 = node_164.wrapping_mul(18446744073709420056);
325        let node_1997 = node_165.wrapping_mul(18446744073709515080);
326        let node_98 = node_90.wrapping_add(node_92);
327        let node_184 = node_160.wrapping_add(node_162);
328        let node_227 = node_164.wrapping_add(node_166);
329        let node_86 = node_69.wrapping_add(node_397);
330        let node_403 = node_1857.wrapping_sub(
331            node_99
332                .wrapping_mul(18446744073709433780)
333                .wrapping_sub(node_1865)
334                .wrapping_sub(node_1869)
335                .wrapping_add(node_1873),
336        );
337        let node_271 = node_177.wrapping_add(node_179);
338        let node_1891 = node_177.wrapping_mul(18446744073709208752);
339        let node_1897 = node_179.wrapping_mul(18446744073709448504);
340        let node_1903 = node_178.wrapping_mul(115728);
341        let node_1909 = node_185.wrapping_mul(18446744073709283688);
342        let node_1933 = node_228.wrapping_mul(18446744073709373568);
343        let node_88 = node_70.wrapping_add(node_702);
344        let node_708 = node_1961
345            .wrapping_add(node_1963)
346            .wrapping_sub(node_1965.wrapping_add(node_1967));
347        let node_1976 = node_178.wrapping_mul(18446744073709448504);
348        let node_1979 = node_179.wrapping_mul(115728);
349        let node_87 = node_69.wrapping_sub(node_397);
350        let node_897 = node_1865
351            .wrapping_add(node_98.wrapping_mul(353264))
352            .wrapping_sub(node_1857)
353            .wrapping_sub(node_1873)
354            .wrapping_sub(node_1869);
355        let node_2007 = node_184.wrapping_mul(18446744073709486416);
356        let node_2013 = node_227.wrapping_mul(180000);
357        let node_89 = node_70.wrapping_sub(node_702);
358        let node_1077 = node_98
359            .wrapping_mul(18446744073709433780)
360            .wrapping_add(node_99.wrapping_mul(353264))
361            .wrapping_sub(node_1961.wrapping_add(node_1963))
362            .wrapping_sub(node_1965.wrapping_add(node_1967));
363        let node_2020 = node_184.wrapping_mul(18446744073709283688);
364        let node_2023 = node_185.wrapping_mul(18446744073709486416);
365        let node_2026 = node_227.wrapping_mul(18446744073709373568);
366        let node_2029 = node_228.wrapping_mul(180000);
367        let node_2035 = node_176.wrapping_mul(18446744073709550688);
368        let node_2038 = node_176.wrapping_mul(18446744073709208752);
369        let node_2041 = node_177.wrapping_mul(18446744073709550688);
370        let node_270 = node_176.wrapping_add(node_178);
371        let node_152 = node_86.wrapping_add(node_403);
372        let node_412 = node_1879.wrapping_sub(
373            node_271
374                .wrapping_mul(18446744073709105640)
375                .wrapping_sub(node_1891)
376                .wrapping_sub(node_1897)
377                .wrapping_add(node_1903)
378                .wrapping_sub(
379                    node_1909
380                        .wrapping_sub(node_1915)
381                        .wrapping_sub(node_1921)
382                        .wrapping_add(node_1927),
383                )
384                .wrapping_sub(
385                    node_1933
386                        .wrapping_sub(node_1939)
387                        .wrapping_sub(node_1945)
388                        .wrapping_add(node_1951),
389                )
390                .wrapping_add(node_1957),
391        );
392        let node_154 = node_88.wrapping_add(node_708);
393        let node_717 = node_1970.wrapping_add(node_1973).wrapping_sub(
394            node_1976
395                .wrapping_add(node_1979)
396                .wrapping_sub(node_1982.wrapping_add(node_1985))
397                .wrapping_sub(node_1988.wrapping_add(node_1991))
398                .wrapping_add(node_1994.wrapping_add(node_1997)),
399        );
400        let node_156 = node_87.wrapping_add(node_897);
401        let node_906 = node_1915
402            .wrapping_add(node_2007)
403            .wrapping_sub(node_1879)
404            .wrapping_sub(node_1927)
405            .wrapping_sub(
406                node_1897
407                    .wrapping_sub(node_1921)
408                    .wrapping_sub(node_1945)
409                    .wrapping_add(
410                        node_1939
411                            .wrapping_add(node_2013)
412                            .wrapping_sub(node_1957)
413                            .wrapping_sub(node_1951),
414                    ),
415            );
416        let node_158 = node_89.wrapping_add(node_1077);
417        let node_1086 = node_2020
418            .wrapping_add(node_2023)
419            .wrapping_sub(node_1970.wrapping_add(node_1973))
420            .wrapping_sub(node_1982.wrapping_add(node_1985))
421            .wrapping_sub(
422                node_2026
423                    .wrapping_add(node_2029)
424                    .wrapping_sub(node_1994.wrapping_add(node_1997))
425                    .wrapping_sub(node_1988.wrapping_add(node_1991)),
426            );
427        let node_153 = node_86.wrapping_sub(node_403);
428        let node_1237 = node_1909
429            .wrapping_sub(node_1915)
430            .wrapping_sub(node_1921)
431            .wrapping_add(node_1927)
432            .wrapping_add(node_2035)
433            .wrapping_sub(node_1879)
434            .wrapping_sub(node_1957)
435            .wrapping_sub(
436                node_1933
437                    .wrapping_sub(node_1939)
438                    .wrapping_sub(node_1945)
439                    .wrapping_add(node_1951),
440            );
441        let node_155 = node_88.wrapping_sub(node_708);
442        let node_1375 = node_1982
443            .wrapping_add(node_1985)
444            .wrapping_add(node_2038.wrapping_add(node_2041))
445            .wrapping_sub(node_1970.wrapping_add(node_1973))
446            .wrapping_sub(node_1994.wrapping_add(node_1997))
447            .wrapping_sub(node_1988.wrapping_add(node_1991));
448        let node_157 = node_87.wrapping_sub(node_897);
449        let node_1492 = node_1921
450            .wrapping_add(
451                node_1891
452                    .wrapping_add(node_270.wrapping_mul(114800))
453                    .wrapping_sub(node_2035)
454                    .wrapping_sub(node_1903),
455            )
456            .wrapping_sub(
457                node_1915
458                    .wrapping_add(node_2007)
459                    .wrapping_sub(node_1879)
460                    .wrapping_sub(node_1927),
461            )
462            .wrapping_sub(
463                node_1939
464                    .wrapping_add(node_2013)
465                    .wrapping_sub(node_1957)
466                    .wrapping_sub(node_1951),
467            )
468            .wrapping_sub(node_1945);
469        let node_159 = node_89.wrapping_sub(node_1077);
470        let node_1657 = node_270
471            .wrapping_mul(18446744073709105640)
472            .wrapping_add(node_271.wrapping_mul(114800))
473            .wrapping_sub(node_2038.wrapping_add(node_2041))
474            .wrapping_sub(node_1976.wrapping_add(node_1979))
475            .wrapping_sub(
476                node_2020
477                    .wrapping_add(node_2023)
478                    .wrapping_sub(node_1970.wrapping_add(node_1973))
479                    .wrapping_sub(node_1982.wrapping_add(node_1985)),
480            )
481            .wrapping_sub(
482                node_2026
483                    .wrapping_add(node_2029)
484                    .wrapping_sub(node_1994.wrapping_add(node_1997))
485                    .wrapping_sub(node_1988.wrapping_add(node_1991)),
486            );
487
488        [
489            node_152.wrapping_add(node_412),
490            node_154.wrapping_add(node_717),
491            node_156.wrapping_add(node_906),
492            node_158.wrapping_add(node_1086),
493            node_153.wrapping_add(node_1237),
494            node_155.wrapping_add(node_1375),
495            node_157.wrapping_add(node_1492),
496            node_159.wrapping_add(node_1657),
497            node_152.wrapping_sub(node_412),
498            node_154.wrapping_sub(node_717),
499            node_156.wrapping_sub(node_906),
500            node_158.wrapping_sub(node_1086),
501            node_153.wrapping_sub(node_1237),
502            node_155.wrapping_sub(node_1375),
503            node_157.wrapping_sub(node_1492),
504            node_159.wrapping_sub(node_1657),
505        ]
506    }
507}
508
509impl Tip5 {
510    #[inline]
511    pub const fn new(domain: Domain) -> Self {
512        let mut state = const { [BFieldElement::ZERO; STATE_SIZE] };
513
514        match domain {
515            Domain::VariableLength => (),
516            Domain::FixedLength => {
517                let mut i = RATE;
518                while i < STATE_SIZE {
519                    state[i] = BFieldElement::ONE;
520                    i += 1;
521                }
522            }
523        }
524
525        Self { state }
526    }
527
528    #[inline(always)]
529    pub fn permutation(&mut self) {
530        for i in 0..NUM_ROUNDS {
531            self.round(i);
532        }
533    }
534
535    /// Functionally equivalent to [`permutation`](Self::permutation). Returns the trace of
536    /// applying the permutation; that is, the initial state of the sponge as well as its state
537    /// after each round.
538    pub fn trace(&mut self) -> [[BFieldElement; STATE_SIZE]; 1 + NUM_ROUNDS] {
539        let mut trace = [[BFieldElement::ZERO; STATE_SIZE]; 1 + NUM_ROUNDS];
540
541        trace[0] = self.state;
542        for i in 0..NUM_ROUNDS {
543            self.round(i);
544            trace[1 + i] = self.state;
545        }
546
547        trace
548    }
549
550    /// Hash 10 [`BFieldElement`]s.
551    ///
552    /// There is no input-padding because the input length is fixed.
553    ///
554    /// When you want to hash together two [`Digest`]s, use [`Self::hash_pair`]
555    /// instead. In some rare cases you do want to hash a fixed-length string
556    /// of individual [`BFieldElement`]s, which is why this function is exposed.
557    ///
558    /// See also: [`Self::hash_pair`], [`Self::hash`], [`Self::hash_varlen`].
559    pub fn hash_10(input: &[BFieldElement; RATE]) -> [BFieldElement; Digest::LEN] {
560        let mut sponge = Self::new(Domain::FixedLength);
561
562        // absorb once
563        sponge.state[..RATE].copy_from_slice(input);
564
565        sponge.permutation();
566
567        // squeeze once
568        sponge.state[..Digest::LEN].try_into().unwrap()
569    }
570
571    /// Hash two [`Digest`]s together.
572    ///
573    /// This function is syntax sugar for calling [`Self::hash_10`] on the
574    /// concatenation of the digests' values.
575    ///
576    /// See also: [`Self::hash_10`], [`Self::hash`], [`Self::hash_varlen`].
577    pub fn hash_pair(left: Digest, right: Digest) -> Digest {
578        let mut sponge = Self::new(Domain::FixedLength);
579        sponge.state[..Digest::LEN].copy_from_slice(&left.values());
580        sponge.state[Digest::LEN..2 * Digest::LEN].copy_from_slice(&right.values());
581
582        sponge.permutation();
583
584        let digest_values = sponge.state[..Digest::LEN].try_into().unwrap();
585        Digest::new(digest_values)
586    }
587
588    /// Hash an object based on its [`BFieldCodec`]-encoding.
589    ///
590    /// Thin wrapper around [`hash_varlen`](Self::hash_varlen).
591    ///
592    /// See also: [`Self::hash_10`], [`Self::hash_pair`], [`Self::hash_varlen`].
593    pub fn hash<T: BFieldCodec>(value: &T) -> Digest {
594        Self::hash_varlen(&value.encode())
595    }
596
597    /// Hash a variable-length sequence of [`BFieldElement`].
598    ///
599    /// This function pads the input as its length is variable.
600    ///
601    /// Note that [`Self::hash_varlen`] and [`Self::hash_10`] are different
602    /// functions, even when the input to the former, after padding, agrees with
603    /// the input to the latter. The difference comes from the initial value of
604    /// the capacity-part of the state, which in the case of variable-length
605    /// hashing is all-ones but in the case of fixed-length hashing is
606    /// all-zeroes.
607    ///
608    /// Prefer [`Self::hash`] whenever an object is being hashed whose type
609    /// implements [`BFieldCodec`]. However, such an object is not always
610    /// available, which is why this function is exposed.
611    ///
612    /// See also: [`Self::hash_10`], [`Self::hash_pair`], [`Self::hash`].
613    //
614    // - Apply the correct padding
615    // - [Sponge::pad_and_absorb_all()]
616    // - Read the digest from the resulting state.
617    pub fn hash_varlen(input: &[BFieldElement]) -> Digest {
618        let mut sponge = Self::init();
619        sponge.pad_and_absorb_all(input);
620        let produce = (&sponge.state[..Digest::LEN]).try_into().unwrap();
621
622        Digest::new(produce)
623    }
624
625    /// Produce `num_indices` random integer values in the range `[0, upper_bound)`. The
626    /// `upper_bound` must be a power of 2.
627    ///
628    /// This method uses von Neumann rejection sampling.
629    /// Specifically, if the top 32 bits of a BFieldElement are all ones, then the bottom 32 bits
630    /// are not uniformly distributed, and so they are dropped. This method invokes squeeze until
631    /// enough uniform u32s have been sampled.
632    ///
633    /// # Panics
634    ///
635    /// Panics if `upper_bound` is not a power of two.
636    pub fn sample_indices(&mut self, upper_bound: u32, num_indices: usize) -> Vec<u32> {
637        assert!(upper_bound.is_power_of_two());
638
639        let mut indices = Vec::with_capacity(num_indices);
640        let mut buffer = const { [BFieldElement::ZERO; RATE] };
641        let mut next_in_buffer = RATE;
642        while indices.len() < num_indices {
643            if next_in_buffer == RATE {
644                buffer = self.squeeze();
645                next_in_buffer = 0;
646            }
647            let element = buffer[next_in_buffer];
648            next_in_buffer += 1;
649
650            if element != const { BFieldElement::new(BFieldElement::MAX) } {
651                indices.push(element.value() as u32 % upper_bound);
652            }
653        }
654
655        indices
656    }
657
658    /// Produce `num_elements` random [`XFieldElement`] values.
659    ///
660    /// If `num_elements` is not divisible by [`RATE`][rate], spill the remaining elements of the
661    /// last [`squeeze`][Sponge::squeeze].
662    ///
663    /// [rate]: Sponge::RATE
664    pub fn sample_scalars(&mut self, num_elements: usize) -> Vec<XFieldElement> {
665        let num_squeezes = (num_elements * EXTENSION_DEGREE).div_ceil(Self::RATE);
666        debug_assert!(num_elements * EXTENSION_DEGREE <= num_squeezes * Self::RATE);
667
668        (0..num_squeezes)
669            .flat_map(|_| self.squeeze())
670            .tuples()
671            .take(num_elements)
672            .map(|(x0, x1, x2)| XFieldElement::new([x0, x1, x2]))
673            .collect()
674    }
675}
676
677impl Sponge for Tip5 {
678    const RATE: usize = RATE;
679
680    fn init() -> Self {
681        Self::new(Domain::VariableLength)
682    }
683
684    fn absorb(&mut self, input: [BFieldElement; RATE]) {
685        self.state[..RATE]
686            .iter_mut()
687            .zip_eq(&input)
688            .for_each(|(a, &b)| *a = b);
689
690        self.permutation();
691    }
692
693    fn squeeze(&mut self) -> [BFieldElement; RATE] {
694        let produce: [BFieldElement; RATE] = (&self.state[..RATE]).try_into().unwrap();
695        self.permutation();
696
697        produce
698    }
699}
700
701impl Hasher for Tip5 {
702    fn finish(&self) -> u64 {
703        self.state[0].value()
704    }
705
706    fn write(&mut self, bytes: &[u8]) {
707        let bfield_elements = bytes.chunks(BFieldElement::BYTES).map(|chunk| {
708            let mut buffer = [0u8; BFieldElement::BYTES];
709            buffer[..chunk.len()].copy_from_slice(chunk);
710            BFieldElement::new(u64::from_le_bytes(buffer))
711        });
712
713        for chunk in bfield_elements.chunks(Tip5::RATE).into_iter() {
714            let mut buffer = [BFieldElement::ZERO; Tip5::RATE];
715            for (buffer_elem, chunk_elem) in buffer.iter_mut().zip(chunk) {
716                *buffer_elem = chunk_elem;
717            }
718            self.absorb(buffer)
719        }
720    }
721}
722
723#[cfg(test)]
724#[cfg_attr(coverage_nightly, coverage(off))]
725pub(crate) mod tests {
726    use std::hash::Hash;
727    use std::ops::Mul;
728
729    use prop::sample::size_range;
730    use proptest::prelude::*;
731    use rand::RngCore;
732    use rayon::prelude::IntoParallelIterator;
733    use rayon::prelude::ParallelIterator;
734
735    use super::*;
736    use crate::math::other::random_elements;
737    use crate::math::x_field_element::XFieldElement;
738    use crate::proptest_arbitrary_interop::arb;
739    use crate::tests::proptest;
740    use crate::tests::test;
741
742    impl proptest::arbitrary::Arbitrary for Tip5 {
743        type Parameters = ();
744
745        fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
746            arb().boxed()
747        }
748
749        type Strategy = BoxedStrategy<Self>;
750    }
751
752    impl Tip5 {
753        fn mds_cyclomul(&mut self) {
754            let mut result = [BFieldElement::ZERO; STATE_SIZE];
755
756            let mut lo: [i64; STATE_SIZE] = [0; STATE_SIZE];
757            let mut hi: [i64; STATE_SIZE] = [0; STATE_SIZE];
758            for (i, b) in self.state.iter().enumerate() {
759                hi[i] = (b.raw_u64() >> 32) as i64;
760                lo[i] = (b.raw_u64() as u32) as i64;
761            }
762
763            lo = Self::fast_cyclomul16(lo, MDS_MATRIX_FIRST_COLUMN);
764            hi = Self::fast_cyclomul16(hi, MDS_MATRIX_FIRST_COLUMN);
765
766            for r in 0..STATE_SIZE {
767                let s = lo[r] as u128 + ((hi[r] as u128) << 32);
768                let s_hi = (s >> 64) as u64;
769                let s_lo = s as u64;
770                let z = (s_hi << 32) - s_hi;
771                let (res, over) = s_lo.overflowing_add(z);
772
773                result[r] = BFieldElement::from_raw_u64(
774                    res.wrapping_add(0u32.wrapping_sub(over as u32) as u64),
775                );
776            }
777            self.state = result;
778        }
779
780        fn fast_cyclomul16(f: [i64; 16], g: [i64; 16]) -> [i64; 16] {
781            const N: usize = 8;
782            let mut ff_lo = [0i64; N];
783            let mut gg_lo = [0i64; N];
784            let mut ff_hi = [0i64; N];
785            let mut gg_hi = [0i64; N];
786            for i in 0..N {
787                ff_lo[i] = f[i] + f[i + N];
788                ff_hi[i] = f[i] - f[i + N];
789                gg_lo[i] = g[i] + g[i + N];
790                gg_hi[i] = g[i] - g[i + N];
791            }
792
793            let hh_lo = Self::fast_cyclomul8(ff_lo, gg_lo);
794            let hh_hi = Self::complex_negacyclomul8(ff_hi, gg_hi);
795
796            let mut hh = [0i64; 2 * N];
797            for i in 0..N {
798                hh[i] = (hh_lo[i] + hh_hi[i]) >> 1;
799                hh[i + N] = (hh_lo[i] - hh_hi[i]) >> 1;
800            }
801
802            hh
803        }
804
805        fn fast_cyclomul8(f: [i64; 8], g: [i64; 8]) -> [i64; 8] {
806            const N: usize = 4;
807            let mut ff_lo = [0i64; N];
808            let mut gg_lo = [0i64; N];
809            let mut ff_hi = [0i64; N];
810            let mut gg_hi = [0i64; N];
811            for i in 0..N {
812                ff_lo[i] = f[i] + f[i + N];
813                ff_hi[i] = f[i] - f[i + N];
814                gg_lo[i] = g[i] + g[i + N];
815                gg_hi[i] = g[i] - g[i + N];
816            }
817
818            let hh_lo = Self::fast_cyclomul4(ff_lo, gg_lo);
819            let hh_hi = Self::complex_negacyclomul4(ff_hi, gg_hi);
820
821            let mut hh = [0i64; 2 * N];
822            for i in 0..N {
823                hh[i] = (hh_lo[i] + hh_hi[i]) >> 1;
824                hh[i + N] = (hh_lo[i] - hh_hi[i]) >> 1;
825            }
826
827            hh
828        }
829
830        fn fast_cyclomul4(f: [i64; 4], g: [i64; 4]) -> [i64; 4] {
831            const N: usize = 2;
832            let mut ff_lo = [0i64; N];
833            let mut gg_lo = [0i64; N];
834            let mut ff_hi = [0i64; N];
835            let mut gg_hi = [0i64; N];
836            for i in 0..N {
837                ff_lo[i] = f[i] + f[i + N];
838                ff_hi[i] = f[i] - f[i + N];
839                gg_lo[i] = g[i] + g[i + N];
840                gg_hi[i] = g[i] - g[i + N];
841            }
842
843            let hh_lo = Self::fast_cyclomul2(ff_lo, gg_lo);
844            let hh_hi = Self::complex_negacyclomul2(ff_hi, gg_hi);
845
846            let mut hh = [0i64; 2 * N];
847            for i in 0..N {
848                hh[i] = (hh_lo[i] + hh_hi[i]) >> 1;
849                hh[i + N] = (hh_lo[i] - hh_hi[i]) >> 1;
850            }
851
852            hh
853        }
854
855        fn fast_cyclomul2(f: [i64; 2], g: [i64; 2]) -> [i64; 2] {
856            let ff_lo = f[0] + f[1];
857            let ff_hi = f[0] - f[1];
858            let gg_lo = g[0] + g[1];
859            let gg_hi = g[0] - g[1];
860
861            let hh_lo = ff_lo * gg_lo;
862            let hh_hi = ff_hi * gg_hi;
863
864            let mut hh = [0i64; 2];
865            hh[0] = (hh_lo + hh_hi) >> 1;
866            hh[1] = (hh_lo - hh_hi) >> 1;
867
868            hh
869        }
870
871        fn complex_negacyclomul8(f: [i64; 8], g: [i64; 8]) -> [i64; 8] {
872            const N: usize = 4;
873
874            let mut f0 = [(0i64, 0i64); N];
875            let mut g0 = [(0i64, 0i64); N];
876
877            for i in 0..N {
878                f0[i] = (f[i], -f[N + i]);
879                g0[i] = (g[i], -g[N + i]);
880            }
881
882            let h0 = Self::complex_karatsuba4(f0, g0);
883
884            let mut h = [0i64; 3 * N - 1];
885            for i in 0..(2 * N - 1) {
886                h[i] += h0[i].0;
887                h[i + N] -= h0[i].1;
888            }
889
890            let mut hh = [0i64; 2 * N];
891            for i in 0..(2 * N) {
892                hh[i] += h[i];
893            }
894            for i in (2 * N)..(3 * N - 1) {
895                hh[i - 2 * N] -= h[i];
896            }
897
898            hh
899        }
900
901        fn complex_negacyclomul4(f: [i64; 4], g: [i64; 4]) -> [i64; 4] {
902            const N: usize = 2;
903
904            let mut f0 = [(0i64, 0i64); N];
905            let mut g0 = [(0i64, 0i64); N];
906
907            for i in 0..N {
908                f0[i] = (f[i], -f[N + i]);
909                g0[i] = (g[i], -g[N + i]);
910            }
911
912            let h0 = Self::complex_karatsuba2(f0, g0);
913
914            let mut h = [0i64; 4 * N - 1];
915            for i in 0..(2 * N - 1) {
916                h[i] += h0[i].0;
917                h[i + N] -= h0[i].1;
918            }
919
920            let mut hh = [0i64; 2 * N];
921            for i in 0..(2 * N) {
922                hh[i] += h[i];
923            }
924            for i in (2 * N)..(4 * N - 1) {
925                hh[i - 2 * N] -= h[i];
926            }
927
928            hh
929        }
930
931        fn complex_negacyclomul2(f: [i64; 2], g: [i64; 2]) -> [i64; 2] {
932            let f0 = (f[0], -f[1]);
933            let g0 = (g[0], -g[1]);
934
935            let h0 = Self::complex_product(f0, g0);
936
937            [h0.0, -h0.1]
938        }
939
940        #[inline(always)]
941        fn complex_karatsuba4(f: [(i64, i64); 4], g: [(i64, i64); 4]) -> [(i64, i64); 7] {
942            const N: usize = 2;
943
944            let ff = Self::complex_sum::<2>(f[..N].try_into().unwrap(), f[N..].try_into().unwrap());
945            let gg = Self::complex_sum::<2>(g[..N].try_into().unwrap(), g[N..].try_into().unwrap());
946
947            let lo =
948                Self::complex_karatsuba2(f[..N].try_into().unwrap(), g[..N].try_into().unwrap());
949            let hi =
950                Self::complex_karatsuba2(f[N..].try_into().unwrap(), g[N..].try_into().unwrap());
951
952            let li = Self::complex_diff::<3>(
953                Self::complex_karatsuba2(ff, gg),
954                Self::complex_sum::<3>(lo, hi),
955            );
956
957            let mut result = [(0i64, 0i64); 4 * N - 1];
958            for i in 0..(2 * N - 1) {
959                result[i].0 = lo[i].0;
960                result[i].1 = lo[i].1;
961            }
962            for i in 0..(2 * N - 1) {
963                result[N + i].0 += li[i].0;
964                result[N + i].1 += li[i].1;
965            }
966            for i in 0..(2 * N - 1) {
967                result[2 * N + i].0 += hi[i].0;
968                result[2 * N + i].1 += hi[i].1;
969            }
970
971            result
972        }
973
974        fn complex_karatsuba2(f: [(i64, i64); 2], g: [(i64, i64); 2]) -> [(i64, i64); 3] {
975            const N: usize = 1;
976
977            let ff = (f[0].0 + f[1].0, f[0].1 + f[1].1);
978            let gg = (g[0].0 + g[1].0, g[0].1 + g[1].1);
979
980            let lo = Self::complex_product(f[0], g[0]);
981            let hi = Self::complex_product(f[1], g[1]);
982
983            let ff_times_gg = Self::complex_product(ff, gg);
984            let lo_plus_hi = (lo.0 + hi.0, lo.1 + hi.1);
985
986            let li = (ff_times_gg.0 - lo_plus_hi.0, ff_times_gg.1 - lo_plus_hi.1);
987
988            let mut result = [(0i64, 0i64); 4 * N - 1];
989            result[0].0 += lo.0;
990            result[0].1 += lo.1;
991            result[N].0 += li.0;
992            result[N].1 += li.1;
993            result[2 * N].0 += hi.0;
994            result[2 * N].1 += hi.1;
995
996            result
997        }
998
999        fn complex_product(f: (i64, i64), g: (i64, i64)) -> (i64, i64) {
1000            // don't karatsuba; this is faster
1001            (f.0 * g.0 - f.1 * g.1, f.0 * g.1 + f.1 * g.0)
1002        }
1003
1004        fn complex_sum<const N: usize>(f: [(i64, i64); N], g: [(i64, i64); N]) -> [(i64, i64); N] {
1005            let mut h = [(0i64, 0i64); N];
1006            for i in 0..N {
1007                h[i].0 = f[i].0 + g[i].0;
1008                h[i].1 = f[i].1 + g[i].1;
1009            }
1010            h
1011        }
1012
1013        fn complex_diff<const N: usize>(f: [(i64, i64); N], g: [(i64, i64); N]) -> [(i64, i64); N] {
1014            let mut h = [(0i64, 0i64); N];
1015            for i in 0..N {
1016                h[i].0 = f[i].0 - g[i].0;
1017                h[i].1 = f[i].1 - g[i].1;
1018            }
1019            h
1020        }
1021
1022        fn offset_fermat_cube_map(x: u16) -> u16 {
1023            let xx = (x + 1) as u64;
1024            let xxx = xx * xx * xx;
1025            ((xxx + 256) % 257) as u16
1026        }
1027    }
1028
1029    #[macro_rules_attr::apply(proptest)]
1030    fn get_size(tip5: Tip5) {
1031        assert_eq!(STATE_SIZE * BFieldElement::ZERO.get_size(), tip5.get_size());
1032    }
1033
1034    #[macro_rules_attr::apply(test)]
1035    fn lookup_table_is_correct() {
1036        let table: [u8; 256] = (0..256)
1037            .map(|t| Tip5::offset_fermat_cube_map(t as u16) as u8)
1038            .collect_vec()
1039            .try_into()
1040            .unwrap();
1041
1042        println!(
1043            "Entire lookup table:\n{}",
1044            table.iter().map(|t| format!("{t:02x}")).join(", ")
1045        );
1046
1047        (0_usize..256).for_each(|i| {
1048            assert_eq!(
1049                LOOKUP_TABLE[i], table[i],
1050                "Lookup tables must agree at every index, including index {i}."
1051            )
1052        });
1053    }
1054
1055    #[macro_rules_attr::apply(test)]
1056    fn round_constants_are_correct() {
1057        let to_int = |bytes: &[u8]| {
1058            bytes
1059                .iter()
1060                .take(16)
1061                .enumerate()
1062                .map(|(i, b)| (*b as u128) << (8 * i))
1063                .sum::<u128>()
1064        };
1065        let round_constants = (0..NUM_ROUNDS * STATE_SIZE)
1066            .map(|i| ["Tip5".to_string().as_bytes(), &[i as u8]].concat())
1067            .map(|bytes| blake3::hash(&bytes))
1068            .map(|hash| *hash.as_bytes())
1069            .map(|bytes| to_int(&bytes))
1070            .map(|i| (i % BFieldElement::P as u128) as u64)
1071            .map(BFieldElement::from_raw_u64)
1072            .collect_vec();
1073
1074        println!(
1075            "In case you changed something, here are all round constants:\n{}",
1076            round_constants.iter().map(|c| format!("{c}")).join(", ")
1077        );
1078
1079        (0_usize..NUM_ROUNDS * STATE_SIZE).for_each(|i| {
1080            assert_eq!(
1081                ROUND_CONSTANTS[i], round_constants[i],
1082                "Round constants must agree at every index, including index {i}."
1083            )
1084        });
1085    }
1086
1087    #[macro_rules_attr::apply(test)]
1088    fn test_fermat_cube_map_is_permutation() {
1089        let mut touched = [false; 256];
1090        for i in 0..256 {
1091            touched[Tip5::offset_fermat_cube_map(i) as usize] = true;
1092        }
1093        assert!(touched.iter().all(|t| *t));
1094        assert_eq!(Tip5::offset_fermat_cube_map(0), 0);
1095        assert_eq!(Tip5::offset_fermat_cube_map(255), 255);
1096    }
1097
1098    /// Ensure that the claims made in [`Tip5::mds_generated`] are true.
1099    ///
1100    /// In particular, `BFieldElement::Add` internally uses the equality
1101    /// `a + b = a - (p - b)`. If `a` is a degenerate representation (i.e., is
1102    /// larger than or equal to [`BFieldElement::P`]), then a small enough `b`
1103    /// makes the result of the addition non-degenerate by removing the
1104    /// “surplus” from `a`. In particular, this correction will happen if the
1105    /// following inequality holds:
1106    ///
1107    ///      p - b > u64::MAX - p
1108    ///   ↔  p - b > 2^64 - 1 - (2^64 - 2^32 + 1)
1109    ///   ↔  p - b > 2^32 - 2
1110    ///   ↔     -b > 2^32 - 2 - p
1111    ///   ↔      b < p + 2 - 2^32
1112    ///
1113    /// While it’s not particularly beautiful to depend on such implementation
1114    /// details, Ferdinand is too scared to change the implementation of Tip5.
1115    /// If you change the behavior of `BFieldElement::Add`, please make sure
1116    /// that [`Tip5`] is not breaking.
1117    ///
1118    /// The test [`round_constants_correct_degenerate_lhs_when_adding`] makes
1119    /// sure that all round constants have the required property.
1120    ///
1121    /// See also: https://www.hyrumslaw.com/. Sorry about that.
1122    #[macro_rules_attr::apply(proptest)]
1123    fn adding_degenerate_lhs_and_small_enough_rhs_makes_sum_non_degenerate(
1124        #[strategy(BFieldElement::P..)] raw_a: u64,
1125        #[strategy(0..BFieldElement::P + 2 - (1 << 32))] raw_b: u64,
1126    ) {
1127        let a = BFieldElement::from_raw_u64(raw_a);
1128        let b = BFieldElement::from_raw_u64(raw_b);
1129        let raw_sum = (a + b).raw_u64();
1130        prop_assert!(raw_sum < BFieldElement::P);
1131    }
1132
1133    /// Ensure that the claims made in [`Tip5::mds_generated`] are true.
1134    ///
1135    /// [`adding_degenerate_lhs_and_small_enough_rhs_makes_sum_non_degenerate`]
1136    /// explains the requirement in greater detail.
1137    #[macro_rules_attr::apply(test)]
1138    fn round_constants_correct_degenerate_lhs_when_adding() {
1139        for constant in ROUND_CONSTANTS {
1140            assert!(constant.raw_u64() < BFieldElement::P + 2 - (1 << 32));
1141        }
1142    }
1143
1144    /// Ensure that the claims made in [`Tip5::mds_generated`] are true.
1145    #[macro_rules_attr::apply(test)]
1146    fn tip5_recovers_from_degenerate_field_element_representations() {
1147        let state = [
1148            0x1063_c4bf_5d8b_b0dd,
1149            0xdb62_75d3_71fe_05d0,
1150            0xde58_cae3_0144_cdae,
1151            0xc774_e646_81d3_622e,
1152            0xc4a9_47d1_0a5a_a466,
1153            0xda55_77a0_0a91_3151,
1154            0xe80e_978b_3836_dcd0,
1155            0x8dd1_61f0_a3ac_00c2,
1156            0x6857_f251_a9c0_f693,
1157            0x4923_a368_3046_178e,
1158            0x6e6f_c54a_9b81_010b,
1159            0xcb84_fa5b_b9fa_ec36,
1160            0x93cb_f9db_4c5c_b1ea,
1161            0xf215_d9b9_2dc8_7266,
1162            0x88f0_9783_d2ae_3c57,
1163            0x6d29_f9ce_94a9_0b71,
1164        ]
1165        .map(BFieldElement::new);
1166        let expected = [
1167            0xa5d3_2d62_9e60_d72e,
1168            0x5516_ef90_d277_3d74,
1169            0x65d3_fa1c_de45_f6cb,
1170            0x7bf0_e725_dfa5_906b,
1171            0x67a2_db4b_141b_90e9,
1172            0x91db_162d_3230_9083,
1173            0xefec_1d00_146a_05c9,
1174            0xcca0_d656_6bca_8186,
1175            0x405b_aeb5_b3f8_7f02,
1176            0xd897_0158_7027_8f76,
1177            0xd4b2_ee48_10aa_c7d1,
1178            0x27b4_51e7_06a5_c2fc,
1179            0xe9b4_177f_0a0e_ffe4,
1180            0x0c60_def0_f2c5_287f,
1181            0x703a_a06d_327c_cc34,
1182            0x536f_2355_0ebf_98f1,
1183        ]
1184        .map(BFieldElement::new);
1185
1186        let mut dbg_tip5 = Tip5 { state };
1187        dbg_tip5.sbox_layer();
1188        dbg_tip5.mds_generated();
1189
1190        // If this assertion fails, you might have improved the internals of
1191        // Tip5 in a way that makes the properties tested for in
1192        // `adding_degenerate_lhs_and_small_enough_rhs_makes_sum_non_degenerate`
1193        // and
1194        // `round_constants_correct_degenerate_lhs_when_adding`
1195        // superfluous. If that is the case, feel free to remove those tests
1196        // as well as this one.
1197        debug_assert!(dbg_tip5.state[1].raw_u64() >= BFieldElement::P);
1198
1199        let mut tip5 = Tip5 { state };
1200        tip5.permutation();
1201        assert_eq!(expected, tip5.state);
1202
1203        let mut naive = naive::NaiveTip5 { state };
1204        naive.permutation();
1205        assert_eq!(expected, naive.state);
1206    }
1207
1208    #[macro_rules_attr::apply(test)]
1209    fn calculate_differential_uniformity() {
1210        // cargo test calculate_differential_uniformity -- --include-ignored --nocapture
1211        // addition-differential
1212        let count_satisfiers_fermat = |a, b| {
1213            (0..(1 << 8))
1214                .map(|x| {
1215                    u16::from(
1216                        (256 + Tip5::offset_fermat_cube_map((x + a) & 0xff)
1217                            - Tip5::offset_fermat_cube_map(x))
1218                            & 0xff
1219                            == b,
1220                    )
1221                })
1222                .sum()
1223        };
1224        let du_fermat: u16 = (1..256)
1225            .into_par_iter()
1226            .map(|a| {
1227                (1..256)
1228                    .map(|b| count_satisfiers_fermat(a, b))
1229                    .max()
1230                    .unwrap()
1231            })
1232            .max()
1233            .unwrap();
1234        println!("additive differential uniformity for fermat cube map: {du_fermat}");
1235
1236        // bitwise-differential
1237        let count_satisfiers_fermat_bitwise = |a: u16, b: u16| {
1238            (0..(1 << 8))
1239                .map(|x| {
1240                    u16::from(
1241                        (Tip5::offset_fermat_cube_map(x ^ a) ^ Tip5::offset_fermat_cube_map(x))
1242                            == b,
1243                    )
1244                })
1245                .sum::<u16>()
1246        };
1247        for a in 1..256 {
1248            for b in 1..256 {
1249                let num_satisfiers = count_satisfiers_fermat_bitwise(a, b);
1250                if num_satisfiers == 256 {
1251                    println!("a: {a}, b: {b} -> 256 satisfiers");
1252                }
1253            }
1254        }
1255        let du_fermat_bitwise: u16 = (1..256)
1256            .into_par_iter()
1257            .map(|a| {
1258                (1..256)
1259                    .map(|b| count_satisfiers_fermat_bitwise(a, b))
1260                    .max()
1261                    .unwrap()
1262            })
1263            .max()
1264            .unwrap();
1265        println!("bitwise differential uniformity for fermat cube map: {du_fermat_bitwise}");
1266    }
1267
1268    #[macro_rules_attr::apply(test)]
1269    fn calculate_approximation_quality() {
1270        let mut fermat_cubed = [0u16; 65536];
1271        let mut bfield_cubed = [0u16; 65536];
1272        for i in 0..65536 {
1273            let cubed = (i as u64) * (i as u64) * (i as u64);
1274            fermat_cubed[i] = (cubed % 65537) as u16;
1275            bfield_cubed[i] = (cubed & 0xffff) as u16;
1276        }
1277        let equal_count = fermat_cubed
1278            .iter()
1279            .zip(bfield_cubed.iter())
1280            .filter(|(a, b)| a == b)
1281            .count();
1282        println!("agreement with low-degree function: {equal_count}");
1283    }
1284
1285    /// A snapshot of a [`Digest`], in hexadecimal form. The exact procedure to
1286    /// arrive at the `Digest` in question is involved and best read from the
1287    /// code below.
1288    ///
1289    /// It is paramount that Tip5 has at least some snapshot tests. The reason
1290    /// is that (with the current implementation), conditional compilation
1291    /// changes the concrete instructions that make up Tip5. Testing different
1292    /// binaries for equivalent behavior is easiest when that behavior is pinned
1293    /// through snapshots.
1294    const MAGIC_SNAPSHOT_HEX: &str =
1295        "109cc2fe453bd9962f754b96d8f5b919b60af030940a275f5540da195fef65ee651c1b6fa19b2c6a";
1296
1297    #[macro_rules_attr::apply(test)]
1298    fn hash10_test_vectors_snapshot() {
1299        let mut preimage = [BFieldElement::ZERO; RATE];
1300        for i in 0..6 {
1301            let digest = Tip5::hash_10(&preimage);
1302            preimage[i..i + Digest::LEN].copy_from_slice(&digest);
1303        }
1304        let final_digest = Digest::new(Tip5::hash_10(&preimage)).to_hex();
1305        assert_eq!(MAGIC_SNAPSHOT_HEX, final_digest);
1306    }
1307
1308    #[macro_rules_attr::apply(test)]
1309    fn hash_varlen_test_vectors() {
1310        let mut digest_sum = [BFieldElement::ZERO; Digest::LEN];
1311        for i in 0..20 {
1312            let preimage = (0..i).map(BFieldElement::new).collect_vec();
1313            let digest = Tip5::hash_varlen(&preimage);
1314            digest_sum
1315                .iter_mut()
1316                .zip(digest.values().iter())
1317                .for_each(|(s, d)| *s += *d);
1318        }
1319
1320        let final_digest = Digest::new(digest_sum).to_hex();
1321        assert_eq!(
1322            "efbafa86622a9c69652f8a1c4ffd734f021ad23a0a8085412a877de0f9170b18ea4ff69b6fff9a03",
1323            final_digest,
1324        );
1325    }
1326
1327    #[macro_rules_attr::apply(test)]
1328    fn snapshot() {
1329        let state = [
1330            0x0000_000f_ffff_fff0,
1331            0x0000_0000_ffff_ffff,
1332            0x0000_0000_ffff_ffff,
1333            0x0000_0028_ffff_ffd7,
1334            0x0000_0006_ffff_fff9,
1335            0x0000_0002_ffff_fffd,
1336            0x0000_0000_ffff_ffff,
1337            0x0000_0030_ffff_ffcf,
1338            0x0000_0397_ffff_fc68,
1339            0x0000_000f_ffff_fff0,
1340            0x316b_fb72_3638_2123,
1341            0x216f_521b_66ef_83f5,
1342            0x5689_d7b3_63f5_2df0,
1343            0xeb2f_59e3_aeae_25fc,
1344            0xb082_99d2_77cb_b4dc,
1345            0xcbe3_d9fd_c534_9140,
1346        ]
1347        .map(BFieldElement::from_raw_u64);
1348
1349        let mut tip5 = Tip5 { state };
1350        tip5.permutation();
1351
1352        let expected = [
1353            0x15d3_8ea9_29f6_632a,
1354            0xf988_e509_ff73_8bb4,
1355            0x48bc_dfae_88a2_e9f3,
1356            0x8733_9e83_2daa_c02a,
1357            0x511e_4126_8150_fdac,
1358        ]
1359        .map(BFieldElement::from_raw_u64);
1360
1361        assert_eq!(&expected, &tip5.state[0..5]);
1362    }
1363
1364    fn manual_hash_varlen(preimage: &[BFieldElement]) -> Digest {
1365        let mut sponge = Tip5::init();
1366        sponge.pad_and_absorb_all(preimage);
1367        let squeeze_result = sponge.squeeze();
1368
1369        Digest::new((&squeeze_result[..Digest::LEN]).try_into().unwrap())
1370    }
1371
1372    #[macro_rules_attr::apply(test)]
1373    fn hash_var_len_equivalence_edge_cases() {
1374        for preimage_length in 0..=11 {
1375            let preimage = vec![BFieldElement::new(42); preimage_length];
1376            let hash_varlen_digest = Tip5::hash_varlen(&preimage);
1377
1378            let digest_through_pad_squeeze_absorb = manual_hash_varlen(&preimage);
1379            assert_eq!(digest_through_pad_squeeze_absorb, hash_varlen_digest);
1380        }
1381    }
1382
1383    #[macro_rules_attr::apply(proptest)]
1384    fn hash_var_len_equivalence(#[strategy(arb())] preimage: Vec<BFieldElement>) {
1385        let hash_varlen_digest = Tip5::hash_varlen(&preimage);
1386        let digest_through_pad_squeeze_absorb = manual_hash_varlen(&preimage);
1387        prop_assert_eq!(digest_through_pad_squeeze_absorb, hash_varlen_digest);
1388    }
1389
1390    #[macro_rules_attr::apply(test)]
1391    fn test_linearity_of_mds() {
1392        type SpongeState = [BFieldElement; STATE_SIZE];
1393
1394        let mds_procedure = |state| {
1395            let mut sponge = Tip5 { state };
1396            sponge.mds_cyclomul();
1397            sponge.state
1398        };
1399
1400        let a: BFieldElement = random_elements(1)[0];
1401        let b: BFieldElement = random_elements(1)[0];
1402
1403        let mul_procedure = |u: SpongeState, v: SpongeState| -> SpongeState {
1404            let mul_result = u.iter().zip(&v).map(|(&uu, &vv)| a * uu + b * vv);
1405            mul_result.collect_vec().try_into().unwrap()
1406        };
1407
1408        let u: SpongeState = random_elements(STATE_SIZE).try_into().unwrap();
1409        let v: SpongeState = random_elements(STATE_SIZE).try_into().unwrap();
1410        let w = mul_procedure(u, v);
1411
1412        let u = mds_procedure(u);
1413        let v = mds_procedure(v);
1414        let w = mds_procedure(w);
1415
1416        let w_ = mul_procedure(u, v);
1417
1418        assert_eq!(w, w_);
1419    }
1420
1421    #[macro_rules_attr::apply(test)]
1422    fn test_mds_circulancy() {
1423        let mut sponge = Tip5::init();
1424        sponge.state = [BFieldElement::ZERO; STATE_SIZE];
1425        sponge.state[0] = BFieldElement::ONE;
1426
1427        sponge.mds_cyclomul();
1428
1429        let mut mat_first_row = [BFieldElement::ZERO; STATE_SIZE];
1430        mat_first_row[0] = sponge.state[0];
1431        for (i, first_row_elem) in mat_first_row.iter_mut().enumerate().skip(1) {
1432            *first_row_elem = sponge.state[STATE_SIZE - i];
1433        }
1434
1435        println!(
1436            "mds matrix first row: {:?}",
1437            mat_first_row.map(|b| b.value())
1438        );
1439
1440        let initial_state: [BFieldElement; STATE_SIZE] =
1441            random_elements(STATE_SIZE).try_into().unwrap();
1442
1443        let mut mv = [BFieldElement::ZERO; STATE_SIZE];
1444        for i in 0..STATE_SIZE {
1445            for j in 0..STATE_SIZE {
1446                mv[i] += mat_first_row[(STATE_SIZE - i + j) % STATE_SIZE] * initial_state[j];
1447            }
1448        }
1449
1450        let mut sponge_2 = Tip5::init();
1451        sponge_2.state = initial_state;
1452        sponge_2.mds_cyclomul();
1453
1454        assert_eq!(sponge_2.state, mv);
1455    }
1456
1457    #[macro_rules_attr::apply(test)]
1458    fn test_complex_karatsuba() {
1459        const N: usize = 4;
1460        let mut f = [(0i64, 0i64); N];
1461        let mut g = [(0i64, 0i64); N];
1462        for i in 0..N {
1463            f[i].0 = i as i64;
1464            g[i].0 = 1;
1465            f[i].1 = 1;
1466            g[i].1 = i as i64;
1467        }
1468
1469        let h0 = Tip5::complex_karatsuba4(f, g);
1470        let h1 = [(0, 1), (0, 2), (0, 4), (0, 8), (0, 13), (0, 14), (0, 10)];
1471
1472        assert_eq!(h0, h1);
1473    }
1474
1475    #[macro_rules_attr::apply(test)]
1476    fn test_complex_product() {
1477        let mut rng = rand::rng();
1478        let mut random_small_i64 = || (rng.next_u32() % (1 << 16)) as i64;
1479        for _ in 0..1000 {
1480            let f = (random_small_i64(), random_small_i64());
1481            let g = (random_small_i64(), random_small_i64());
1482            let h0 = Tip5::complex_product(f, g);
1483            let h1 = (f.0 * g.0 - f.1 * g.1, f.0 * g.1 + f.1 * g.0);
1484            assert_eq!(h1, h0);
1485        }
1486    }
1487
1488    #[macro_rules_attr::apply(proptest)]
1489    fn sample_scalars(mut tip5: Tip5, #[strategy(0_usize..=100)] num_scalars: usize) {
1490        tip5.permutation(); // remove any 0s that exist due to shrinking
1491
1492        let scalars = tip5.sample_scalars(num_scalars);
1493        prop_assert_eq!(num_scalars, scalars.len());
1494
1495        let product = scalars
1496            .into_iter()
1497            .fold(XFieldElement::ONE, XFieldElement::mul);
1498        prop_assert_ne!(product, XFieldElement::ZERO);
1499    }
1500
1501    // Function `mds_generated` is not available if the AVX-512 functions are.
1502    #[cfg(not(all(
1503        target_feature = "avx512ifma",
1504        target_feature = "avx512f",
1505        target_feature = "avx512bw",
1506        target_feature = "avx512vbmi"
1507    )))]
1508    #[macro_rules_attr::apply(proptest)]
1509    fn test_mds_matrix_mul_methods_agree(state: [BFieldElement; STATE_SIZE]) {
1510        let mut sponge_cyclomut = Tip5 { state };
1511        let mut sponge_generated = Tip5 { state };
1512
1513        sponge_cyclomut.mds_cyclomul();
1514        sponge_generated.mds_generated();
1515
1516        prop_assert_eq!(
1517            &sponge_cyclomut,
1518            &sponge_generated,
1519            "cyclomul =/= generated\n{}\n{}",
1520            sponge_cyclomut.state.into_iter().join(","),
1521            sponge_generated.state.into_iter().join(",")
1522        );
1523    }
1524
1525    #[macro_rules_attr::apply(test)]
1526    fn tip5_hasher_trait_snapshot_test() {
1527        let mut hasher = Tip5::init();
1528        let data = b"hello world";
1529        hasher.write(data);
1530        assert_eq!(hasher.finish(), 2267905471610932299);
1531    }
1532
1533    #[macro_rules_attr::apply(proptest)]
1534    fn tip5_hasher_consumes_small_data(#[filter(!#bytes.is_empty())] bytes: Vec<u8>) {
1535        let mut hasher = Tip5::init();
1536        bytes.hash(&mut hasher);
1537
1538        prop_assert_ne!(Tip5::init().finish(), hasher.finish());
1539    }
1540
1541    #[macro_rules_attr::apply(proptest)]
1542    fn appending_small_data_to_big_data_changes_tip5_hash(
1543        #[any(size_range(2_000..8_000).lift())] big_data: Vec<u8>,
1544        #[filter(!#small_data.is_empty())] small_data: Vec<u8>,
1545    ) {
1546        let mut hasher = Tip5::init();
1547        big_data.hash(&mut hasher);
1548        let big_data_hash = hasher.finish();
1549
1550        // finish doesn't terminate the hasher; see it's documentation
1551        small_data.hash(&mut hasher);
1552        let all_data_hash = hasher.finish();
1553
1554        prop_assert_ne!(big_data_hash, all_data_hash);
1555    }
1556
1557    #[macro_rules_attr::apply(proptest)]
1558    fn tip5_trace_starts_with_initial_state_and_is_equivalent_to_permutation(mut tip5: Tip5) {
1559        let [first, .., last] = tip5.clone().trace();
1560        prop_assert_eq!(first, tip5.state);
1561
1562        tip5.permutation();
1563        prop_assert_eq!(last, tip5.state);
1564    }
1565}