Skip to main content

cryptography/hash/
sha2.rs

1//! SHA-2 family from FIPS 180-4.
2//!
3//! This module includes both the 32-bit and 64-bit SHA-2 lines:
4//!
5//! - `Sha224`, `Sha256`
6//! - `Sha384`, `Sha512`
7//! - `Sha512_224`, `Sha512_256`
8//!
9//! The implementation keeps the two compression functions separate because the
10//! 32-bit and 64-bit schedules really are different algorithms at the round
11//! level, even though the high-level padding rules are shared.
12
13use super::Digest;
14
15// FIPS 180-4 §4.2.2: first 32 bits of the fractional parts of the cube roots
16// of the first 64 primes (2..311).
17const K32: [u32; 64] = [
18    0x428a_2f98,
19    0x7137_4491,
20    0xb5c0_fbcf,
21    0xe9b5_dba5,
22    0x3956_c25b,
23    0x59f1_11f1,
24    0x923f_82a4,
25    0xab1c_5ed5,
26    0xd807_aa98,
27    0x1283_5b01,
28    0x2431_85be,
29    0x550c_7dc3,
30    0x72be_5d74,
31    0x80de_b1fe,
32    0x9bdc_06a7,
33    0xc19b_f174,
34    0xe49b_69c1,
35    0xefbe_4786,
36    0x0fc1_9dc6,
37    0x240c_a1cc,
38    0x2de9_2c6f,
39    0x4a74_84aa,
40    0x5cb0_a9dc,
41    0x76f9_88da,
42    0x983e_5152,
43    0xa831_c66d,
44    0xb003_27c8,
45    0xbf59_7fc7,
46    0xc6e0_0bf3,
47    0xd5a7_9147,
48    0x06ca_6351,
49    0x1429_2967,
50    0x27b7_0a85,
51    0x2e1b_2138,
52    0x4d2c_6dfc,
53    0x5338_0d13,
54    0x650a_7354,
55    0x766a_0abb,
56    0x81c2_c92e,
57    0x9272_2c85,
58    0xa2bf_e8a1,
59    0xa81a_664b,
60    0xc24b_8b70,
61    0xc76c_51a3,
62    0xd192_e819,
63    0xd699_0624,
64    0xf40e_3585,
65    0x106a_a070,
66    0x19a4_c116,
67    0x1e37_6c08,
68    0x2748_774c,
69    0x34b0_bcb5,
70    0x391c_0cb3,
71    0x4ed8_aa4a,
72    0x5b9c_ca4f,
73    0x682e_6ff3,
74    0x748f_82ee,
75    0x78a5_636f,
76    0x84c8_7814,
77    0x8cc7_0208,
78    0x90be_fffa,
79    0xa450_6ceb,
80    0xbef9_a3f7,
81    0xc671_78f2,
82];
83
84// FIPS 180-4 §4.2.3: first 64 bits of the fractional parts of the cube roots
85// of the first 80 primes (2..409).
86const K64: [u64; 80] = [
87    0x428a_2f98_d728_ae22,
88    0x7137_4491_23ef_65cd,
89    0xb5c0_fbcf_ec4d_3b2f,
90    0xe9b5_dba5_8189_dbbc,
91    0x3956_c25b_f348_b538,
92    0x59f1_11f1_b605_d019,
93    0x923f_82a4_af19_4f9b,
94    0xab1c_5ed5_da6d_8118,
95    0xd807_aa98_a303_0242,
96    0x1283_5b01_4570_6fbe,
97    0x2431_85be_4ee4_b28c,
98    0x550c_7dc3_d5ff_b4e2,
99    0x72be_5d74_f27b_896f,
100    0x80de_b1fe_3b16_96b1,
101    0x9bdc_06a7_25c7_1235,
102    0xc19b_f174_cf69_2694,
103    0xe49b_69c1_9ef1_4ad2,
104    0xefbe_4786_384f_25e3,
105    0x0fc1_9dc6_8b8c_d5b5,
106    0x240c_a1cc_77ac_9c65,
107    0x2de9_2c6f_592b_0275,
108    0x4a74_84aa_6ea6_e483,
109    0x5cb0_a9dc_bd41_fbd4,
110    0x76f9_88da_8311_53b5,
111    0x983e_5152_ee66_dfab,
112    0xa831_c66d_2db4_3210,
113    0xb003_27c8_98fb_213f,
114    0xbf59_7fc7_beef_0ee4,
115    0xc6e0_0bf3_3da8_8fc2,
116    0xd5a7_9147_930a_a725,
117    0x06ca_6351_e003_826f,
118    0x1429_2967_0a0e_6e70,
119    0x27b7_0a85_46d2_2ffc,
120    0x2e1b_2138_5c26_c926,
121    0x4d2c_6dfc_5ac4_2aed,
122    0x5338_0d13_9d95_b3df,
123    0x650a_7354_8baf_63de,
124    0x766a_0abb_3c77_b2a8,
125    0x81c2_c92e_47ed_aee6,
126    0x9272_2c85_1482_353b,
127    0xa2bf_e8a1_4cf1_0364,
128    0xa81a_664b_bc42_3001,
129    0xc24b_8b70_d0f8_9791,
130    0xc76c_51a3_0654_be30,
131    0xd192_e819_d6ef_5218,
132    0xd699_0624_5565_a910,
133    0xf40e_3585_5771_202a,
134    0x106a_a070_32bb_d1b8,
135    0x19a4_c116_b8d2_d0c8,
136    0x1e37_6c08_5141_ab53,
137    0x2748_774c_df8e_eb99,
138    0x34b0_bcb5_e19b_48a8,
139    0x391c_0cb3_c5c9_5a63,
140    0x4ed8_aa4a_e341_8acb,
141    0x5b9c_ca4f_7763_e373,
142    0x682e_6ff3_d6b2_b8a3,
143    0x748f_82ee_5def_b2fc,
144    0x78a5_636f_4317_2f60,
145    0x84c8_7814_a1f0_ab72,
146    0x8cc7_0208_1a64_39ec,
147    0x90be_fffa_2363_1e28,
148    0xa450_6ceb_de82_bde9,
149    0xbef9_a3f7_b2c6_7915,
150    0xc671_78f2_e372_532b,
151    0xca27_3ece_ea26_619c,
152    0xd186_b8c7_21c0_c207,
153    0xeada_7dd6_cde0_eb1e,
154    0xf57d_4f7f_ee6e_d178,
155    0x06f0_67aa_7217_6fba,
156    0x0a63_7dc5_a2c8_98a6,
157    0x113f_9804_bef9_0dae,
158    0x1b71_0b35_131c_471b,
159    0x28db_77f5_2304_7d84,
160    0x32ca_ab7b_40c7_2493,
161    0x3c9e_be0a_15c9_bebc,
162    0x431d_67c4_9c10_0d4c,
163    0x4cc5_d4be_cb3e_42b6,
164    0x597f_299c_fc65_7e2a,
165    0x5fcb_6fab_3ad6_faec,
166    0x6c44_198c_4a47_5817,
167];
168
169#[inline]
170fn compress32(state: &mut [u32; 8], block: &[u8; 64]) {
171    let mut schedule = [0u32; 64];
172    for (i, chunk) in block.chunks_exact(4).enumerate() {
173        schedule[i] = u32::from_be_bytes(chunk.try_into().unwrap());
174    }
175    for word_idx in 16..64 {
176        let sigma0 = schedule[word_idx - 15].rotate_right(7)
177            ^ schedule[word_idx - 15].rotate_right(18)
178            ^ (schedule[word_idx - 15] >> 3);
179        let sigma1 = schedule[word_idx - 2].rotate_right(17)
180            ^ schedule[word_idx - 2].rotate_right(19)
181            ^ (schedule[word_idx - 2] >> 10);
182        schedule[word_idx] = schedule[word_idx - 16]
183            .wrapping_add(sigma0)
184            .wrapping_add(schedule[word_idx - 7])
185            .wrapping_add(sigma1);
186    }
187
188    let mut a_reg = state[0];
189    let mut b_reg = state[1];
190    let mut c_reg = state[2];
191    let mut d_reg = state[3];
192    let mut e_reg = state[4];
193    let mut f_reg = state[5];
194    let mut g_reg = state[6];
195    let mut h_reg = state[7];
196
197    for (round_idx, &schedule_word) in schedule.iter().enumerate() {
198        let sigma1 = e_reg.rotate_right(6) ^ e_reg.rotate_right(11) ^ e_reg.rotate_right(25);
199        let choose = (e_reg & f_reg) ^ ((!e_reg) & g_reg);
200        let temp1 = h_reg
201            .wrapping_add(sigma1)
202            .wrapping_add(choose)
203            .wrapping_add(K32[round_idx])
204            .wrapping_add(schedule_word);
205        let sigma0 = a_reg.rotate_right(2) ^ a_reg.rotate_right(13) ^ a_reg.rotate_right(22);
206        let majority = (a_reg & b_reg) ^ (a_reg & c_reg) ^ (b_reg & c_reg);
207        let temp2 = sigma0.wrapping_add(majority);
208
209        h_reg = g_reg;
210        g_reg = f_reg;
211        f_reg = e_reg;
212        e_reg = d_reg.wrapping_add(temp1);
213        d_reg = c_reg;
214        c_reg = b_reg;
215        b_reg = a_reg;
216        a_reg = temp1.wrapping_add(temp2);
217    }
218
219    state[0] = state[0].wrapping_add(a_reg);
220    state[1] = state[1].wrapping_add(b_reg);
221    state[2] = state[2].wrapping_add(c_reg);
222    state[3] = state[3].wrapping_add(d_reg);
223    state[4] = state[4].wrapping_add(e_reg);
224    state[5] = state[5].wrapping_add(f_reg);
225    state[6] = state[6].wrapping_add(g_reg);
226    state[7] = state[7].wrapping_add(h_reg);
227}
228
229#[inline]
230fn compress64(state: &mut [u64; 8], block: &[u8; 128]) {
231    let mut schedule = [0u64; 80];
232    for (i, chunk) in block.chunks_exact(8).enumerate() {
233        schedule[i] = u64::from_be_bytes(chunk.try_into().unwrap());
234    }
235    for word_idx in 16..80 {
236        let sigma0 = schedule[word_idx - 15].rotate_right(1)
237            ^ schedule[word_idx - 15].rotate_right(8)
238            ^ (schedule[word_idx - 15] >> 7);
239        let sigma1 = schedule[word_idx - 2].rotate_right(19)
240            ^ schedule[word_idx - 2].rotate_right(61)
241            ^ (schedule[word_idx - 2] >> 6);
242        schedule[word_idx] = schedule[word_idx - 16]
243            .wrapping_add(sigma0)
244            .wrapping_add(schedule[word_idx - 7])
245            .wrapping_add(sigma1);
246    }
247
248    let mut a_reg = state[0];
249    let mut b_reg = state[1];
250    let mut c_reg = state[2];
251    let mut d_reg = state[3];
252    let mut e_reg = state[4];
253    let mut f_reg = state[5];
254    let mut g_reg = state[6];
255    let mut h_reg = state[7];
256
257    for (round_idx, &schedule_word) in schedule.iter().enumerate() {
258        let sigma1 = e_reg.rotate_right(14) ^ e_reg.rotate_right(18) ^ e_reg.rotate_right(41);
259        let choose = (e_reg & f_reg) ^ ((!e_reg) & g_reg);
260        let temp1 = h_reg
261            .wrapping_add(sigma1)
262            .wrapping_add(choose)
263            .wrapping_add(K64[round_idx])
264            .wrapping_add(schedule_word);
265        let sigma0 = a_reg.rotate_right(28) ^ a_reg.rotate_right(34) ^ a_reg.rotate_right(39);
266        let majority = (a_reg & b_reg) ^ (a_reg & c_reg) ^ (b_reg & c_reg);
267        let temp2 = sigma0.wrapping_add(majority);
268
269        h_reg = g_reg;
270        g_reg = f_reg;
271        f_reg = e_reg;
272        e_reg = d_reg.wrapping_add(temp1);
273        d_reg = c_reg;
274        c_reg = b_reg;
275        b_reg = a_reg;
276        a_reg = temp1.wrapping_add(temp2);
277    }
278
279    state[0] = state[0].wrapping_add(a_reg);
280    state[1] = state[1].wrapping_add(b_reg);
281    state[2] = state[2].wrapping_add(c_reg);
282    state[3] = state[3].wrapping_add(d_reg);
283    state[4] = state[4].wrapping_add(e_reg);
284    state[5] = state[5].wrapping_add(f_reg);
285    state[6] = state[6].wrapping_add(g_reg);
286    state[7] = state[7].wrapping_add(h_reg);
287}
288
289#[derive(Clone)]
290struct Sha2_32Core {
291    state: [u32; 8],
292    block: [u8; 64],
293    pos: usize,
294    bit_len: u64,
295}
296
297impl Sha2_32Core {
298    fn new(iv: [u32; 8]) -> Self {
299        Self {
300            state: iv,
301            block: [0u8; 64],
302            pos: 0,
303            bit_len: 0,
304        }
305    }
306
307    fn update(&mut self, mut data: &[u8]) {
308        while !data.is_empty() {
309            let take = (64 - self.pos).min(data.len());
310            self.block[self.pos..self.pos + take].copy_from_slice(&data[..take]);
311            self.pos += take;
312            data = &data[take..];
313
314            if self.pos == 64 {
315                compress32(&mut self.state, &self.block);
316                self.block = [0u8; 64];
317                self.pos = 0;
318                self.bit_len = self.bit_len.wrapping_add(512);
319            }
320        }
321    }
322
323    fn finalize<const OUT: usize>(mut self) -> [u8; OUT] {
324        self.bit_len = self.bit_len.wrapping_add((self.pos as u64) * 8);
325        self.block[self.pos] = 0x80;
326        self.pos += 1;
327
328        if self.pos > 56 {
329            self.block[self.pos..].fill(0);
330            compress32(&mut self.state, &self.block);
331            self.block = [0u8; 64];
332            self.pos = 0;
333        }
334
335        self.block[self.pos..56].fill(0);
336        self.block[56..].copy_from_slice(&self.bit_len.to_be_bytes());
337        compress32(&mut self.state, &self.block);
338
339        let mut full = [0u8; 32];
340        for (chunk, word) in full.chunks_exact_mut(4).zip(self.state.iter()) {
341            chunk.copy_from_slice(&word.to_be_bytes());
342        }
343        let mut out = [0u8; OUT];
344        out.copy_from_slice(&full[..OUT]);
345        out
346    }
347
348    fn finalize_into_reset<const OUT: usize>(&mut self, out: &mut [u8; OUT]) {
349        self.bit_len = self.bit_len.wrapping_add((self.pos as u64) * 8);
350        self.block[self.pos] = 0x80;
351        self.pos += 1;
352
353        if self.pos > 56 {
354            self.block[self.pos..].fill(0);
355            compress32(&mut self.state, &self.block);
356            self.block = [0u8; 64];
357            self.pos = 0;
358        }
359
360        self.block[self.pos..56].fill(0);
361        self.block[56..].copy_from_slice(&self.bit_len.to_be_bytes());
362        compress32(&mut self.state, &self.block);
363
364        let mut full = [0u8; 32];
365        for (chunk, word) in full.chunks_exact_mut(4).zip(self.state.iter()) {
366            chunk.copy_from_slice(&word.to_be_bytes());
367        }
368        out.copy_from_slice(&full[..OUT]);
369
370        self.zeroize();
371    }
372
373    fn zeroize(&mut self) {
374        crate::ct::zeroize_slice(self.state.as_mut_slice());
375        crate::ct::zeroize_slice(self.block.as_mut_slice());
376        self.pos = 0;
377        self.bit_len = 0;
378    }
379}
380
381#[derive(Clone)]
382struct Sha2_64Core {
383    state: [u64; 8],
384    block: [u8; 128],
385    pos: usize,
386    bit_len: u128,
387}
388
389impl Sha2_64Core {
390    fn new(iv: [u64; 8]) -> Self {
391        Self {
392            state: iv,
393            block: [0u8; 128],
394            pos: 0,
395            bit_len: 0,
396        }
397    }
398
399    fn update(&mut self, mut data: &[u8]) {
400        while !data.is_empty() {
401            let take = (128 - self.pos).min(data.len());
402            self.block[self.pos..self.pos + take].copy_from_slice(&data[..take]);
403            self.pos += take;
404            data = &data[take..];
405
406            if self.pos == 128 {
407                compress64(&mut self.state, &self.block);
408                self.block = [0u8; 128];
409                self.pos = 0;
410                self.bit_len = self.bit_len.wrapping_add(1024);
411            }
412        }
413    }
414
415    fn finalize<const OUT: usize>(mut self) -> [u8; OUT] {
416        self.bit_len = self.bit_len.wrapping_add((self.pos as u128) * 8);
417        self.block[self.pos] = 0x80;
418        self.pos += 1;
419
420        if self.pos > 112 {
421            self.block[self.pos..].fill(0);
422            compress64(&mut self.state, &self.block);
423            self.block = [0u8; 128];
424            self.pos = 0;
425        }
426
427        self.block[self.pos..112].fill(0);
428        self.block[112..].copy_from_slice(&self.bit_len.to_be_bytes());
429        compress64(&mut self.state, &self.block);
430
431        let mut full = [0u8; 64];
432        for (chunk, word) in full.chunks_exact_mut(8).zip(self.state.iter()) {
433            chunk.copy_from_slice(&word.to_be_bytes());
434        }
435        let mut out = [0u8; OUT];
436        out.copy_from_slice(&full[..OUT]);
437        out
438    }
439
440    fn finalize_into_reset<const OUT: usize>(&mut self, out: &mut [u8; OUT]) {
441        self.bit_len = self.bit_len.wrapping_add((self.pos as u128) * 8);
442        self.block[self.pos] = 0x80;
443        self.pos += 1;
444
445        if self.pos > 112 {
446            self.block[self.pos..].fill(0);
447            compress64(&mut self.state, &self.block);
448            self.block = [0u8; 128];
449            self.pos = 0;
450        }
451
452        self.block[self.pos..112].fill(0);
453        self.block[112..].copy_from_slice(&self.bit_len.to_be_bytes());
454        compress64(&mut self.state, &self.block);
455
456        let mut full = [0u8; 64];
457        for (chunk, word) in full.chunks_exact_mut(8).zip(self.state.iter()) {
458            chunk.copy_from_slice(&word.to_be_bytes());
459        }
460        out.copy_from_slice(&full[..OUT]);
461
462        self.zeroize();
463    }
464
465    fn zeroize(&mut self) {
466        crate::ct::zeroize_slice(self.state.as_mut_slice());
467        crate::ct::zeroize_slice(self.block.as_mut_slice());
468        self.pos = 0;
469        self.bit_len = 0;
470    }
471}
472
473macro_rules! define_sha2_32 {
474    ($name:ident, $out_len:expr, $iv:expr) => {
475        #[derive(Clone)]
476        pub struct $name {
477            inner: Sha2_32Core,
478        }
479
480        impl Default for $name {
481            fn default() -> Self {
482                Self::new()
483            }
484        }
485
486        impl $name {
487            pub const BLOCK_LEN: usize = 64;
488            pub const OUTPUT_LEN: usize = $out_len;
489
490            #[must_use]
491            pub fn new() -> Self {
492                Self {
493                    inner: Sha2_32Core::new($iv),
494                }
495            }
496
497            pub fn update(&mut self, data: &[u8]) {
498                self.inner.update(data);
499            }
500
501            #[must_use]
502            pub fn finalize(self) -> [u8; $out_len] {
503                self.inner.finalize::<$out_len>()
504            }
505
506            #[must_use]
507            pub fn digest(data: &[u8]) -> [u8; $out_len] {
508                let mut h = Self::new();
509                h.update(data);
510                h.finalize()
511            }
512        }
513
514        impl Digest for $name {
515            const BLOCK_LEN: usize = 64;
516            const OUTPUT_LEN: usize = $out_len;
517
518            fn new() -> Self {
519                Self::new()
520            }
521
522            fn update(&mut self, data: &[u8]) {
523                self.inner.update(data);
524            }
525
526            fn finalize_into(self, out: &mut [u8]) {
527                assert_eq!(out.len(), $out_len, "wrong digest length");
528                out.copy_from_slice(&self.inner.finalize::<$out_len>());
529            }
530
531            fn finalize_reset(&mut self, out: &mut [u8]) {
532                let out: &mut [u8; $out_len] = out.try_into().expect("wrong digest length");
533                self.inner.finalize_into_reset::<$out_len>(out);
534            }
535
536            fn zeroize(&mut self) {
537                self.inner.zeroize();
538            }
539        }
540    };
541}
542
543macro_rules! define_sha2_64 {
544    ($name:ident, $out_len:expr, $iv:expr) => {
545        #[derive(Clone)]
546        pub struct $name {
547            inner: Sha2_64Core,
548        }
549
550        impl Default for $name {
551            fn default() -> Self {
552                Self::new()
553            }
554        }
555
556        impl $name {
557            pub const BLOCK_LEN: usize = 128;
558            pub const OUTPUT_LEN: usize = $out_len;
559
560            #[must_use]
561            pub fn new() -> Self {
562                Self {
563                    inner: Sha2_64Core::new($iv),
564                }
565            }
566
567            pub fn update(&mut self, data: &[u8]) {
568                self.inner.update(data);
569            }
570
571            #[must_use]
572            pub fn finalize(self) -> [u8; $out_len] {
573                self.inner.finalize::<$out_len>()
574            }
575
576            #[must_use]
577            pub fn digest(data: &[u8]) -> [u8; $out_len] {
578                let mut h = Self::new();
579                h.update(data);
580                h.finalize()
581            }
582        }
583
584        impl Digest for $name {
585            const BLOCK_LEN: usize = 128;
586            const OUTPUT_LEN: usize = $out_len;
587
588            fn new() -> Self {
589                Self::new()
590            }
591
592            fn update(&mut self, data: &[u8]) {
593                self.inner.update(data);
594            }
595
596            fn finalize_into(self, out: &mut [u8]) {
597                assert_eq!(out.len(), $out_len, "wrong digest length");
598                out.copy_from_slice(&self.inner.finalize::<$out_len>());
599            }
600
601            fn finalize_reset(&mut self, out: &mut [u8]) {
602                let out: &mut [u8; $out_len] = out.try_into().expect("wrong digest length");
603                self.inner.finalize_into_reset::<$out_len>(out);
604            }
605
606            fn zeroize(&mut self) {
607                self.inner.zeroize();
608            }
609        }
610    };
611}
612
613// FIPS 180-4 §5.3.2 initial hash value H(0) for SHA-224.
614define_sha2_32!(
615    Sha224,
616    28,
617    [
618        0xc105_9ed8,
619        0x367c_d507,
620        0x3070_dd17,
621        0xf70e_5939,
622        0xffc0_0b31,
623        0x6858_1511,
624        0x64f9_8fa7,
625        0xbefa_4fa4,
626    ]
627);
628
629// FIPS 180-4 §5.3.3 initial hash value H(0) for SHA-256.
630define_sha2_32!(
631    Sha256,
632    32,
633    [
634        0x6a09_e667,
635        0xbb67_ae85,
636        0x3c6e_f372,
637        0xa54f_f53a,
638        0x510e_527f,
639        0x9b05_688c,
640        0x1f83_d9ab,
641        0x5be0_cd19,
642    ]
643);
644
645// FIPS 180-4 §5.3.4 initial hash value H(0) for SHA-384.
646define_sha2_64!(
647    Sha384,
648    48,
649    [
650        0xcbbb_9d5d_c105_9ed8,
651        0x629a_292a_367c_d507,
652        0x9159_015a_3070_dd17,
653        0x152f_ecd8_f70e_5939,
654        0x6733_2667_ffc0_0b31,
655        0x8eb4_4a87_6858_1511,
656        0xdb0c_2e0d_64f9_8fa7,
657        0x47b5_481d_befa_4fa4,
658    ]
659);
660
661// FIPS 180-4 §5.3.5 initial hash value H(0) for SHA-512.
662define_sha2_64!(
663    Sha512,
664    64,
665    [
666        0x6a09_e667_f3bc_c908,
667        0xbb67_ae85_84ca_a73b,
668        0x3c6e_f372_fe94_f82b,
669        0xa54f_f53a_5f1d_36f1,
670        0x510e_527f_ade6_82d1,
671        0x9b05_688c_2b3e_6c1f,
672        0x1f83_d9ab_fb41_bd6b,
673        0x5be0_cd19_137e_2179,
674    ]
675);
676
677// FIPS 180-4 §5.3.6 initial hash value H(0) for SHA-512/224.
678define_sha2_64!(
679    Sha512_224,
680    28,
681    [
682        0x8c3d_37c8_1954_4da2,
683        0x73e1_9966_89dc_d4d6,
684        0x1dfa_b7ae_32ff_9c82,
685        0x679d_d514_582f_9fcf,
686        0x0f6d_2b69_7bd4_4da8,
687        0x77e3_6f73_04c4_8942,
688        0x3f9d_85a8_6a1d_36c8,
689        0x1112_e6ad_91d6_92a1,
690    ]
691);
692
693// FIPS 180-4 §5.3.7 initial hash value H(0) for SHA-512/256.
694define_sha2_64!(
695    Sha512_256,
696    32,
697    [
698        0x2231_2194_fc2b_f72c,
699        0x9f55_5fa3_c84c_64c2,
700        0x2393_b86b_6f53_b151,
701        0x9638_7719_5940_eabd,
702        0x9628_3ee2_a88e_ffe3,
703        0xbe5e_1e25_5386_3992,
704        0x2b01_99fc_2c85_b8aa,
705        0x0eb7_2ddc_81c5_2ca2,
706    ]
707);
708
709#[cfg(test)]
710mod tests {
711    use super::*;
712
713    fn hex(bytes: &[u8]) -> String {
714        let mut out = String::with_capacity(bytes.len() * 2);
715        for b in bytes {
716            use core::fmt::Write;
717            let _ = write!(&mut out, "{b:02x}");
718        }
719        out
720    }
721
722    #[test]
723    fn sha224_empty() {
724        assert_eq!(
725            hex(&Sha224::digest(b"")),
726            "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"
727        );
728    }
729
730    #[test]
731    fn sha224_abc_streaming() {
732        let mut h = Sha224::new();
733        h.update(b"a");
734        h.update(b"b");
735        h.update(b"c");
736        assert_eq!(
737            hex(&h.finalize()),
738            "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"
739        );
740    }
741
742    #[test]
743    fn sha256_empty() {
744        assert_eq!(
745            hex(&Sha256::digest(b"")),
746            "e3b0c44298fc1c149afbf4c8996fb924".to_owned() + "27ae41e4649b934ca495991b7852b855"
747        );
748    }
749
750    #[test]
751    fn sha256_abc_streaming() {
752        let mut h = Sha256::new();
753        h.update(b"a");
754        h.update(b"b");
755        h.update(b"c");
756        assert_eq!(
757            hex(&h.finalize()),
758            "ba7816bf8f01cfea414140de5dae2223".to_owned() + "b00361a396177a9cb410ff61f20015ad"
759        );
760    }
761
762    #[test]
763    fn sha384_empty() {
764        assert_eq!(
765            hex(&Sha384::digest(b"")),
766            "38b060a751ac96384cd9327eb1b1e36a".to_owned()
767                + "21fdb71114be07434c0cc7bf63f6e1da"
768                + "274edebfe76f65fbd51ad2f14898b95b"
769        );
770    }
771
772    #[test]
773    fn sha384_abc_streaming() {
774        let mut h = Sha384::new();
775        h.update(b"a");
776        h.update(b"b");
777        h.update(b"c");
778        assert_eq!(
779            hex(&h.finalize()),
780            "cb00753f45a35e8bb5a03d699ac65007".to_owned()
781                + "272c32ab0eded1631a8b605a43ff5bed"
782                + "8086072ba1e7cc2358baeca134c825a7"
783        );
784    }
785
786    #[test]
787    fn sha512_empty() {
788        assert_eq!(
789            hex(&Sha512::digest(b"")),
790            "cf83e1357eefb8bdf1542850d66d8007".to_owned()
791                + "d620e4050b5715dc83f4a921d36ce9ce"
792                + "47d0d13c5d85f2b0ff8318d2877eec2f"
793                + "63b931bd47417a81a538327af927da3e"
794        );
795    }
796
797    #[test]
798    fn sha512_abc_streaming() {
799        let mut h = Sha512::new();
800        h.update(b"a");
801        h.update(b"b");
802        h.update(b"c");
803        assert_eq!(
804            hex(&h.finalize()),
805            "ddaf35a193617abacc417349ae204131".to_owned()
806                + "12e6fa4e89a97ea20a9eeee64b55d39a"
807                + "2192992a274fc1a836ba3c23a3feebbd"
808                + "454d4423643ce80e2a9ac94fa54ca49f"
809        );
810    }
811
812    #[test]
813    fn sha512_224_empty() {
814        assert_eq!(
815            hex(&Sha512_224::digest(b"")),
816            "6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"
817        );
818    }
819
820    #[test]
821    fn sha512_224_abc_streaming() {
822        let mut h = Sha512_224::new();
823        h.update(b"a");
824        h.update(b"b");
825        h.update(b"c");
826        assert_eq!(
827            hex(&h.finalize()),
828            "4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"
829        );
830    }
831
832    #[test]
833    fn sha512_256_empty() {
834        assert_eq!(
835            hex(&Sha512_256::digest(b"")),
836            "c672b8d1ef56ed28ab87c3622c511406".to_owned() + "9bdd3ad7b8f9737498d0c01ecef0967a"
837        );
838    }
839
840    #[test]
841    fn sha512_256_abc_streaming() {
842        let mut h = Sha512_256::new();
843        h.update(b"a");
844        h.update(b"b");
845        h.update(b"c");
846        assert_eq!(
847            hex(&h.finalize()),
848            "53048e2681941ef99b2e29b76b4c7dab".to_owned() + "e4c2d0c634fc6d46e0e2f13107e7af23"
849        );
850    }
851
852    #[test]
853    fn sha256_matches_openssl() {
854        let msg = b"The quick brown fox jumps over the lazy dog";
855        let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha256", "-binary"], msg) else {
856            return;
857        };
858        assert_eq!(Sha256::digest(msg).as_slice(), expected.as_slice());
859    }
860
861    #[test]
862    fn sha224_matches_openssl() {
863        let msg = b"The quick brown fox jumps over the lazy dog";
864        let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha224", "-binary"], msg) else {
865            return;
866        };
867        assert_eq!(Sha224::digest(msg).as_slice(), expected.as_slice());
868    }
869
870    #[test]
871    fn sha384_matches_openssl() {
872        let msg = b"The quick brown fox jumps over the lazy dog";
873        let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha384", "-binary"], msg) else {
874            return;
875        };
876        assert_eq!(Sha384::digest(msg).as_slice(), expected.as_slice());
877    }
878
879    #[test]
880    fn sha512_matches_openssl() {
881        let msg = b"The quick brown fox jumps over the lazy dog";
882        let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha512", "-binary"], msg) else {
883            return;
884        };
885        assert_eq!(Sha512::digest(msg).as_slice(), expected.as_slice());
886    }
887
888    #[test]
889    fn sha512_224_matches_openssl() {
890        let msg = b"The quick brown fox jumps over the lazy dog";
891        let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha512-224", "-binary"], msg)
892        else {
893            return;
894        };
895        assert_eq!(Sha512_224::digest(msg).as_slice(), expected.as_slice());
896    }
897
898    #[test]
899    fn sha512_256_matches_openssl() {
900        let msg = b"The quick brown fox jumps over the lazy dog";
901        let Some(expected) = crate::test_utils::run_openssl(&["dgst", "-sha512-256", "-binary"], msg)
902        else {
903            return;
904        };
905        assert_eq!(Sha512_256::digest(msg).as_slice(), expected.as_slice());
906    }
907}