krypteia-quantica 0.1.0

Pure-Rust post-quantum cryptography: FIPS 203 ML-KEM, FIPS 204 ML-DSA, and FIPS 205 SLH-DSA. First-order arithmetic masking, shuffled NTT, FORS recompute-and-compare redundancy, constant-time rejection sampling. Targets embedded (no_std), STM32 M0/M4/M33, ESP32-C3 RISC-V. Zero runtime dependencies.
Documentation
//! SHA-3 / SHAKE high-level wrappers used by SLH-DSA (FIPS 205).
//!
//! This module no longer carries its own copy of the Keccak
//! permutation: it builds on top of the shared sponge core in
//! `crate::sha3`. SLH-DSA only uses SHAKE256, exposed here through
//! the `Shake256` streaming context plus two one-shot helpers.

pub use crate::sha3::KeccakState;
use crate::sha3::SHAKE256_RATE as CORE_SHAKE256_RATE;
use alloc::vec::Vec;

/// SHAKE256 rate in bytes (1600-bit state minus 2ยท256-bit capacity, divided by 8).
pub const SHAKE256_RATE: usize = CORE_SHAKE256_RATE;

/// One-shot SHAKE256: absorb `data` and squeeze into `out`.
pub fn shake256_into(data: &[u8], out: &mut [u8]) {
    let mut state = KeccakState::new(SHAKE256_RATE, 0x1f);
    state.absorb(data);
    state.squeeze(out);
}

/// One-shot SHAKE256: absorb `data` and return `out_len` bytes of
/// output. Allocates a Vec for the output.
pub fn shake256(data: &[u8], out_len: usize) -> Vec<u8> {
    let mut out = vec![0u8; out_len];
    shake256_into(data, &mut out);
    out
}

/// Incremental SHAKE256 context for multi-part absorb and squeeze.
///
/// This is the primary hash interface used by the SLH-DSA hash
/// wrappers in [`super::hash`]. Data is absorbed in parts via
/// [`absorb`](Self::absorb), then the final output is obtained via
/// [`finalize`](Self::finalize) (which consumes the context) or
/// [`squeeze`](Self::squeeze) (which allows multiple squeeze calls).
pub struct Shake256 {
    state: KeccakState,
}

impl Shake256 {
    /// Create a new SHAKE256 context ready for absorbing.
    pub fn new() -> Self {
        Self {
            state: KeccakState::new(SHAKE256_RATE, 0x1f),
        }
    }

    /// Absorb input data. Can be called multiple times before squeezing.
    pub fn absorb(&mut self, data: &[u8]) {
        self.state.absorb(data);
    }

    /// Squeeze output bytes. Can be called multiple times for streaming output.
    pub fn squeeze(&mut self, out: &mut [u8]) {
        self.state.squeeze(out);
    }

    /// Finalize and return exactly `out_len` bytes of output, consuming the context.
    pub fn finalize(mut self, out_len: usize) -> Vec<u8> {
        let mut out = vec![0u8; out_len];
        self.state.squeeze(&mut out);
        out
    }
}