1use tiny_keccak::{Hasher as TinyHasher, Keccak};
4
5use crate::hash::{BuildHasher, Hash, Hasher};
6
7pub struct KeccakBuilder;
12
13impl BuildHasher for KeccakBuilder {
14 type Hasher = Keccak256;
15
16 #[inline]
17 fn build_hasher(&self) -> Self::Hasher {
18 Keccak256(Keccak::v256())
19 }
20}
21
22pub struct Keccak256(Keccak);
27
28impl Hasher for Keccak256 {
29 type Output = [u8; 32];
30
31 fn update(&mut self, input: impl AsRef<[u8]>) {
32 self.0.update(input.as_ref());
33 }
34
35 fn finalize(self) -> Self::Output {
36 let mut buffer = [0u8; 32];
37 self.0.finalize(&mut buffer);
38 buffer
39 }
40}
41
42impl Hash for [u8; 32] {
43 #[inline]
44 fn hash<H: Hasher>(&self, state: &mut H) {
45 state.update(self);
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use proptest::prelude::*;
52
53 use super::*;
54 use crate::test_helpers::non_empty_u8_vec_strategy;
55
56 #[test]
57 fn single_bit_change_affects_output() {
58 proptest!(|(data in non_empty_u8_vec_strategy())| {
59 let mut modified = data.clone();
60 modified[0] ^= 1;
61
62 let mut hasher1 = KeccakBuilder.build_hasher();
63 let mut hasher2 = KeccakBuilder.build_hasher();
64 hasher1.update(&data);
65 hasher2.update(&modified);
66
67 prop_assert_ne!(hasher1.finalize(), hasher2.finalize());
68 });
69 }
70
71 #[test]
72 fn sequential_updates_match_concatenated() {
73 proptest!(|(data1: Vec<u8>, data2: Vec<u8>)| {
74 let builder = KeccakBuilder;
75
76 let mut hasher1 = builder.build_hasher();
77 hasher1.update(&data1);
78 hasher1.update(&data2);
79 let result1 = hasher1.finalize();
80
81 let mut hasher2 = builder.build_hasher();
82 let mut concatenated = data1.clone();
83 concatenated.extend_from_slice(&data2);
84 hasher2.update(concatenated);
85 let result2 = hasher2.finalize();
86
87 prop_assert_eq!(result1, result2);
88 });
89 }
90
91 #[test]
92 fn split_updates_match_full_update() {
93 proptest!(|(data in non_empty_u8_vec_strategy(), split_point: usize)| {
94 let builder = KeccakBuilder;
95 let split_at = split_point % data.len();
96
97 let mut hasher1 = builder.build_hasher();
98 hasher1.update(&data[..split_at]);
99 hasher1.update(&data[split_at..]);
100 let result1 = hasher1.finalize();
101
102 let mut hasher2 = builder.build_hasher();
103 hasher2.update(&data);
104 let result2 = hasher2.finalize();
105
106 prop_assert_eq!(result1, result2);
107 });
108 }
109
110 #[test]
111 fn multiple_hasher_instances_are_consistent() {
112 proptest!(|(data1: Vec<u8>, data2: Vec<u8>)| {
113 let builder = KeccakBuilder;
114
115 let mut hasher1 = builder.build_hasher();
116 hasher1.update(&data1);
117 hasher1.update(&data2);
118 let result1 = hasher1.finalize();
119
120 let mut hasher2 = builder.build_hasher();
121 hasher2.update(&data1);
122 hasher2.update(&data2);
123 let result2 = hasher2.finalize();
124
125 prop_assert_eq!(result1, result2);
126 });
127 }
128
129 #[test]
130 fn output_is_always_32_bytes() {
131 proptest!(|(data: Vec<u8>)| {
132 let builder = KeccakBuilder;
133 let mut hasher = builder.build_hasher();
134 hasher.update(&data);
135 let result = hasher.finalize();
136 assert_eq!(result.len(), 32);
137 });
138 }
139
140 #[test]
141 fn update_order_dependence() {
142 proptest!(|(data1 in non_empty_u8_vec_strategy(),
143 data2 in non_empty_u8_vec_strategy())| {
144 prop_assume!(data1 != data2);
145
146 let mut hasher1 = KeccakBuilder.build_hasher();
147 hasher1.update(&data1);
148 hasher1.update(&data2);
149
150 let mut hasher2 = KeccakBuilder.build_hasher();
151 hasher2.update(&data2);
152 hasher2.update(&data1);
153
154 prop_assert_ne!(hasher1.finalize(), hasher2.finalize());
155 });
156 }
157
158 #[test]
159 fn empty_input_order_independence() {
160 proptest!(|(data in non_empty_u8_vec_strategy())| {
161 let empty = vec![];
162
163 let mut hasher1 = KeccakBuilder.build_hasher();
164 hasher1.update(&data);
165 hasher1.update(&empty);
166
167 let mut hasher2 = KeccakBuilder.build_hasher();
168 hasher2.update(&empty);
169 hasher2.update(&data);
170
171 prop_assert_eq!(hasher1.finalize(), hasher2.finalize());
172 });
173 }
174
175 #[test]
176 fn trailing_zero_affects_output() {
177 proptest!(|(data: Vec<u8>)| {
178 let mut hasher1 = KeccakBuilder.build_hasher();
179 hasher1.update(&data);
180
181 let mut padded = data.clone();
182 padded.push(0);
183
184 let mut hasher2 = KeccakBuilder.build_hasher();
185 hasher2.update(&padded);
186
187 prop_assert_ne!(hasher1.finalize(), hasher2.finalize());
188 });
189 }
190
191 #[test]
192 fn leading_zeros_affect_output() {
193 proptest!(|(data in non_empty_u8_vec_strategy())| {
194 let mut hasher1 = KeccakBuilder.build_hasher();
195 hasher1.update(&data);
196 let hash1 = hasher1.finalize();
197
198 let mut padded = vec![0u8; 32];
199 padded.extend(data.iter());
200
201 let mut hasher2 = KeccakBuilder.build_hasher();
202 hasher2.update(&padded);
203 let hash2 = hasher2.finalize();
204
205 prop_assert_ne!(hash1, hash2);
206 });
207 }
208
209 #[test]
210 fn no_trivial_collisions_same_length() {
211 proptest!(|(data in non_empty_u8_vec_strategy())| {
212 let mut hasher1 = KeccakBuilder.build_hasher();
213 hasher1.update(&data);
214
215 let mut modified = data.clone();
216 modified[data.len() - 1] = modified[data.len() - 1].wrapping_add(1);
217
218 let mut hasher2 = KeccakBuilder.build_hasher();
219 hasher2.update(&modified);
220
221 prop_assert_ne!(hasher1.finalize(), hasher2.finalize());
222 });
223 }
224
225 #[test]
226 fn length_extension_attack_resistance() {
227 proptest!(|(data1 in non_empty_u8_vec_strategy(), data2 in non_empty_u8_vec_strategy())| {
228 let mut hasher1 = KeccakBuilder.build_hasher();
229 hasher1.update(&data1);
230 let hash1 = hasher1.finalize();
231
232 let mut hasher2 = KeccakBuilder.build_hasher();
233 hasher2.update(&data1);
234 hasher2.update(&data2);
235 let hash2 = hasher2.finalize();
236
237 let mut hasher3 = KeccakBuilder.build_hasher();
238 hasher3.update(hash1);
239 hasher3.update(&data2);
240 let hash3 = hasher3.finalize();
241
242 prop_assert_ne!(hash2, hash3);
243 });
244 }
245
246 #[test]
247 fn empty_input() {
248 let builder = KeccakBuilder;
249 let mut hasher = builder.build_hasher();
250 hasher.update([]);
251 let result = hasher.finalize();
252 let expected: [u8; 32] = [
253 0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d,
254 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82,
255 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70,
256 ];
257 assert_eq!(result, expected);
258 }
259
260 #[test]
261 fn known_hash() {
262 let builder = KeccakBuilder;
263 let mut hasher = builder.build_hasher();
264 hasher.update(b"hello");
265 let result = hasher.finalize();
266 let expected: [u8; 32] = [
267 0x1c, 0x8a, 0xff, 0x95, 0x06, 0x85, 0xc2, 0xed, 0x4b, 0xc3, 0x17,
268 0x4f, 0x34, 0x72, 0x28, 0x7b, 0x56, 0xd9, 0x51, 0x7b, 0x9c, 0x94,
269 0x81, 0x27, 0x31, 0x9a, 0x09, 0xa7, 0xa3, 0x6d, 0xea, 0xc8,
270 ];
271 assert_eq!(result, expected);
272 }
273}