1use crate::sha3::Sponge;
37use ic_core::traits::Algorithm;
38
39pub const MAX_ENCODE: usize = 9;
42
43pub fn left_encode(x: u64, buf: &mut [u8; MAX_ENCODE]) -> usize {
47 let n = value_bytes(x);
48 buf[0] = n as u8;
49 for i in 0..n {
50 buf[1 + i] = (x >> (8 * (n - 1 - i))) as u8;
51 }
52 n + 1
53}
54
55pub fn right_encode(x: u64, buf: &mut [u8; MAX_ENCODE]) -> usize {
57 let n = value_bytes(x);
58 for (i, slot) in buf.iter_mut().take(n).enumerate() {
59 *slot = (x >> (8 * (n - 1 - i))) as u8;
60 }
61 buf[n] = n as u8;
62 n + 1
63}
64
65fn value_bytes(x: u64) -> usize {
68 if x == 0 {
69 1
70 } else {
71 8 - (x.leading_zeros() / 8) as usize
72 }
73}
74
75pub(crate) struct CountingAbsorb<'a> {
82 sponge: &'a mut Sponge,
83 written: usize,
84}
85
86impl<'a> CountingAbsorb<'a> {
87 pub(crate) fn new(sponge: &'a mut Sponge) -> Self {
88 Self { sponge, written: 0 }
89 }
90
91 pub(crate) fn feed(&mut self, data: &[u8]) {
92 self.sponge.absorb(data);
93 self.written += data.len();
94 }
95
96 pub(crate) fn feed_encoded_string(&mut self, s: &[u8]) {
98 let mut buf = [0u8; MAX_ENCODE];
99 let n = left_encode((s.len() as u64) * 8, &mut buf);
103 self.feed(&buf[..n]);
104 self.feed(s);
105 }
106
107 pub(crate) fn finish_bytepad(self, w: usize) {
109 let remainder = self.written % w;
110 if remainder != 0 {
111 let zeros = [0u8; 168];
112 let mut left = w - remainder;
113 while left > 0 {
114 let chunk = core::cmp::min(left, zeros.len());
115 self.sponge.absorb(&zeros[..chunk]);
116 left -= chunk;
117 }
118 }
119 }
120}
121
122macro_rules! cshake {
124 ($name:ident, $id:literal, $disp:literal, $rate:literal, $doc:literal) => {
125 #[doc = $doc]
126 #[derive(Clone)]
127 pub struct $name {
128 sponge: Sponge,
129 }
130
131 impl Algorithm for $name {
132 const ID: &'static str = $id;
133 const NAME: &'static str = $disp;
134 }
135
136 impl $name {
137 pub const RATE: usize = $rate;
139
140 pub fn new(n: &[u8], s: &[u8]) -> Self {
148 if n.is_empty() && s.is_empty() {
149 return Self {
150 sponge: Sponge::new($rate, 0x1f),
151 };
152 }
153 let mut sponge = Sponge::new($rate, 0x04);
156 let mut prefix = CountingAbsorb::new(&mut sponge);
157 let mut buf = [0u8; MAX_ENCODE];
158 let used = left_encode($rate as u64, &mut buf);
159 prefix.feed(&buf[..used]);
160 prefix.feed_encoded_string(n);
161 prefix.feed_encoded_string(s);
162 prefix.finish_bytepad($rate);
163 Self { sponge }
164 }
165
166 pub fn update(&mut self, data: &[u8]) {
168 self.sponge.absorb(data);
169 }
170
171 pub fn finalize_xof(mut self, out: &mut [u8]) {
173 self.sponge.finish();
174 self.sponge.squeeze(out);
175 }
176
177 pub fn xof(n: &[u8], s: &[u8], data: &[u8], out: &mut [u8]) {
179 let mut x = Self::new(n, s);
180 x.update(data);
181 x.finalize_xof(out);
182 }
183
184 pub fn absorb_bytepadded_string(&mut self, s: &[u8]) {
191 let mut pad = CountingAbsorb::new(&mut self.sponge);
192 let mut buf = [0u8; MAX_ENCODE];
193 let used = left_encode($rate as u64, &mut buf);
194 pad.feed(&buf[..used]);
195 pad.feed_encoded_string(s);
196 pad.finish_bytepad($rate);
197 }
198 }
199 };
200}
201
202macro_rules! cshake_self_test {
205 ($name:ident, $shake:ty, $id:literal) => {
206 impl ic_core::traits::SelfTest for $name {
207 fn self_test() -> ic_core::Result<()> {
215 let mut plain = [0u8; 32];
216 let mut shake = [0u8; 32];
217 Self::xof(b"", b"", b"abc", &mut plain);
218 <$shake>::xof(b"abc", &mut shake);
219 ic_core::ensure!(ic_core::ct::verify(&plain, &shake), SelfTestFailed, $id);
220
221 let mut customized = [0u8; 32];
222 Self::xof(b"", b"self-test", b"abc", &mut customized);
223 ic_core::ensure!(
224 !ic_core::ct::verify(&plain, &customized),
225 SelfTestFailed,
226 $id
227 );
228 Ok(())
229 }
230 }
231 };
232}
233
234cshake!(
235 CShake128,
236 "cshake128",
237 "cSHAKE128",
238 168,
239 "SP 800-185 cSHAKE128: SHAKE128 with a customization string."
240);
241cshake!(
242 CShake256,
243 "cshake256",
244 "cSHAKE256",
245 136,
246 "SP 800-185 cSHAKE256: SHAKE256 with a customization string."
247);
248
249cshake_self_test!(CShake128, crate::Shake128, "cshake128");
250cshake_self_test!(CShake256, crate::Shake256, "cshake256");
251
252macro_rules! tuple_hash {
258 ($name:ident, $cshake:ty, $id:literal, $disp:literal, $doc:literal) => {
259 #[doc = $doc]
260 #[derive(Clone)]
261 pub struct $name {
262 inner: $cshake,
263 }
264
265 impl Algorithm for $name {
266 const ID: &'static str = $id;
267 const NAME: &'static str = $disp;
268 }
269
270 impl $name {
271 pub fn new(custom: &[u8]) -> Self {
273 Self {
274 inner: <$cshake>::new(b"TupleHash", custom),
275 }
276 }
277
278 pub fn update(&mut self, element: &[u8]) {
283 let mut buf = [0u8; MAX_ENCODE];
284 let used = left_encode((element.len() as u64) * 8, &mut buf);
285 self.inner.update(&buf[..used]);
286 self.inner.update(element);
287 }
288
289 pub fn finalize(mut self, out: &mut [u8]) {
291 let mut buf = [0u8; MAX_ENCODE];
292 let used = right_encode((out.len() as u64) * 8, &mut buf);
293 self.inner.update(&buf[..used]);
294 self.inner.finalize_xof(out);
295 }
296
297 pub fn finalize_xof(mut self, out: &mut [u8]) {
299 let mut buf = [0u8; MAX_ENCODE];
300 let used = right_encode(0, &mut buf);
301 self.inner.update(&buf[..used]);
302 self.inner.finalize_xof(out);
303 }
304
305 pub fn hash(custom: &[u8], elements: &[&[u8]], out: &mut [u8]) {
307 let mut t = Self::new(custom);
308 for element in elements {
309 t.update(element);
310 }
311 t.finalize(out);
312 }
313
314 pub fn hash_xof(custom: &[u8], elements: &[&[u8]], out: &mut [u8]) {
316 let mut t = Self::new(custom);
317 for element in elements {
318 t.update(element);
319 }
320 t.finalize_xof(out);
321 }
322 }
323
324 impl ic_core::traits::SelfTest for $name {
325 fn self_test() -> ic_core::Result<()> {
328 let mut a = [0u8; 32];
329 let mut b = [0u8; 32];
330 Self::hash(b"self-test", &[b"abc", b"d"], &mut a);
331 Self::hash(b"self-test", &[b"ab", b"cd"], &mut b);
332 ic_core::ensure!(!ic_core::ct::verify(&a, &b), SelfTestFailed, $id);
333
334 let mut again = [0u8; 32];
336 Self::hash(b"self-test", &[b"abc", b"d"], &mut again);
337 ic_core::ensure!(ic_core::ct::verify(&a, &again), SelfTestFailed, $id);
338 Ok(())
339 }
340 }
341 };
342}
343
344tuple_hash!(
345 TupleHash128,
346 CShake128,
347 "tuplehash128",
348 "TupleHash128",
349 "SP 800-185 TupleHash128: hashes a *sequence* of strings unambiguously.\n\
350 \n\
351 Hashing `a || b` cannot distinguish `(\"abc\", \"d\")` from `(\"ab\", \"cd\")`,\n\
352 and a protocol that concatenates fields before hashing them has a\n\
353 forgery waiting in it. TupleHash length-prefixes every element, so\n\
354 distinct tuples always hash distinctly."
355);
356tuple_hash!(
357 TupleHash256,
358 CShake256,
359 "tuplehash256",
360 "TupleHash256",
361 "SP 800-185 TupleHash256, at the 256-bit security level."
362);
363
364macro_rules! parallel_hash {
373 ($name:ident, $cshake:ty, $chain:literal, $id:literal, $disp:literal, $doc:literal) => {
374 #[doc = $doc]
375 pub struct $name;
376
377 impl Algorithm for $name {
378 const ID: &'static str = $id;
379 const NAME: &'static str = $disp;
380 }
381
382 impl $name {
383 pub const CHAINING_LEN: usize = $chain;
385
386 pub fn hash(custom: &[u8], block_size: usize, data: &[u8], out: &mut [u8]) {
391 Self::run(custom, block_size, data, out, false)
392 }
393
394 pub fn hash_xof(custom: &[u8], block_size: usize, data: &[u8], out: &mut [u8]) {
396 Self::run(custom, block_size, data, out, true)
397 }
398
399 fn run(custom: &[u8], block_size: usize, data: &[u8], out: &mut [u8], xof: bool) {
400 assert!(block_size > 0, "parallelhash block size must be positive");
401 let mut outer = <$cshake>::new(b"ParallelHash", custom);
402 let mut buf = [0u8; MAX_ENCODE];
403
404 let used = left_encode(block_size as u64, &mut buf);
405 outer.update(&buf[..used]);
406
407 let mut blocks = 0u64;
408 for block in data.chunks(block_size) {
409 let mut chain = [0u8; $chain];
412 <$cshake>::xof(b"", b"", block, &mut chain);
413 outer.update(&chain);
414 blocks += 1;
415 }
416
417 let used = right_encode(blocks, &mut buf);
418 outer.update(&buf[..used]);
419 let used = right_encode(if xof { 0 } else { (out.len() as u64) * 8 }, &mut buf);
420 outer.update(&buf[..used]);
421 outer.finalize_xof(out);
422 }
423 }
424
425 impl ic_core::traits::SelfTest for $name {
426 fn self_test() -> ic_core::Result<()> {
430 let data = [0x5au8; 200];
431 let mut a = [0u8; 32];
432 let mut b = [0u8; 32];
433 Self::hash(b"self-test", 16, &data, &mut a);
434 Self::hash(b"self-test", 32, &data, &mut b);
435 ic_core::ensure!(!ic_core::ct::verify(&a, &b), SelfTestFailed, $id);
436
437 let mut again = [0u8; 32];
438 Self::hash(b"self-test", 16, &data, &mut again);
439 ic_core::ensure!(ic_core::ct::verify(&a, &again), SelfTestFailed, $id);
440 Ok(())
441 }
442 }
443 };
444}
445
446parallel_hash!(
447 ParallelHash128,
448 CShake128,
449 32,
450 "parallelhash128",
451 "ParallelHash128",
452 "SP 800-185 ParallelHash128: hashes fixed-size blocks independently, then\n\
453 hashes their digests.\n\
454 \n\
455 The structure is designed so the per-block work can be spread across\n\
456 cores. This implementation does them in order — the workspace has no\n\
457 threading and `no_std` targets have no threads to spread onto — so what\n\
458 it buys here is interoperability with implementations that do, not\n\
459 speed. The output is identical either way."
460);
461parallel_hash!(
462 ParallelHash256,
463 CShake256,
464 64,
465 "parallelhash256",
466 "ParallelHash256",
467 "SP 800-185 ParallelHash256, at the 256-bit security level."
468);
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473 use crate::{Sha3_256, Shake128, Shake256};
474 use ic_core::codec::hex;
475 use ic_core::traits::Digest;
476
477 fn reference_left_encode(x: u64) -> Vec<u8> {
482 let mut bytes = x.to_be_bytes().to_vec();
483 while bytes.len() > 1 && bytes[0] == 0 {
484 bytes.remove(0);
485 }
486 let mut out = vec![bytes.len() as u8];
487 out.extend_from_slice(&bytes);
488 out
489 }
490
491 fn reference_right_encode(x: u64) -> Vec<u8> {
492 let mut bytes = x.to_be_bytes().to_vec();
493 while bytes.len() > 1 && bytes[0] == 0 {
494 bytes.remove(0);
495 }
496 let n = bytes.len() as u8;
497 bytes.push(n);
498 bytes
499 }
500
501 #[test]
502 fn the_encodings_match_an_independent_construction() {
503 for x in [
504 0u64,
505 1,
506 2,
507 127,
508 128,
509 255,
510 256,
511 65535,
512 65536,
513 1 << 24,
514 u32::MAX as u64,
515 u64::MAX,
516 ] {
517 let mut buf = [0u8; MAX_ENCODE];
518 let n = left_encode(x, &mut buf);
519 assert_eq!(&buf[..n], &reference_left_encode(x)[..], "left_encode({x})");
520
521 let n = right_encode(x, &mut buf);
522 assert_eq!(
523 &buf[..n],
524 &reference_right_encode(x)[..],
525 "right_encode({x})"
526 );
527 }
528 }
529
530 #[test]
532 fn the_encodings_match_the_published_examples() {
533 let mut buf = [0u8; MAX_ENCODE];
534 let n = left_encode(0, &mut buf);
535 assert_eq!(&buf[..n], &[0x01, 0x00]);
536 let n = right_encode(0, &mut buf);
537 assert_eq!(&buf[..n], &[0x00, 0x01]);
538 let n = left_encode(1, &mut buf);
542 assert_eq!(&buf[..n], &[0x01, 0x01]);
543 let n = right_encode(1, &mut buf);
544 assert_eq!(&buf[..n], &[0x01, 0x01]);
545 let n = left_encode(256, &mut buf);
546 assert_eq!(&buf[..n], &[0x02, 0x01, 0x00]);
547 let n = right_encode(256, &mut buf);
548 assert_eq!(&buf[..n], &[0x01, 0x00, 0x02]);
549 }
550
551 #[allow(clippy::needless_range_loop)]
567 fn reference_keccak(lanes: &mut [[u64; 5]; 5]) {
568 const RC: [u64; 24] = [
569 0x0000000000000001,
570 0x0000000000008082,
571 0x800000000000808a,
572 0x8000000080008000,
573 0x000000000000808b,
574 0x0000000080000001,
575 0x8000000080008081,
576 0x8000000000008009,
577 0x000000000000008a,
578 0x0000000000000088,
579 0x0000000080008009,
580 0x000000008000000a,
581 0x000000008000808b,
582 0x800000000000008b,
583 0x8000000000008089,
584 0x8000000000008003,
585 0x8000000000008002,
586 0x8000000000000080,
587 0x000000000000800a,
588 0x800000008000000a,
589 0x8000000080008081,
590 0x8000000000008080,
591 0x0000000080000001,
592 0x8000000080008008,
593 ];
594 const R: [[u32; 5]; 5] = [
596 [0, 36, 3, 41, 18],
597 [1, 44, 10, 45, 2],
598 [62, 6, 43, 15, 61],
599 [28, 55, 25, 21, 56],
600 [27, 20, 39, 8, 14],
601 ];
602
603 for rc in RC {
604 let mut c = [0u64; 5];
606 for (x, cx) in c.iter_mut().enumerate() {
607 *cx = lanes[x][0] ^ lanes[x][1] ^ lanes[x][2] ^ lanes[x][3] ^ lanes[x][4];
608 }
609 let mut d = [0u64; 5];
610 for x in 0..5 {
611 d[x] = c[(x + 4) % 5] ^ c[(x + 1) % 5].rotate_left(1);
612 }
613 for x in 0..5 {
614 for y in 0..5 {
615 lanes[x][y] ^= d[x];
616 }
617 }
618
619 let mut b = [[0u64; 5]; 5];
621 for x in 0..5 {
622 for y in 0..5 {
623 b[y][(2 * x + 3 * y) % 5] = lanes[x][y].rotate_left(R[x][y]);
624 }
625 }
626
627 for x in 0..5 {
629 for y in 0..5 {
630 lanes[x][y] = b[x][y] ^ ((!b[(x + 1) % 5][y]) & b[(x + 2) % 5][y]);
631 }
632 }
633
634 lanes[0][0] ^= rc;
636 }
637 }
638
639 fn reference_sponge(rate: usize, pad: u8, input: &[u8], out: &mut [u8]) {
641 let mut lanes = [[0u64; 5]; 5];
642 let put = |lanes: &mut [[u64; 5]; 5], i: usize, byte: u8| {
643 let lane = i / 8;
644 lanes[lane % 5][lane / 5] ^= (byte as u64) << (8 * (i % 8));
645 };
646 let get = |lanes: &[[u64; 5]; 5], i: usize| -> u8 {
647 let lane = i / 8;
648 (lanes[lane % 5][lane / 5] >> (8 * (i % 8))) as u8
649 };
650
651 let mut padded = input.to_vec();
653 padded.push(pad);
654 while padded.len() % rate != 0 {
655 padded.push(0);
656 }
657 let last = padded.len() - 1;
658 padded[last] |= 0x80;
659
660 for block in padded.chunks(rate) {
661 for (i, byte) in block.iter().enumerate() {
662 put(&mut lanes, i, *byte);
663 }
664 reference_keccak(&mut lanes);
665 }
666
667 let mut produced = 0;
669 while produced < out.len() {
670 let take = core::cmp::min(rate, out.len() - produced);
671 for i in 0..take {
672 out[produced + i] = get(&lanes, i);
673 }
674 produced += take;
675 if produced < out.len() {
676 reference_keccak(&mut lanes);
677 }
678 }
679 }
680
681 #[test]
684 fn the_reference_keccak_reproduces_sha3() {
685 let mut got = [0u8; 32];
686 reference_sponge(136, 0x06, b"abc", &mut got);
688 assert_eq!(
689 hex(&got),
690 "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"
691 );
692 assert_eq!(hex(Sha3_256::digest(b"abc").as_ref()), hex(&got));
694 }
695
696 #[test]
700 fn empty_customization_is_plain_shake() {
701 for len in [1usize, 16, 32, 168, 169, 512] {
702 let mut a = vec![0u8; len];
703 let mut b = vec![0u8; len];
704 CShake128::xof(b"", b"", b"the quick brown fox", &mut a);
705 Shake128::xof(b"the quick brown fox", &mut b);
706 assert_eq!(a, b, "cSHAKE128 with no customization, {len} bytes");
707
708 CShake256::xof(b"", b"", b"the quick brown fox", &mut a);
709 Shake256::xof(b"the quick brown fox", &mut b);
710 assert_eq!(a, b, "cSHAKE256 with no customization, {len} bytes");
711 }
712 }
713
714 #[test]
718 fn the_customized_path_matches_the_reference_keccak() {
719 let cases: &[(&[u8], &[u8], &[u8])] = &[
720 (b"", b"Email Signature", b"\x00\x01\x02\x03"),
721 (b"KMAC", b"", b"hello"),
722 (b"KMAC", b"My Tagged Application", b""),
723 (b"N", b"S", &[0x5a; 200]),
724 ];
725
726 for (n, s, data) in cases {
727 for (rate, is_128) in [(168usize, true), (136, false)] {
728 let mut prefix = reference_left_encode(rate as u64);
731 prefix.extend_from_slice(&reference_left_encode((n.len() as u64) * 8));
732 prefix.extend_from_slice(n);
733 prefix.extend_from_slice(&reference_left_encode((s.len() as u64) * 8));
734 prefix.extend_from_slice(s);
735 while prefix.len() % rate != 0 {
736 prefix.push(0);
737 }
738 prefix.extend_from_slice(data);
739
740 let mut want = [0u8; 64];
741 reference_sponge(rate, 0x04, &prefix, &mut want);
742
743 let mut got = [0u8; 64];
744 if is_128 {
745 CShake128::xof(n, s, data, &mut got);
746 } else {
747 CShake256::xof(n, s, data, &mut got);
748 }
749 assert_eq!(
750 hex(&got),
751 hex(&want),
752 "cSHAKE rate {rate}, N={n:?}, S={s:?}"
753 );
754 }
755 }
756 }
757
758 #[test]
761 fn customization_changes_the_output() {
762 let mut a = [0u8; 32];
763 let mut b = [0u8; 32];
764 let mut c = [0u8; 32];
765 CShake128::xof(b"", b"one", b"message", &mut a);
766 CShake128::xof(b"", b"two", b"message", &mut b);
767 CShake128::xof(b"", b"", b"message", &mut c);
768 assert_ne!(a, b, "S is bound into the output");
769 assert_ne!(a, c, "and distinguishes customized from plain");
770 }
771
772 #[test]
773 fn streaming_matches_the_one_shot() {
774 let data = [0x37u8; 500];
775 let mut one = [0u8; 64];
776 CShake128::xof(b"KMAC", b"S", &data, &mut one);
777
778 let mut x = CShake128::new(b"KMAC", b"S");
779 for chunk in data.chunks(7) {
780 x.update(chunk);
781 }
782 let mut streamed = [0u8; 64];
783 x.finalize_xof(&mut streamed);
784 assert_eq!(one, streamed);
785 }
786}
787
788#[cfg(test)]
789mod tuple_parallel_tests {
790 use super::*;
791 use ic_core::traits::SelfTest;
792
793 fn enc(x: u64) -> Vec<u8> {
794 let mut bytes = x.to_be_bytes().to_vec();
795 while bytes.len() > 1 && bytes[0] == 0 {
796 bytes.remove(0);
797 }
798 let mut v = vec![bytes.len() as u8];
799 v.extend_from_slice(&bytes);
800 v
801 }
802
803 fn renc(x: u64) -> Vec<u8> {
804 let mut bytes = x.to_be_bytes().to_vec();
805 while bytes.len() > 1 && bytes[0] == 0 {
806 bytes.remove(0);
807 }
808 let n = bytes.len() as u8;
809 bytes.push(n);
810 bytes
811 }
812
813 fn reference_tuple_hash(
820 wide: bool,
821 custom: &[u8],
822 elements: &[&[u8]],
823 out: &mut [u8],
824 xof: bool,
825 ) {
826 let mut z = Vec::new();
827 for e in elements {
828 z.extend_from_slice(&enc((e.len() as u64) * 8));
829 z.extend_from_slice(e);
830 }
831 z.extend_from_slice(&renc(if xof { 0 } else { (out.len() as u64) * 8 }));
832 if wide {
833 CShake256::xof(b"TupleHash", custom, &z, out);
834 } else {
835 CShake128::xof(b"TupleHash", custom, &z, out);
836 }
837 }
838
839 fn reference_parallel_hash(
841 wide: bool,
842 custom: &[u8],
843 block_size: usize,
844 data: &[u8],
845 out: &mut [u8],
846 xof: bool,
847 ) {
848 let chain_len = if wide { 64 } else { 32 };
849 let mut z = enc(block_size as u64);
850 let mut n = 0u64;
851 for block in data.chunks(block_size) {
852 let mut chain = vec![0u8; chain_len];
853 if wide {
854 CShake256::xof(b"", b"", block, &mut chain);
855 } else {
856 CShake128::xof(b"", b"", block, &mut chain);
857 }
858 z.extend_from_slice(&chain);
859 n += 1;
860 }
861 z.extend_from_slice(&renc(n));
862 z.extend_from_slice(&renc(if xof { 0 } else { (out.len() as u64) * 8 }));
863 if wide {
864 CShake256::xof(b"ParallelHash", custom, &z, out);
865 } else {
866 CShake128::xof(b"ParallelHash", custom, &z, out);
867 }
868 }
869
870 #[test]
871 fn tuple_hash_matches_an_independent_construction() {
872 let cases: &[(&[u8], &[&[u8]])] = &[
873 (b"", &[]),
874 (b"", &[b"abc"]),
875 (b"My Tupled App", &[b"abc", b"d"]),
876 (b"", &[b"", b"", b""]),
877 (b"S", &[&[0x5au8; 300][..], b"x", &[0u8; 168][..]]),
878 ];
879
880 for (custom, elements) in cases {
881 for len in [16usize, 32, 64] {
882 let mut want = vec![0u8; len];
883 let mut got = vec![0u8; len];
884
885 reference_tuple_hash(false, custom, elements, &mut want, false);
886 TupleHash128::hash(custom, elements, &mut got);
887 assert_eq!(got, want, "TupleHash128 fixed, {len} bytes");
888
889 reference_tuple_hash(true, custom, elements, &mut want, false);
890 TupleHash256::hash(custom, elements, &mut got);
891 assert_eq!(got, want, "TupleHash256 fixed, {len} bytes");
892
893 reference_tuple_hash(false, custom, elements, &mut want, true);
894 TupleHash128::hash_xof(custom, elements, &mut got);
895 assert_eq!(got, want, "TupleHash128 xof, {len} bytes");
896 }
897 }
898 }
899
900 #[test]
903 fn tuples_that_concatenate_alike_hash_differently() {
904 let splits: &[&[&[u8]]] = &[
905 &[b"abc", b"d"],
906 &[b"ab", b"cd"],
907 &[b"a", b"bcd"],
908 &[b"abcd"],
909 &[b"abcd", b""],
910 &[b"", b"abcd"],
911 ];
912
913 let mut seen: Vec<[u8; 32]> = Vec::new();
914 for elements in splits {
915 let mut out = [0u8; 32];
916 TupleHash128::hash(b"", elements, &mut out);
917 assert!(
918 !seen.contains(&out),
919 "two different tuples collided: {elements:?}"
920 );
921 seen.push(out);
922 }
923 }
924
925 #[test]
926 fn tuple_hash_streams() {
927 let mut one = [0u8; 32];
928 TupleHash128::hash(b"S", &[b"alpha", b"beta", b"gamma"], &mut one);
929
930 let mut t = TupleHash128::new(b"S");
931 t.update(b"alpha");
932 t.update(b"beta");
933 t.update(b"gamma");
934 let mut streamed = [0u8; 32];
935 t.finalize(&mut streamed);
936 assert_eq!(one, streamed);
937 }
938
939 #[test]
940 fn parallel_hash_matches_an_independent_construction() {
941 let data = [0x37u8; 500];
942 for block_size in [1usize, 8, 32, 137, 500, 1024] {
943 for len in [16usize, 32, 64] {
944 let mut want = vec![0u8; len];
945 let mut got = vec![0u8; len];
946
947 reference_parallel_hash(false, b"S", block_size, &data, &mut want, false);
948 ParallelHash128::hash(b"S", block_size, &data, &mut got);
949 assert_eq!(got, want, "ParallelHash128 B={block_size}, {len} bytes");
950
951 reference_parallel_hash(true, b"", block_size, &data, &mut want, false);
952 ParallelHash256::hash(b"", block_size, &data, &mut got);
953 assert_eq!(got, want, "ParallelHash256 B={block_size}, {len} bytes");
954
955 reference_parallel_hash(false, b"S", block_size, &data, &mut want, true);
956 ParallelHash128::hash_xof(b"S", block_size, &data, &mut got);
957 assert_eq!(got, want, "ParallelHash128 xof B={block_size}");
958 }
959 }
960 }
961
962 #[test]
964 fn parallel_hash_handles_an_empty_input() {
965 let mut want = [0u8; 32];
966 let mut got = [0u8; 32];
967 reference_parallel_hash(false, b"", 64, b"", &mut want, false);
968 ParallelHash128::hash(b"", 64, b"", &mut got);
969 assert_eq!(got, want);
970 }
971
972 #[test]
974 fn the_block_size_changes_the_result() {
975 let data = [0xa1u8; 256];
976 let mut a = [0u8; 32];
977 let mut b = [0u8; 32];
978 ParallelHash128::hash(b"", 32, &data, &mut a);
979 ParallelHash128::hash(b"", 64, &data, &mut b);
980 assert_ne!(a, b, "B is bound into the output");
981 }
982
983 #[test]
984 fn output_length_is_bound_in_for_both() {
985 let mut short = [0u8; 32];
986 let mut long = [0u8; 64];
987 TupleHash128::hash(b"", &[b"x"], &mut short);
988 TupleHash128::hash(b"", &[b"x"], &mut long);
989 assert_ne!(short[..], long[..32], "TupleHash binds L");
990
991 ParallelHash128::hash(b"", 32, b"x", &mut short);
992 ParallelHash128::hash(b"", 32, b"x", &mut long);
993 assert_ne!(short[..], long[..32], "ParallelHash binds L");
994
995 TupleHash128::hash_xof(b"", &[b"x"], &mut short);
997 TupleHash128::hash_xof(b"", &[b"x"], &mut long);
998 assert_eq!(short[..], long[..32], "the xof form is a stream");
999 }
1000
1001 #[test]
1002 fn every_self_test_passes() {
1003 TupleHash128::self_test().unwrap();
1004 TupleHash256::self_test().unwrap();
1005 ParallelHash128::self_test().unwrap();
1006 ParallelHash256::self_test().unwrap();
1007 }
1008}