Skip to main content

arcana/hash/
sha512_trunc.rs

1//! SHA-512/224 and SHA-512/256 hash functions (FIPS 180-4).
2//!
3//! Truncated variants of SHA-512 with distinct IV values generated
4//! per FIPS 180-4 §5.3.6. These use the SHA-512 compression
5//! function (128-byte blocks, 64-bit words) but produce shorter
6//! outputs, which can matter for constrained protocols.
7
8use super::sha512::Sha512;
9use crate::Hasher;
10
11// IV for SHA-512/224 (FIPS 180-4 §5.3.6.1).
12const H0_512_224: [u64; 8] = [
13    0x8C3D37C819544DA2,
14    0x73E1996689DCD4D6,
15    0x1DFAB7AE32FF9C82,
16    0x679DD514582F9FCF,
17    0x0F6D2B697BD44DA8,
18    0x77E36F7304C48942,
19    0x3F9D85A86A1D36C8,
20    0x1112E6AD91D692A1,
21];
22
23// IV for SHA-512/256 (FIPS 180-4 §5.3.6.2).
24const H0_512_256: [u64; 8] = [
25    0x22312194FC2BF72C,
26    0x9F555FA3C84C64C2,
27    0x2393B86B6F53B151,
28    0x963877195940EABD,
29    0x96283EE2A88EFFE3,
30    0xBE5E1E2553863992,
31    0x2B0199FC2C85B8AA,
32    0x0EB72DDC81C52CA2,
33];
34
35/// SHA-512/224 hasher (FIPS 180-4). 224-bit output, 128-byte blocks.
36///
37/// Uses the SHA-512 compression function (efficient on 64-bit
38/// hardware) with a distinct IV and a 28-byte truncation.
39#[derive(Clone)]
40pub struct Sha512_224 {
41    inner: Sha512,
42}
43
44impl Hasher for Sha512_224 {
45    const OUTPUT_LEN: usize = 28;
46    const BLOCK_LEN: usize = 128;
47
48    fn new() -> Self {
49        Self {
50            inner: Sha512::new_with_iv(H0_512_224),
51        }
52    }
53
54    fn update(&mut self, data: &[u8]) {
55        self.inner.update(data);
56    }
57
58    fn finalize(self) -> Vec<u8> {
59        let mut out = vec![0u8; 28];
60        self.finalize_into(&mut out);
61        out
62    }
63
64    fn finalize_into(self, out: &mut [u8]) {
65        let mut full = [0u8; 64];
66        self.inner.finalize_into(&mut full);
67        let len = out.len().min(28);
68        out[..len].copy_from_slice(&full[..len]);
69    }
70}
71
72/// SHA-512/256 hasher (FIPS 180-4). 256-bit output, 128-byte blocks.
73///
74/// Uses the SHA-512 compression function with a distinct IV and a
75/// 32-byte truncation. Provides 128-bit collision resistance (same
76/// as SHA-256) but runs on the 64-bit oriented SHA-512 core, which
77/// is faster than SHA-256 on 64-bit platforms.
78#[derive(Clone)]
79pub struct Sha512_256 {
80    inner: Sha512,
81}
82
83impl Hasher for Sha512_256 {
84    const OUTPUT_LEN: usize = 32;
85    const BLOCK_LEN: usize = 128;
86
87    fn new() -> Self {
88        Self {
89            inner: Sha512::new_with_iv(H0_512_256),
90        }
91    }
92
93    fn update(&mut self, data: &[u8]) {
94        self.inner.update(data);
95    }
96
97    fn finalize(self) -> Vec<u8> {
98        let mut out = vec![0u8; 32];
99        self.finalize_into(&mut out);
100        out
101    }
102
103    fn finalize_into(self, out: &mut [u8]) {
104        let mut full = [0u8; 64];
105        self.inner.finalize_into(&mut full);
106        let len = out.len().min(32);
107        out[..len].copy_from_slice(&full[..len]);
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use crate::Hasher;
115
116    // FIPS 180-4 examples (from NIST CSRC).
117
118    #[test]
119    fn test_sha512_224_empty() {
120        let digest = Sha512_224::hash(b"");
121        let expected: [u8; 28] = [
122            0x6e, 0xd0, 0xdd, 0x02, 0x80, 0x6f, 0xa8, 0x9e, 0x25, 0xde, 0x06, 0x0c, 0x19, 0xd3, 0xac, 0x86, 0xca, 0xbb,
123            0x87, 0xd6, 0xa0, 0xdd, 0xd0, 0x5c, 0x33, 0x3b, 0x84, 0xf4,
124        ];
125        assert_eq!(&digest[..], &expected[..]);
126    }
127
128    #[test]
129    fn test_sha512_224_abc() {
130        let digest = Sha512_224::hash(b"abc");
131        let expected: [u8; 28] = [
132            0x46, 0x34, 0x27, 0x0F, 0x70, 0x7B, 0x6A, 0x54, 0xDA, 0xAE, 0x75, 0x30, 0x46, 0x08, 0x42, 0xE2, 0x0E, 0x37,
133            0xED, 0x26, 0x5C, 0xEE, 0xE9, 0xA4, 0x3E, 0x89, 0x24, 0xAA,
134        ];
135        assert_eq!(&digest[..], &expected[..]);
136    }
137
138    #[test]
139    fn test_sha512_256_empty() {
140        let digest = Sha512_256::hash(b"");
141        let expected: [u8; 32] = [
142            0xc6, 0x72, 0xb8, 0xd1, 0xef, 0x56, 0xed, 0x28, 0xab, 0x87, 0xc3, 0x62, 0x2c, 0x51, 0x14, 0x06, 0x9b, 0xdd,
143            0x3a, 0xd7, 0xb8, 0xf9, 0x73, 0x74, 0x98, 0xd0, 0xc0, 0x1e, 0xce, 0xf0, 0x96, 0x7a,
144        ];
145        assert_eq!(&digest[..], &expected[..]);
146    }
147
148    #[test]
149    fn test_sha512_256_abc() {
150        let digest = Sha512_256::hash(b"abc");
151        let expected: [u8; 32] = [
152            0x53, 0x04, 0x8E, 0x26, 0x81, 0x94, 0x1E, 0xF9, 0x9B, 0x2E, 0x29, 0xB7, 0x6B, 0x4C, 0x7D, 0xAB, 0xE4, 0xC2,
153            0xD0, 0xC6, 0x34, 0xFC, 0x6D, 0x46, 0xE0, 0xE2, 0xF1, 0x31, 0x07, 0xE7, 0xAF, 0x23,
154        ];
155        assert_eq!(&digest[..], &expected[..]);
156    }
157}