Skip to main content

bouncycastle_sha2/
sha512.rs

1use crate::SHA2Params;
2use bouncycastle_core::errors::{HashError, SuspendableError};
3use bouncycastle_core::suspendable_state::{add_lib_ver, check_lib_ver};
4use bouncycastle_core::traits::{Algorithm, Hash, SecurityStrength, Suspendable};
5use bouncycastle_utils::{min, secret::Secret};
6use core::slice;
7
8const SHA512_K: [u64; 80] = [
9    0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC,
10    0x3956C25BF348B538, 0x59F111F1B605D019, 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118,
11    0xD807AA98A3030242, 0x12835B0145706FBE, 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2,
12    0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, 0x9BDC06A725C71235, 0xC19BF174CF692694,
13    0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65,
14    0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5,
15    0x983E5152EE66DFAB, 0xA831C66D2DB43210, 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4,
16    0xC6E00BF33DA88FC2, 0xD5A79147930AA725, 0x06CA6351E003826F, 0x142929670A0E6E70,
17    0x27B70A8546D22FFC, 0x2E1B21385C26C926, 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF,
18    0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, 0x81C2C92E47EDAEE6, 0x92722C851482353B,
19    0xA2BFE8A14CF10364, 0xA81A664BBC423001, 0xC24B8B70D0F89791, 0xC76C51A30654BE30,
20    0xD192E819D6EF5218, 0xD69906245565A910, 0xF40E35855771202A, 0x106AA07032BBD1B8,
21    0x19A4C116B8D2D0C8, 0x1E376C085141AB53, 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8,
22    0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3,
23    0x748F82EE5DEFB2FC, 0x78A5636F43172F60, 0x84C87814A1F0AB72, 0x8CC702081A6439EC,
24    0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, 0xBEF9A3F7B2C67915, 0xC67178F2E372532B,
25    0xCA273ECEEA26619C, 0xD186B8C721C0C207, 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178,
26    0x06F067AA72176FBA, 0x0A637DC5A2C898A6, 0x113F9804BEF90DAE, 0x1B710B35131C471B,
27    0x28DB77F523047D84, 0x32CAAB7B40C72493, 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C,
28    0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817,
29];
30
31#[inline]
32fn ch(x: u64, y: u64, z: u64) -> u64 {
33    (x & y) ^ (!x & z)
34}
35
36#[inline]
37fn maj(x: u64, y: u64, z: u64) -> u64 {
38    (x & y) | (z & (x ^ y))
39}
40
41#[inline]
42fn sum0(x: u64) -> u64 {
43    x.rotate_right(28) ^ x.rotate_right(34) ^ x.rotate_right(39)
44}
45
46#[inline]
47fn sum1(x: u64) -> u64 {
48    x.rotate_right(14) ^ x.rotate_right(18) ^ x.rotate_right(41)
49}
50
51#[inline]
52fn theta0(x: u64) -> u64 {
53    x.rotate_right(1) ^ x.rotate_right(8) ^ (x >> 7)
54}
55
56#[inline]
57fn theta1(x: u64) -> u64 {
58    x.rotate_right(19) ^ x.rotate_right(61) ^ (x >> 6)
59}
60
61// todo -- cleanup
62// #[derive(Clone, Copy)]
63#[derive(Clone)]
64pub(crate) struct Sha512State<PARAMS: SHA2Params> {
65    _params: std::marker::PhantomData<PARAMS>,
66    h: Secret<[u64; 8]>,
67}
68
69impl<PARAMS: SHA2Params> Sha512State<PARAMS> {
70    pub(crate) fn new() -> Self {
71        let mut h = Secret::<[u64; 8]>::new();
72        match PARAMS::OUTPUT_LEN * 8 {
73            384 => {
74                h.copy_from_slice(&[
75                    0xCBBB9D5DC1059ED8, 0x629A292A367CD507, 0x9159015A3070DD17, 0x152FECD8F70E5939,
76                    0x67332667FFC00B31, 0x8EB44A8768581511, 0xDB0C2E0D64F98FA7, 0x47B5481DBEFA4FA4,
77                ]);
78                Self { _params: std::marker::PhantomData, h }
79            }
80            512 => {
81                h.copy_from_slice(&[
82                    0x6A09E667F3BCC908, 0xBB67AE8584CAA73B, 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1,
83                    0x510E527FADE682D1, 0x9B05688C2B3E6C1F, 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179,
84                ]);
85                Self { _params: std::marker::PhantomData, h }
86            }
87            _ => panic!("Invalid SHA-2 bit size"),
88        }
89    }
90
91    fn compress(&mut self, blocks: &[[u8; 128]]) {
92        let mut x = [0u64; 80];
93
94        let s = &mut *self.h;
95        let &mut [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h] = s;
96
97        for block in blocks {
98            let (chunks, _remainder) = block.as_chunks::<8>();
99            for (i, w) in x[..16].iter_mut().zip(chunks) {
100                *i = u64::from_be_bytes(*w);
101            }
102
103            for i in 16..80 {
104                x[i] = theta1(x[i - 2])
105                    .wrapping_add(x[i - 7])
106                    .wrapping_add(theta0(x[i - 15]))
107                    .wrapping_add(x[i - 16]);
108            }
109
110            macro_rules! sha512_round {
111                ($a:ident,$b:ident,$c:ident,$d:ident,$e:ident,$f:ident,$g:ident,$h:ident,$t:ident,$K:ident,$x:ident) => {
112                    $h = $h
113                        .wrapping_add(sum1($e))
114                        .wrapping_add(ch($e, $f, $g))
115                        .wrapping_add($K[$t])
116                        .wrapping_add($x[$t]);
117                    $d = $d.wrapping_add($h);
118                    $h = $h.wrapping_add(sum0($a)).wrapping_add(maj($a, $b, $c));
119                    $t += 1;
120                };
121            }
122
123            let mut t: usize = 0;
124            for _ in 0..10 {
125                sha512_round!(a, b, c, d, e, f, g, h, t, SHA512_K, x);
126                sha512_round!(h, a, b, c, d, e, f, g, t, SHA512_K, x);
127                sha512_round!(g, h, a, b, c, d, e, f, t, SHA512_K, x);
128                sha512_round!(f, g, h, a, b, c, d, e, t, SHA512_K, x);
129                sha512_round!(e, f, g, h, a, b, c, d, t, SHA512_K, x);
130                sha512_round!(d, e, f, g, h, a, b, c, t, SHA512_K, x);
131                sha512_round!(c, d, e, f, g, h, a, b, t, SHA512_K, x);
132                sha512_round!(b, c, d, e, f, g, h, a, t, SHA512_K, x);
133            }
134
135            a = a.wrapping_add(s[0]);
136            b = b.wrapping_add(s[1]);
137            c = c.wrapping_add(s[2]);
138            d = d.wrapping_add(s[3]);
139            e = e.wrapping_add(s[4]);
140            f = f.wrapping_add(s[5]);
141            g = g.wrapping_add(s[6]);
142            h = h.wrapping_add(s[7]);
143
144            s[0] = a;
145            s[1] = b;
146            s[2] = c;
147            s[3] = d;
148            s[4] = e;
149            s[5] = f;
150            s[6] = g;
151            s[7] = h;
152        }
153    }
154}
155
156/// Internal struct for SHA512.
157/// This uses a private bound so that you cannot instantiate it directly and have to use the
158/// provided and NIST-approved parameters.
159#[derive(Clone)]
160pub struct SHA512Internal<PARAMS: SHA2Params> {
161    _params: std::marker::PhantomData<PARAMS>,
162    state: Sha512State<PARAMS>,
163    // NOTE The code currently only supports 2^67 bits, not the full 2^128
164    byte_count: u64, 
165    x_buf: Secret<[u8; 128]>,
166    x_buf_off: usize,
167}
168
169impl<PARAMS: SHA2Params> SHA512Internal<PARAMS> {
170    /// Creates a new SHA512 instance, ready for use.
171    pub fn new() -> Self {
172        Self {
173            _params: std::marker::PhantomData,
174            state: Sha512State::<PARAMS>::new(),
175            byte_count: 0,
176            x_buf: Secret::new(),
177            x_buf_off: 0_usize,
178        }
179    }
180}
181
182impl<PARAMS: SHA2Params> Default for SHA512Internal<PARAMS> {
183    fn default() -> Self {
184        Self::new()
185    }
186}
187
188impl<PARAMS: SHA2Params> Algorithm for SHA512Internal<PARAMS> {
189    const ALG_NAME: &'static str = PARAMS::ALG_NAME;
190    const MAX_SECURITY_STRENGTH: SecurityStrength = PARAMS::MAX_SECURITY_STRENGTH;
191}
192
193impl<PARAMS: SHA2Params> Hash for SHA512Internal<PARAMS> {
194    /// As per FIPS 180-4 Figure 1
195    fn block_bitlen(&self) -> usize {
196        1024
197    }
198
199    fn output_len(&self) -> usize {
200        PARAMS::OUTPUT_LEN
201    }
202
203    fn hash(self, data: &[u8]) -> Vec<u8> {
204        let mut output = vec![0u8; self.output_len()];
205        self.hash_out(data, &mut output);
206        output
207    }
208
209    fn hash_out(mut self, data: &[u8], output: &mut [u8]) -> usize {
210        output.fill(0);
211
212        self.do_update(data);
213        self.do_final_out(output)
214    }
215
216    fn do_update(&mut self, block: &[u8]) {
217        let len = block.len();
218
219        // TODO: Check there is enough space left in 'byte_count' to allow this operation,
220        // TODO: although overflowing a u64 is unlikely to happen in practice, and rust will throw an error anyway.
221        self.byte_count += len as u64;
222
223        let available = 128 - self.x_buf_off;
224        if len < available {
225            self.x_buf[self.x_buf_off..self.x_buf_off + len].copy_from_slice(block);
226            self.x_buf_off += len;
227            return;
228        }
229
230        let mut block = block;
231        if self.x_buf_off != 0 {
232            self.x_buf[self.x_buf_off..].copy_from_slice(&block[..available]);
233            block = &block[available..];
234
235            self.state.compress(slice::from_ref(&self.x_buf));
236            //self.x_buf_off = 0;
237        }
238
239        let (chunks, remainder) = block.as_chunks::<128>();
240
241        self.state.compress(chunks);
242
243        let remaining = remainder.len();
244        self.x_buf[..remaining].copy_from_slice(remainder);
245        self.x_buf_off = remaining;
246    }
247
248    fn do_final(self) -> Vec<u8> {
249        let mut output = vec![0u8; PARAMS::OUTPUT_LEN];
250        self.do_final_out(&mut output);
251        output
252    }
253
254    fn do_final_out(mut self, output: &mut [u8]) -> usize {
255        output.fill(0);
256
257        let n = *min(&output.len(), &PARAMS::OUTPUT_LEN);
258
259        let bit_len_hi: u64 = self.byte_count >> 61;
260        let bit_len_lo: u64 = self.byte_count << 3;
261
262        self.x_buf[self.x_buf_off] = 0x80;
263        self.x_buf_off += 1;
264
265        if self.x_buf_off > 112 {
266            self.x_buf[self.x_buf_off..].fill(0x00);
267            self.state.compress(slice::from_ref(&self.x_buf));
268            self.x_buf_off = 0;
269        }
270
271        self.x_buf[self.x_buf_off..112].fill(0x00);
272        self.x_buf[112..120].copy_from_slice(&bit_len_hi.to_be_bytes());
273        self.x_buf[120..128].copy_from_slice(&bit_len_lo.to_be_bytes());
274        self.state.compress(slice::from_ref(&self.x_buf));
275
276        let h = &self.state.h;
277
278        for i in 0..(n / 8) {
279            output[i * 8..i * 8 + 8].copy_from_slice(&h[i].to_be_bytes());
280        }
281        if !n.is_multiple_of(8) {
282            output[((n / 8) * 8)..((n / 8) * 8) + (n % 8)]
283                .copy_from_slice(&h[n / 8].to_be_bytes()[0..(n % 8)]);
284        }
285
286        n
287    }
288
289    /// TODO: This is defined in FIPS 180-4 s. 5.1.2
290    /// TODO: <https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html>
291    /// TODO: It can be implemented if required
292    #[allow(unused)]
293    fn do_final_partial_bits(
294        self,
295        partial_byte: u8,
296        num_partial_bits: usize,
297    ) -> Result<Vec<u8>, HashError> {
298        unimplemented!()
299    }
300
301    /// TODO: This is defined in FIPS 180-4 s. 5.1.2
302    /// TODO: <https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html>
303    /// TODO: It can be implemented if required
304    #[allow(unused)]
305    fn do_final_partial_bits_out(
306        self,
307        partial_byte: u8,
308        num_partial_bits: usize,
309        output: &mut [u8],
310    ) -> Result<usize, HashError> {
311        unimplemented!()
312    }
313
314    fn max_security_strength(&self) -> SecurityStrength {
315        SecurityStrength::from_bytes(PARAMS::OUTPUT_LEN / 2)
316    }
317}
318
319/// Length in bytes of the serialized state of SHA384 and SHA512.
320pub const SUSPENDED_SHA512_STATE_LEN: usize = 204;
321
322impl<PARAMS: SHA2Params> Suspendable<SUSPENDED_SHA512_STATE_LEN> for SHA512Internal<PARAMS> {
323    fn suspend(self) -> [u8; SUSPENDED_SHA512_STATE_LEN] {
324        debug_assert_eq!(SUSPENDED_SHA512_STATE_LEN, 204);
325
326        let mut out_to_return = [0u8; SUSPENDED_SHA512_STATE_LEN];
327
328        // insert the version tag
329        // infallible: add_lib_ver returns a slice of exactly SUSPENDED_SHA512_STATE_LEN - 3 = 201 bytes.
330        let out: &mut [u8; 201] = add_lib_ver(&mut out_to_return).try_into().unwrap();
331
332        // state.h: [u64; 8]
333        // 8 * 8 = 64
334        for i in 0..8 {
335            out[i * 8..(i * 8) + 8].copy_from_slice(&self.state.h[i].to_le_bytes());
336        }
337
338        // byte_count: u64
339        out[64..72].copy_from_slice(&self.byte_count.to_le_bytes());
340
341        // x_buf: [u8; 128]
342        out[72..200].copy_from_slice(&*self.x_buf);
343
344        // x_buf_off: usize
345        // in general, a usize should be serialized into a u64, but in this case, it can't ever be larger than 128
346        debug_assert!(self.x_buf_off < 128);
347        out[200] = self.x_buf_off as u8;
348
349        out_to_return
350    }
351
352    fn from_suspended(
353        serialized_state: [u8; SUSPENDED_SHA512_STATE_LEN],
354    ) -> Result<Self, SuspendableError> {
355        // check the version tag
356        // At the moment, we have no not_before version to specify.
357        // infallible: check_lib_ver returns a slice of exactly SUSPENDED_SHA512_STATE_LEN - 3 = 201 bytes.
358        let input: &[u8; 201] = check_lib_ver(&serialized_state, None)?.try_into().unwrap();
359
360        // state.h: [u64; 8]
361        // 8 * 8 = 64
362        let mut h = Secret::<[u64; 8]>::new();
363        for i in 0..8 {
364            h[i] = u64::from_le_bytes(input[i * 8..(i * 8) + 8].try_into().unwrap());
365        }
366
367        // byte_count: u64
368        let byte_count: u64 = u64::from_le_bytes(input[64..72].try_into().unwrap());
369
370        // x_buf: [u8; 128]
371        let mut x_buf = Secret::<[u8; 128]>::new();
372        x_buf.copy_from_slice(&input[72..200]);
373
374        // x_buf_off: usize
375        // in general, a usize should be serialized into a u64, but in this case, it can't ever be larger than 128
376        let x_buf_off: usize = input[200] as usize;
377        if x_buf_off >= 128 {
378            return Err(SuspendableError::InvalidData);
379        }
380
381        // Construct the object
382        let state = Sha512State { _params: core::marker::PhantomData, h };
383        Ok(SHA512Internal {
384            _params: core::marker::PhantomData,
385            state,
386            byte_count,
387            x_buf,
388            x_buf_off,
389        })
390    }
391}