|
| 1 | +use std::hash::Hasher; |
| 2 | + |
1 | 3 | use arbitrary::Arbitrary; |
2 | 4 | use get_size2::GetSize; |
3 | 5 | use itertools::Itertools; |
@@ -673,10 +675,35 @@ impl Sponge for Tip5 { |
673 | 675 | } |
674 | 676 | } |
675 | 677 |
|
| 678 | +impl Hasher for Tip5 { |
| 679 | + fn finish(&self) -> u64 { |
| 680 | + self.state[0].value() |
| 681 | + } |
| 682 | + |
| 683 | + fn write(&mut self, bytes: &[u8]) { |
| 684 | + let bfield_elements = bytes.chunks(BFieldElement::BYTES).map(|chunk| { |
| 685 | + let mut buffer = [0u8; BFieldElement::BYTES]; |
| 686 | + buffer[..chunk.len()].copy_from_slice(chunk); |
| 687 | + BFieldElement::new(u64::from_le_bytes(buffer)) |
| 688 | + }); |
| 689 | + |
| 690 | + for chunk in bfield_elements.chunks(Tip5::RATE).into_iter() { |
| 691 | + let mut buffer = [BFieldElement::ZERO; Tip5::RATE]; |
| 692 | + for (buffer_elem, chunk_elem) in buffer.iter_mut().zip(chunk) { |
| 693 | + *buffer_elem = chunk_elem; |
| 694 | + } |
| 695 | + self.absorb(buffer) |
| 696 | + } |
| 697 | + } |
| 698 | +} |
| 699 | + |
676 | 700 | #[cfg(test)] |
677 | 701 | pub(crate) mod tip5_tests { |
| 702 | + use std::hash::Hash; |
678 | 703 | use std::ops::Mul; |
679 | 704 |
|
| 705 | + use insta::assert_snapshot; |
| 706 | + use prop::sample::size_range; |
680 | 707 | use proptest::prelude::*; |
681 | 708 | use proptest_arbitrary_interop::arb; |
682 | 709 | use rand::thread_rng; |
@@ -1087,6 +1114,38 @@ pub(crate) mod tip5_tests { |
1087 | 1114 | ); |
1088 | 1115 | } |
1089 | 1116 |
|
| 1117 | + #[test] |
| 1118 | + fn tip5_hasher_trait_test() { |
| 1119 | + let mut hasher = Tip5::init(); |
| 1120 | + let data = b"hello world"; |
| 1121 | + hasher.write(data); |
| 1122 | + assert_snapshot!(hasher.finish(), @"2267905471610932299"); |
| 1123 | + } |
| 1124 | + |
| 1125 | + #[proptest] |
| 1126 | + fn tip5_hasher_consumes_small_data(#[filter(!#bytes.is_empty())] bytes: Vec<u8>) { |
| 1127 | + let mut hasher = Tip5::init(); |
| 1128 | + bytes.hash(&mut hasher); |
| 1129 | + |
| 1130 | + prop_assert_ne!(Tip5::init().finish(), hasher.finish()); |
| 1131 | + } |
| 1132 | + |
| 1133 | + #[proptest] |
| 1134 | + fn appending_small_data_to_big_data_changes_tip5_hash( |
| 1135 | + #[any(size_range(2_000..8_000).lift())] big_data: Vec<u8>, |
| 1136 | + #[filter(!#small_data.is_empty())] small_data: Vec<u8>, |
| 1137 | + ) { |
| 1138 | + let mut hasher = Tip5::init(); |
| 1139 | + big_data.hash(&mut hasher); |
| 1140 | + let big_data_hash = hasher.finish(); |
| 1141 | + |
| 1142 | + // finish doesn't terminate the hasher; see it's documentation |
| 1143 | + small_data.hash(&mut hasher); |
| 1144 | + let all_data_hash = hasher.finish(); |
| 1145 | + |
| 1146 | + prop_assert_ne!(big_data_hash, all_data_hash); |
| 1147 | + } |
| 1148 | + |
1090 | 1149 | #[proptest] |
1091 | 1150 | fn tip5_trace_starts_with_initial_state_and_is_equivalent_to_permutation( |
1092 | 1151 | #[strategy(arb())] mut tip5: Tip5, |
|
0 commit comments