1#![allow(clippy::needless_range_loop)]
10
11use ic_core::traits::{Algorithm, Digest, SelfTest};
12use ic_core::{ensure, Result, Zeroize};
13
14#[cfg(all(target_arch = "x86_64", feature = "std"))]
18mod x86;
19
20#[cfg(all(target_arch = "x86_64", feature = "std"))]
26fn sha_ni() -> bool {
27 use core::sync::atomic::{AtomicU8, Ordering};
28 static CACHED: AtomicU8 = AtomicU8::new(0);
30 match CACHED.load(Ordering::Relaxed) {
31 1 => true,
32 2 => false,
33 _ => {
34 let have = std::is_x86_feature_detected!("sha")
35 && std::is_x86_feature_detected!("sse2")
36 && std::is_x86_feature_detected!("ssse3")
37 && std::is_x86_feature_detected!("sse4.1");
38 CACHED.store(u8::from(!have) + 1, Ordering::Relaxed);
39 have
40 }
41 }
42}
43
44const K256: [u32; 64] = [
45 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
46 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
47 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
48 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
49 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
50 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
51 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
52 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
53];
54
55const K512: [u64; 80] = [
56 0x428a2f98d728ae22,
57 0x7137449123ef65cd,
58 0xb5c0fbcfec4d3b2f,
59 0xe9b5dba58189dbbc,
60 0x3956c25bf348b538,
61 0x59f111f1b605d019,
62 0x923f82a4af194f9b,
63 0xab1c5ed5da6d8118,
64 0xd807aa98a3030242,
65 0x12835b0145706fbe,
66 0x243185be4ee4b28c,
67 0x550c7dc3d5ffb4e2,
68 0x72be5d74f27b896f,
69 0x80deb1fe3b1696b1,
70 0x9bdc06a725c71235,
71 0xc19bf174cf692694,
72 0xe49b69c19ef14ad2,
73 0xefbe4786384f25e3,
74 0x0fc19dc68b8cd5b5,
75 0x240ca1cc77ac9c65,
76 0x2de92c6f592b0275,
77 0x4a7484aa6ea6e483,
78 0x5cb0a9dcbd41fbd4,
79 0x76f988da831153b5,
80 0x983e5152ee66dfab,
81 0xa831c66d2db43210,
82 0xb00327c898fb213f,
83 0xbf597fc7beef0ee4,
84 0xc6e00bf33da88fc2,
85 0xd5a79147930aa725,
86 0x06ca6351e003826f,
87 0x142929670a0e6e70,
88 0x27b70a8546d22ffc,
89 0x2e1b21385c26c926,
90 0x4d2c6dfc5ac42aed,
91 0x53380d139d95b3df,
92 0x650a73548baf63de,
93 0x766a0abb3c77b2a8,
94 0x81c2c92e47edaee6,
95 0x92722c851482353b,
96 0xa2bfe8a14cf10364,
97 0xa81a664bbc423001,
98 0xc24b8b70d0f89791,
99 0xc76c51a30654be30,
100 0xd192e819d6ef5218,
101 0xd69906245565a910,
102 0xf40e35855771202a,
103 0x106aa07032bbd1b8,
104 0x19a4c116b8d2d0c8,
105 0x1e376c085141ab53,
106 0x2748774cdf8eeb99,
107 0x34b0bcb5e19b48a8,
108 0x391c0cb3c5c95a63,
109 0x4ed8aa4ae3418acb,
110 0x5b9cca4f7763e373,
111 0x682e6ff3d6b2b8a3,
112 0x748f82ee5defb2fc,
113 0x78a5636f43172f60,
114 0x84c87814a1f0ab72,
115 0x8cc702081a6439ec,
116 0x90befffa23631e28,
117 0xa4506cebde82bde9,
118 0xbef9a3f7b2c67915,
119 0xc67178f2e372532b,
120 0xca273eceea26619c,
121 0xd186b8c721c0c207,
122 0xeada7dd6cde0eb1e,
123 0xf57d4f7fee6ed178,
124 0x06f067aa72176fba,
125 0x0a637dc5a2c898a6,
126 0x113f9804bef90dae,
127 0x1b710b35131c471b,
128 0x28db77f523047d84,
129 0x32caab7b40c72493,
130 0x3c9ebe0a15c9bebc,
131 0x431d67c49c100d4c,
132 0x4cc5d4becb3e42b6,
133 0x597f299cfc657e2a,
134 0x5fcb6fab3ad6faec,
135 0x6c44198c4a475817,
136];
137
138#[derive(Clone)]
140struct Core256 {
141 h: [u32; 8],
142 buf: [u8; 64],
143 buffered: usize,
144 len: u64,
145}
146
147impl Drop for Core256 {
148 fn drop(&mut self) {
157 self.h.zeroize();
158 self.buf.zeroize();
159 self.buffered = 0;
160 self.len = 0;
161 }
162}
163
164impl Core256 {
165 const fn new(iv: [u32; 8]) -> Self {
166 Self {
167 h: iv,
168 buf: [0u8; 64],
169 buffered: 0,
170 len: 0,
171 }
172 }
173
174 fn compress(&mut self, block: &[u8]) {
175 let mut w = [0u32; 64];
176 for i in 0..16 {
177 w[i] = u32::from_be_bytes([
178 block[i * 4],
179 block[i * 4 + 1],
180 block[i * 4 + 2],
181 block[i * 4 + 3],
182 ]);
183 }
184 for i in 16..64 {
185 let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
186 let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
187 w[i] = w[i - 16]
188 .wrapping_add(s0)
189 .wrapping_add(w[i - 7])
190 .wrapping_add(s1);
191 }
192 let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] = self.h;
193 for i in 0..64 {
194 let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
195 let ch = (e & f) ^ ((!e) & g);
196 let t1 = hh
197 .wrapping_add(s1)
198 .wrapping_add(ch)
199 .wrapping_add(K256[i])
200 .wrapping_add(w[i]);
201 let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
202 let maj = (a & b) ^ (a & c) ^ (b & c);
203 let t2 = s0.wrapping_add(maj);
204 hh = g;
205 g = f;
206 f = e;
207 e = d.wrapping_add(t1);
208 d = c;
209 c = b;
210 b = a;
211 a = t1.wrapping_add(t2);
212 }
213 let upd = [a, b, c, d, e, f, g, hh];
214 for i in 0..8 {
215 self.h[i] = self.h[i].wrapping_add(upd[i]);
216 }
217 w.zeroize();
218 }
219
220 fn compress_blocks(&mut self, data: &[u8]) {
227 debug_assert!(data.len() % 64 == 0);
228 if data.is_empty() {
229 return;
230 }
231 #[cfg(all(target_arch = "x86_64", feature = "std"))]
232 if sha_ni() {
233 unsafe { x86::compress(&mut self.h, data) };
236 return;
237 }
238 for block in data.chunks_exact(64) {
239 self.compress(block);
240 }
241 }
242
243 fn update(&mut self, mut data: &[u8]) {
244 self.len = self.len.wrapping_add(data.len() as u64);
245 if self.buffered > 0 {
246 let need = 64 - self.buffered;
247 let take = core::cmp::min(need, data.len());
248 self.buf[self.buffered..self.buffered + take].copy_from_slice(&data[..take]);
249 self.buffered += take;
250 data = &data[take..];
251 if self.buffered < 64 {
252 return;
254 }
255 let block = self.buf;
256 self.compress(&block);
257 self.buffered = 0;
258 }
259 let whole = data.len() - data.len() % 64;
260 self.compress_blocks(&data[..whole]);
261 let rest = &data[whole..];
262 self.buf[..rest.len()].copy_from_slice(rest);
263 self.buffered = rest.len();
264 }
265
266 fn finalize(mut self) -> [u32; 8] {
267 let bit_len = self.len.wrapping_mul(8);
268 let mut pad = [0u8; 72];
269 pad[0] = 0x80;
270 let zeros = (55 + 64 - (self.buffered % 64)) % 64;
272 pad[1 + zeros..1 + zeros + 8].copy_from_slice(&bit_len.to_be_bytes());
273 self.update_no_count(&pad[..1 + zeros + 8]);
274 let out = self.h;
275 self.buf.zeroize();
276 self.h.zeroize();
277 out
278 }
279
280 fn update_no_count(&mut self, data: &[u8]) {
282 let saved = self.len;
283 self.update(data);
284 self.len = saved;
285 }
286}
287
288#[derive(Clone)]
290struct Core512 {
291 h: [u64; 8],
292 buf: [u8; 128],
293 buffered: usize,
294 len: u128,
295}
296
297impl Drop for Core512 {
298 fn drop(&mut self) {
300 self.h.zeroize();
301 self.buf.zeroize();
302 self.buffered = 0;
303 self.len = 0;
304 }
305}
306
307impl Core512 {
332 const fn new(iv: [u64; 8]) -> Self {
333 Self {
334 h: iv,
335 buf: [0u8; 128],
336 buffered: 0,
337 len: 0,
338 }
339 }
340
341 fn compress(&mut self, block: &[u8]) {
342 let mut w = [0u64; 80];
343 for i in 0..16 {
344 let mut b = [0u8; 8];
345 b.copy_from_slice(&block[i * 8..i * 8 + 8]);
346 w[i] = u64::from_be_bytes(b);
347 }
348 for i in 16..80 {
349 let s0 = w[i - 15].rotate_right(1) ^ w[i - 15].rotate_right(8) ^ (w[i - 15] >> 7);
350 let s1 = w[i - 2].rotate_right(19) ^ w[i - 2].rotate_right(61) ^ (w[i - 2] >> 6);
351 w[i] = w[i - 16]
352 .wrapping_add(s0)
353 .wrapping_add(w[i - 7])
354 .wrapping_add(s1);
355 }
356 let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] = self.h;
357 for i in 0..80 {
358 let s1 = e.rotate_right(14) ^ e.rotate_right(18) ^ e.rotate_right(41);
359 let ch = (e & f) ^ ((!e) & g);
360 let t1 = hh
361 .wrapping_add(s1)
362 .wrapping_add(ch)
363 .wrapping_add(K512[i])
364 .wrapping_add(w[i]);
365 let s0 = a.rotate_right(28) ^ a.rotate_right(34) ^ a.rotate_right(39);
366 let maj = (a & b) ^ (a & c) ^ (b & c);
367 let t2 = s0.wrapping_add(maj);
368 hh = g;
369 g = f;
370 f = e;
371 e = d.wrapping_add(t1);
372 d = c;
373 c = b;
374 b = a;
375 a = t1.wrapping_add(t2);
376 }
377 let upd = [a, b, c, d, e, f, g, hh];
378 for i in 0..8 {
379 self.h[i] = self.h[i].wrapping_add(upd[i]);
380 }
381 w.zeroize();
382 }
383
384 fn update(&mut self, mut data: &[u8]) {
385 self.len = self.len.wrapping_add(data.len() as u128);
386 if self.buffered > 0 {
387 let need = 128 - self.buffered;
388 let take = core::cmp::min(need, data.len());
389 self.buf[self.buffered..self.buffered + take].copy_from_slice(&data[..take]);
390 self.buffered += take;
391 data = &data[take..];
392 if self.buffered < 128 {
393 return;
395 }
396 let block = self.buf;
397 self.compress(&block);
398 self.buffered = 0;
399 }
400 let mut chunks = data.chunks_exact(128);
401 for block in &mut chunks {
402 self.compress(block);
403 }
404 let rest = chunks.remainder();
405 self.buf[..rest.len()].copy_from_slice(rest);
406 self.buffered = rest.len();
407 }
408
409 fn finalize(mut self) -> [u64; 8] {
410 let bit_len = self.len.wrapping_mul(8);
411 let mut pad = [0u8; 145];
412 pad[0] = 0x80;
413 let zeros = (111 + 128 - (self.buffered % 128)) % 128;
414 pad[1 + zeros..1 + zeros + 16].copy_from_slice(&bit_len.to_be_bytes());
415 let saved = self.len;
416 self.update(&pad[..1 + zeros + 16]);
417 self.len = saved;
418 let out = self.h;
419 self.buf.zeroize();
420 self.h.zeroize();
421 out
422 }
423}
424
425macro_rules! sha2_32 {
426 ($name:ident, $id:literal, $disp:literal, $out:literal, $iv:expr, $kat:literal) => {
427 #[doc = concat!("FIPS 180-4 ", $disp, ".")]
428 #[derive(Clone)]
429 pub struct $name(Core256);
430
431 impl Default for $name {
432 fn default() -> Self {
433 Self(Core256::new($iv))
434 }
435 }
436
437 impl Algorithm for $name {
438 const ID: &'static str = $id;
439 const NAME: &'static str = $disp;
440 }
441
442 impl Digest for $name {
443 type Output = [u8; $out];
444 const OUTPUT_LEN: usize = $out;
445 const BLOCK_LEN: usize = 64;
446
447 fn update(&mut self, data: &[u8]) {
448 self.0.update(data);
449 }
450
451 fn finalize(self) -> Self::Output {
452 let h = self.0.finalize();
453 let mut full = [0u8; 32];
454 for i in 0..8 {
455 full[i * 4..i * 4 + 4].copy_from_slice(&h[i].to_be_bytes());
456 }
457 let mut out = [0u8; $out];
458 out.copy_from_slice(&full[..$out]);
459 out
460 }
461 }
462
463 impl SelfTest for $name {
464 fn self_test() -> Result<()> {
465 let got = <Self as Digest>::digest(b"abc");
466 let mut want = [0u8; $out];
467 ic_core::codec::hex_decode($kat.as_bytes(), &mut want)?;
468 ensure!(
469 ic_core::ct::verify(&want, got.as_ref()),
470 SelfTestFailed,
471 $id
472 );
473 Ok(())
474 }
475 }
476 };
477}
478
479macro_rules! sha2_64 {
480 ($name:ident, $id:literal, $disp:literal, $out:literal, $iv:expr, $kat:literal) => {
481 #[doc = concat!("FIPS 180-4 ", $disp, ".")]
482 #[derive(Clone)]
483 pub struct $name(Core512);
484
485 impl Default for $name {
486 fn default() -> Self {
487 Self(Core512::new($iv))
488 }
489 }
490
491 impl Algorithm for $name {
492 const ID: &'static str = $id;
493 const NAME: &'static str = $disp;
494 }
495
496 impl Digest for $name {
497 type Output = [u8; $out];
498 const OUTPUT_LEN: usize = $out;
499 const BLOCK_LEN: usize = 128;
500
501 fn update(&mut self, data: &[u8]) {
502 self.0.update(data);
503 }
504
505 fn finalize(self) -> Self::Output {
506 let h = self.0.finalize();
507 let mut full = [0u8; 64];
508 for i in 0..8 {
509 full[i * 8..i * 8 + 8].copy_from_slice(&h[i].to_be_bytes());
510 }
511 let mut out = [0u8; $out];
512 out.copy_from_slice(&full[..$out]);
513 out
514 }
515 }
516
517 impl SelfTest for $name {
518 fn self_test() -> Result<()> {
519 let got = <Self as Digest>::digest(b"abc");
520 let mut want = [0u8; $out];
521 ic_core::codec::hex_decode($kat.as_bytes(), &mut want)?;
522 ensure!(
523 ic_core::ct::verify(&want, got.as_ref()),
524 SelfTestFailed,
525 $id
526 );
527 Ok(())
528 }
529 }
530 };
531}
532
533sha2_32!(
534 Sha224,
535 "sha2-224",
536 "SHA-224",
537 28,
538 [
539 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7,
540 0xbefa4fa4
541 ],
542 "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"
543);
544
545sha2_32!(
546 Sha256,
547 "sha2-256",
548 "SHA-256",
549 32,
550 [
551 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
552 0x5be0cd19
553 ],
554 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
555);
556
557sha2_64!(
558 Sha384,
559 "sha2-384",
560 "SHA-384",
561 48,
562 [
563 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939,
564 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4
565 ],
566 "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"
567);
568
569sha2_64!(
570 Sha512,
571 "sha2-512",
572 "SHA-512",
573 64,
574 [
575 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
576 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
577 ],
578 "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
579);
580
581sha2_64!(
582 Sha512_224,
583 "sha2-512-224",
584 "SHA-512/224",
585 28,
586 [
587 0x8c3d37c819544da2,
588 0x73e1996689dcd4d6,
589 0x1dfab7ae32ff9c82,
590 0x679dd514582f9fcf,
591 0x0f6d2b697bd44da8,
592 0x77e36f7304c48942,
593 0x3f9d85a86a1d36c8,
594 0x1112e6ad91d692a1
595 ],
596 "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"
597);
598
599sha2_64!(
600 Sha512_256,
601 "sha2-512-256",
602 "SHA-512/256",
603 32,
604 [
605 0x22312194fc2bf72c,
606 0x9f555fa3c84c64c2,
607 0x2393b86b6f53b151,
608 0x963877195940eabd,
609 0x96283ee2a88effe3,
610 0xbe5e1e2553863992,
611 0x2b0199fc2c85b8aa,
612 0x0eb72ddc81c52ca2
613 ],
614 "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"
615);
616
617#[cfg(test)]
618mod tests {
619 use super::*;
620
621 #[cfg(all(target_arch = "x86_64", feature = "std"))]
633 #[test]
634 fn the_sha_ni_backend_agrees_with_the_portable_one() {
635 if !sha_ni() {
636 println!("no SHA-NI on this CPU; the backend was not exercised");
637 return;
638 }
639
640 let mut checked = 0;
643 for blocks in [1usize, 2, 3, 4, 7, 16] {
644 let mut data = vec![0u8; blocks * 64];
645 for (i, b) in data.iter_mut().enumerate() {
648 *b = ((i as u64).wrapping_mul(0x9e37_79b9).rotate_left(7) & 0xff) as u8;
649 }
650
651 const IV: [u32; 8] = [
653 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab,
654 0x5be0cd19,
655 ];
656 let mut portable = Core256::new(IV);
657 for block in data.chunks_exact(64) {
658 portable.compress(block);
659 }
660
661 let mut hardware = Core256::new(IV);
662 unsafe { x86::compress(&mut hardware.h, &data) };
664
665 assert_eq!(
666 portable.h, hardware.h,
667 "SHA-NI and portable disagree after {blocks} blocks"
668 );
669 checked += 1;
670 }
671 assert_eq!(checked, 6, "the comparison did not run");
672 }
673
674 #[cfg(all(target_arch = "x86_64", feature = "std"))]
677 #[test]
678 fn the_active_sha256_path_is_reported() {
679 println!(
680 "sha-256 backend: {}",
681 if sha_ni() { "SHA-NI" } else { "portable" }
682 );
683 }
684
685 #[test]
686 fn nist_abc_vectors() {
687 assert_eq!(
688 ic_core::codec::hex(Sha256::digest(b"abc").as_ref()),
689 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
690 );
691 assert_eq!(
692 ic_core::codec::hex(Sha224::digest(b"abc").as_ref()),
693 "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"
694 );
695 assert_eq!(
696 ic_core::codec::hex(Sha512::digest(b"abc").as_ref()),
697 "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
698 );
699 assert_eq!(
700 ic_core::codec::hex(Sha384::digest(b"abc").as_ref()),
701 "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"
702 );
703 }
704
705 #[test]
706 fn empty_input_vectors() {
707 assert_eq!(
708 ic_core::codec::hex(Sha256::digest(b"").as_ref()),
709 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
710 );
711 assert_eq!(
712 ic_core::codec::hex(Sha512::digest(b"").as_ref()),
713 "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
714 );
715 }
716
717 #[test]
719 fn two_block_vector() {
720 let msg = b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
721 assert_eq!(
722 ic_core::codec::hex(Sha256::digest(msg).as_ref()),
723 "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
724 );
725 }
726
727 #[test]
728 fn million_a_vector() {
729 let mut h = Sha256::new();
730 let chunk = [b'a'; 1000];
731 for _ in 0..1000 {
732 h.update(&chunk);
733 }
734 assert_eq!(
735 ic_core::codec::hex(h.finalize().as_ref()),
736 "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"
737 );
738 }
739
740 #[test]
741 fn streaming_matches_one_shot() {
742 let data: [u8; 300] = core::array::from_fn(|i| i as u8);
743 for split in [0usize, 1, 63, 64, 65, 127, 128, 200, 300] {
744 let mut h = Sha512::new();
745 h.update(&data[..split]);
746 h.update(&data[split..]);
747 assert_eq!(h.finalize(), Sha512::digest(&data), "split at {split}");
748 }
749 }
750
751 #[test]
752 fn truncated_variants() {
753 assert_eq!(
754 ic_core::codec::hex(Sha512_224::digest(b"abc").as_ref()),
755 "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"
756 );
757 assert_eq!(
758 ic_core::codec::hex(Sha512_256::digest(b"abc").as_ref()),
759 "53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"
760 );
761 }
762
763 #[test]
764 fn all_self_tests_pass() {
765 Sha224::self_test().unwrap();
766 Sha256::self_test().unwrap();
767 Sha384::self_test().unwrap();
768 Sha512::self_test().unwrap();
769 Sha512_224::self_test().unwrap();
770 Sha512_256::self_test().unwrap();
771 }
772}