dcrypt_algorithms/xof/shake/
mod.rs

1//! SHAKE extendable output functions
2//!
3//! This module implements the SHAKE family of extendable output functions (XOFs)
4//! as specified in FIPS PUB 202.
5//!
6//! These are distinct from the SHAKE implementations in the hash module,
7//! which provide fixed-output hash function interfaces. This module provides
8//! the proper XOF interface for variable-length output generation.
9
10#[cfg(not(feature = "std"))]
11use alloc::vec::Vec;
12use zeroize::{Zeroize, ZeroizeOnDrop};
13
14use super::ExtendableOutputFunction;
15use crate::error::{validate, Error, Result};
16
17// Import security types from dcrypt-core
18use dcrypt_common::security::{barrier, EphemeralSecret, SecretBuffer, SecureZeroingType};
19
20// SHAKE constants
21const KECCAK_ROUNDS: usize = 24;
22const KECCAK_STATE_SIZE: usize = 25; // 5x5 of 64-bit words
23
24// SHAKE rates (in bytes): r = 1600 - 2*security_level
25const SHAKE128_RATE: usize = 168; // 1600 - 2*128 = 1600 - 256 = 1344 bits = 168 bytes
26const SHAKE256_RATE: usize = 136; // 1600 - 2*256 = 1600 - 512 = 1088 bits = 136 bytes
27
28// Round constants for Keccak
29const RC: [u64; KECCAK_ROUNDS] = [
30    0x0000000000000001,
31    0x0000000000008082,
32    0x800000000000808A,
33    0x8000000080008000,
34    0x000000000000808B,
35    0x0000000080000001,
36    0x8000000080008081,
37    0x8000000000008009,
38    0x000000000000008A,
39    0x0000000000000088,
40    0x0000000080008009,
41    0x000000008000000A,
42    0x000000008000808B,
43    0x800000000000008B,
44    0x8000000000008089,
45    0x8000000000008003,
46    0x8000000000008002,
47    0x8000000000000080,
48    0x000000000000800A,
49    0x800000008000000A,
50    0x8000000080008081,
51    0x8000000000008080,
52    0x0000000080000001,
53    0x8000000080008008,
54];
55
56// Rotation offsets
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 from index positions to x,y coordinates in the state array
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// Helper struct for secure Keccak state operations
67#[derive(Clone, Zeroize)]
68struct SecureKeccakState {
69    state: SecretBuffer<200>, // 25 * 8 bytes
70}
71
72impl SecureKeccakState {
73    fn new() -> Self {
74        Self {
75            state: SecretBuffer::zeroed(),
76        }
77    }
78
79    fn from_u64_array(array: [u64; KECCAK_STATE_SIZE]) -> Self {
80        let mut bytes = [0u8; 200];
81        for (i, &word) in array.iter().enumerate() {
82            let word_bytes = word.to_le_bytes();
83            bytes[i * 8..(i + 1) * 8].copy_from_slice(&word_bytes);
84        }
85        Self {
86            state: SecretBuffer::new(bytes),
87        }
88    }
89
90    fn to_u64_array(&self) -> [u64; KECCAK_STATE_SIZE] {
91        let mut array = [0u64; KECCAK_STATE_SIZE];
92        let bytes = self.state.as_ref();
93        for (i, word) in array.iter_mut().enumerate() {
94            let start = i * 8;
95            *word = u64::from_le_bytes([
96                bytes[start],
97                bytes[start + 1],
98                bytes[start + 2],
99                bytes[start + 3],
100                bytes[start + 4],
101                bytes[start + 5],
102                bytes[start + 6],
103                bytes[start + 7],
104            ]);
105        }
106        array
107    }
108
109    fn apply_permutation(&mut self) {
110        let mut state_array = self.to_u64_array();
111        keccak_f1600(&mut state_array);
112        *self = Self::from_u64_array(state_array);
113    }
114}
115
116impl SecureZeroingType for SecureKeccakState {
117    fn zeroed() -> Self {
118        Self::new()
119    }
120
121    fn secure_clone(&self) -> Self {
122        Self {
123            state: self.state.secure_clone(),
124        }
125    }
126}
127
128/// SHAKE-128 extendable output function with secure memory handling
129#[derive(Clone, ZeroizeOnDrop)]
130pub struct ShakeXof128 {
131    state: SecureKeccakState,
132    buffer: SecretBuffer<SHAKE128_RATE>,
133    buffer_idx: usize,
134    is_finalized: bool,
135    squeezing: bool,
136}
137
138impl Zeroize for ShakeXof128 {
139    fn zeroize(&mut self) {
140        self.state.zeroize();
141        self.buffer.zeroize();
142        self.buffer_idx.zeroize();
143        self.is_finalized = false;
144        self.squeezing = false;
145    }
146}
147
148/// SHAKE-256 extendable output function with secure memory handling
149#[derive(Clone, ZeroizeOnDrop)]
150pub struct ShakeXof256 {
151    state: SecureKeccakState,
152    buffer: SecretBuffer<SHAKE256_RATE>,
153    buffer_idx: usize,
154    is_finalized: bool,
155    squeezing: bool,
156}
157
158impl Zeroize for ShakeXof256 {
159    fn zeroize(&mut self) {
160        self.state.zeroize();
161        self.buffer.zeroize();
162        self.buffer_idx.zeroize();
163        self.is_finalized = false;
164        self.squeezing = false;
165    }
166}
167
168// Helper functions for Keccak permutation
169
170/// Performs a full Keccak-f[1600] permutation on the state
171fn keccak_f1600(state: &mut [u64; KECCAK_STATE_SIZE]) {
172    // Use EphemeralSecret for temporary arrays
173    for (_round, &rc) in RC.iter().enumerate().take(KECCAK_ROUNDS) {
174        // Theta step with secure temporary storage
175        let mut c = EphemeralSecret::new([0u64; 5]);
176        for x in 0..5 {
177            c.as_mut()[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
178        }
179
180        let mut d = EphemeralSecret::new([0u64; 5]);
181        for x in 0..5 {
182            d.as_mut()[x] = c.as_ref()[(x + 4) % 5] ^ c.as_ref()[(x + 1) % 5].rotate_left(1);
183        }
184
185        for y in 0..5 {
186            for x in 0..5 {
187                state[x + 5 * y] ^= d.as_ref()[x];
188            }
189        }
190
191        // Rho and Pi steps with secure temporary storage
192        let mut b = EphemeralSecret::new([0u64; KECCAK_STATE_SIZE]);
193        let mut x = 1;
194        let mut y = 0;
195        b.as_mut()[0] = state[0];
196
197        for i in 0..24 {
198            let idx = x + 5 * y;
199            b.as_mut()[PI[i]] = state[idx].rotate_left(RHO[i]);
200            let temp = y;
201            y = (2 * x + 3 * y) % 5;
202            x = temp;
203        }
204
205        // Chi step
206        for y in 0..5 {
207            for x in 0..5 {
208                let idx = x + 5 * y;
209                state[idx] = b.as_ref()[idx]
210                    ^ ((!b.as_ref()[(x + 1) % 5 + 5 * y]) & b.as_ref()[(x + 2) % 5 + 5 * y]);
211            }
212        }
213
214        // Iota step
215        state[0] ^= rc;
216    }
217
218    // Insert memory barrier after permutation
219    barrier::compiler_fence_seq_cst();
220}
221
222/// Absorbs data into the sponge state with secure handling
223fn keccak_absorb(state: &mut SecureKeccakState, data: &[u8], rate: usize) {
224    // Get state as u64 array for processing
225    let mut state_array = state.to_u64_array();
226
227    for (i, &byte) in data.iter().enumerate() {
228        let pos = i % rate;
229        let byte_idx = pos % 8;
230        let word_idx = pos / 8;
231        state_array[word_idx] ^= (byte as u64) << (8 * byte_idx);
232    }
233
234    if !data.is_empty() && data.len() % rate == 0 {
235        keccak_f1600(&mut state_array);
236    }
237
238    // Update secure state
239    *state = SecureKeccakState::from_u64_array(state_array);
240}
241
242impl ShakeXof128 {
243    fn init() -> Self {
244        ShakeXof128 {
245            state: SecureKeccakState::new(),
246            buffer: SecretBuffer::zeroed(),
247            buffer_idx: 0,
248            is_finalized: false,
249            squeezing: false,
250        }
251    }
252}
253
254impl ExtendableOutputFunction for ShakeXof128 {
255    fn new() -> Self {
256        Self::init()
257    }
258
259    fn update(&mut self, data: &[u8]) -> Result<()> {
260        if self.is_finalized {
261            return Err(Error::xof_finalized());
262        }
263        if self.squeezing {
264            return Err(Error::xof_squeezing());
265        }
266
267        let mut idx = 0;
268        if self.buffer_idx > 0 {
269            let to_copy = (SHAKE128_RATE - self.buffer_idx).min(data.len());
270            let buffer_slice =
271                &mut self.buffer.as_mut()[self.buffer_idx..self.buffer_idx + to_copy];
272            buffer_slice.copy_from_slice(&data[..to_copy]);
273            self.buffer_idx += to_copy;
274            idx = to_copy;
275
276            if self.buffer_idx == SHAKE128_RATE {
277                keccak_absorb(&mut self.state, self.buffer.as_ref(), SHAKE128_RATE);
278                self.buffer_idx = 0;
279            }
280        }
281
282        let remaining = data.len() - idx;
283        let full_blocks = remaining / SHAKE128_RATE;
284        for i in 0..full_blocks {
285            let start = idx + i * SHAKE128_RATE;
286            let block = &data[start..start + SHAKE128_RATE];
287
288            // Process directly without copying to buffer
289            keccak_absorb(&mut self.state, block, SHAKE128_RATE);
290        }
291        idx += full_blocks * SHAKE128_RATE;
292
293        if idx < data.len() {
294            let rem = data.len() - idx;
295            self.buffer.as_mut()[..rem].copy_from_slice(&data[idx..]);
296            self.buffer_idx = rem;
297        }
298
299        Ok(())
300    }
301
302    fn finalize(&mut self) -> Result<()> {
303        if self.is_finalized {
304            return Ok(());
305        }
306
307        // Use SecretBuffer for pad block
308        let mut pad_block = SecretBuffer::<SHAKE128_RATE>::zeroed();
309        pad_block.as_mut()[..self.buffer_idx]
310            .copy_from_slice(&self.buffer.as_ref()[..self.buffer_idx]);
311        pad_block.as_mut()[self.buffer_idx] ^= 0x1F;
312        pad_block.as_mut()[SHAKE128_RATE - 1] ^= 0x80;
313
314        keccak_absorb(&mut self.state, pad_block.as_ref(), SHAKE128_RATE);
315
316        self.is_finalized = true;
317        self.buffer_idx = 0;
318        Ok(())
319    }
320
321    fn squeeze(&mut self, output: &mut [u8]) -> Result<()> {
322        validate::parameter(
323            !output.is_empty(),
324            "output_length",
325            "Output buffer must not be empty",
326        )?;
327
328        if !self.is_finalized {
329            self.finalize()?;
330        }
331        self.squeezing = true;
332
333        let mut offset = 0;
334        let rate = SHAKE128_RATE;
335
336        while offset < output.len() {
337            if self.buffer_idx >= rate {
338                self.state.apply_permutation();
339                self.buffer_idx = 0;
340            }
341
342            if self.buffer_idx == 0 {
343                // Extract state into buffer
344                let state_array = self.state.to_u64_array();
345                let buffer_mut = self.buffer.as_mut();
346
347                for i in 0..(rate / 8) {
348                    let lane = state_array[i];
349                    for j in 0..8 {
350                        if i * 8 + j < rate {
351                            buffer_mut[i * 8 + j] = ((lane >> (8 * j)) & 0xFF) as u8;
352                        }
353                    }
354                }
355            }
356
357            let available = rate - self.buffer_idx;
358            let needed = output.len() - offset;
359            let to_copy = available.min(needed);
360
361            output[offset..offset + to_copy]
362                .copy_from_slice(&self.buffer.as_ref()[self.buffer_idx..self.buffer_idx + to_copy]);
363
364            offset += to_copy;
365            self.buffer_idx += to_copy;
366        }
367
368        // Memory barrier after squeeze operation
369        barrier::compiler_fence_seq_cst();
370        Ok(())
371    }
372
373    fn squeeze_into_vec(&mut self, len: usize) -> Result<Vec<u8>> {
374        validate::parameter(
375            len > 0,
376            "output_length",
377            "Output length must be greater than 0",
378        )?;
379
380        let mut v = vec![0u8; len];
381        self.squeeze(&mut v)?;
382        Ok(v)
383    }
384
385    fn reset(&mut self) -> Result<()> {
386        *self = Self::new();
387        Ok(())
388    }
389
390    fn security_level() -> usize {
391        128
392    }
393}
394
395impl ShakeXof256 {
396    fn init() -> Self {
397        ShakeXof256 {
398            state: SecureKeccakState::new(),
399            buffer: SecretBuffer::zeroed(),
400            buffer_idx: 0,
401            is_finalized: false,
402            squeezing: false,
403        }
404    }
405}
406
407impl ExtendableOutputFunction for ShakeXof256 {
408    fn new() -> Self {
409        Self::init()
410    }
411
412    fn update(&mut self, data: &[u8]) -> Result<()> {
413        if self.is_finalized {
414            return Err(Error::xof_finalized());
415        }
416        if self.squeezing {
417            return Err(Error::xof_squeezing());
418        }
419
420        let mut idx = 0;
421        if self.buffer_idx > 0 {
422            let to_copy = (SHAKE256_RATE - self.buffer_idx).min(data.len());
423            let buffer_slice =
424                &mut self.buffer.as_mut()[self.buffer_idx..self.buffer_idx + to_copy];
425            buffer_slice.copy_from_slice(&data[..to_copy]);
426            self.buffer_idx += to_copy;
427            idx = to_copy;
428
429            if self.buffer_idx == SHAKE256_RATE {
430                keccak_absorb(&mut self.state, self.buffer.as_ref(), SHAKE256_RATE);
431                self.buffer_idx = 0;
432            }
433        }
434
435        let remaining = data.len() - idx;
436        let full_blocks = remaining / SHAKE256_RATE;
437        for i in 0..full_blocks {
438            let start = idx + i * SHAKE256_RATE;
439            let block = &data[start..start + SHAKE256_RATE];
440
441            // Process directly without copying to buffer
442            keccak_absorb(&mut self.state, block, SHAKE256_RATE);
443        }
444        idx += full_blocks * SHAKE256_RATE;
445
446        if idx < data.len() {
447            let rem = data.len() - idx;
448            self.buffer.as_mut()[..rem].copy_from_slice(&data[idx..]);
449            self.buffer_idx = rem;
450        }
451
452        Ok(())
453    }
454
455    fn finalize(&mut self) -> Result<()> {
456        if self.is_finalized {
457            return Ok(());
458        }
459
460        // Use SecretBuffer for pad block
461        let mut pad_block = SecretBuffer::<SHAKE256_RATE>::zeroed();
462        pad_block.as_mut()[..self.buffer_idx]
463            .copy_from_slice(&self.buffer.as_ref()[..self.buffer_idx]);
464        pad_block.as_mut()[self.buffer_idx] ^= 0x1F;
465        pad_block.as_mut()[SHAKE256_RATE - 1] ^= 0x80;
466
467        keccak_absorb(&mut self.state, pad_block.as_ref(), SHAKE256_RATE);
468
469        self.is_finalized = true;
470        self.buffer_idx = 0;
471        Ok(())
472    }
473
474    fn squeeze(&mut self, output: &mut [u8]) -> Result<()> {
475        validate::parameter(
476            !output.is_empty(),
477            "output_length",
478            "Output buffer must not be empty",
479        )?;
480
481        if !self.is_finalized {
482            self.finalize()?;
483        }
484        self.squeezing = true;
485
486        let mut offset = 0;
487        let rate = SHAKE256_RATE;
488
489        while offset < output.len() {
490            if self.buffer_idx >= rate {
491                self.state.apply_permutation();
492                self.buffer_idx = 0;
493            }
494
495            if self.buffer_idx == 0 {
496                // Extract state into buffer
497                let state_array = self.state.to_u64_array();
498                let buffer_mut = self.buffer.as_mut();
499
500                for i in 0..(rate / 8) {
501                    let lane = state_array[i];
502                    for j in 0..8 {
503                        if i * 8 + j < rate {
504                            buffer_mut[i * 8 + j] = ((lane >> (8 * j)) & 0xFF) as u8;
505                        }
506                    }
507                }
508            }
509
510            let available = rate - self.buffer_idx;
511            let needed = output.len() - offset;
512            let to_copy = available.min(needed);
513
514            output[offset..offset + to_copy]
515                .copy_from_slice(&self.buffer.as_ref()[self.buffer_idx..self.buffer_idx + to_copy]);
516
517            offset += to_copy;
518            self.buffer_idx += to_copy;
519        }
520
521        // Memory barrier after squeeze operation
522        barrier::compiler_fence_seq_cst();
523        Ok(())
524    }
525
526    fn squeeze_into_vec(&mut self, len: usize) -> Result<Vec<u8>> {
527        validate::parameter(
528            len > 0,
529            "output_length",
530            "Output length must be greater than 0",
531        )?;
532
533        let mut v = vec![0u8; len];
534        self.squeeze(&mut v)?;
535        Ok(v)
536    }
537
538    fn reset(&mut self) -> Result<()> {
539        *self = Self::new();
540        Ok(())
541    }
542
543    fn security_level() -> usize {
544        256
545    }
546}
547
548#[cfg(test)]
549mod tests;