Skip to main content

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(feature = "alloc")]
9use crate::alloc_prelude::*;
10use dcrypt_internal::zeroing::{Zeroize, ZeroizeOnDrop, Zeroizing};
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)]
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)]
97pub struct Shake256 {
98    state: [u64; KECCAK_STATE_SIZE],
99    buffer: [u8; SHAKE256_RATE],
100    buffer_idx: usize,
101}
102
103macro_rules! impl_shake_zeroize {
104    ($name:ident) => {
105        impl Zeroize for $name {
106            fn zeroize(&mut self) {
107                self.state.zeroize();
108                self.buffer.zeroize();
109                self.buffer_idx.zeroize();
110            }
111        }
112
113        impl Drop for $name {
114            fn drop(&mut self) {
115                self.zeroize();
116            }
117        }
118
119        impl ZeroizeOnDrop for $name {}
120    };
121}
122
123impl_shake_zeroize!(Shake128);
124impl_shake_zeroize!(Shake256);
125
126// Helper function for the Keccak-f[1600] permutation
127fn keccak_f1600(state: &mut [u64; KECCAK_STATE_SIZE]) {
128    for &rc in RC.iter().take(KECCAK_ROUNDS) {
129        // Theta step
130        let mut c = Zeroizing::new([0u64; 5]);
131        for x in 0..5 {
132            c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
133        }
134        let mut d = Zeroizing::new([0u64; 5]);
135        for x in 0..5 {
136            d[x] = c[(x + 4) % 5] ^ c[(x + 1) % 5].rotate_left(1);
137        }
138        for y in 0..5 {
139            for x in 0..5 {
140                state[x + 5 * y] ^= d[x];
141            }
142        }
143
144        // Rho and Pi steps
145        let mut b = Zeroizing::new([0u64; KECCAK_STATE_SIZE]);
146        let mut x = 1;
147        let mut y = 0;
148        b[0] = state[0];
149        for i in 0..24 {
150            let idx = x + 5 * y;
151            b[PI[i]] = state[idx].rotate_left(RHO[i]);
152            let temp = y;
153            y = (2 * x + 3 * y) % 5;
154            x = temp;
155        }
156
157        // Chi step
158        for y in 0..5 {
159            for x in 0..5 {
160                let idx = x + 5 * y;
161                state[idx] = b[idx] ^ ((!b[(x + 1) % 5 + 5 * y]) & b[(x + 2) % 5 + 5 * y]);
162            }
163        }
164
165        // Iota step
166        state[0] ^= rc;
167    }
168}
169
170impl Shake128 {
171    fn init() -> Self {
172        Shake128 {
173            state: [0u64; KECCAK_STATE_SIZE],
174            buffer: [0u8; SHAKE128_RATE],
175            buffer_idx: 0,
176        }
177    }
178
179    fn update_internal(&mut self, data: &[u8]) -> Result<()> {
180        let mut idx = 0;
181
182        // Fill existing partial block
183        if self.buffer_idx > 0 {
184            let to_copy = (SHAKE128_RATE - self.buffer_idx).min(data.len());
185            self.buffer[self.buffer_idx..self.buffer_idx + to_copy]
186                .copy_from_slice(&data[..to_copy]);
187            self.buffer_idx += to_copy;
188            idx += to_copy;
189
190            if self.buffer_idx == SHAKE128_RATE {
191                // Absorb full block
192                for (i, chunk) in self.buffer.chunks_exact(8).enumerate() {
193                    let mut lane = 0u64;
194                    for (j, &b) in chunk.iter().enumerate() {
195                        lane |= (b as u64) << (8 * j);
196                    }
197                    self.state[i] ^= lane;
198                    lane.zeroize();
199                }
200                keccak_f1600(&mut self.state);
201                self.buffer.zeroize();
202                self.buffer_idx = 0;
203            }
204        }
205
206        // Process full blocks
207        while idx + SHAKE128_RATE <= data.len() {
208            let block = &data[idx..idx + SHAKE128_RATE];
209            for (i, chunk) in block.chunks_exact(8).enumerate() {
210                let mut lane = 0u64;
211                for (j, &b) in chunk.iter().enumerate() {
212                    lane |= (b as u64) << (8 * j);
213                }
214                self.state[i] ^= lane;
215                lane.zeroize();
216            }
217            keccak_f1600(&mut self.state);
218            idx += SHAKE128_RATE;
219        }
220
221        // Store remainder
222        if idx < data.len() {
223            let rem = data.len() - idx;
224            self.buffer[..rem].copy_from_slice(&data[idx..]);
225            self.buffer_idx = rem;
226        }
227
228        Ok(())
229    }
230
231    fn finalize_internal(&mut self) -> Result<Zeroizing<[u8; SHAKE128_OUTPUT_SIZE]>> {
232        // Padding: SHAKE domain separator 0x1F, then pad with zeros and final 0x80
233        let mut pad_block = Zeroizing::new([0u8; SHAKE128_RATE]);
234        pad_block[..self.buffer_idx].copy_from_slice(&self.buffer[..self.buffer_idx]);
235        pad_block[self.buffer_idx] = 0x1F;
236        pad_block[SHAKE128_RATE - 1] |= 0x80;
237
238        // Absorb final block
239        for (i, chunk) in pad_block.chunks_exact(8).enumerate() {
240            let mut lane = 0u64;
241            for (j, &b) in chunk.iter().enumerate() {
242                lane |= (b as u64) << (8 * j);
243            }
244            self.state[i] ^= lane;
245            lane.zeroize();
246        }
247        keccak_f1600(&mut self.state);
248
249        // Squeeze output
250        let mut result = Zeroizing::new([0u8; SHAKE128_OUTPUT_SIZE]);
251        let mut offset = 0;
252
253        while offset < SHAKE128_OUTPUT_SIZE {
254            let to_copy = (SHAKE128_OUTPUT_SIZE - offset).min(SHAKE128_RATE);
255
256            // Extract bytes from state
257            for i in 0..to_copy {
258                let lane_idx = i / 8;
259                let byte_idx = i % 8;
260                result[offset + i] = ((self.state[lane_idx] >> (8 * byte_idx)) & 0xFF) as u8;
261            }
262
263            offset += to_copy;
264
265            // Apply Keccak-f[1600] permutation if more output is needed
266            if offset < SHAKE128_OUTPUT_SIZE {
267                keccak_f1600(&mut self.state);
268            }
269        }
270
271        self.zeroize();
272        Ok(result)
273    }
274}
275
276impl Shake256 {
277    fn init() -> Self {
278        Shake256 {
279            state: [0u64; KECCAK_STATE_SIZE],
280            buffer: [0u8; SHAKE256_RATE],
281            buffer_idx: 0,
282        }
283    }
284
285    fn update_internal(&mut self, data: &[u8]) -> Result<()> {
286        let mut idx = 0;
287
288        // Fill existing partial block
289        if self.buffer_idx > 0 {
290            let to_copy = (SHAKE256_RATE - self.buffer_idx).min(data.len());
291            self.buffer[self.buffer_idx..self.buffer_idx + to_copy]
292                .copy_from_slice(&data[..to_copy]);
293            self.buffer_idx += to_copy;
294            idx += to_copy;
295
296            if self.buffer_idx == SHAKE256_RATE {
297                // Absorb full block
298                for (i, chunk) in self.buffer.chunks_exact(8).enumerate() {
299                    let mut lane = 0u64;
300                    for (j, &b) in chunk.iter().enumerate() {
301                        lane |= (b as u64) << (8 * j);
302                    }
303                    self.state[i] ^= lane;
304                    lane.zeroize();
305                }
306                keccak_f1600(&mut self.state);
307                self.buffer.zeroize();
308                self.buffer_idx = 0;
309            }
310        }
311
312        // Process full blocks
313        while idx + SHAKE256_RATE <= data.len() {
314            let block = &data[idx..idx + SHAKE256_RATE];
315            for (i, chunk) in block.chunks_exact(8).enumerate() {
316                let mut lane = 0u64;
317                for (j, &b) in chunk.iter().enumerate() {
318                    lane |= (b as u64) << (8 * j);
319                }
320                self.state[i] ^= lane;
321                lane.zeroize();
322            }
323            keccak_f1600(&mut self.state);
324            idx += SHAKE256_RATE;
325        }
326
327        // Store remainder
328        if idx < data.len() {
329            let rem = data.len() - idx;
330            self.buffer[..rem].copy_from_slice(&data[idx..]);
331            self.buffer_idx = rem;
332        }
333
334        Ok(())
335    }
336
337    fn finalize_internal(&mut self) -> Result<Zeroizing<[u8; SHAKE256_OUTPUT_SIZE]>> {
338        // Padding: SHAKE domain separator 0x1F, then pad with zeros and final 0x80
339        let mut pad_block = Zeroizing::new([0u8; SHAKE256_RATE]);
340        pad_block[..self.buffer_idx].copy_from_slice(&self.buffer[..self.buffer_idx]);
341        pad_block[self.buffer_idx] = 0x1F;
342        pad_block[SHAKE256_RATE - 1] |= 0x80;
343
344        // Absorb final block
345        for (i, chunk) in pad_block.chunks_exact(8).enumerate() {
346            let mut lane = 0u64;
347            for (j, &b) in chunk.iter().enumerate() {
348                lane |= (b as u64) << (8 * j);
349            }
350            self.state[i] ^= lane;
351            lane.zeroize();
352        }
353        keccak_f1600(&mut self.state);
354
355        // Squeeze output
356        let mut result = Zeroizing::new([0u8; SHAKE256_OUTPUT_SIZE]);
357        let mut offset = 0;
358
359        while offset < SHAKE256_OUTPUT_SIZE {
360            let to_copy = (SHAKE256_OUTPUT_SIZE - offset).min(SHAKE256_RATE);
361
362            // Extract bytes from state
363            for i in 0..to_copy {
364                let lane_idx = i / 8;
365                let byte_idx = i % 8;
366                result[offset + i] = ((self.state[lane_idx] >> (8 * byte_idx)) & 0xFF) as u8;
367            }
368
369            offset += to_copy;
370
371            // Apply Keccak-f[1600] permutation if more output is needed
372            if offset < SHAKE256_OUTPUT_SIZE {
373                keccak_f1600(&mut self.state);
374            }
375        }
376
377        self.zeroize();
378        Ok(result)
379    }
380}
381
382// Implement HashFunction for SHAKE128
383impl HashFunction for Shake128 {
384    type Algorithm = Shake128Algorithm;
385    type Output = Digest<SHAKE128_OUTPUT_SIZE>;
386
387    fn new() -> Self {
388        Self::init()
389    }
390
391    fn update(&mut self, data: &[u8]) -> Result<&mut Self> {
392        self.update_internal(data)?;
393        Ok(self)
394    }
395
396    fn finalize(&mut self) -> Result<Self::Output> {
397        let hash = self.finalize_internal()?;
398        let mut digest = Digest::<SHAKE128_OUTPUT_SIZE>::zeroed();
399        digest.as_mut().copy_from_slice(&hash[..]);
400        Ok(digest)
401    }
402
403    fn output_size() -> usize {
404        Self::Algorithm::OUTPUT_SIZE
405    }
406
407    fn block_size() -> usize {
408        Self::Algorithm::BLOCK_SIZE
409    }
410
411    fn name() -> String {
412        Self::Algorithm::ALGORITHM_ID.to_string()
413    }
414}
415
416// Implement HashFunction for SHAKE256
417impl HashFunction for Shake256 {
418    type Algorithm = Shake256Algorithm;
419    type Output = Digest<SHAKE256_OUTPUT_SIZE>;
420
421    fn new() -> Self {
422        Self::init()
423    }
424
425    fn update(&mut self, data: &[u8]) -> Result<&mut Self> {
426        self.update_internal(data)?;
427        Ok(self)
428    }
429
430    fn finalize(&mut self) -> Result<Self::Output> {
431        let hash = self.finalize_internal()?;
432        let mut digest = Digest::<SHAKE256_OUTPUT_SIZE>::zeroed();
433        digest.as_mut().copy_from_slice(&hash[..]);
434        Ok(digest)
435    }
436
437    fn output_size() -> usize {
438        Self::Algorithm::OUTPUT_SIZE
439    }
440
441    fn block_size() -> usize {
442        Self::Algorithm::BLOCK_SIZE
443    }
444
445    fn name() -> String {
446        Self::Algorithm::ALGORITHM_ID.to_string()
447    }
448}
449
450#[cfg(test)]
451mod tests;