1use 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(target_arch = "x86_64")]
37mod avx512;
38#[cfg(test)]
39mod inverse;
40#[cfg(test)]
41mod naive;
42
43pub const LOOKUP_TABLE: [u8; 256] = [
46 0, 7, 26, 63, 124, 215, 85, 254, 214, 228, 45, 185, 140, 173, 33, 240, 29, 177, 176, 32, 8,
47 110, 87, 202, 204, 99, 150, 106, 230, 14, 235, 128, 213, 239, 212, 138, 23, 130, 208, 6, 44,
48 71, 93, 116, 146, 189, 251, 81, 199, 97, 38, 28, 73, 179, 95, 84, 152, 48, 35, 119, 49, 88,
49 242, 3, 148, 169, 72, 120, 62, 161, 166, 83, 175, 191, 137, 19, 100, 129, 112, 55, 221, 102,
50 218, 61, 151, 237, 68, 164, 17, 147, 46, 234, 203, 216, 22, 141, 65, 57, 123, 12, 244, 54, 219,
51 231, 96, 77, 180, 154, 5, 253, 133, 165, 98, 195, 205, 134, 245, 30, 9, 188, 59, 142, 186, 197,
52 181, 144, 92, 31, 224, 163, 111, 74, 58, 69, 113, 196, 67, 246, 225, 10, 121, 50, 60, 157, 90,
53 122, 2, 250, 101, 75, 178, 159, 24, 36, 201, 11, 243, 132, 198, 190, 114, 233, 39, 52, 21, 209,
54 108, 238, 91, 187, 18, 104, 194, 37, 153, 34, 200, 143, 126, 155, 236, 118, 64, 80, 172, 89,
55 94, 193, 135, 183, 86, 107, 252, 13, 167, 206, 136, 220, 207, 103, 171, 160, 76, 182, 227, 217,
56 158, 56, 174, 4, 66, 109, 139, 162, 184, 211, 249, 47, 125, 232, 117, 43, 16, 42, 127, 20, 241,
57 25, 149, 105, 156, 51, 53, 168, 145, 247, 223, 79, 78, 226, 15, 222, 82, 115, 70, 210, 27, 41,
58 1, 170, 40, 131, 192, 229, 248, 255,
59];
60
61pub const ROUND_CONSTANTS: [BFieldElement; NUM_ROUNDS * STATE_SIZE] = [
64 BFieldElement::new(13630775303355457758),
65 BFieldElement::new(16896927574093233874),
66 BFieldElement::new(10379449653650130495),
67 BFieldElement::new(1965408364413093495),
68 BFieldElement::new(15232538947090185111),
69 BFieldElement::new(15892634398091747074),
70 BFieldElement::new(3989134140024871768),
71 BFieldElement::new(2851411912127730865),
72 BFieldElement::new(8709136439293758776),
73 BFieldElement::new(3694858669662939734),
74 BFieldElement::new(12692440244315327141),
75 BFieldElement::new(10722316166358076749),
76 BFieldElement::new(12745429320441639448),
77 BFieldElement::new(17932424223723990421),
78 BFieldElement::new(7558102534867937463),
79 BFieldElement::new(15551047435855531404),
80 BFieldElement::new(17532528648579384106),
81 BFieldElement::new(5216785850422679555),
82 BFieldElement::new(15418071332095031847),
83 BFieldElement::new(11921929762955146258),
84 BFieldElement::new(9738718993677019874),
85 BFieldElement::new(3464580399432997147),
86 BFieldElement::new(13408434769117164050),
87 BFieldElement::new(264428218649616431),
88 BFieldElement::new(4436247869008081381),
89 BFieldElement::new(4063129435850804221),
90 BFieldElement::new(2865073155741120117),
91 BFieldElement::new(5749834437609765994),
92 BFieldElement::new(6804196764189408435),
93 BFieldElement::new(17060469201292988508),
94 BFieldElement::new(9475383556737206708),
95 BFieldElement::new(12876344085611465020),
96 BFieldElement::new(13835756199368269249),
97 BFieldElement::new(1648753455944344172),
98 BFieldElement::new(9836124473569258483),
99 BFieldElement::new(12867641597107932229),
100 BFieldElement::new(11254152636692960595),
101 BFieldElement::new(16550832737139861108),
102 BFieldElement::new(11861573970480733262),
103 BFieldElement::new(1256660473588673495),
104 BFieldElement::new(13879506000676455136),
105 BFieldElement::new(10564103842682358721),
106 BFieldElement::new(16142842524796397521),
107 BFieldElement::new(3287098591948630584),
108 BFieldElement::new(685911471061284805),
109 BFieldElement::new(5285298776918878023),
110 BFieldElement::new(18310953571768047354),
111 BFieldElement::new(3142266350630002035),
112 BFieldElement::new(549990724933663297),
113 BFieldElement::new(4901984846118077401),
114 BFieldElement::new(11458643033696775769),
115 BFieldElement::new(8706785264119212710),
116 BFieldElement::new(12521758138015724072),
117 BFieldElement::new(11877914062416978196),
118 BFieldElement::new(11333318251134523752),
119 BFieldElement::new(3933899631278608623),
120 BFieldElement::new(16635128972021157924),
121 BFieldElement::new(10291337173108950450),
122 BFieldElement::new(4142107155024199350),
123 BFieldElement::new(16973934533787743537),
124 BFieldElement::new(11068111539125175221),
125 BFieldElement::new(17546769694830203606),
126 BFieldElement::new(5315217744825068993),
127 BFieldElement::new(4609594252909613081),
128 BFieldElement::new(3350107164315270407),
129 BFieldElement::new(17715942834299349177),
130 BFieldElement::new(9600609149219873996),
131 BFieldElement::new(12894357635820003949),
132 BFieldElement::new(4597649658040514631),
133 BFieldElement::new(7735563950920491847),
134 BFieldElement::new(1663379455870887181),
135 BFieldElement::new(13889298103638829706),
136 BFieldElement::new(7375530351220884434),
137 BFieldElement::new(3502022433285269151),
138 BFieldElement::new(9231805330431056952),
139 BFieldElement::new(9252272755288523725),
140 BFieldElement::new(10014268662326746219),
141 BFieldElement::new(15565031632950843234),
142 BFieldElement::new(1209725273521819323),
143 BFieldElement::new(6024642864597845108),
144];
145
146pub const MDS_MATRIX_FIRST_COLUMN: [i64; STATE_SIZE] = [
150 61402, 1108, 28750, 33823, 7454, 43244, 53865, 12034, 56951, 27521, 41351, 40901, 12021, 59689,
151 26798, 17845,
152];
153
154pub(super) trait WrappingArithmetic: Copy {
156 fn wrapping_add(self, rhs: Self) -> Self;
157 fn wrapping_sub(self, rhs: Self) -> Self;
158 fn wrapping_mul(self, rhs: u64) -> Self;
159}
160
161impl WrappingArithmetic for u64 {
162 #[inline(always)]
163 fn wrapping_add(self, rhs: Self) -> Self {
164 u64::wrapping_add(self, rhs)
165 }
166
167 #[inline(always)]
168 fn wrapping_sub(self, rhs: Self) -> Self {
169 u64::wrapping_sub(self, rhs)
170 }
171
172 #[inline(always)]
173 fn wrapping_mul(self, rhs: u64) -> Self {
174 u64::wrapping_mul(self, rhs)
175 }
176}
177
178#[derive(
179 Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, GetSize, BFieldCodec, Arbitrary,
180)]
181#[repr(align(64))] pub struct Tip5 {
183 pub state: [BFieldElement; STATE_SIZE],
184}
185
186impl Tip5 {
187 #[inline(always)]
189 fn round(&mut self, round_index: usize) {
190 self.sbox_layer();
191 self.mds_generated();
192 for i in 0..STATE_SIZE {
193 self.state[i] += ROUND_CONSTANTS[round_index * STATE_SIZE + i];
194 }
195 }
196
197 #[inline(always)]
198 fn sbox_layer(&mut self) {
199 let (lookup_elements, power_map_elements) = self.state.split_at_mut(NUM_SPLIT_AND_LOOKUP);
200 for element in lookup_elements {
201 Self::split_and_lookup(element);
202 }
203
204 for element in power_map_elements {
205 let sq = *element * *element;
206 let qu = sq * sq;
207 *element *= sq * qu;
208 }
209 }
210
211 #[inline]
212 fn split_and_lookup(element: &mut BFieldElement) {
213 let mut bytes = element.raw_bytes();
215
216 for i in 0..8 {
217 bytes[i] = LOOKUP_TABLE[bytes[i] as usize];
219 }
220
221 *element = BFieldElement::from_raw_bytes(&bytes);
222 }
223
224 #[inline(always)]
225 fn mds_generated(&mut self) {
226 let mut lo: [u64; STATE_SIZE] = [0; STATE_SIZE];
227 let mut hi: [u64; STATE_SIZE] = [0; STATE_SIZE];
228 for i in 0..STATE_SIZE {
229 let b = self.state[i].raw_u64();
230 hi[i] = b >> 32;
231 lo[i] = b & 0xffff_ffff;
232 }
233
234 lo = Self::generated_function(lo);
235 hi = Self::generated_function(hi);
236
237 for r in 0..STATE_SIZE {
259 let s = (lo[r] >> 4) as u128 + ((hi[r] as u128) << 28);
260
261 let s_hi = (s >> 64) as u64;
262 let s_lo = s as u64;
263
264 let (res, over) = s_lo.overflowing_add(s_hi * 0xffff_ffff);
265
266 self.state[r] = BFieldElement::from_raw_u64(if over { res + 0xffff_ffff } else { res });
267 }
268 }
269
270 #[inline(always)]
278 pub(super) fn generated_function<T: WrappingArithmetic>(
279 input: [T; STATE_SIZE],
280 ) -> [T; STATE_SIZE] {
281 let node_34 = input[0].wrapping_add(input[8]);
282 let node_38 = input[4].wrapping_add(input[12]);
283 let node_36 = input[2].wrapping_add(input[10]);
284 let node_40 = input[6].wrapping_add(input[14]);
285 let node_35 = input[1].wrapping_add(input[9]);
286 let node_39 = input[5].wrapping_add(input[13]);
287 let node_37 = input[3].wrapping_add(input[11]);
288 let node_41 = input[7].wrapping_add(input[15]);
289 let node_50 = node_34.wrapping_add(node_38);
290 let node_52 = node_36.wrapping_add(node_40);
291 let node_51 = node_35.wrapping_add(node_39);
292 let node_53 = node_37.wrapping_add(node_41);
293 let node_160 = input[0].wrapping_sub(input[8]);
294 let node_161 = input[1].wrapping_sub(input[9]);
295 let node_165 = input[5].wrapping_sub(input[13]);
296 let node_163 = input[3].wrapping_sub(input[11]);
297 let node_167 = input[7].wrapping_sub(input[15]);
298 let node_162 = input[2].wrapping_sub(input[10]);
299 let node_166 = input[6].wrapping_sub(input[14]);
300 let node_164 = input[4].wrapping_sub(input[12]);
301 let node_58 = node_50.wrapping_add(node_52);
302 let node_59 = node_51.wrapping_add(node_53);
303 let node_90 = node_34.wrapping_sub(node_38);
304 let node_91 = node_35.wrapping_sub(node_39);
305 let node_93 = node_37.wrapping_sub(node_41);
306 let node_92 = node_36.wrapping_sub(node_40);
307 let node_64 = node_58.wrapping_add(node_59).wrapping_mul(524757);
308 let node_67 = node_58.wrapping_sub(node_59).wrapping_mul(52427);
309 let node_71 = node_50.wrapping_sub(node_52);
310 let node_72 = node_51.wrapping_sub(node_53);
311 let node_177 = node_161.wrapping_add(node_165);
312 let node_179 = node_163.wrapping_add(node_167);
313 let node_178 = node_162.wrapping_add(node_166);
314 let node_176 = node_160.wrapping_add(node_164);
315 let node_69 = node_64.wrapping_add(node_67);
316 let node_397 = node_71
317 .wrapping_mul(18446744073709525744)
318 .wrapping_sub(node_72.wrapping_mul(53918));
319 let node_1857 = node_90.wrapping_mul(395512);
320 let node_99 = node_91.wrapping_add(node_93);
321 let node_1865 = node_91.wrapping_mul(18446744073709254400);
322 let node_1869 = node_93.wrapping_mul(179380);
323 let node_1873 = node_92.wrapping_mul(18446744073709509368);
324 let node_1879 = node_160.wrapping_mul(35608);
325 let node_185 = node_161.wrapping_add(node_163);
326 let node_1915 = node_161.wrapping_mul(18446744073709340312);
327 let node_1921 = node_163.wrapping_mul(18446744073709494992);
328 let node_1927 = node_162.wrapping_mul(18446744073709450808);
329 let node_228 = node_165.wrapping_add(node_167);
330 let node_1939 = node_165.wrapping_mul(18446744073709420056);
331 let node_1945 = node_167.wrapping_mul(18446744073709505128);
332 let node_1951 = node_166.wrapping_mul(216536);
333 let node_1957 = node_164.wrapping_mul(18446744073709515080);
334 let node_70 = node_64.wrapping_sub(node_67);
335 let node_702 = node_71
336 .wrapping_mul(53918)
337 .wrapping_add(node_72.wrapping_mul(18446744073709525744));
338 let node_1961 = node_90.wrapping_mul(18446744073709254400);
339 let node_1963 = node_91.wrapping_mul(395512);
340 let node_1965 = node_92.wrapping_mul(179380);
341 let node_1967 = node_93.wrapping_mul(18446744073709509368);
342 let node_1970 = node_160.wrapping_mul(18446744073709340312);
343 let node_1973 = node_161.wrapping_mul(35608);
344 let node_1982 = node_162.wrapping_mul(18446744073709494992);
345 let node_1985 = node_163.wrapping_mul(18446744073709450808);
346 let node_1988 = node_166.wrapping_mul(18446744073709505128);
347 let node_1991 = node_167.wrapping_mul(216536);
348 let node_1994 = node_164.wrapping_mul(18446744073709420056);
349 let node_1997 = node_165.wrapping_mul(18446744073709515080);
350 let node_98 = node_90.wrapping_add(node_92);
351 let node_184 = node_160.wrapping_add(node_162);
352 let node_227 = node_164.wrapping_add(node_166);
353 let node_86 = node_69.wrapping_add(node_397);
354 let node_403 = node_1857.wrapping_sub(
355 node_99
356 .wrapping_mul(18446744073709433780)
357 .wrapping_sub(node_1865)
358 .wrapping_sub(node_1869)
359 .wrapping_add(node_1873),
360 );
361 let node_271 = node_177.wrapping_add(node_179);
362 let node_1891 = node_177.wrapping_mul(18446744073709208752);
363 let node_1897 = node_179.wrapping_mul(18446744073709448504);
364 let node_1903 = node_178.wrapping_mul(115728);
365 let node_1909 = node_185.wrapping_mul(18446744073709283688);
366 let node_1933 = node_228.wrapping_mul(18446744073709373568);
367 let node_88 = node_70.wrapping_add(node_702);
368 let node_708 = node_1961
369 .wrapping_add(node_1963)
370 .wrapping_sub(node_1965.wrapping_add(node_1967));
371 let node_1976 = node_178.wrapping_mul(18446744073709448504);
372 let node_1979 = node_179.wrapping_mul(115728);
373 let node_87 = node_69.wrapping_sub(node_397);
374 let node_897 = node_1865
375 .wrapping_add(node_98.wrapping_mul(353264))
376 .wrapping_sub(node_1857)
377 .wrapping_sub(node_1873)
378 .wrapping_sub(node_1869);
379 let node_2007 = node_184.wrapping_mul(18446744073709486416);
380 let node_2013 = node_227.wrapping_mul(180000);
381 let node_89 = node_70.wrapping_sub(node_702);
382 let node_1077 = node_98
383 .wrapping_mul(18446744073709433780)
384 .wrapping_add(node_99.wrapping_mul(353264))
385 .wrapping_sub(node_1961.wrapping_add(node_1963))
386 .wrapping_sub(node_1965.wrapping_add(node_1967));
387 let node_2020 = node_184.wrapping_mul(18446744073709283688);
388 let node_2023 = node_185.wrapping_mul(18446744073709486416);
389 let node_2026 = node_227.wrapping_mul(18446744073709373568);
390 let node_2029 = node_228.wrapping_mul(180000);
391 let node_2035 = node_176.wrapping_mul(18446744073709550688);
392 let node_2038 = node_176.wrapping_mul(18446744073709208752);
393 let node_2041 = node_177.wrapping_mul(18446744073709550688);
394 let node_270 = node_176.wrapping_add(node_178);
395 let node_152 = node_86.wrapping_add(node_403);
396 let node_412 = node_1879.wrapping_sub(
397 node_271
398 .wrapping_mul(18446744073709105640)
399 .wrapping_sub(node_1891)
400 .wrapping_sub(node_1897)
401 .wrapping_add(node_1903)
402 .wrapping_sub(
403 node_1909
404 .wrapping_sub(node_1915)
405 .wrapping_sub(node_1921)
406 .wrapping_add(node_1927),
407 )
408 .wrapping_sub(
409 node_1933
410 .wrapping_sub(node_1939)
411 .wrapping_sub(node_1945)
412 .wrapping_add(node_1951),
413 )
414 .wrapping_add(node_1957),
415 );
416 let node_154 = node_88.wrapping_add(node_708);
417 let node_717 = node_1970.wrapping_add(node_1973).wrapping_sub(
418 node_1976
419 .wrapping_add(node_1979)
420 .wrapping_sub(node_1982.wrapping_add(node_1985))
421 .wrapping_sub(node_1988.wrapping_add(node_1991))
422 .wrapping_add(node_1994.wrapping_add(node_1997)),
423 );
424 let node_156 = node_87.wrapping_add(node_897);
425 let node_906 = node_1915
426 .wrapping_add(node_2007)
427 .wrapping_sub(node_1879)
428 .wrapping_sub(node_1927)
429 .wrapping_sub(
430 node_1897
431 .wrapping_sub(node_1921)
432 .wrapping_sub(node_1945)
433 .wrapping_add(
434 node_1939
435 .wrapping_add(node_2013)
436 .wrapping_sub(node_1957)
437 .wrapping_sub(node_1951),
438 ),
439 );
440 let node_158 = node_89.wrapping_add(node_1077);
441 let node_1086 = node_2020
442 .wrapping_add(node_2023)
443 .wrapping_sub(node_1970.wrapping_add(node_1973))
444 .wrapping_sub(node_1982.wrapping_add(node_1985))
445 .wrapping_sub(
446 node_2026
447 .wrapping_add(node_2029)
448 .wrapping_sub(node_1994.wrapping_add(node_1997))
449 .wrapping_sub(node_1988.wrapping_add(node_1991)),
450 );
451 let node_153 = node_86.wrapping_sub(node_403);
452 let node_1237 = node_1909
453 .wrapping_sub(node_1915)
454 .wrapping_sub(node_1921)
455 .wrapping_add(node_1927)
456 .wrapping_add(node_2035)
457 .wrapping_sub(node_1879)
458 .wrapping_sub(node_1957)
459 .wrapping_sub(
460 node_1933
461 .wrapping_sub(node_1939)
462 .wrapping_sub(node_1945)
463 .wrapping_add(node_1951),
464 );
465 let node_155 = node_88.wrapping_sub(node_708);
466 let node_1375 = node_1982
467 .wrapping_add(node_1985)
468 .wrapping_add(node_2038.wrapping_add(node_2041))
469 .wrapping_sub(node_1970.wrapping_add(node_1973))
470 .wrapping_sub(node_1994.wrapping_add(node_1997))
471 .wrapping_sub(node_1988.wrapping_add(node_1991));
472 let node_157 = node_87.wrapping_sub(node_897);
473 let node_1492 = node_1921
474 .wrapping_add(
475 node_1891
476 .wrapping_add(node_270.wrapping_mul(114800))
477 .wrapping_sub(node_2035)
478 .wrapping_sub(node_1903),
479 )
480 .wrapping_sub(
481 node_1915
482 .wrapping_add(node_2007)
483 .wrapping_sub(node_1879)
484 .wrapping_sub(node_1927),
485 )
486 .wrapping_sub(
487 node_1939
488 .wrapping_add(node_2013)
489 .wrapping_sub(node_1957)
490 .wrapping_sub(node_1951),
491 )
492 .wrapping_sub(node_1945);
493 let node_159 = node_89.wrapping_sub(node_1077);
494 let node_1657 = node_270
495 .wrapping_mul(18446744073709105640)
496 .wrapping_add(node_271.wrapping_mul(114800))
497 .wrapping_sub(node_2038.wrapping_add(node_2041))
498 .wrapping_sub(node_1976.wrapping_add(node_1979))
499 .wrapping_sub(
500 node_2020
501 .wrapping_add(node_2023)
502 .wrapping_sub(node_1970.wrapping_add(node_1973))
503 .wrapping_sub(node_1982.wrapping_add(node_1985)),
504 )
505 .wrapping_sub(
506 node_2026
507 .wrapping_add(node_2029)
508 .wrapping_sub(node_1994.wrapping_add(node_1997))
509 .wrapping_sub(node_1988.wrapping_add(node_1991)),
510 );
511
512 [
513 node_152.wrapping_add(node_412),
514 node_154.wrapping_add(node_717),
515 node_156.wrapping_add(node_906),
516 node_158.wrapping_add(node_1086),
517 node_153.wrapping_add(node_1237),
518 node_155.wrapping_add(node_1375),
519 node_157.wrapping_add(node_1492),
520 node_159.wrapping_add(node_1657),
521 node_152.wrapping_sub(node_412),
522 node_154.wrapping_sub(node_717),
523 node_156.wrapping_sub(node_906),
524 node_158.wrapping_sub(node_1086),
525 node_153.wrapping_sub(node_1237),
526 node_155.wrapping_sub(node_1375),
527 node_157.wrapping_sub(node_1492),
528 node_159.wrapping_sub(node_1657),
529 ]
530 }
531}
532
533impl Tip5 {
534 #[inline]
535 pub const fn new(domain: Domain) -> Self {
536 let mut state = const { [BFieldElement::ZERO; STATE_SIZE] };
537
538 match domain {
539 Domain::VariableLength => (),
540 Domain::FixedLength => {
541 let mut i = RATE;
542 while i < STATE_SIZE {
543 state[i] = BFieldElement::ONE;
544 i += 1;
545 }
546 }
547 }
548
549 Self { state }
550 }
551
552 #[inline]
553 pub fn permutation(&mut self) {
554 #[cfg(target_arch = "x86_64")]
555 if Self::avx512_is_available() {
556 unsafe { self.permutation_avx512() };
558 return;
559 }
560
561 self.permutation_scalar();
562 }
563
564 #[inline]
566 fn permutation_scalar(&mut self) {
567 for i in 0..NUM_ROUNDS {
568 self.round(i);
569 }
570 }
571
572 #[inline]
574 fn round_dispatching(&mut self, round_index: usize) {
575 #[cfg(target_arch = "x86_64")]
576 if Self::avx512_is_available() {
577 unsafe { self.round_avx512(round_index) };
579 return;
580 }
581
582 self.round(round_index);
583 }
584
585 pub fn trace(&mut self) -> [[BFieldElement; STATE_SIZE]; 1 + NUM_ROUNDS] {
589 let mut trace = [[BFieldElement::ZERO; STATE_SIZE]; 1 + NUM_ROUNDS];
590
591 trace[0] = self.state;
592 for i in 0..NUM_ROUNDS {
593 self.round_dispatching(i);
594 trace[1 + i] = self.state;
595 }
596
597 trace
598 }
599
600 pub fn hash_10(input: &[BFieldElement; RATE]) -> [BFieldElement; Digest::LEN] {
610 let mut sponge = Self::new(Domain::FixedLength);
611
612 sponge.state[..RATE].copy_from_slice(input);
614
615 sponge.permutation();
616
617 sponge.state[..Digest::LEN].try_into().unwrap()
619 }
620
621 pub fn hash_pair(left: Digest, right: Digest) -> Digest {
628 let mut sponge = Self::new(Domain::FixedLength);
629 sponge.state[..Digest::LEN].copy_from_slice(&left.values());
630 sponge.state[Digest::LEN..2 * Digest::LEN].copy_from_slice(&right.values());
631
632 sponge.permutation();
633
634 let digest_values = sponge.state[..Digest::LEN].try_into().unwrap();
635 Digest::new(digest_values)
636 }
637
638 pub fn hash<T: BFieldCodec>(value: &T) -> Digest {
644 Self::hash_varlen(&value.encode())
645 }
646
647 pub fn hash_varlen(input: &[BFieldElement]) -> Digest {
668 let mut sponge = Self::init();
669 sponge.pad_and_absorb_all(input);
670 let produce = (&sponge.state[..Digest::LEN]).try_into().unwrap();
671
672 Digest::new(produce)
673 }
674
675 pub fn hash_varlen_many(inputs: &[&[BFieldElement]]) -> Vec<Digest> {
683 #[cfg(target_arch = "x86_64")]
684 if Self::avx512_is_available() {
685 let (batches, remainder) = inputs.as_chunks::<{ avx512::BATCH_SIZE }>();
686 let mut digests = Vec::with_capacity(inputs.len());
687 for batch in batches {
688 let len = batch[0].len();
689 if batch.iter().all(|input| input.len() == len) {
690 digests.extend(unsafe { Self::hash_varlen_batch(*batch) });
692 } else {
693 digests.extend(batch.iter().map(|input| Self::hash_varlen(input)));
694 }
695 }
696 digests.extend(remainder.iter().map(|input| Self::hash_varlen(input)));
697 return digests;
698 }
699
700 Self::hash_varlen_many_scalar(inputs)
701 }
702
703 fn hash_varlen_many_scalar(inputs: &[&[BFieldElement]]) -> Vec<Digest> {
705 inputs
706 .iter()
707 .map(|input| Self::hash_varlen(input))
708 .collect()
709 }
710
711 pub fn hash_pair_many(pairs: &[[Digest; 2]]) -> Vec<Digest> {
719 #[cfg(target_arch = "x86_64")]
720 if Self::avx512_is_available() {
721 let (batches, remainder) = pairs.as_chunks::<{ avx512::BATCH_SIZE }>();
722 let mut digests = Vec::with_capacity(pairs.len());
723 for batch in batches {
724 digests.extend(unsafe { Self::hash_pair_batch(batch) });
726 }
727 digests.extend(remainder.iter().map(|&[l, r]| Self::hash_pair(l, r)));
728 return digests;
729 }
730
731 Self::hash_pair_many_scalar(pairs)
732 }
733
734 fn hash_pair_many_scalar(pairs: &[[Digest; 2]]) -> Vec<Digest> {
736 pairs.iter().map(|&[l, r]| Self::hash_pair(l, r)).collect()
737 }
738
739 pub fn sample_indices(&mut self, upper_bound: u32, num_indices: usize) -> Vec<u32> {
751 assert!(upper_bound.is_power_of_two());
752
753 let mut indices = Vec::with_capacity(num_indices);
754 let mut buffer = const { [BFieldElement::ZERO; RATE] };
755 let mut next_in_buffer = RATE;
756 while indices.len() < num_indices {
757 if next_in_buffer == RATE {
758 buffer = self.squeeze();
759 next_in_buffer = 0;
760 }
761 let element = buffer[next_in_buffer];
762 next_in_buffer += 1;
763
764 if element != const { BFieldElement::new(BFieldElement::MAX) } {
765 indices.push(element.value() as u32 % upper_bound);
766 }
767 }
768
769 indices
770 }
771
772 pub fn sample_scalars(&mut self, num_elements: usize) -> Vec<XFieldElement> {
779 let num_squeezes = (num_elements * EXTENSION_DEGREE).div_ceil(Self::RATE);
780 debug_assert!(num_elements * EXTENSION_DEGREE <= num_squeezes * Self::RATE);
781
782 (0..num_squeezes)
783 .flat_map(|_| self.squeeze())
784 .tuples()
785 .take(num_elements)
786 .map(|(x0, x1, x2)| XFieldElement::new([x0, x1, x2]))
787 .collect()
788 }
789}
790
791impl Sponge for Tip5 {
792 const RATE: usize = RATE;
793
794 fn init() -> Self {
795 Self::new(Domain::VariableLength)
796 }
797
798 fn absorb(&mut self, input: [BFieldElement; RATE]) {
799 self.state[..RATE]
800 .iter_mut()
801 .zip_eq(&input)
802 .for_each(|(a, &b)| *a = b);
803
804 self.permutation();
805 }
806
807 fn squeeze(&mut self) -> [BFieldElement; RATE] {
808 let produce: [BFieldElement; RATE] = (&self.state[..RATE]).try_into().unwrap();
809 self.permutation();
810
811 produce
812 }
813}
814
815impl Hasher for Tip5 {
816 fn finish(&self) -> u64 {
817 self.state[0].value()
818 }
819
820 fn write(&mut self, bytes: &[u8]) {
821 let bfield_elements = bytes.chunks(BFieldElement::BYTES).map(|chunk| {
822 let mut buffer = [0u8; BFieldElement::BYTES];
823 buffer[..chunk.len()].copy_from_slice(chunk);
824 BFieldElement::new(u64::from_le_bytes(buffer))
825 });
826
827 for chunk in bfield_elements.chunks(Tip5::RATE).into_iter() {
828 let mut buffer = [BFieldElement::ZERO; Tip5::RATE];
829 for (buffer_elem, chunk_elem) in buffer.iter_mut().zip(chunk) {
830 *buffer_elem = chunk_elem;
831 }
832 self.absorb(buffer)
833 }
834 }
835}
836
837#[cfg(test)]
838#[cfg_attr(coverage_nightly, coverage(off))]
839pub(crate) mod tests {
840 use std::hash::Hash;
841 use std::ops::Mul;
842
843 use prop::sample::size_range;
844 use proptest::prelude::*;
845 use proptest_arbitrary_adapter::arb;
846 use rand::Rng;
847 use rayon::prelude::IntoParallelIterator;
848 use rayon::prelude::ParallelIterator;
849
850 use super::*;
851 use crate::math::other::random_elements;
852 use crate::math::x_field_element::XFieldElement;
853 use crate::tests::proptest;
854 use crate::tests::test;
855
856 impl proptest::arbitrary::Arbitrary for Tip5 {
857 type Parameters = ();
858
859 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
860 arb().boxed()
861 }
862
863 type Strategy = BoxedStrategy<Self>;
864 }
865
866 impl Tip5 {
867 fn mds_cyclomul(&mut self) {
868 let mut result = [BFieldElement::ZERO; STATE_SIZE];
869
870 let mut lo: [i64; STATE_SIZE] = [0; STATE_SIZE];
871 let mut hi: [i64; STATE_SIZE] = [0; STATE_SIZE];
872 for (i, b) in self.state.iter().enumerate() {
873 hi[i] = (b.raw_u64() >> 32) as i64;
874 lo[i] = (b.raw_u64() as u32) as i64;
875 }
876
877 lo = Self::fast_cyclomul16(lo, MDS_MATRIX_FIRST_COLUMN);
878 hi = Self::fast_cyclomul16(hi, MDS_MATRIX_FIRST_COLUMN);
879
880 for r in 0..STATE_SIZE {
881 let s = lo[r] as u128 + ((hi[r] as u128) << 32);
882 let s_hi = (s >> 64) as u64;
883 let s_lo = s as u64;
884 let z = (s_hi << 32) - s_hi;
885 let (res, over) = s_lo.overflowing_add(z);
886
887 result[r] = BFieldElement::from_raw_u64(
888 res.wrapping_add(0u32.wrapping_sub(over as u32) as u64),
889 );
890 }
891 self.state = result;
892 }
893
894 fn fast_cyclomul16(f: [i64; 16], g: [i64; 16]) -> [i64; 16] {
895 const N: usize = 8;
896 let mut ff_lo = [0i64; N];
897 let mut gg_lo = [0i64; N];
898 let mut ff_hi = [0i64; N];
899 let mut gg_hi = [0i64; N];
900 for i in 0..N {
901 ff_lo[i] = f[i] + f[i + N];
902 ff_hi[i] = f[i] - f[i + N];
903 gg_lo[i] = g[i] + g[i + N];
904 gg_hi[i] = g[i] - g[i + N];
905 }
906
907 let hh_lo = Self::fast_cyclomul8(ff_lo, gg_lo);
908 let hh_hi = Self::complex_negacyclomul8(ff_hi, gg_hi);
909
910 let mut hh = [0i64; 2 * N];
911 for i in 0..N {
912 hh[i] = (hh_lo[i] + hh_hi[i]) >> 1;
913 hh[i + N] = (hh_lo[i] - hh_hi[i]) >> 1;
914 }
915
916 hh
917 }
918
919 fn fast_cyclomul8(f: [i64; 8], g: [i64; 8]) -> [i64; 8] {
920 const N: usize = 4;
921 let mut ff_lo = [0i64; N];
922 let mut gg_lo = [0i64; N];
923 let mut ff_hi = [0i64; N];
924 let mut gg_hi = [0i64; N];
925 for i in 0..N {
926 ff_lo[i] = f[i] + f[i + N];
927 ff_hi[i] = f[i] - f[i + N];
928 gg_lo[i] = g[i] + g[i + N];
929 gg_hi[i] = g[i] - g[i + N];
930 }
931
932 let hh_lo = Self::fast_cyclomul4(ff_lo, gg_lo);
933 let hh_hi = Self::complex_negacyclomul4(ff_hi, gg_hi);
934
935 let mut hh = [0i64; 2 * N];
936 for i in 0..N {
937 hh[i] = (hh_lo[i] + hh_hi[i]) >> 1;
938 hh[i + N] = (hh_lo[i] - hh_hi[i]) >> 1;
939 }
940
941 hh
942 }
943
944 fn fast_cyclomul4(f: [i64; 4], g: [i64; 4]) -> [i64; 4] {
945 const N: usize = 2;
946 let mut ff_lo = [0i64; N];
947 let mut gg_lo = [0i64; N];
948 let mut ff_hi = [0i64; N];
949 let mut gg_hi = [0i64; N];
950 for i in 0..N {
951 ff_lo[i] = f[i] + f[i + N];
952 ff_hi[i] = f[i] - f[i + N];
953 gg_lo[i] = g[i] + g[i + N];
954 gg_hi[i] = g[i] - g[i + N];
955 }
956
957 let hh_lo = Self::fast_cyclomul2(ff_lo, gg_lo);
958 let hh_hi = Self::complex_negacyclomul2(ff_hi, gg_hi);
959
960 let mut hh = [0i64; 2 * N];
961 for i in 0..N {
962 hh[i] = (hh_lo[i] + hh_hi[i]) >> 1;
963 hh[i + N] = (hh_lo[i] - hh_hi[i]) >> 1;
964 }
965
966 hh
967 }
968
969 fn fast_cyclomul2(f: [i64; 2], g: [i64; 2]) -> [i64; 2] {
970 let ff_lo = f[0] + f[1];
971 let ff_hi = f[0] - f[1];
972 let gg_lo = g[0] + g[1];
973 let gg_hi = g[0] - g[1];
974
975 let hh_lo = ff_lo * gg_lo;
976 let hh_hi = ff_hi * gg_hi;
977
978 let mut hh = [0i64; 2];
979 hh[0] = (hh_lo + hh_hi) >> 1;
980 hh[1] = (hh_lo - hh_hi) >> 1;
981
982 hh
983 }
984
985 fn complex_negacyclomul8(f: [i64; 8], g: [i64; 8]) -> [i64; 8] {
986 const N: usize = 4;
987
988 let mut f0 = [(0i64, 0i64); N];
989 let mut g0 = [(0i64, 0i64); N];
990
991 for i in 0..N {
992 f0[i] = (f[i], -f[N + i]);
993 g0[i] = (g[i], -g[N + i]);
994 }
995
996 let h0 = Self::complex_karatsuba4(f0, g0);
997
998 let mut h = [0i64; 3 * N - 1];
999 for i in 0..(2 * N - 1) {
1000 h[i] += h0[i].0;
1001 h[i + N] -= h0[i].1;
1002 }
1003
1004 let mut hh = [0i64; 2 * N];
1005 for i in 0..(2 * N) {
1006 hh[i] += h[i];
1007 }
1008 for i in (2 * N)..(3 * N - 1) {
1009 hh[i - 2 * N] -= h[i];
1010 }
1011
1012 hh
1013 }
1014
1015 fn complex_negacyclomul4(f: [i64; 4], g: [i64; 4]) -> [i64; 4] {
1016 const N: usize = 2;
1017
1018 let mut f0 = [(0i64, 0i64); N];
1019 let mut g0 = [(0i64, 0i64); N];
1020
1021 for i in 0..N {
1022 f0[i] = (f[i], -f[N + i]);
1023 g0[i] = (g[i], -g[N + i]);
1024 }
1025
1026 let h0 = Self::complex_karatsuba2(f0, g0);
1027
1028 let mut h = [0i64; 4 * N - 1];
1029 for i in 0..(2 * N - 1) {
1030 h[i] += h0[i].0;
1031 h[i + N] -= h0[i].1;
1032 }
1033
1034 let mut hh = [0i64; 2 * N];
1035 for i in 0..(2 * N) {
1036 hh[i] += h[i];
1037 }
1038 for i in (2 * N)..(4 * N - 1) {
1039 hh[i - 2 * N] -= h[i];
1040 }
1041
1042 hh
1043 }
1044
1045 fn complex_negacyclomul2(f: [i64; 2], g: [i64; 2]) -> [i64; 2] {
1046 let f0 = (f[0], -f[1]);
1047 let g0 = (g[0], -g[1]);
1048
1049 let h0 = Self::complex_product(f0, g0);
1050
1051 [h0.0, -h0.1]
1052 }
1053
1054 #[inline(always)]
1055 fn complex_karatsuba4(f: [(i64, i64); 4], g: [(i64, i64); 4]) -> [(i64, i64); 7] {
1056 const N: usize = 2;
1057
1058 let ff = Self::complex_sum::<2>(f[..N].try_into().unwrap(), f[N..].try_into().unwrap());
1059 let gg = Self::complex_sum::<2>(g[..N].try_into().unwrap(), g[N..].try_into().unwrap());
1060
1061 let lo =
1062 Self::complex_karatsuba2(f[..N].try_into().unwrap(), g[..N].try_into().unwrap());
1063 let hi =
1064 Self::complex_karatsuba2(f[N..].try_into().unwrap(), g[N..].try_into().unwrap());
1065
1066 let li = Self::complex_diff::<3>(
1067 Self::complex_karatsuba2(ff, gg),
1068 Self::complex_sum::<3>(lo, hi),
1069 );
1070
1071 let mut result = [(0i64, 0i64); 4 * N - 1];
1072 for i in 0..(2 * N - 1) {
1073 result[i].0 = lo[i].0;
1074 result[i].1 = lo[i].1;
1075 }
1076 for i in 0..(2 * N - 1) {
1077 result[N + i].0 += li[i].0;
1078 result[N + i].1 += li[i].1;
1079 }
1080 for i in 0..(2 * N - 1) {
1081 result[2 * N + i].0 += hi[i].0;
1082 result[2 * N + i].1 += hi[i].1;
1083 }
1084
1085 result
1086 }
1087
1088 fn complex_karatsuba2(f: [(i64, i64); 2], g: [(i64, i64); 2]) -> [(i64, i64); 3] {
1089 const N: usize = 1;
1090
1091 let ff = (f[0].0 + f[1].0, f[0].1 + f[1].1);
1092 let gg = (g[0].0 + g[1].0, g[0].1 + g[1].1);
1093
1094 let lo = Self::complex_product(f[0], g[0]);
1095 let hi = Self::complex_product(f[1], g[1]);
1096
1097 let ff_times_gg = Self::complex_product(ff, gg);
1098 let lo_plus_hi = (lo.0 + hi.0, lo.1 + hi.1);
1099
1100 let li = (ff_times_gg.0 - lo_plus_hi.0, ff_times_gg.1 - lo_plus_hi.1);
1101
1102 let mut result = [(0i64, 0i64); 4 * N - 1];
1103 result[0].0 += lo.0;
1104 result[0].1 += lo.1;
1105 result[N].0 += li.0;
1106 result[N].1 += li.1;
1107 result[2 * N].0 += hi.0;
1108 result[2 * N].1 += hi.1;
1109
1110 result
1111 }
1112
1113 fn complex_product(f: (i64, i64), g: (i64, i64)) -> (i64, i64) {
1114 (f.0 * g.0 - f.1 * g.1, f.0 * g.1 + f.1 * g.0)
1116 }
1117
1118 fn complex_sum<const N: usize>(f: [(i64, i64); N], g: [(i64, i64); N]) -> [(i64, i64); N] {
1119 let mut h = [(0i64, 0i64); N];
1120 for i in 0..N {
1121 h[i].0 = f[i].0 + g[i].0;
1122 h[i].1 = f[i].1 + g[i].1;
1123 }
1124 h
1125 }
1126
1127 fn complex_diff<const N: usize>(f: [(i64, i64); N], g: [(i64, i64); N]) -> [(i64, i64); N] {
1128 let mut h = [(0i64, 0i64); N];
1129 for i in 0..N {
1130 h[i].0 = f[i].0 - g[i].0;
1131 h[i].1 = f[i].1 - g[i].1;
1132 }
1133 h
1134 }
1135
1136 fn offset_fermat_cube_map(x: u16) -> u16 {
1137 let xx = (x + 1) as u64;
1138 let xxx = xx * xx * xx;
1139 ((xxx + 256) % 257) as u16
1140 }
1141 }
1142
1143 #[macro_rules_attr::apply(proptest)]
1144 fn get_size(tip5: Tip5) {
1145 assert_eq!(STATE_SIZE * BFieldElement::ZERO.get_size(), tip5.get_size());
1146 }
1147
1148 #[macro_rules_attr::apply(test)]
1149 fn lookup_table_is_correct() {
1150 let table: [u8; 256] = (0..256)
1151 .map(|t| Tip5::offset_fermat_cube_map(t as u16) as u8)
1152 .collect_vec()
1153 .try_into()
1154 .unwrap();
1155
1156 println!(
1157 "Entire lookup table:\n{}",
1158 table.iter().map(|t| format!("{t:02x}")).join(", ")
1159 );
1160
1161 (0_usize..256).for_each(|i| {
1162 assert_eq!(
1163 LOOKUP_TABLE[i], table[i],
1164 "Lookup tables must agree at every index, including index {i}."
1165 )
1166 });
1167 }
1168
1169 #[macro_rules_attr::apply(test)]
1170 fn round_constants_are_correct() {
1171 let to_int = |bytes: &[u8]| {
1172 bytes
1173 .iter()
1174 .take(16)
1175 .enumerate()
1176 .map(|(i, b)| (*b as u128) << (8 * i))
1177 .sum::<u128>()
1178 };
1179 let round_constants = (0..NUM_ROUNDS * STATE_SIZE)
1180 .map(|i| ["Tip5".to_string().as_bytes(), &[i as u8]].concat())
1181 .map(|bytes| blake3::hash(&bytes))
1182 .map(|hash| *hash.as_bytes())
1183 .map(|bytes| to_int(&bytes))
1184 .map(|i| (i % BFieldElement::P as u128) as u64)
1185 .map(BFieldElement::from_raw_u64)
1186 .collect_vec();
1187
1188 println!(
1189 "In case you changed something, here are all round constants:\n{}",
1190 round_constants.iter().map(|c| format!("{c}")).join(", ")
1191 );
1192
1193 (0_usize..NUM_ROUNDS * STATE_SIZE).for_each(|i| {
1194 assert_eq!(
1195 ROUND_CONSTANTS[i], round_constants[i],
1196 "Round constants must agree at every index, including index {i}."
1197 )
1198 });
1199 }
1200
1201 #[macro_rules_attr::apply(test)]
1202 fn test_fermat_cube_map_is_permutation() {
1203 let mut touched = [false; 256];
1204 for i in 0..256 {
1205 touched[Tip5::offset_fermat_cube_map(i) as usize] = true;
1206 }
1207 assert!(touched.iter().all(|t| *t));
1208 assert_eq!(Tip5::offset_fermat_cube_map(0), 0);
1209 assert_eq!(Tip5::offset_fermat_cube_map(255), 255);
1210 }
1211
1212 #[macro_rules_attr::apply(proptest)]
1237 fn adding_degenerate_lhs_and_small_enough_rhs_makes_sum_non_degenerate(
1238 #[strategy(BFieldElement::P..)] raw_a: u64,
1239 #[strategy(0..BFieldElement::P + 2 - (1 << 32))] raw_b: u64,
1240 ) {
1241 let a = BFieldElement::from_raw_u64(raw_a);
1242 let b = BFieldElement::from_raw_u64(raw_b);
1243 let raw_sum = (a + b).raw_u64();
1244 prop_assert!(raw_sum < BFieldElement::P);
1245 }
1246
1247 #[macro_rules_attr::apply(test)]
1252 fn round_constants_correct_degenerate_lhs_when_adding() {
1253 for constant in ROUND_CONSTANTS {
1254 assert!(constant.raw_u64() < BFieldElement::P + 2 - (1 << 32));
1255 }
1256 }
1257
1258 #[macro_rules_attr::apply(test)]
1260 fn tip5_recovers_from_degenerate_field_element_representations() {
1261 let state = [
1262 0x1063_c4bf_5d8b_b0dd,
1263 0xdb62_75d3_71fe_05d0,
1264 0xde58_cae3_0144_cdae,
1265 0xc774_e646_81d3_622e,
1266 0xc4a9_47d1_0a5a_a466,
1267 0xda55_77a0_0a91_3151,
1268 0xe80e_978b_3836_dcd0,
1269 0x8dd1_61f0_a3ac_00c2,
1270 0x6857_f251_a9c0_f693,
1271 0x4923_a368_3046_178e,
1272 0x6e6f_c54a_9b81_010b,
1273 0xcb84_fa5b_b9fa_ec36,
1274 0x93cb_f9db_4c5c_b1ea,
1275 0xf215_d9b9_2dc8_7266,
1276 0x88f0_9783_d2ae_3c57,
1277 0x6d29_f9ce_94a9_0b71,
1278 ]
1279 .map(BFieldElement::new);
1280 let expected = [
1281 0xa5d3_2d62_9e60_d72e,
1282 0x5516_ef90_d277_3d74,
1283 0x65d3_fa1c_de45_f6cb,
1284 0x7bf0_e725_dfa5_906b,
1285 0x67a2_db4b_141b_90e9,
1286 0x91db_162d_3230_9083,
1287 0xefec_1d00_146a_05c9,
1288 0xcca0_d656_6bca_8186,
1289 0x405b_aeb5_b3f8_7f02,
1290 0xd897_0158_7027_8f76,
1291 0xd4b2_ee48_10aa_c7d1,
1292 0x27b4_51e7_06a5_c2fc,
1293 0xe9b4_177f_0a0e_ffe4,
1294 0x0c60_def0_f2c5_287f,
1295 0x703a_a06d_327c_cc34,
1296 0x536f_2355_0ebf_98f1,
1297 ]
1298 .map(BFieldElement::new);
1299
1300 let mut dbg_tip5 = Tip5 { state };
1301 dbg_tip5.sbox_layer();
1302 dbg_tip5.mds_generated();
1303
1304 debug_assert!(dbg_tip5.state[1].raw_u64() >= BFieldElement::P);
1312
1313 let mut tip5 = Tip5 { state };
1314 tip5.permutation();
1315 assert_eq!(expected, tip5.state);
1316
1317 let mut naive = naive::NaiveTip5 { state };
1318 naive.permutation();
1319 assert_eq!(expected, naive.state);
1320
1321 #[cfg(target_arch = "x86_64")]
1323 if Tip5::avx512_is_available() {
1324 let mut batch = unsafe { avx512::Tip5Batch::new(Domain::VariableLength) };
1327 for (i, &element) in state.iter().enumerate() {
1328 unsafe { batch.set(i, [element; avx512::BATCH_SIZE]) };
1329 }
1330 unsafe { batch.permutation() };
1331 for (i, &element) in expected.iter().enumerate() {
1332 assert_eq!([element; avx512::BATCH_SIZE], unsafe { batch.get(i) });
1333 }
1334 }
1335 }
1336
1337 #[macro_rules_attr::apply(test)]
1338 fn calculate_differential_uniformity() {
1339 let count_satisfiers_fermat = |a, b| {
1342 (0..(1 << 8))
1343 .map(|x| {
1344 u16::from(
1345 (256 + Tip5::offset_fermat_cube_map((x + a) & 0xff)
1346 - Tip5::offset_fermat_cube_map(x))
1347 & 0xff
1348 == b,
1349 )
1350 })
1351 .sum()
1352 };
1353 let du_fermat: u16 = (1..256)
1354 .into_par_iter()
1355 .map(|a| {
1356 (1..256)
1357 .map(|b| count_satisfiers_fermat(a, b))
1358 .max()
1359 .unwrap()
1360 })
1361 .max()
1362 .unwrap();
1363 println!("additive differential uniformity for fermat cube map: {du_fermat}");
1364
1365 let count_satisfiers_fermat_bitwise = |a: u16, b: u16| {
1367 (0..(1 << 8))
1368 .map(|x| {
1369 u16::from(
1370 (Tip5::offset_fermat_cube_map(x ^ a) ^ Tip5::offset_fermat_cube_map(x))
1371 == b,
1372 )
1373 })
1374 .sum::<u16>()
1375 };
1376 for a in 1..256 {
1377 for b in 1..256 {
1378 let num_satisfiers = count_satisfiers_fermat_bitwise(a, b);
1379 if num_satisfiers == 256 {
1380 println!("a: {a}, b: {b} -> 256 satisfiers");
1381 }
1382 }
1383 }
1384 let du_fermat_bitwise: u16 = (1..256)
1385 .into_par_iter()
1386 .map(|a| {
1387 (1..256)
1388 .map(|b| count_satisfiers_fermat_bitwise(a, b))
1389 .max()
1390 .unwrap()
1391 })
1392 .max()
1393 .unwrap();
1394 println!("bitwise differential uniformity for fermat cube map: {du_fermat_bitwise}");
1395 }
1396
1397 #[macro_rules_attr::apply(test)]
1398 fn calculate_approximation_quality() {
1399 let mut fermat_cubed = [0u16; 65536];
1400 let mut bfield_cubed = [0u16; 65536];
1401 for i in 0..65536 {
1402 let cubed = (i as u64) * (i as u64) * (i as u64);
1403 fermat_cubed[i] = (cubed % 65537) as u16;
1404 bfield_cubed[i] = (cubed & 0xffff) as u16;
1405 }
1406 let equal_count = fermat_cubed
1407 .iter()
1408 .zip(bfield_cubed.iter())
1409 .filter(|(a, b)| a == b)
1410 .count();
1411 println!("agreement with low-degree function: {equal_count}");
1412 }
1413
1414 const MAGIC_SNAPSHOT_HEX: &str =
1424 "109cc2fe453bd9962f754b96d8f5b919b60af030940a275f5540da195fef65ee651c1b6fa19b2c6a";
1425
1426 #[macro_rules_attr::apply(test)]
1427 fn hash10_test_vectors_snapshot() {
1428 let mut preimage = [BFieldElement::ZERO; RATE];
1429 for i in 0..6 {
1430 let digest = Tip5::hash_10(&preimage);
1431 preimage[i..i + Digest::LEN].copy_from_slice(&digest);
1432 }
1433 let final_digest = Digest::new(Tip5::hash_10(&preimage)).to_hex();
1434 assert_eq!(MAGIC_SNAPSHOT_HEX, final_digest);
1435 }
1436
1437 #[macro_rules_attr::apply(test)]
1438 fn hash_varlen_test_vectors() {
1439 let mut digest_sum = [BFieldElement::ZERO; Digest::LEN];
1440 for i in 0..20 {
1441 let preimage = (0..i).map(BFieldElement::new).collect_vec();
1442 let digest = Tip5::hash_varlen(&preimage);
1443 digest_sum
1444 .iter_mut()
1445 .zip(digest.values().iter())
1446 .for_each(|(s, d)| *s += *d);
1447 }
1448
1449 let final_digest = Digest::new(digest_sum).to_hex();
1450 assert_eq!(
1451 "efbafa86622a9c69652f8a1c4ffd734f021ad23a0a8085412a877de0f9170b18ea4ff69b6fff9a03",
1452 final_digest,
1453 );
1454 }
1455
1456 #[macro_rules_attr::apply(test)]
1457 fn snapshot() {
1458 let state = [
1459 0x0000_000f_ffff_fff0,
1460 0x0000_0000_ffff_ffff,
1461 0x0000_0000_ffff_ffff,
1462 0x0000_0028_ffff_ffd7,
1463 0x0000_0006_ffff_fff9,
1464 0x0000_0002_ffff_fffd,
1465 0x0000_0000_ffff_ffff,
1466 0x0000_0030_ffff_ffcf,
1467 0x0000_0397_ffff_fc68,
1468 0x0000_000f_ffff_fff0,
1469 0x316b_fb72_3638_2123,
1470 0x216f_521b_66ef_83f5,
1471 0x5689_d7b3_63f5_2df0,
1472 0xeb2f_59e3_aeae_25fc,
1473 0xb082_99d2_77cb_b4dc,
1474 0xcbe3_d9fd_c534_9140,
1475 ]
1476 .map(BFieldElement::from_raw_u64);
1477
1478 let mut tip5 = Tip5 { state };
1479 tip5.permutation();
1480
1481 let expected = [
1482 0x15d3_8ea9_29f6_632a,
1483 0xf988_e509_ff73_8bb4,
1484 0x48bc_dfae_88a2_e9f3,
1485 0x8733_9e83_2daa_c02a,
1486 0x511e_4126_8150_fdac,
1487 ]
1488 .map(BFieldElement::from_raw_u64);
1489
1490 assert_eq!(&expected, &tip5.state[0..5]);
1491
1492 #[cfg(target_arch = "x86_64")]
1496 if Tip5::avx512_is_available() {
1497 let mut batch = unsafe { avx512::Tip5Batch::new(Domain::VariableLength) };
1500 for (i, &element) in state.iter().enumerate() {
1501 unsafe { batch.set(i, [element; avx512::BATCH_SIZE]) };
1502 }
1503 unsafe { batch.permutation() };
1504 for (i, &element) in expected.iter().enumerate() {
1505 assert_eq!([element; avx512::BATCH_SIZE], unsafe { batch.get(i) });
1506 }
1507 }
1508 }
1509
1510 fn manual_hash_varlen(preimage: &[BFieldElement]) -> Digest {
1511 let mut sponge = Tip5::init();
1512 sponge.pad_and_absorb_all(preimage);
1513 let squeeze_result = sponge.squeeze();
1514
1515 Digest::new((&squeeze_result[..Digest::LEN]).try_into().unwrap())
1516 }
1517
1518 #[macro_rules_attr::apply(test)]
1519 fn hash_var_len_equivalence_edge_cases() {
1520 for preimage_length in 0..=11 {
1521 let preimage = vec![BFieldElement::new(42); preimage_length];
1522 let hash_varlen_digest = Tip5::hash_varlen(&preimage);
1523
1524 let digest_through_pad_squeeze_absorb = manual_hash_varlen(&preimage);
1525 assert_eq!(digest_through_pad_squeeze_absorb, hash_varlen_digest);
1526 }
1527 }
1528
1529 #[macro_rules_attr::apply(proptest)]
1530 fn hash_var_len_equivalence(#[strategy(arb())] preimage: Vec<BFieldElement>) {
1531 let hash_varlen_digest = Tip5::hash_varlen(&preimage);
1532 let digest_through_pad_squeeze_absorb = manual_hash_varlen(&preimage);
1533 prop_assert_eq!(digest_through_pad_squeeze_absorb, hash_varlen_digest);
1534 }
1535
1536 #[macro_rules_attr::apply(test)]
1537 fn test_linearity_of_mds() {
1538 type SpongeState = [BFieldElement; STATE_SIZE];
1539
1540 let mds_procedure = |state| {
1541 let mut sponge = Tip5 { state };
1542 sponge.mds_cyclomul();
1543 sponge.state
1544 };
1545
1546 let a: BFieldElement = random_elements(1)[0];
1547 let b: BFieldElement = random_elements(1)[0];
1548
1549 let mul_procedure = |u: SpongeState, v: SpongeState| -> SpongeState {
1550 let mul_result = u.iter().zip(&v).map(|(&uu, &vv)| a * uu + b * vv);
1551 mul_result.collect_vec().try_into().unwrap()
1552 };
1553
1554 let u: SpongeState = random_elements(STATE_SIZE).try_into().unwrap();
1555 let v: SpongeState = random_elements(STATE_SIZE).try_into().unwrap();
1556 let w = mul_procedure(u, v);
1557
1558 let u = mds_procedure(u);
1559 let v = mds_procedure(v);
1560 let w = mds_procedure(w);
1561
1562 let w_ = mul_procedure(u, v);
1563
1564 assert_eq!(w, w_);
1565 }
1566
1567 #[macro_rules_attr::apply(test)]
1568 fn test_mds_circulancy() {
1569 let mut sponge = Tip5::init();
1570 sponge.state = [BFieldElement::ZERO; STATE_SIZE];
1571 sponge.state[0] = BFieldElement::ONE;
1572
1573 sponge.mds_cyclomul();
1574
1575 let mut mat_first_row = [BFieldElement::ZERO; STATE_SIZE];
1576 mat_first_row[0] = sponge.state[0];
1577 for (i, first_row_elem) in mat_first_row.iter_mut().enumerate().skip(1) {
1578 *first_row_elem = sponge.state[STATE_SIZE - i];
1579 }
1580
1581 println!(
1582 "mds matrix first row: {:?}",
1583 mat_first_row.map(|b| b.value())
1584 );
1585
1586 let initial_state: [BFieldElement; STATE_SIZE] =
1587 random_elements(STATE_SIZE).try_into().unwrap();
1588
1589 let mut mv = [BFieldElement::ZERO; STATE_SIZE];
1590 for i in 0..STATE_SIZE {
1591 for j in 0..STATE_SIZE {
1592 mv[i] += mat_first_row[(STATE_SIZE - i + j) % STATE_SIZE] * initial_state[j];
1593 }
1594 }
1595
1596 let mut sponge_2 = Tip5::init();
1597 sponge_2.state = initial_state;
1598 sponge_2.mds_cyclomul();
1599
1600 assert_eq!(sponge_2.state, mv);
1601 }
1602
1603 #[macro_rules_attr::apply(test)]
1604 fn test_complex_karatsuba() {
1605 const N: usize = 4;
1606 let mut f = [(0i64, 0i64); N];
1607 let mut g = [(0i64, 0i64); N];
1608 for i in 0..N {
1609 f[i].0 = i as i64;
1610 g[i].0 = 1;
1611 f[i].1 = 1;
1612 g[i].1 = i as i64;
1613 }
1614
1615 let h0 = Tip5::complex_karatsuba4(f, g);
1616 let h1 = [(0, 1), (0, 2), (0, 4), (0, 8), (0, 13), (0, 14), (0, 10)];
1617
1618 assert_eq!(h0, h1);
1619 }
1620
1621 #[macro_rules_attr::apply(test)]
1622 fn test_complex_product() {
1623 let mut rng = rand::rng();
1624 let mut random_small_i64 = || (rng.next_u32() % (1 << 16)) as i64;
1625 for _ in 0..1000 {
1626 let f = (random_small_i64(), random_small_i64());
1627 let g = (random_small_i64(), random_small_i64());
1628 let h0 = Tip5::complex_product(f, g);
1629 let h1 = (f.0 * g.0 - f.1 * g.1, f.0 * g.1 + f.1 * g.0);
1630 assert_eq!(h1, h0);
1631 }
1632 }
1633
1634 #[macro_rules_attr::apply(proptest)]
1635 fn sample_scalars(mut tip5: Tip5, #[strategy(0_usize..=100)] num_scalars: usize) {
1636 tip5.permutation(); let scalars = tip5.sample_scalars(num_scalars);
1639 prop_assert_eq!(num_scalars, scalars.len());
1640
1641 let product = scalars
1642 .into_iter()
1643 .fold(XFieldElement::ONE, XFieldElement::mul);
1644 prop_assert_ne!(product, XFieldElement::ZERO);
1645 }
1646
1647 #[macro_rules_attr::apply(proptest)]
1648 fn test_mds_matrix_mul_methods_agree(state: [BFieldElement; STATE_SIZE]) {
1649 let mut sponge_cyclomut = Tip5 { state };
1650 let mut sponge_generated = Tip5 { state };
1651
1652 sponge_cyclomut.mds_cyclomul();
1653 sponge_generated.mds_generated();
1654
1655 prop_assert_eq!(
1656 &sponge_cyclomut,
1657 &sponge_generated,
1658 "cyclomul =/= generated\n{}\n{}",
1659 sponge_cyclomut.state.into_iter().join(","),
1660 sponge_generated.state.into_iter().join(",")
1661 );
1662 }
1663
1664 #[cfg(target_arch = "x86_64")]
1667 #[macro_rules_attr::apply(proptest)]
1668 fn avx512_permutation_agrees_with_scalar_permutation(state: [BFieldElement; STATE_SIZE]) {
1669 if !Tip5::avx512_is_available() {
1670 return Ok(());
1671 }
1672
1673 let mut scalar = Tip5 { state };
1674 let mut avx512 = Tip5 { state };
1675 for round_index in 0..NUM_ROUNDS {
1676 scalar.round(round_index);
1677 unsafe { avx512.round_avx512(round_index) };
1679 prop_assert_eq!(&scalar, &avx512, "round {}", round_index);
1680 }
1681
1682 let mut scalar_permutation = Tip5 { state };
1683 let mut avx512_permutation = Tip5 { state };
1684 scalar_permutation.permutation_scalar();
1685 unsafe { avx512_permutation.permutation_avx512() };
1687 prop_assert_eq!(scalar_permutation, avx512_permutation);
1688 }
1689
1690 #[cfg(target_arch = "x86_64")]
1693 #[macro_rules_attr::apply(proptest)]
1694 fn batched_permutation_agrees_with_naive_permutation(
1695 states: [[BFieldElement; STATE_SIZE]; avx512::BATCH_SIZE],
1696 ) {
1697 if !Tip5::avx512_is_available() {
1698 return Ok(());
1699 }
1700
1701 let mut batch = unsafe { avx512::Tip5Batch::new(Domain::VariableLength) };
1704 for i in 0..STATE_SIZE {
1705 unsafe { batch.set(i, states.map(|state| state[i])) };
1706 }
1707 unsafe { batch.permutation() };
1708
1709 for (lane, state) in states.into_iter().enumerate() {
1710 let mut naive = naive::NaiveTip5 { state };
1711 naive.permutation();
1712 for (i, &naive_element) in naive.state.iter().enumerate() {
1713 let batched_element = unsafe { batch.get(i) }[lane];
1714 prop_assert_eq!(
1715 naive_element,
1716 batched_element,
1717 "lane {}, element {}",
1718 lane,
1719 i
1720 );
1721 }
1722 }
1723 }
1724
1725 #[macro_rules_attr::apply(proptest)]
1726 fn hash_varlen_many_agrees_with_hash_varlen(
1727 #[strategy(proptest::collection::vec(proptest::collection::vec(arb(), 0..35), 0..30))]
1728 inputs: Vec<Vec<BFieldElement>>,
1729 ) {
1730 let inputs = inputs.iter().map(Vec::as_slice).collect_vec();
1731 let individually = inputs
1732 .iter()
1733 .map(|input| Tip5::hash_varlen(input))
1734 .collect_vec();
1735 prop_assert_eq!(individually.clone(), Tip5::hash_varlen_many(&inputs));
1736 prop_assert_eq!(individually, Tip5::hash_varlen_many_scalar(&inputs));
1737 }
1738
1739 #[macro_rules_attr::apply(proptest)]
1740 fn hash_varlen_many_agrees_with_hash_varlen_for_equal_lengths(
1741 #[strategy(0_usize..35)] _len: usize,
1742 #[strategy(proptest::collection::vec(proptest::collection::vec(arb(), #_len), 0..30))]
1743 inputs: Vec<Vec<BFieldElement>>,
1744 ) {
1745 let inputs = inputs.iter().map(Vec::as_slice).collect_vec();
1746 let individually = inputs
1747 .iter()
1748 .map(|input| Tip5::hash_varlen(input))
1749 .collect_vec();
1750 prop_assert_eq!(individually, Tip5::hash_varlen_many(&inputs));
1751 }
1752
1753 #[macro_rules_attr::apply(proptest)]
1754 fn hash_pair_many_agrees_with_hash_pair(
1755 #[strategy(proptest::collection::vec(arb(), 0..30))] pairs: Vec<[Digest; 2]>,
1756 ) {
1757 let individually = pairs
1758 .iter()
1759 .map(|&[l, r]| Tip5::hash_pair(l, r))
1760 .collect_vec();
1761 prop_assert_eq!(individually.clone(), Tip5::hash_pair_many_scalar(&pairs));
1762 prop_assert_eq!(individually, Tip5::hash_pair_many(&pairs));
1763 }
1764
1765 #[macro_rules_attr::apply(test)]
1766 fn avx512_reduction_regression_vector() {
1767 let state = crate::bfe_array![
1768 0x0000_000f_ffff_fff0_u64,
1769 0x0000_0000_ffff_ffff_u64,
1770 0x0000_0000_ffff_ffff_u64,
1771 0x0000_0028_ffff_ffd7_u64,
1772 0x0000_0006_ffff_fff9_u64,
1773 0x0000_0002_ffff_fffd_u64,
1774 0x0000_0000_ffff_ffff_u64,
1775 0x0000_0030_ffff_ffcf_u64,
1776 0x0000_0397_ffff_fc68_u64,
1777 0x0000_000f_ffff_fff0_u64,
1778 0x316b_fb72_3638_2123_u64,
1779 0x216f_521b_66ef_83f5_u64,
1780 0x5689_d7b3_63f5_2df0_u64,
1781 0xeb2f_59e3_aeae_25fc_u64,
1782 0xb082_99d2_77cb_b4dc_u64,
1783 0xcbe3_d9fd_c534_9140_u64,
1784 ];
1785 let expected = crate::bfe_array![
1786 0x231d_a775_9f66_2fd9_u64,
1787 0x9bad_454d_eb24_c327_u64,
1788 0x8b9f_67d4_0440_bc7e_u64,
1789 0xfd56_ad41_eb9d_2514_u64,
1790 0xeb4e_1c7c_2b83_5d30_u64,
1791 0x9e86_05fe_9bc2_891f_u64,
1792 0x0da2_ae9f_e4a6_d684_u64,
1793 0x3944_688b_8da6_e25d_u64,
1794 0x4bc6_d6d4_e868_ecbe_u64,
1795 0x2293_50dc_cc9c_4677_u64,
1796 0xe532_c7c1_200d_0349_u64,
1797 0x92c2_1e76_49ef_2e40_u64,
1798 0xa24f_5d2f_dfd9_fc52_u64,
1799 0xd391_72f3_43db_c0f0_u64,
1800 0x4236_2d8f_3a6e_9720_u64,
1801 0xf7ee_7105_bb49_bd3e_u64,
1802 ];
1803
1804 let mut naive = naive::NaiveTip5 { state };
1805 naive.permutation();
1806 assert_eq!(expected, naive.state);
1807
1808 let mut scalar = Tip5 { state };
1809 for round_index in 0..NUM_ROUNDS {
1810 scalar.round(round_index);
1811 }
1812 assert_eq!(expected, scalar.state);
1813
1814 let mut dispatching = Tip5 { state };
1815 dispatching.permutation();
1816 assert_eq!(expected, dispatching.state);
1817
1818 #[cfg(target_arch = "x86_64")]
1819 if Tip5::avx512_is_available() {
1820 let mut batch = unsafe { avx512::Tip5Batch::new(Domain::VariableLength) };
1823 for (i, &element) in state.iter().enumerate() {
1824 unsafe { batch.set(i, [element; avx512::BATCH_SIZE]) };
1825 }
1826 unsafe { batch.permutation() };
1827 for (i, &element) in expected.iter().enumerate() {
1828 assert_eq!([element; avx512::BATCH_SIZE], unsafe { batch.get(i) });
1829 }
1830 }
1831 }
1832
1833 #[macro_rules_attr::apply(test)]
1834 fn tip5_hasher_trait_snapshot_test() {
1835 let mut hasher = Tip5::init();
1836 let data = b"hello world";
1837 hasher.write(data);
1838 assert_eq!(hasher.finish(), 2267905471610932299);
1839 }
1840
1841 #[macro_rules_attr::apply(proptest)]
1842 fn tip5_hasher_consumes_small_data(#[filter(!#bytes.is_empty())] bytes: Vec<u8>) {
1843 let mut hasher = Tip5::init();
1844 bytes.hash(&mut hasher);
1845
1846 prop_assert_ne!(Tip5::init().finish(), hasher.finish());
1847 }
1848
1849 #[macro_rules_attr::apply(proptest)]
1850 fn appending_small_data_to_big_data_changes_tip5_hash(
1851 #[any(size_range(2_000..8_000).lift())] big_data: Vec<u8>,
1852 #[filter(!#small_data.is_empty())] small_data: Vec<u8>,
1853 ) {
1854 let mut hasher = Tip5::init();
1855 big_data.hash(&mut hasher);
1856 let big_data_hash = hasher.finish();
1857
1858 small_data.hash(&mut hasher);
1860 let all_data_hash = hasher.finish();
1861
1862 prop_assert_ne!(big_data_hash, all_data_hash);
1863 }
1864
1865 #[macro_rules_attr::apply(proptest)]
1866 fn tip5_trace_starts_with_initial_state_and_is_equivalent_to_permutation(mut tip5: Tip5) {
1867 let [first, .., last] = tip5.clone().trace();
1868 prop_assert_eq!(first, tip5.state);
1869
1870 tip5.permutation();
1871 prop_assert_eq!(last, tip5.state);
1872 }
1873}