dcrypt_algorithms/hash/shake/
mod.rs

1//! SHAKE hash functions with fixed output length
2//!
3//! This module implements the SHAKE family as standard fixed-output hash functions
4//! as specified in FIPS PUB 202.
5//!
6//! For variable-length output, use the XOF implementations in the xof module.
7
8#[cfg(not(feature = "std"))]
9use alloc::vec::Vec;
10use zeroize::{Zeroize, ZeroizeOnDrop};
11
12use crate::error::Result;
13use crate::hash::{HashAlgorithm, HashFunction};
14use crate::types::Digest;
15
16/// Default output size for SHAKE128 (256 bits / 32 bytes)
17pub const SHAKE128_OUTPUT_SIZE: usize = 32; // 256 bits
18
19/// Default output size for SHAKE256 (512 bits / 64 bytes)
20pub const SHAKE256_OUTPUT_SIZE: usize = 64; // 512 bits
21
22// SHAKE rates (in bytes): r = 1600 - 2*security_level
23const SHAKE128_RATE: usize = 168; // 1600 - 2*128 = 1344 bits = 168 bytes
24const SHAKE256_RATE: usize = 136; // 1600 - 2*256 = 1088 bits = 136 bytes
25
26// Keccak constants
27const KECCAK_ROUNDS: usize = 24;
28const KECCAK_STATE_SIZE: usize = 25; // 5x5 of 64-bit words
29
30// Round constants for Keccak
31const RC: [u64; KECCAK_ROUNDS] = [
32    0x0000000000000001,
33    0x0000000000008082,
34    0x800000000000808A,
35    0x8000000080008000,
36    0x000000000000808B,
37    0x0000000080000001,
38    0x8000000080008081,
39    0x8000000000008009,
40    0x000000000000008A,
41    0x0000000000000088,
42    0x0000000080008009,
43    0x000000008000000A,
44    0x000000008000808B,
45    0x800000000000008B,
46    0x8000000000008089,
47    0x8000000000008003,
48    0x8000000000008002,
49    0x8000000000000080,
50    0x000000000000800A,
51    0x800000008000000A,
52    0x8000000080008081,
53    0x8000000000008080,
54    0x0000000080000001,
55    0x8000000080008008,
56];
57
58// Rotation offsets
59const RHO: [u32; 24] = [
60    1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
61];
62
63// Mapping from index positions to x,y coordinates in the state array
64const PI: [usize; 24] = [
65    10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
66];
67
68/// Marker type for SHAKE128 algorithm
69pub enum Shake128Algorithm {}
70
71/// Marker type for SHAKE256 algorithm
72pub enum Shake256Algorithm {}
73
74// Implement HashAlgorithm for each marker type
75impl HashAlgorithm for Shake128Algorithm {
76    const OUTPUT_SIZE: usize = SHAKE128_OUTPUT_SIZE;
77    const BLOCK_SIZE: usize = SHAKE128_RATE;
78    const ALGORITHM_ID: &'static str = "SHAKE-128";
79}
80
81impl HashAlgorithm for Shake256Algorithm {
82    const OUTPUT_SIZE: usize = SHAKE256_OUTPUT_SIZE;
83    const BLOCK_SIZE: usize = SHAKE256_RATE;
84    const ALGORITHM_ID: &'static str = "SHAKE-256";
85}
86
87/// SHAKE-128 hash function with fixed output size (32 bytes)
88#[derive(Clone, Zeroize, ZeroizeOnDrop)]
89pub struct Shake128 {
90    state: [u64; KECCAK_STATE_SIZE],
91    buffer: [u8; SHAKE128_RATE],
92    buffer_idx: usize,
93}
94
95/// SHAKE-256 hash function with fixed output size (64 bytes)
96#[derive(Clone, Zeroize, ZeroizeOnDrop)]
97pub struct Shake256 {
98    state: [u64; KECCAK_STATE_SIZE],
99    buffer: [u8; SHAKE256_RATE],
100    buffer_idx: usize,
101}
102
103// Helper function for the Keccak-f[1600] permutation
104fn keccak_f1600(state: &mut [u64; KECCAK_STATE_SIZE]) {
105    for &rc in RC.iter().take(KECCAK_ROUNDS) {
106        // Theta step
107        let mut c = [0u64; 5];
108        for x in 0..5 {
109            c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
110        }
111        let mut d = [0u64; 5];
112        for x in 0..5 {
113            d[x] = c[(x + 4) % 5] ^ c[(x + 1) % 5].rotate_left(1);
114        }
115        for y in 0..5 {
116            for x in 0..5 {
117                state[x + 5 * y] ^= d[x];
118            }
119        }
120
121        // Rho and Pi steps
122        let mut b = [0u64; KECCAK_STATE_SIZE];
123        let mut x = 1;
124        let mut y = 0;
125        b[0] = state[0];
126        for i in 0..24 {
127            let idx = x + 5 * y;
128            b[PI[i]] = state[idx].rotate_left(RHO[i]);
129            let temp = y;
130            y = (2 * x + 3 * y) % 5;
131            x = temp;
132        }
133
134        // Chi step
135        for y in 0..5 {
136            for x in 0..5 {
137                let idx = x + 5 * y;
138                state[idx] = b[idx] ^ ((!b[(x + 1) % 5 + 5 * y]) & b[(x + 2) % 5 + 5 * y]);
139            }
140        }
141
142        // Iota step
143        state[0] ^= rc;
144    }
145}
146
147impl Shake128 {
148    fn init() -> Self {
149        Shake128 {
150            state: [0u64; KECCAK_STATE_SIZE],
151            buffer: [0u8; SHAKE128_RATE],
152            buffer_idx: 0,
153        }
154    }
155
156    fn update_internal(&mut self, data: &[u8]) -> Result<()> {
157        let mut idx = 0;
158
159        // Fill existing partial block
160        if self.buffer_idx > 0 {
161            let to_copy = (SHAKE128_RATE - self.buffer_idx).min(data.len());
162            self.buffer[self.buffer_idx..self.buffer_idx + to_copy]
163                .copy_from_slice(&data[..to_copy]);
164            self.buffer_idx += to_copy;
165            idx += to_copy;
166
167            if self.buffer_idx == SHAKE128_RATE {
168                // Absorb full block
169                for (i, chunk) in self.buffer.chunks_exact(8).enumerate() {
170                    let mut lane = 0u64;
171                    for (j, &b) in chunk.iter().enumerate() {
172                        lane |= (b as u64) << (8 * j);
173                    }
174                    self.state[i] ^= lane;
175                }
176                keccak_f1600(&mut self.state);
177                self.buffer_idx = 0;
178            }
179        }
180
181        // Process full blocks
182        while idx + SHAKE128_RATE <= data.len() {
183            let block = &data[idx..idx + SHAKE128_RATE];
184            for (i, chunk) in block.chunks_exact(8).enumerate() {
185                let mut lane = 0u64;
186                for (j, &b) in chunk.iter().enumerate() {
187                    lane |= (b as u64) << (8 * j);
188                }
189                self.state[i] ^= lane;
190            }
191            keccak_f1600(&mut self.state);
192            idx += SHAKE128_RATE;
193        }
194
195        // Store remainder
196        if idx < data.len() {
197            let rem = data.len() - idx;
198            self.buffer[..rem].copy_from_slice(&data[idx..]);
199            self.buffer_idx = rem;
200        }
201
202        Ok(())
203    }
204
205    fn finalize_internal(&mut self) -> Result<Vec<u8>> {
206        // Padding: SHAKE domain separator 0x1F, then pad with zeros and final 0x80
207        let mut pad_block = [0u8; SHAKE128_RATE];
208        pad_block[..self.buffer_idx].copy_from_slice(&self.buffer[..self.buffer_idx]);
209        pad_block[self.buffer_idx] = 0x1F;
210        pad_block[SHAKE128_RATE - 1] |= 0x80;
211
212        // Absorb final block
213        for (i, chunk) in pad_block.chunks_exact(8).enumerate() {
214            let mut lane = 0u64;
215            for (j, &b) in chunk.iter().enumerate() {
216                lane |= (b as u64) << (8 * j);
217            }
218            self.state[i] ^= lane;
219        }
220        keccak_f1600(&mut self.state);
221
222        // Squeeze output
223        let mut result = vec![0u8; SHAKE128_OUTPUT_SIZE];
224        let mut offset = 0;
225
226        while offset < SHAKE128_OUTPUT_SIZE {
227            let to_copy = (SHAKE128_OUTPUT_SIZE - offset).min(SHAKE128_RATE);
228
229            // Extract bytes from state
230            for i in 0..to_copy {
231                let lane_idx = i / 8;
232                let byte_idx = i % 8;
233                result[offset + i] = ((self.state[lane_idx] >> (8 * byte_idx)) & 0xFF) as u8;
234            }
235
236            offset += to_copy;
237
238            // Apply Keccak-f[1600] permutation if more output is needed
239            if offset < SHAKE128_OUTPUT_SIZE {
240                keccak_f1600(&mut self.state);
241            }
242        }
243
244        Ok(result)
245    }
246}
247
248impl Shake256 {
249    fn init() -> Self {
250        Shake256 {
251            state: [0u64; KECCAK_STATE_SIZE],
252            buffer: [0u8; SHAKE256_RATE],
253            buffer_idx: 0,
254        }
255    }
256
257    fn update_internal(&mut self, data: &[u8]) -> Result<()> {
258        let mut idx = 0;
259
260        // Fill existing partial block
261        if self.buffer_idx > 0 {
262            let to_copy = (SHAKE256_RATE - self.buffer_idx).min(data.len());
263            self.buffer[self.buffer_idx..self.buffer_idx + to_copy]
264                .copy_from_slice(&data[..to_copy]);
265            self.buffer_idx += to_copy;
266            idx += to_copy;
267
268            if self.buffer_idx == SHAKE256_RATE {
269                // Absorb full block
270                for (i, chunk) in self.buffer.chunks_exact(8).enumerate() {
271                    let mut lane = 0u64;
272                    for (j, &b) in chunk.iter().enumerate() {
273                        lane |= (b as u64) << (8 * j);
274                    }
275                    self.state[i] ^= lane;
276                }
277                keccak_f1600(&mut self.state);
278                self.buffer_idx = 0;
279            }
280        }
281
282        // Process full blocks
283        while idx + SHAKE256_RATE <= data.len() {
284            let block = &data[idx..idx + SHAKE256_RATE];
285            for (i, chunk) in block.chunks_exact(8).enumerate() {
286                let mut lane = 0u64;
287                for (j, &b) in chunk.iter().enumerate() {
288                    lane |= (b as u64) << (8 * j);
289                }
290                self.state[i] ^= lane;
291            }
292            keccak_f1600(&mut self.state);
293            idx += SHAKE256_RATE;
294        }
295
296        // Store remainder
297        if idx < data.len() {
298            let rem = data.len() - idx;
299            self.buffer[..rem].copy_from_slice(&data[idx..]);
300            self.buffer_idx = rem;
301        }
302
303        Ok(())
304    }
305
306    fn finalize_internal(&mut self) -> Result<Vec<u8>> {
307        // Padding: SHAKE domain separator 0x1F, then pad with zeros and final 0x80
308        let mut pad_block = [0u8; SHAKE256_RATE];
309        pad_block[..self.buffer_idx].copy_from_slice(&self.buffer[..self.buffer_idx]);
310        pad_block[self.buffer_idx] = 0x1F;
311        pad_block[SHAKE256_RATE - 1] |= 0x80;
312
313        // Absorb final block
314        for (i, chunk) in pad_block.chunks_exact(8).enumerate() {
315            let mut lane = 0u64;
316            for (j, &b) in chunk.iter().enumerate() {
317                lane |= (b as u64) << (8 * j);
318            }
319            self.state[i] ^= lane;
320        }
321        keccak_f1600(&mut self.state);
322
323        // Squeeze output
324        let mut result = vec![0u8; SHAKE256_OUTPUT_SIZE];
325        let mut offset = 0;
326
327        while offset < SHAKE256_OUTPUT_SIZE {
328            let to_copy = (SHAKE256_OUTPUT_SIZE - offset).min(SHAKE256_RATE);
329
330            // Extract bytes from state
331            for i in 0..to_copy {
332                let lane_idx = i / 8;
333                let byte_idx = i % 8;
334                result[offset + i] = ((self.state[lane_idx] >> (8 * byte_idx)) & 0xFF) as u8;
335            }
336
337            offset += to_copy;
338
339            // Apply Keccak-f[1600] permutation if more output is needed
340            if offset < SHAKE256_OUTPUT_SIZE {
341                keccak_f1600(&mut self.state);
342            }
343        }
344
345        Ok(result)
346    }
347}
348
349// Implement HashFunction for SHAKE128
350impl HashFunction for Shake128 {
351    type Algorithm = Shake128Algorithm;
352    type Output = Digest<SHAKE128_OUTPUT_SIZE>;
353
354    fn new() -> Self {
355        Self::init()
356    }
357
358    fn update(&mut self, data: &[u8]) -> Result<&mut Self> {
359        self.update_internal(data)?;
360        Ok(self)
361    }
362
363    fn finalize(&mut self) -> Result<Self::Output> {
364        let hash = self.finalize_internal()?;
365        let mut digest = [0u8; SHAKE128_OUTPUT_SIZE];
366        digest.copy_from_slice(&hash);
367        Ok(Digest::new(digest))
368    }
369
370    fn output_size() -> usize {
371        Self::Algorithm::OUTPUT_SIZE
372    }
373
374    fn block_size() -> usize {
375        Self::Algorithm::BLOCK_SIZE
376    }
377
378    fn name() -> String {
379        Self::Algorithm::ALGORITHM_ID.to_string()
380    }
381}
382
383// Implement HashFunction for SHAKE256
384impl HashFunction for Shake256 {
385    type Algorithm = Shake256Algorithm;
386    type Output = Digest<SHAKE256_OUTPUT_SIZE>;
387
388    fn new() -> Self {
389        Self::init()
390    }
391
392    fn update(&mut self, data: &[u8]) -> Result<&mut Self> {
393        self.update_internal(data)?;
394        Ok(self)
395    }
396
397    fn finalize(&mut self) -> Result<Self::Output> {
398        let hash = self.finalize_internal()?;
399        let mut digest = [0u8; SHAKE256_OUTPUT_SIZE];
400        digest.copy_from_slice(&hash);
401        Ok(Digest::new(digest))
402    }
403
404    fn output_size() -> usize {
405        Self::Algorithm::OUTPUT_SIZE
406    }
407
408    fn block_size() -> usize {
409        Self::Algorithm::BLOCK_SIZE
410    }
411
412    fn name() -> String {
413        Self::Algorithm::ALGORITHM_ID.to_string()
414    }
415}
416
417#[cfg(test)]
418mod tests;