|
| 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