crylib/hash/
sha512.rs

1//! A software implementation of SHA-512.
2
3use super::{BlockHasher, Hasher};
4
5const K: [u64; 80] = [
6    0x428a2f98d728ae22,
7    0x7137449123ef65cd,
8    0xb5c0fbcfec4d3b2f,
9    0xe9b5dba58189dbbc,
10    0x3956c25bf348b538,
11    0x59f111f1b605d019,
12    0x923f82a4af194f9b,
13    0xab1c5ed5da6d8118,
14    0xd807aa98a3030242,
15    0x12835b0145706fbe,
16    0x243185be4ee4b28c,
17    0x550c7dc3d5ffb4e2,
18    0x72be5d74f27b896f,
19    0x80deb1fe3b1696b1,
20    0x9bdc06a725c71235,
21    0xc19bf174cf692694,
22    0xe49b69c19ef14ad2,
23    0xefbe4786384f25e3,
24    0x0fc19dc68b8cd5b5,
25    0x240ca1cc77ac9c65,
26    0x2de92c6f592b0275,
27    0x4a7484aa6ea6e483,
28    0x5cb0a9dcbd41fbd4,
29    0x76f988da831153b5,
30    0x983e5152ee66dfab,
31    0xa831c66d2db43210,
32    0xb00327c898fb213f,
33    0xbf597fc7beef0ee4,
34    0xc6e00bf33da88fc2,
35    0xd5a79147930aa725,
36    0x06ca6351e003826f,
37    0x142929670a0e6e70,
38    0x27b70a8546d22ffc,
39    0x2e1b21385c26c926,
40    0x4d2c6dfc5ac42aed,
41    0x53380d139d95b3df,
42    0x650a73548baf63de,
43    0x766a0abb3c77b2a8,
44    0x81c2c92e47edaee6,
45    0x92722c851482353b,
46    0xa2bfe8a14cf10364,
47    0xa81a664bbc423001,
48    0xc24b8b70d0f89791,
49    0xc76c51a30654be30,
50    0xd192e819d6ef5218,
51    0xd69906245565a910,
52    0xf40e35855771202a,
53    0x106aa07032bbd1b8,
54    0x19a4c116b8d2d0c8,
55    0x1e376c085141ab53,
56    0x2748774cdf8eeb99,
57    0x34b0bcb5e19b48a8,
58    0x391c0cb3c5c95a63,
59    0x4ed8aa4ae3418acb,
60    0x5b9cca4f7763e373,
61    0x682e6ff3d6b2b8a3,
62    0x748f82ee5defb2fc,
63    0x78a5636f43172f60,
64    0x84c87814a1f0ab72,
65    0x8cc702081a6439ec,
66    0x90befffa23631e28,
67    0xa4506cebde82bde9,
68    0xbef9a3f7b2c67915,
69    0xc67178f2e372532b,
70    0xca273eceea26619c,
71    0xd186b8c721c0c207,
72    0xeada7dd6cde0eb1e,
73    0xf57d4f7fee6ed178,
74    0x06f067aa72176fba,
75    0x0a637dc5a2c898a6,
76    0x113f9804bef90dae,
77    0x1b710b35131c471b,
78    0x28db77f523047d84,
79    0x32caab7b40c72493,
80    0x3c9ebe0a15c9bebc,
81    0x431d67c49c100d4c,
82    0x4cc5d4becb3e42b6,
83    0x597f299cfc657e2a,
84    0x5fcb6fab3ad6faec,
85    0x6c44198c4a475817,
86];
87
88const fn ch(x: u64, y: u64, z: u64) -> u64 {
89    (x & y) ^ (!x & z)
90}
91
92const fn maj(x: u64, y: u64, z: u64) -> u64 {
93    (x & y) ^ (x & z) ^ (y & z)
94}
95
96const fn sigma_0(x: u64) -> u64 {
97    x.rotate_right(28) ^ x.rotate_right(34) ^ x.rotate_right(39)
98}
99
100const fn sigma_1(x: u64) -> u64 {
101    x.rotate_right(14) ^ x.rotate_right(18) ^ x.rotate_right(41)
102}
103
104const fn little_sigma_0(x: u64) -> u64 {
105    x.rotate_right(1) ^ x.rotate_right(8) ^ x >> 7
106}
107
108const fn little_sigma_1(x: u64) -> u64 {
109    x.rotate_right(19) ^ x.rotate_right(61) ^ x >> 6
110}
111
112#[derive(Clone)]
113pub struct Sha512 {
114    state: [u64; Self::HASH_SIZE / size_of::<u64>()],
115    len: u128,
116}
117
118impl Hasher<{ Sha512::HASH_SIZE }> for Sha512 {
119    fn new() -> Self {
120        Self {
121            state: [
122                0x6a09e667f3bcc908,
123                0xbb67ae8584caa73b,
124                0x3c6ef372fe94f82b,
125                0xa54ff53a5f1d36f1,
126                0x510e527fade682d1,
127                0x9b05688c2b3e6c1f,
128                0x1f83d9abfb41bd6b,
129                0x5be0cd19137e2179,
130            ],
131            len: 0,
132        }
133    }
134
135    fn finish_with(mut self, msg: &[u8]) -> [u8; Self::HASH_SIZE] {
136        // TODO: use `array_chunks` once stabilized
137        let chunks = msg.chunks_exact(Self::BLOCK_SIZE);
138        let remainder = chunks.remainder();
139
140        for block in chunks {
141            self.update_countless(&block.try_into().unwrap());
142        }
143
144        let mut last_block = [0; Self::BLOCK_SIZE];
145        // we can safely write here because the excess must be less than `BLOCK_SIZE`
146        last_block[..remainder.len()].copy_from_slice(remainder);
147
148        last_block[remainder.len()] = 0x80;
149
150        // does the length info fit without adding an extra block?
151        if remainder.len() < Self::BLOCK_SIZE - size_of::<u128>() {
152            last_block[Self::BLOCK_SIZE - size_of::<u128>()..]
153                .copy_from_slice(&((msg.len() as u128 + self.len) * 8).to_be_bytes());
154        } else {
155            self.update_countless(&last_block);
156            last_block = [0; Self::BLOCK_SIZE];
157            last_block[Self::BLOCK_SIZE - size_of::<u128>()..]
158                .copy_from_slice(&((msg.len() as u128 + self.len) * 8).to_be_bytes());
159        }
160
161        self.update(&last_block);
162        u64_array_to_be_bytes(&self.state)
163    }
164
165    fn finish(mut self) -> [u8; Self::HASH_SIZE] {
166        let mut padding = [0; Self::BLOCK_SIZE];
167        padding[0] = 0x80;
168        padding[Self::BLOCK_SIZE - size_of::<u128>()..]
169            .copy_from_slice(&(self.len * 8).to_be_bytes());
170        self.update_countless(&padding);
171
172        u64_array_to_be_bytes(&self.state)
173    }
174
175    fn hash(msg: &[u8]) -> [u8; Self::HASH_SIZE] {
176        let hasher = Self::new();
177        hasher.finish_with(msg)
178    }
179}
180
181impl BlockHasher<{ Self::HASH_SIZE }, { Self::BLOCK_SIZE }> for Sha512 {
182    fn update(&mut self, block: &[u8; Self::BLOCK_SIZE]) {
183        self.update_countless(block);
184        self.len += Self::BLOCK_SIZE as u128;
185    }
186}
187
188impl Sha512 {
189    pub const HASH_SIZE: usize = 64;
190    pub const BLOCK_SIZE: usize = 128;
191
192    fn update_countless(&mut self, block: &[u8; Self::BLOCK_SIZE]) {
193        let block = be_bytes_to_u64_array(block);
194        let mut message_schedule = [0; 80];
195        message_schedule[..block.len()].copy_from_slice(&block);
196
197        for i in 16..message_schedule.len() {
198            message_schedule[i] = little_sigma_1(message_schedule[i - 2])
199                .wrapping_add(message_schedule[i - 7])
200                .wrapping_add(little_sigma_0(message_schedule[i - 15]))
201                .wrapping_add(message_schedule[i - 16]);
202        }
203
204        let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = self.state;
205
206        for i in 0..80 {
207            let temp1 = h
208                .wrapping_add(sigma_1(e))
209                .wrapping_add(ch(e, f, g))
210                .wrapping_add(K[i])
211                .wrapping_add(message_schedule[i]);
212            let temp2 = sigma_0(a).wrapping_add(maj(a, b, c));
213            h = g;
214            g = f;
215            f = e;
216            e = d.wrapping_add(temp1);
217            d = c;
218            c = b;
219            b = a;
220            a = temp1.wrapping_add(temp2);
221        }
222        self.state[0] = self.state[0].wrapping_add(a);
223        self.state[1] = self.state[1].wrapping_add(b);
224        self.state[2] = self.state[2].wrapping_add(c);
225        self.state[3] = self.state[3].wrapping_add(d);
226        self.state[4] = self.state[4].wrapping_add(e);
227        self.state[5] = self.state[5].wrapping_add(f);
228        self.state[6] = self.state[6].wrapping_add(g);
229        self.state[7] = self.state[7].wrapping_add(h);
230    }
231}
232
233fn be_bytes_to_u64_array(bytes: &[u8; Sha512::BLOCK_SIZE]) -> [u64; Sha512::BLOCK_SIZE / 8] {
234    // TODO: consider using uninitialized array
235    let mut as_u64 = [64; Sha512::BLOCK_SIZE / 8];
236    // TODO: use `array_chunks` once stabilized
237    for (int, chunk) in as_u64.iter_mut().zip(bytes.chunks_exact(8)) {
238        // we can safely unwrap because `chunk` is guaranteed to have a length of `8`
239        *int = u64::from_be_bytes(chunk.try_into().unwrap());
240    }
241    as_u64
242}
243
244fn u64_array_to_be_bytes(
245    array: &[u64; Sha512::HASH_SIZE / size_of::<u64>()],
246) -> [u8; Sha512::HASH_SIZE] {
247    // TODO: consider using uninitialized array
248    let mut as_bytes = [0u8; Sha512::HASH_SIZE];
249    // TODO: use `array_chunks` once stabilized
250    for (chunk, int) in as_bytes.chunks_exact_mut(8).zip(array) {
251        chunk.copy_from_slice(&int.to_be_bytes())
252    }
253    as_bytes
254}
255
256#[cfg(test)]
257mod tests {
258
259    use super::*;
260
261    #[test]
262    fn hash_1() {
263        let msg = [0x6f, 0x8d, 0x58, 0xb7, 0xca, 0xb1, 0x88, 0x8c];
264        let digest = [
265            0xa3, 0x94, 0x1d, 0xef, 0x28, 0x03, 0xc8, 0xdf, 0xc0, 0x8f, 0x20, 0xc0, 0x6b, 0xa7,
266            0xe9, 0xa3, 0x32, 0xae, 0x0c, 0x67, 0xe4, 0x7a, 0xe5, 0x73, 0x65, 0xc2, 0x43, 0xef,
267            0x40, 0x05, 0x9b, 0x11, 0xbe, 0x22, 0xc9, 0x1d, 0xa6, 0xa8, 0x0c, 0x2c, 0xff, 0x07,
268            0x42, 0xa8, 0xf4, 0xbc, 0xd9, 0x41, 0xbd, 0xee, 0x0b, 0x86, 0x1e, 0xc8, 0x72, 0xb2,
269            0x15, 0x43, 0x3c, 0xe8, 0xdc, 0xf3, 0xc0, 0x31,
270        ];
271        assert_eq!(Sha512::hash(&msg), digest);
272    }
273
274    #[test]
275    fn hash_2() {
276        let msg = [
277            0x4f, 0x05, 0x60, 0x09, 0x50, 0x66, 0x4d, 0x51, 0x90, 0xa2, 0xeb, 0xc2, 0x9c, 0x9e,
278            0xdb, 0x89, 0xc2, 0x00, 0x79, 0xa4, 0xd3, 0xe6, 0xbc, 0x3b, 0x27, 0xd7, 0x5e, 0x34,
279            0xe2, 0xfa, 0x3d, 0x02, 0x76, 0x85, 0x02, 0xbd, 0x69, 0x79, 0x00, 0x78, 0x59, 0x8d,
280            0x5f, 0xcf, 0x3d, 0x67, 0x79, 0xbf, 0xed, 0x12, 0x84, 0xbb, 0xe5, 0xad, 0x72, 0xfb,
281            0x45, 0x60, 0x15, 0x18, 0x1d, 0x95, 0x87, 0xd6, 0xe8, 0x64, 0xc9, 0x40, 0x56, 0x4e,
282            0xaa, 0xfb, 0x4f, 0x2f, 0xea, 0xd4, 0x34, 0x6e, 0xa0, 0x9b, 0x68, 0x77, 0xd9, 0x34,
283            0x0f, 0x6b, 0x82, 0xeb, 0x15, 0x15, 0x88, 0x08, 0x72, 0x21, 0x3d, 0xa3, 0xad, 0x88,
284            0xfe, 0xba, 0x9f, 0x4f, 0x13, 0x81, 0x7a, 0x71, 0xd6, 0xf9, 0x0a, 0x1a, 0x17, 0xc4,
285            0x3a, 0x15, 0xc0, 0x38, 0xd9, 0x88, 0xb5, 0xb2, 0x9e, 0xdf, 0xfe, 0x2d, 0x6a, 0x06,
286            0x28, 0x13, 0xce, 0xdb, 0xe8, 0x52, 0xcd, 0xe3, 0x02, 0xb3, 0xe3, 0x3b, 0x69, 0x68,
287            0x46, 0xd2, 0xa8, 0xe3, 0x6b, 0xd6, 0x80, 0xef, 0xcc, 0x6c, 0xd3, 0xf9, 0xe9, 0xa4,
288            0xc1, 0xae, 0x8c, 0xac, 0x10, 0xcc, 0x52, 0x44, 0xd1, 0x31, 0x67, 0x71, 0x40, 0x39,
289            0x91, 0x76, 0xed, 0x46, 0x70, 0x00, 0x19, 0xa0, 0x04, 0xa1, 0x63, 0x80, 0x6f, 0x7f,
290            0xa4, 0x67, 0xfc, 0x4e, 0x17, 0xb4, 0x61, 0x7b, 0xbd, 0x76, 0x41, 0xaa, 0xff, 0x7f,
291            0xf5, 0x63, 0x96, 0xba, 0x8c, 0x08, 0xa8, 0xbe, 0x10, 0x0b, 0x33, 0xa2, 0x0b, 0x5d,
292            0xaf, 0x13, 0x4a, 0x2a, 0xef, 0xa5, 0xe1, 0xc3, 0x49, 0x67, 0x70, 0xdc, 0xf6, 0xba,
293            0xa4, 0xf7, 0xbb,
294        ];
295        let digest = [
296            0xa9, 0xdb, 0x49, 0x0c, 0x70, 0x8c, 0xc7, 0x25, 0x48, 0xd7, 0x86, 0x35, 0xaa, 0x7d,
297            0xa7, 0x9b, 0xb2, 0x53, 0xf9, 0x45, 0xd7, 0x10, 0xe5, 0xcb, 0x67, 0x7a, 0x47, 0x4e,
298            0xfc, 0x7c, 0x65, 0xa2, 0xaa, 0xb4, 0x5b, 0xc7, 0xca, 0x11, 0x13, 0xc8, 0xce, 0x0f,
299            0x3c, 0x32, 0xe1, 0x39, 0x9d, 0xe9, 0xc4, 0x59, 0x53, 0x5e, 0x88, 0x16, 0x52, 0x1a,
300            0xb7, 0x14, 0xb2, 0xa6, 0xcd, 0x20, 0x05, 0x25,
301        ];
302        assert_eq!(Sha512::hash(&msg), digest);
303    }
304
305    #[test]
306    fn update() {
307        let msg = [
308            0x4f, 0x05, 0x60, 0x09, 0x50, 0x66, 0x4d, 0x51, 0x90, 0xa2, 0xeb, 0xc2, 0x9c, 0x9e,
309            0xdb, 0x89, 0xc2, 0x00, 0x79, 0xa4, 0xd3, 0xe6, 0xbc, 0x3b, 0x27, 0xd7, 0x5e, 0x34,
310            0xe2, 0xfa, 0x3d, 0x02, 0x76, 0x85, 0x02, 0xbd, 0x69, 0x79, 0x00, 0x78, 0x59, 0x8d,
311            0x5f, 0xcf, 0x3d, 0x67, 0x79, 0xbf, 0xed, 0x12, 0x84, 0xbb, 0xe5, 0xad, 0x72, 0xfb,
312            0x45, 0x60, 0x15, 0x18, 0x1d, 0x95, 0x87, 0xd6, 0xe8, 0x64, 0xc9, 0x40, 0x56, 0x4e,
313            0xaa, 0xfb, 0x4f, 0x2f, 0xea, 0xd4, 0x34, 0x6e, 0xa0, 0x9b, 0x68, 0x77, 0xd9, 0x34,
314            0x0f, 0x6b, 0x82, 0xeb, 0x15, 0x15, 0x88, 0x08, 0x72, 0x21, 0x3d, 0xa3, 0xad, 0x88,
315            0xfe, 0xba, 0x9f, 0x4f, 0x13, 0x81, 0x7a, 0x71, 0xd6, 0xf9, 0x0a, 0x1a, 0x17, 0xc4,
316            0x3a, 0x15, 0xc0, 0x38, 0xd9, 0x88, 0xb5, 0xb2, 0x9e, 0xdf, 0xfe, 0x2d, 0x6a, 0x06,
317            0x28, 0x13, 0xce, 0xdb, 0xe8, 0x52, 0xcd, 0xe3, 0x02, 0xb3, 0xe3, 0x3b, 0x69, 0x68,
318            0x46, 0xd2, 0xa8, 0xe3, 0x6b, 0xd6, 0x80, 0xef, 0xcc, 0x6c, 0xd3, 0xf9, 0xe9, 0xa4,
319            0xc1, 0xae, 0x8c, 0xac, 0x10, 0xcc, 0x52, 0x44, 0xd1, 0x31, 0x67, 0x71, 0x40, 0x39,
320            0x91, 0x76, 0xed, 0x46, 0x70, 0x00, 0x19, 0xa0, 0x04, 0xa1, 0x63, 0x80, 0x6f, 0x7f,
321            0xa4, 0x67, 0xfc, 0x4e, 0x17, 0xb4, 0x61, 0x7b, 0xbd, 0x76, 0x41, 0xaa, 0xff, 0x7f,
322            0xf5, 0x63, 0x96, 0xba, 0x8c, 0x08, 0xa8, 0xbe, 0x10, 0x0b, 0x33, 0xa2, 0x0b, 0x5d,
323            0xaf, 0x13, 0x4a, 0x2a, 0xef, 0xa5, 0xe1, 0xc3, 0x49, 0x67, 0x70, 0xdc, 0xf6, 0xba,
324            0xa4, 0xf7, 0xbb,
325        ];
326        let digest = [
327            0xa9, 0xdb, 0x49, 0x0c, 0x70, 0x8c, 0xc7, 0x25, 0x48, 0xd7, 0x86, 0x35, 0xaa, 0x7d,
328            0xa7, 0x9b, 0xb2, 0x53, 0xf9, 0x45, 0xd7, 0x10, 0xe5, 0xcb, 0x67, 0x7a, 0x47, 0x4e,
329            0xfc, 0x7c, 0x65, 0xa2, 0xaa, 0xb4, 0x5b, 0xc7, 0xca, 0x11, 0x13, 0xc8, 0xce, 0x0f,
330            0x3c, 0x32, 0xe1, 0x39, 0x9d, 0xe9, 0xc4, 0x59, 0x53, 0x5e, 0x88, 0x16, 0x52, 0x1a,
331            0xb7, 0x14, 0xb2, 0xa6, 0xcd, 0x20, 0x05, 0x25,
332        ];
333        let mut hasher = Sha512::new();
334
335        let blocks = msg.chunks_exact(Sha512::BLOCK_SIZE);
336        let remainder = blocks.remainder();
337
338        for block in blocks {
339            hasher.update(block.try_into().unwrap());
340        }
341        assert_eq!(hasher.finish_with(remainder), digest);
342    }
343}