dcrypt_algorithms/hash/sha3/
mod.rs

1//! SHA-3 hash function implementations
2//!
3//! Constant-time & side-channel-hardened Keccak sponge (FIPS 202).
4
5#[cfg(not(feature = "std"))]
6use alloc::vec::Vec;
7use zeroize::Zeroize;
8
9use crate::error::{validate, Result};
10use crate::hash::{Hash, HashAlgorithm, HashFunction};
11use crate::types::Digest;
12
13use core::sync::atomic::{compiler_fence, Ordering};
14
15// ──────────────────────────────── constants ────────────────────────────────
16
17use dcrypt_params::utils::hash::{
18    SHA3_224_OUTPUT_SIZE, SHA3_256_OUTPUT_SIZE, SHA3_384_OUTPUT_SIZE, SHA3_512_OUTPUT_SIZE,
19};
20
21const KECCAK_ROUNDS: usize = 24;
22const KECCAK_STATE_SIZE: usize = 25; // 5 × 5 u64
23const SHA3_224_RATE: usize = 144; // 1152 bits
24const SHA3_256_RATE: usize = 136; // 1088 bits
25const SHA3_384_RATE: usize = 104; // 832 bits
26const SHA3_512_RATE: usize = 72; // 576 bits
27
28/// Keccak round constants.
29const RC: [u64; KECCAK_ROUNDS] = [
30    0x0000_0000_0000_0001,
31    0x0000_0000_0000_8082,
32    0x8000_0000_0000_808A,
33    0x8000_0000_8000_8000,
34    0x0000_0000_0000_808B,
35    0x0000_0000_8000_0001,
36    0x8000_0000_8000_8081,
37    0x8000_0000_0000_8009,
38    0x0000_0000_0000_008A,
39    0x0000_0000_0000_0088,
40    0x0000_0000_8000_8009,
41    0x0000_0000_8000_000A,
42    0x0000_0000_8000_808B,
43    0x8000_0000_0000_008B,
44    0x8000_0000_0000_8089,
45    0x8000_0000_0000_8003,
46    0x8000_0000_0000_8002,
47    0x8000_0000_0000_0080,
48    0x0000_0000_0000_800A,
49    0x8000_0000_8000_000A,
50    0x8000_0000_8000_8081,
51    0x8000_0000_0000_8080,
52    0x0000_0000_8000_0001,
53    0x8000_0000_8000_8008,
54];
55
56/// Rotation offsets for the ρ step.
57const RHO: [u32; 24] = [
58    1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
59];
60
61/// π-mapping indexes.
62const PI: [usize; 24] = [
63    10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
64];
65
66// ────────────────────────── constant-time helpers ─────────────────────────
67
68#[inline(always)]
69fn get_byte_from_state(state: &[u64; KECCAK_STATE_SIZE], pos: usize) -> u8 {
70    let word = pos / 8;
71    let shift = (pos % 8) * 8;
72    ((state[word] >> shift) & 0xFF) as u8
73}
74
75#[inline(always)]
76fn xor_byte_in_state(state: &mut [u64; KECCAK_STATE_SIZE], pos: usize, val: u8) {
77    // Perform an unconditionally-executed, explicit read-modify-write so
78    // hashing an all-zero block still incurs the same memory traffic as
79    // hashing random data (mitigates store-elimination optimisations).
80    let word = pos / 8;
81    let shift = (pos % 8) * 8;
82    let mask = (val as u64) << shift;
83
84    let before = state[word];
85    state[word] = before ^ mask;
86
87    // Prevent the compiler from hoisting or eliminating the store.
88    compiler_fence(Ordering::SeqCst);
89}
90
91// ──────────────────────── marker algorithm types ──────────────────────────
92
93/// Marker type for **SHA3-224**.
94pub enum Sha3_224Algorithm {}
95/// Marker type for **SHA3-256**.
96pub enum Sha3_256Algorithm {}
97/// Marker type for **SHA3-384**.
98pub enum Sha3_384Algorithm {}
99/// Marker type for **SHA3-512**.
100pub enum Sha3_512Algorithm {}
101
102impl HashAlgorithm for Sha3_224Algorithm {
103    const OUTPUT_SIZE: usize = SHA3_224_OUTPUT_SIZE;
104    const BLOCK_SIZE: usize = SHA3_224_RATE;
105    const ALGORITHM_ID: &'static str = "SHA3-224";
106}
107impl HashAlgorithm for Sha3_256Algorithm {
108    const OUTPUT_SIZE: usize = SHA3_256_OUTPUT_SIZE;
109    const BLOCK_SIZE: usize = SHA3_256_RATE;
110    const ALGORITHM_ID: &'static str = "SHA3-256";
111}
112impl HashAlgorithm for Sha3_384Algorithm {
113    const OUTPUT_SIZE: usize = SHA3_384_OUTPUT_SIZE;
114    const BLOCK_SIZE: usize = SHA3_384_RATE;
115    const ALGORITHM_ID: &'static str = "SHA3-384";
116}
117impl HashAlgorithm for Sha3_512Algorithm {
118    const OUTPUT_SIZE: usize = SHA3_512_OUTPUT_SIZE;
119    const BLOCK_SIZE: usize = SHA3_512_RATE;
120    const ALGORITHM_ID: &'static str = "SHA3-512";
121}
122
123// ───────────────────── engine structs (state + pointer) ───────────────────
124
125/// Streaming **SHA3-224** engine.
126#[derive(Clone, Zeroize)]
127pub struct Sha3_224 {
128    state: [u64; KECCAK_STATE_SIZE],
129    pt: usize,
130}
131
132/// Streaming **SHA3-256** engine.
133#[derive(Clone, Zeroize)]
134pub struct Sha3_256 {
135    state: [u64; KECCAK_STATE_SIZE],
136    pt: usize,
137}
138
139/// Streaming **SHA3-384** engine.
140#[derive(Clone, Zeroize)]
141pub struct Sha3_384 {
142    state: [u64; KECCAK_STATE_SIZE],
143    pt: usize,
144}
145
146/// Streaming **SHA3-512** engine.
147#[derive(Clone, Zeroize)]
148pub struct Sha3_512 {
149    state: [u64; KECCAK_STATE_SIZE],
150    pt: usize,
151}
152
153// ─────────────────────── shared engine-helper macro ───────────────────────
154
155macro_rules! impl_sha3_variant {
156    ($name:ident, $rate:expr, $out:expr, $alg:ty) => {
157        impl $name {
158            #[inline(always)]
159            fn init() -> Self {
160                Self {
161                    state: [0u64; KECCAK_STATE_SIZE],
162                    pt: 0,
163                }
164            }
165            #[inline(always)]
166            fn rate() -> usize {
167                $rate
168            }
169
170            fn update_internal(&mut self, data: &[u8]) -> Result<()> {
171                validate::parameter(
172                    self.pt.checked_add(data.len()).is_some(),
173                    "data_length",
174                    "Integer overflow",
175                )?;
176                let r = Self::rate();
177                for &b in data {
178                    xor_byte_in_state(&mut self.state, self.pt, b);
179                    self.pt += 1;
180                    if self.pt == r {
181                        keccak_f1600(&mut self.state);
182                        self.pt = 0;
183                    }
184                }
185                Ok(())
186            }
187
188            fn finalize_internal(&mut self) -> Result<Hash> {
189                let r = Self::rate();
190                xor_byte_in_state(&mut self.state, self.pt, 0x06);
191                xor_byte_in_state(&mut self.state, r - 1, 0x80);
192                keccak_f1600(&mut self.state);
193
194                let mut out = vec![0u8; $out];
195                for i in 0..$out {
196                    out[i] = get_byte_from_state(&self.state, i);
197                }
198
199                self.state = [0u64; KECCAK_STATE_SIZE];
200                self.pt = 0;
201                Ok(out)
202            }
203        }
204
205        impl HashFunction for $name {
206            type Algorithm = $alg;
207            type Output = Digest<$out>;
208
209            fn new() -> Self {
210                Self::init()
211            }
212
213            fn update(&mut self, data: &[u8]) -> Result<&mut Self> {
214                self.update_internal(data)?;
215                Ok(self)
216            }
217
218            fn finalize(&mut self) -> Result<Self::Output> {
219                let h = self.finalize_internal()?;
220                let mut d = [0u8; $out];
221                d.copy_from_slice(&h);
222                Ok(Digest::new(d))
223            }
224
225            #[inline(always)]
226            fn output_size() -> usize {
227                <$alg as HashAlgorithm>::OUTPUT_SIZE
228            }
229            #[inline(always)]
230            fn block_size() -> usize {
231                <$alg as HashAlgorithm>::BLOCK_SIZE
232            }
233            #[inline(always)]
234            fn name() -> String {
235                <$alg as HashAlgorithm>::ALGORITHM_ID.to_string()
236            }
237        }
238    };
239}
240
241impl_sha3_variant!(
242    Sha3_224,
243    SHA3_224_RATE,
244    SHA3_224_OUTPUT_SIZE,
245    Sha3_224Algorithm
246);
247impl_sha3_variant!(
248    Sha3_256,
249    SHA3_256_RATE,
250    SHA3_256_OUTPUT_SIZE,
251    Sha3_256Algorithm
252);
253impl_sha3_variant!(
254    Sha3_384,
255    SHA3_384_RATE,
256    SHA3_384_OUTPUT_SIZE,
257    Sha3_384Algorithm
258);
259impl_sha3_variant!(
260    Sha3_512,
261    SHA3_512_RATE,
262    SHA3_512_OUTPUT_SIZE,
263    Sha3_512Algorithm
264);
265
266// ───────────────────────────── permutation ────────────────────────────────
267
268fn keccak_f1600(state: &mut [u64; KECCAK_STATE_SIZE]) {
269    for &rc in RC.iter().take(KECCAK_ROUNDS) {
270        // θ
271        let mut c = [0u64; 5];
272        for x in 0..5 {
273            c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
274        }
275        for x in 0..5 {
276            let d = c[(x + 4) % 5] ^ c[(x + 1) % 5].rotate_left(1);
277            for y in 0..5 {
278                state[x + 5 * y] ^= d;
279            }
280        }
281        // ρ + π
282        let mut t = state[1];
283        for i in 0..24 {
284            let j = PI[i];
285            let tmp = state[j];
286            state[j] = t.rotate_left(RHO[i]);
287            t = tmp;
288        }
289        // χ
290        for y in 0..5 {
291            let mut row = [0u64; 5];
292            for x in 0..5 {
293                row[x] = state[x + 5 * y];
294            }
295            for x in 0..5 {
296                state[x + 5 * y] ^= (!row[(x + 1) % 5]) & row[(x + 2) % 5];
297            }
298        }
299        // ι
300        state[0] ^= rc;
301    }
302}
303
304#[cfg(test)]
305mod tests;