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}