Skip to main content

quantica/slh_dsa/
sha3.rs

1//! SHA-3 / SHAKE high-level wrappers used by SLH-DSA (FIPS 205).
2//!
3//! This module no longer carries its own copy of the Keccak
4//! permutation: it builds on top of the shared sponge core in
5//! `crate::sha3`. SLH-DSA only uses SHAKE256, exposed here through
6//! the `Shake256` streaming context plus two one-shot helpers.
7
8pub use crate::sha3::KeccakState;
9use crate::sha3::SHAKE256_RATE as CORE_SHAKE256_RATE;
10use alloc::vec::Vec;
11
12/// SHAKE256 rate in bytes (1600-bit state minus 2ยท256-bit capacity, divided by 8).
13pub const SHAKE256_RATE: usize = CORE_SHAKE256_RATE;
14
15/// One-shot SHAKE256: absorb `data` and squeeze into `out`.
16pub fn shake256_into(data: &[u8], out: &mut [u8]) {
17    let mut state = KeccakState::new(SHAKE256_RATE, 0x1f);
18    state.absorb(data);
19    state.squeeze(out);
20}
21
22/// One-shot SHAKE256: absorb `data` and return `out_len` bytes of
23/// output. Allocates a Vec for the output.
24pub fn shake256(data: &[u8], out_len: usize) -> Vec<u8> {
25    let mut out = vec![0u8; out_len];
26    shake256_into(data, &mut out);
27    out
28}
29
30/// Incremental SHAKE256 context for multi-part absorb and squeeze.
31///
32/// This is the primary hash interface used by the SLH-DSA hash
33/// wrappers in [`super::hash`]. Data is absorbed in parts via
34/// [`absorb`](Self::absorb), then the final output is obtained via
35/// [`finalize`](Self::finalize) (which consumes the context) or
36/// [`squeeze`](Self::squeeze) (which allows multiple squeeze calls).
37pub struct Shake256 {
38    state: KeccakState,
39}
40
41impl Shake256 {
42    /// Create a new SHAKE256 context ready for absorbing.
43    pub fn new() -> Self {
44        Self {
45            state: KeccakState::new(SHAKE256_RATE, 0x1f),
46        }
47    }
48
49    /// Absorb input data. Can be called multiple times before squeezing.
50    pub fn absorb(&mut self, data: &[u8]) {
51        self.state.absorb(data);
52    }
53
54    /// Squeeze output bytes. Can be called multiple times for streaming output.
55    pub fn squeeze(&mut self, out: &mut [u8]) {
56        self.state.squeeze(out);
57    }
58
59    /// Finalize and return exactly `out_len` bytes of output, consuming the context.
60    pub fn finalize(mut self, out_len: usize) -> Vec<u8> {
61        let mut out = vec![0u8; out_len];
62        self.state.squeeze(&mut out);
63        out
64    }
65}