1const OUT_LEN: usize = 32;
31const BLOCK_LEN: usize = 64;
32const CHUNK_LEN: usize = 1024;
33
34const CHUNK_START: u32 = 1 << 0;
35const CHUNK_END: u32 = 1 << 1;
36const PARENT: u32 = 1 << 2;
37const ROOT: u32 = 1 << 3;
38
39const IV: [u32; 8] = [
40 0x6A09_E667,
41 0xBB67_AE85,
42 0x3C6E_F372,
43 0xA54F_F53A,
44 0x510E_527F,
45 0x9B05_688C,
46 0x1F83_D9AB,
47 0x5BE0_CD19,
48];
49
50const MSG_PERMUTATION: [usize; 16] = [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8];
51
52#[must_use]
57pub fn hash(input: &[u8]) -> [u8; OUT_LEN] {
58 let mut h = Hasher::new();
59 h.update(input);
60 h.finalize()
61}
62
63#[must_use]
66pub fn to_hex(bytes: &[u8]) -> String {
67 const DIGITS: &[u8; 16] = b"0123456789abcdef";
68 let mut s = String::with_capacity(bytes.len() * 2);
69 for b in bytes {
70 s.push(DIGITS[usize::from(b >> 4)] as char);
71 s.push(DIGITS[usize::from(b & 0x0f)] as char);
72 }
73 s
74}
75
76#[inline]
78fn g(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize, mx: u32, my: u32) {
79 state[a] = state[a].wrapping_add(state[b]).wrapping_add(mx);
80 state[d] = (state[d] ^ state[a]).rotate_right(16);
81 state[c] = state[c].wrapping_add(state[d]);
82 state[b] = (state[b] ^ state[c]).rotate_right(12);
83 state[a] = state[a].wrapping_add(state[b]).wrapping_add(my);
84 state[d] = (state[d] ^ state[a]).rotate_right(8);
85 state[c] = state[c].wrapping_add(state[d]);
86 state[b] = (state[b] ^ state[c]).rotate_right(7);
87}
88
89#[inline]
90fn round(state: &mut [u32; 16], m: &[u32; 16]) {
91 g(state, 0, 4, 8, 12, m[0], m[1]);
93 g(state, 1, 5, 9, 13, m[2], m[3]);
94 g(state, 2, 6, 10, 14, m[4], m[5]);
95 g(state, 3, 7, 11, 15, m[6], m[7]);
96 g(state, 0, 5, 10, 15, m[8], m[9]);
98 g(state, 1, 6, 11, 12, m[10], m[11]);
99 g(state, 2, 7, 8, 13, m[12], m[13]);
100 g(state, 3, 4, 9, 14, m[14], m[15]);
101}
102
103#[inline]
104fn permute(m: &mut [u32; 16]) {
105 let old = *m;
106 for (i, &p) in MSG_PERMUTATION.iter().enumerate() {
107 m[i] = old[p];
108 }
109}
110
111fn compress(
114 chaining_value: &[u32; 8],
115 block_words: &[u32; 16],
116 counter: u64,
117 block_len: u32,
118 flags: u32,
119) -> [u32; 16] {
120 let counter_low = counter as u32;
121 let counter_high = (counter >> 32) as u32;
122 let mut state = [
123 chaining_value[0],
124 chaining_value[1],
125 chaining_value[2],
126 chaining_value[3],
127 chaining_value[4],
128 chaining_value[5],
129 chaining_value[6],
130 chaining_value[7],
131 IV[0],
132 IV[1],
133 IV[2],
134 IV[3],
135 counter_low,
136 counter_high,
137 block_len,
138 flags,
139 ];
140 let mut block = *block_words;
141
142 for _ in 0..6 {
143 round(&mut state, &block);
144 permute(&mut block);
145 }
146 round(&mut state, &block);
147
148 for i in 0..8 {
149 state[i] ^= state[i + 8];
150 state[i + 8] ^= chaining_value[i];
151 }
152 state
153}
154
155fn first_8(state: [u32; 16]) -> [u32; 8] {
156 let mut cv = [0u32; 8];
157 cv.copy_from_slice(&state[..8]);
158 cv
159}
160
161fn words_from_le(block: &[u8; BLOCK_LEN]) -> [u32; 16] {
162 let mut words = [0u32; 16];
163 let (quads, _) = block.as_chunks::<4>();
164 for (word, quad) in words.iter_mut().zip(quads) {
165 *word = u32::from_le_bytes(*quad);
166 }
167 words
168}
169
170struct Output {
173 input_chaining_value: [u32; 8],
174 block_words: [u32; 16],
175 counter: u64,
176 block_len: u32,
177 flags: u32,
178}
179
180impl Output {
181 fn chaining_value(&self) -> [u32; 8] {
182 first_8(compress(
183 &self.input_chaining_value,
184 &self.block_words,
185 self.counter,
186 self.block_len,
187 self.flags,
188 ))
189 }
190
191 fn root_bytes(&self) -> [u8; OUT_LEN] {
192 let state = compress(
193 &self.input_chaining_value,
194 &self.block_words,
195 0,
196 self.block_len,
197 self.flags | ROOT,
198 );
199 let mut out = [0u8; OUT_LEN];
200 let (quads, _) = out.as_chunks_mut::<4>();
201 for (word, quad) in state[..8].iter().zip(quads) {
202 *quad = word.to_le_bytes();
203 }
204 out
205 }
206}
207
208struct ChunkState {
210 chaining_value: [u32; 8],
211 counter: u64,
212 block: [u8; BLOCK_LEN],
213 block_len: u8,
214 blocks_compressed: u8,
215}
216
217impl ChunkState {
218 fn new(counter: u64) -> ChunkState {
219 ChunkState {
220 chaining_value: IV,
221 counter,
222 block: [0; BLOCK_LEN],
223 block_len: 0,
224 blocks_compressed: 0,
225 }
226 }
227
228 fn len(&self) -> usize {
229 BLOCK_LEN * usize::from(self.blocks_compressed) + usize::from(self.block_len)
230 }
231
232 fn start_flag(&self) -> u32 {
233 if self.blocks_compressed == 0 {
234 CHUNK_START
235 } else {
236 0
237 }
238 }
239
240 fn update(&mut self, mut input: &[u8]) {
241 while !input.is_empty() {
242 if usize::from(self.block_len) == BLOCK_LEN {
243 let words = words_from_le(&self.block);
244 self.chaining_value = first_8(compress(
245 &self.chaining_value,
246 &words,
247 self.counter,
248 BLOCK_LEN as u32,
249 self.start_flag(),
250 ));
251 self.blocks_compressed += 1;
252 self.block = [0; BLOCK_LEN];
253 self.block_len = 0;
254 }
255
256 let want = BLOCK_LEN - usize::from(self.block_len);
257 let take = want.min(input.len());
258 let at = usize::from(self.block_len);
259 self.block[at..at + take].copy_from_slice(&input[..take]);
260 self.block_len += take as u8;
261 input = &input[take..];
262 }
263 }
264
265 fn output(&self) -> Output {
266 Output {
267 input_chaining_value: self.chaining_value,
268 block_words: words_from_le(&self.block),
269 counter: self.counter,
270 block_len: u32::from(self.block_len),
271 flags: self.start_flag() | CHUNK_END,
272 }
273 }
274}
275
276fn parent_output(left: [u32; 8], right: [u32; 8]) -> Output {
277 let mut block_words = [0u32; 16];
278 block_words[..8].copy_from_slice(&left);
279 block_words[8..].copy_from_slice(&right);
280 Output {
281 input_chaining_value: IV,
282 block_words,
283 counter: 0,
284 block_len: BLOCK_LEN as u32,
285 flags: PARENT,
286 }
287}
288
289pub struct Hasher {
297 chunk: ChunkState,
298 stack: [[u32; 8]; 54],
299 stack_len: u8,
300}
301
302impl Default for Hasher {
303 fn default() -> Hasher {
304 Hasher::new()
305 }
306}
307
308impl Hasher {
309 #[must_use]
311 pub fn new() -> Hasher {
312 Hasher {
313 chunk: ChunkState::new(0),
314 stack: [[0; 8]; 54],
315 stack_len: 0,
316 }
317 }
318
319 fn push(&mut self, cv: [u32; 8]) {
320 self.stack[usize::from(self.stack_len)] = cv;
321 self.stack_len += 1;
322 }
323
324 fn pop(&mut self) -> [u32; 8] {
325 self.stack_len -= 1;
326 self.stack[usize::from(self.stack_len)]
327 }
328
329 fn add_chunk(&mut self, mut cv: [u32; 8], total_chunks: u64) {
333 let mut chunks = total_chunks;
334 while chunks & 1 == 0 {
335 let left = self.pop();
336 cv = parent_output(left, cv).chaining_value();
337 chunks >>= 1;
338 }
339 self.push(cv);
340 }
341
342 pub fn update(&mut self, mut input: &[u8]) {
344 while !input.is_empty() {
345 if self.chunk.len() == CHUNK_LEN {
346 let cv = self.chunk.output().chaining_value();
347 let total = self.chunk.counter + 1;
348 self.add_chunk(cv, total);
349 self.chunk = ChunkState::new(total);
350 }
351
352 let want = CHUNK_LEN - self.chunk.len();
353 let take = want.min(input.len());
354 self.chunk.update(&input[..take]);
355 input = &input[take..];
356 }
357 }
358
359 #[must_use]
361 pub fn finalize(&self) -> [u8; OUT_LEN] {
362 let mut output = self.chunk.output();
363 let mut remaining = usize::from(self.stack_len);
364 while remaining > 0 {
365 remaining -= 1;
366 output = parent_output(self.stack[remaining], output.chaining_value());
367 }
368 output.root_bytes()
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use super::*;
375
376 fn pattern(len: usize) -> Vec<u8> {
380 (0..len).map(|i| (i % 251) as u8).collect()
381 }
382
383 const VECTORS: &[(usize, &str)] = &[
388 (
389 0,
390 "af1349b9f5f9a1a6a0404dea36dcc9499bcb25c9adc112b7cc9a93cae41f3262",
391 ),
392 (
393 1,
394 "2d3adedff11b61f14c886e35afa036736dcd87a74d27b5c1510225d0f592e213",
395 ),
396 (
397 2,
398 "7b7015bb92cf0b318037702a6cdd81dee41224f734684c2c122cd6359cb1ee63",
399 ),
400 (
401 3,
402 "e1be4d7a8ab5560aa4199eea339849ba8e293d55ca0a81006726d184519e647f",
403 ),
404 (
405 4,
406 "f30f5ab28fe047904037f77b6da4fea1e27241c5d132638d8bedce9d40494f32",
407 ),
408 (
409 5,
410 "b40b44dfd97e7a84a996a91af8b85188c66c126940ba7aad2e7ae6b385402aa2",
411 ),
412 (
413 6,
414 "06c4e8ffb6872fad96f9aaca5eee1553eb62aed0ad7198cef42e87f6a616c844",
415 ),
416 (
417 7,
418 "3f8770f387faad08faa9d8414e9f449ac68e6ff0417f673f602a646a891419fe",
419 ),
420 (
421 8,
422 "2351207d04fc16ade43ccab08600939c7c1fa70a5c0aaca76063d04c3228eaeb",
423 ),
424 (
425 63,
426 "e9bc37a594daad83be9470df7f7b3798297c3d834ce80ba85d6e207627b7db7b",
427 ),
428 (
429 64,
430 "4eed7141ea4a5cd4b788606bd23f46e212af9cacebacdc7d1f4c6dc7f2511b98",
431 ),
432 (
433 65,
434 "de1e5fa0be70df6d2be8fffd0e99ceaa8eb6e8c93a63f2d8d1c30ecb6b263dee",
435 ),
436 (
437 127,
438 "d81293fda863f008c09e92fc382a81f5a0b4a1251cba1634016a0f86a6bd640d",
439 ),
440 (
441 128,
442 "f17e570564b26578c33bb7f44643f539624b05df1a76c81f30acd548c44b45ef",
443 ),
444 (
445 129,
446 "683aaae9f3c5ba37eaaf072aed0f9e30bac0865137bae68b1fde4ca2aebdcb12",
447 ),
448 (
449 1023,
450 "10108970eeda3eb932baac1428c7a2163b0e924c9a9e25b35bba72b28f70bd11",
451 ),
452 (
453 1024,
454 "42214739f095a406f3fc83deb889744ac00df831c10daa55189b5d121c855af7",
455 ),
456 (
457 1025,
458 "d00278ae47eb27b34faecf67b4fe263f82d5412916c1ffd97c8cb7fb814b8444",
459 ),
460 (
461 2048,
462 "e776b6028c7cd22a4d0ba182a8bf62205d2ef576467e838ed6f2529b85fba24a",
463 ),
464 (
465 2049,
466 "5f4d72f40d7a5f82b15ca2b2e44b1de3c2ef86c426c95c1af0b6879522563030",
467 ),
468 (
469 3072,
470 "b98cb0ff3623be03326b373de6b9095218513e64f1ee2edd2525c7ad1e5cffd2",
471 ),
472 (
473 3073,
474 "7124b49501012f81cc7f11ca069ec9226cecb8a2c850cfe644e327d22d3e1cd3",
475 ),
476 (
477 4096,
478 "015094013f57a5277b59d8475c0501042c0b642e531b0a1c8f58d2163229e969",
479 ),
480 (
481 4097,
482 "9b4052b38f1c5fc8b1f9ff7ac7b27cd242487b3d890d15c96a1c25b8aa0fb995",
483 ),
484 (
485 5120,
486 "9cadc15fed8b5d854562b26a9536d9707cadeda9b143978f319ab34230535833",
487 ),
488 (
489 5121,
490 "628bd2cb2004694adaab7bbd778a25df25c47b9d4155a55f8fbd79f2fe154cff",
491 ),
492 (
493 6144,
494 "3e2e5b74e048f3add6d21faab3f83aa44d3b2278afb83b80b3c35164ebeca205",
495 ),
496 (
497 6145,
498 "f1323a8631446cc50536a9f705ee5cb619424d46887f3c376c695b70e0f0507f",
499 ),
500 (
501 7168,
502 "61da957ec2499a95d6b8023e2b0e604ec7f6b50e80a9678b89d2628e99ada77a",
503 ),
504 (
505 7169,
506 "a003fc7a51754a9b3c7fae0367ab3d782dccf28855a03d435f8cfe74605e7817",
507 ),
508 (
509 8192,
510 "aae792484c8efe4f19e2ca7d371d8c467ffb10748d8a5a1ae579948f718a2a63",
511 ),
512 (
513 8193,
514 "bab6c09cb8ce8cf459261398d2e7aef35700bf488116ceb94a36d0f5f1b7bc3b",
515 ),
516 (
517 16_384,
518 "f875d6646de28985646f34ee13be9a576fd515f76b5b0a26bb324735041ddde4",
519 ),
520 (
521 31_744,
522 "62b6960e1a44bcc1eb1a611a8d6235b6b4b78f32e7abc4fb4c6cdcce94895c47",
523 ),
524 (
525 102_400,
526 "bc3e3d41a1146b069abffad3c0d44860cf664390afce4d9661f7902e7943e085",
527 ),
528 ];
529
530 #[test]
531 fn the_official_vectors_pass() {
532 for &(len, want) in VECTORS {
533 let got = to_hex(&hash(&pattern(len)));
534 assert_eq!(got, want, "length {len}");
535 }
536 }
537
538 #[test]
542 fn the_split_does_not_matter() {
543 let input = pattern(4097);
544 let want = hash(&input);
545 for step in [1usize, 7, 63, 64, 65, 1023, 1024, 1025, 1500] {
546 let mut h = Hasher::new();
547 for piece in input.chunks(step) {
548 h.update(piece);
549 }
550 assert_eq!(h.finalize(), want, "step {step}");
551 }
552 }
553
554 #[test]
556 fn empty_updates_are_free() {
557 let mut h = Hasher::new();
558 h.update(b"");
559 h.update(b"yo");
560 h.update(b"");
561 assert_eq!(h.finalize(), hash(b"yo"));
562 }
563
564 #[test]
565 fn hex_is_lower_case_and_padded() {
566 assert_eq!(to_hex(&[0x00, 0x0f, 0xa0, 0xff]), "000fa0ff");
567 assert_eq!(to_hex(&[]), "");
568 }
569}