Skip to main content

noxtls_crypto/hash/mdigest/
sha512.rs

1// Copyright (c) 2019-2026, Argenox Technologies LLC
2// All rights reserved.
3//
4// SPDX-License-Identifier: GPL-2.0-only OR LicenseRef-Argenox-Commercial-License
5//
6// This file is part of the NoxTLS Library.
7//
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU General Public License as published by the
10// Free Software Foundation; version 2 of the License.
11//
12// Alternatively, this file may be used under the terms of a commercial
13// license from Argenox Technologies LLC.
14//
15// See `noxtls/LICENSE` and `noxtls/LICENSE.md` in this repository for full details.
16// CONTACT: info@argenox.com
17
18use super::Digest;
19use crate::internal_alloc::Vec;
20
21/// Implements SHA-512 compression and streaming digest state.
22pub struct Sha512 {
23    state: [u64; 8],
24    buffer: [u8; 128],
25    buffer_len: usize,
26    bit_len: u128,
27}
28
29impl Default for Sha512 {
30    /// Builds a SHA-512 hasher with standard IV constants and an empty buffer.
31    ///
32    /// # Returns
33    ///
34    /// Fresh [`Sha512`] in the initial state.
35    ///
36    /// # Panics
37    ///
38    /// This function does not panic.
39    fn default() -> Self {
40        Self {
41            state: [
42                0x6a09e667f3bcc908,
43                0xbb67ae8584caa73b,
44                0x3c6ef372fe94f82b,
45                0xa54ff53a5f1d36f1,
46                0x510e527fade682d1,
47                0x9b05688c2b3e6c1f,
48                0x1f83d9abfb41bd6b,
49                0x5be0cd19137e2179,
50            ],
51            buffer: [0_u8; 128],
52            buffer_len: 0,
53            bit_len: 0,
54        }
55    }
56}
57
58impl Sha512 {
59    /// Creates a new SHA-512 hasher initialized with standard IV constants.
60    ///
61    /// # Returns
62    /// A fresh `Sha512` instance with empty input state.
63    ///
64    /// # Panics
65    ///
66    /// This function does not panic.
67    pub fn new() -> Self {
68        Self::default()
69    }
70
71    /// Builds internal SHA-512 machinery initialized for the SHA-384 IV set.
72    ///
73    /// # Returns
74    ///
75    /// [`Sha512`] configured for SHA-384 digests (48-byte truncation at the API boundary).
76    ///
77    /// # Panics
78    ///
79    /// This function does not panic.
80    fn new_sha384() -> Self {
81        Self {
82            state: [
83                0xcbbb9d5dc1059ed8,
84                0x629a292a367cd507,
85                0x9159015a3070dd17,
86                0x152fecd8f70e5939,
87                0x67332667ffc00b31,
88                0x8eb44a8768581511,
89                0xdb0c2e0d64f98fa7,
90                0x47b5481dbefa4fa4,
91            ],
92            ..Self::default()
93        }
94    }
95
96    /// Compresses one 1024-bit SHA-512 message block into the eight working words.
97    ///
98    /// # Arguments
99    ///
100    /// * `self` — Running hasher whose `state` is updated.
101    /// * `block` — One fully prepared 128-byte big-endian message block.
102    ///
103    /// # Returns
104    ///
105    /// `()`; folds the round function output into `self.state`.
106    ///
107    /// # Panics
108    ///
109    /// Panics only if internal word parsing fails; callers pass full blocks produced by this type.
110    fn compress(&mut self, block: &[u8; 128]) {
111        const K: [u64; 80] = [
112            0x428a2f98d728ae22,
113            0x7137449123ef65cd,
114            0xb5c0fbcfec4d3b2f,
115            0xe9b5dba58189dbbc,
116            0x3956c25bf348b538,
117            0x59f111f1b605d019,
118            0x923f82a4af194f9b,
119            0xab1c5ed5da6d8118,
120            0xd807aa98a3030242,
121            0x12835b0145706fbe,
122            0x243185be4ee4b28c,
123            0x550c7dc3d5ffb4e2,
124            0x72be5d74f27b896f,
125            0x80deb1fe3b1696b1,
126            0x9bdc06a725c71235,
127            0xc19bf174cf692694,
128            0xe49b69c19ef14ad2,
129            0xefbe4786384f25e3,
130            0x0fc19dc68b8cd5b5,
131            0x240ca1cc77ac9c65,
132            0x2de92c6f592b0275,
133            0x4a7484aa6ea6e483,
134            0x5cb0a9dcbd41fbd4,
135            0x76f988da831153b5,
136            0x983e5152ee66dfab,
137            0xa831c66d2db43210,
138            0xb00327c898fb213f,
139            0xbf597fc7beef0ee4,
140            0xc6e00bf33da88fc2,
141            0xd5a79147930aa725,
142            0x06ca6351e003826f,
143            0x142929670a0e6e70,
144            0x27b70a8546d22ffc,
145            0x2e1b21385c26c926,
146            0x4d2c6dfc5ac42aed,
147            0x53380d139d95b3df,
148            0x650a73548baf63de,
149            0x766a0abb3c77b2a8,
150            0x81c2c92e47edaee6,
151            0x92722c851482353b,
152            0xa2bfe8a14cf10364,
153            0xa81a664bbc423001,
154            0xc24b8b70d0f89791,
155            0xc76c51a30654be30,
156            0xd192e819d6ef5218,
157            0xd69906245565a910,
158            0xf40e35855771202a,
159            0x106aa07032bbd1b8,
160            0x19a4c116b8d2d0c8,
161            0x1e376c085141ab53,
162            0x2748774cdf8eeb99,
163            0x34b0bcb5e19b48a8,
164            0x391c0cb3c5c95a63,
165            0x4ed8aa4ae3418acb,
166            0x5b9cca4f7763e373,
167            0x682e6ff3d6b2b8a3,
168            0x748f82ee5defb2fc,
169            0x78a5636f43172f60,
170            0x84c87814a1f0ab72,
171            0x8cc702081a6439ec,
172            0x90befffa23631e28,
173            0xa4506cebde82bde9,
174            0xbef9a3f7b2c67915,
175            0xc67178f2e372532b,
176            0xca273eceea26619c,
177            0xd186b8c721c0c207,
178            0xeada7dd6cde0eb1e,
179            0xf57d4f7fee6ed178,
180            0x06f067aa72176fba,
181            0x0a637dc5a2c898a6,
182            0x113f9804bef90dae,
183            0x1b710b35131c471b,
184            0x28db77f523047d84,
185            0x32caab7b40c72493,
186            0x3c9ebe0a15c9bebc,
187            0x431d67c49c100d4c,
188            0x4cc5d4becb3e42b6,
189            0x597f299cfc657e2a,
190            0x5fcb6fab3ad6faec,
191            0x6c44198c4a475817,
192        ];
193        let mut w = [0_u64; 80];
194        for (i, chunk) in block.chunks_exact(8).enumerate().take(16) {
195            w[i] = u64::from_be_bytes(
196                chunk
197                    .try_into()
198                    .expect("sha512 block chunk length must be 8"),
199            );
200        }
201        for i in 16..80 {
202            let s0 = w[i - 15].rotate_right(1) ^ w[i - 15].rotate_right(8) ^ (w[i - 15] >> 7);
203            let s1 = w[i - 2].rotate_right(19) ^ w[i - 2].rotate_right(61) ^ (w[i - 2] >> 6);
204            w[i] = w[i - 16]
205                .wrapping_add(s0)
206                .wrapping_add(w[i - 7])
207                .wrapping_add(s1);
208        }
209
210        let mut a = self.state[0];
211        let mut b = self.state[1];
212        let mut c = self.state[2];
213        let mut d = self.state[3];
214        let mut e = self.state[4];
215        let mut f = self.state[5];
216        let mut g = self.state[6];
217        let mut h = self.state[7];
218
219        for i in 0..80 {
220            let s1 = e.rotate_right(14) ^ e.rotate_right(18) ^ e.rotate_right(41);
221            let ch = (e & f) ^ ((!e) & g);
222            let temp1 = h
223                .wrapping_add(s1)
224                .wrapping_add(ch)
225                .wrapping_add(K[i])
226                .wrapping_add(w[i]);
227            let s0 = a.rotate_right(28) ^ a.rotate_right(34) ^ a.rotate_right(39);
228            let maj = (a & b) ^ (a & c) ^ (b & c);
229            let temp2 = s0.wrapping_add(maj);
230
231            h = g;
232            g = f;
233            f = e;
234            e = d.wrapping_add(temp1);
235            d = c;
236            c = b;
237            b = a;
238            a = temp1.wrapping_add(temp2);
239        }
240
241        self.state[0] = self.state[0].wrapping_add(a);
242        self.state[1] = self.state[1].wrapping_add(b);
243        self.state[2] = self.state[2].wrapping_add(c);
244        self.state[3] = self.state[3].wrapping_add(d);
245        self.state[4] = self.state[4].wrapping_add(e);
246        self.state[5] = self.state[5].wrapping_add(f);
247        self.state[6] = self.state[6].wrapping_add(g);
248        self.state[7] = self.state[7].wrapping_add(h);
249    }
250}
251
252impl Digest for Sha512 {
253    /// Absorbs `data` into the running digest, compressing whenever 128 bytes accumulate.
254    ///
255    /// # Arguments
256    ///
257    /// * `self` — Running SHA-512 hasher.
258    /// * `data` — Next message bytes to append.
259    ///
260    /// # Returns
261    ///
262    /// `()`.
263    ///
264    /// # Panics
265    ///
266    /// This function does not panic.
267    fn update(&mut self, mut data: &[u8]) {
268        self.bit_len = self.bit_len.wrapping_add((data.len() as u128) * 8);
269        while !data.is_empty() {
270            let to_copy = (128 - self.buffer_len).min(data.len());
271            self.buffer[self.buffer_len..self.buffer_len + to_copy]
272                .copy_from_slice(&data[..to_copy]);
273            self.buffer_len += to_copy;
274            data = &data[to_copy..];
275            if self.buffer_len == 128 {
276                let block = self.buffer;
277                self.compress(&block);
278                self.buffer_len = 0;
279            }
280        }
281    }
282
283    /// Finalizes SHA-512 padding and returns the 64-byte digest in a newly allocated vector.
284    ///
285    /// # Arguments
286    ///
287    /// * `self` — Consumed hasher holding buffered tail bytes and the 128-bit length counter.
288    ///
289    /// # Returns
290    ///
291    /// [`Vec`] containing exactly 64 digest bytes (big-endian words from internal state).
292    ///
293    /// # Panics
294    ///
295    /// This function does not panic.
296    fn finalize(mut self) -> Vec<u8> {
297        self.buffer[self.buffer_len] = 0x80;
298        self.buffer_len += 1;
299        if self.buffer_len > 112 {
300            self.buffer[self.buffer_len..].fill(0);
301            let block = self.buffer;
302            self.compress(&block);
303            self.buffer_len = 0;
304        }
305        self.buffer[self.buffer_len..112].fill(0);
306        self.buffer[112..128].copy_from_slice(&self.bit_len.to_be_bytes());
307        let block = self.buffer;
308        self.compress(&block);
309
310        let mut out = Vec::with_capacity(64);
311        for word in self.state {
312            out.extend_from_slice(&word.to_be_bytes());
313        }
314        out
315    }
316}
317
318/// Computes SHA-512 of `data` and returns 64 digest bytes.
319///
320/// # Arguments
321/// * `data`: Input bytes to hash.
322///
323/// # Returns
324/// A 64-byte SHA-512 digest.
325///
326/// # Panics
327///
328/// This function does not panic.
329#[must_use]
330pub fn sha512(data: &[u8]) -> [u8; 64] {
331    let mut hasher = Sha512::new();
332    hasher.update(data);
333    let digest = hasher.finalize();
334    let mut out = [0_u8; 64];
335    out.copy_from_slice(&digest);
336    out
337}
338
339/// Computes SHA-384 of `data` and returns 48 digest bytes.
340///
341/// # Arguments
342/// * `data`: Input bytes to hash.
343///
344/// # Returns
345/// A 48-byte SHA-384 digest.
346///
347/// # Panics
348///
349/// This function does not panic.
350#[must_use]
351pub fn sha384(data: &[u8]) -> [u8; 48] {
352    let mut hasher = Sha512::new_sha384();
353    hasher.update(data);
354    let digest = hasher.finalize();
355    let mut out = [0_u8; 48];
356    out.copy_from_slice(&digest[..48]);
357    out
358}