krypteia-tessera 0.2.0

Shared pure-Rust hash, XOF, MAC and KDF primitives for the krypteia workspace: SHA-1/2/3, Keccak, SHAKE/cSHAKE, RIPEMD-160, BLAKE2/BLAKE3, HMAC, SP 800-185 (KMAC/TupleHash/ParallelHash), the KDF family (HKDF, PBKDF2, SP 800-108, scrypt, Argon2), MGF1 and CRC-16/32. no_std, zero runtime dependencies.
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Cédric Mesnil <cslashm@pm.me>

//! MGF1 mask generation function (PKCS#1 v2.2 / RFC 8017 §B.2.1).
//!
//! `MGF1(seed, len) = T[0..len]` where `T = H(seed ‖ C(0)) ‖ H(seed ‖ C(1)) ‖ …`
//! and `C(i)` is the 4-octet big-endian counter. Generic over any
//! [`Digest`]; used by RSA-OAEP and RSA-PSS.

use crate::Digest;

/// Fill `out` with `MGF1` output derived from `seed`, using digest `H`.
///
/// Panics in debug builds if `H::OUTPUT_LEN` exceeds 64 (no hash in this
/// crate does).
pub fn mgf1<H: Digest>(seed: &[u8], out: &mut [u8]) {
    let hlen = H::OUTPUT_LEN;
    debug_assert!(hlen <= 64, "MGF1 scratch buffer is sized for digests up to 64 octets");
    let mut block = [0u8; 64];
    let mut counter: u32 = 0;
    let mut pos = 0;
    while pos < out.len() {
        let mut h = H::new();
        h.update(seed);
        h.update(&counter.to_be_bytes());
        h.finalize(&mut block[..hlen]);
        let n = hlen.min(out.len() - pos);
        out[pos..pos + n].copy_from_slice(&block[..n]);
        pos += n;
        counter += 1;
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::sha2::Sha256;

    // Smoke test: MGF1's first block must equal H(seed || 0x00000000)
    // (PKCS#1 / FIPS 186 MGF1 definition). The multi-block / determinism
    // checks live in the integration suite `tests/misc_hashes.rs`.
    #[test]
    fn mgf1_first_block_smoke() {
        let seed = b"krypteia-mgf1";
        let mut out = [0u8; 32];
        mgf1::<Sha256>(seed, &mut out);
        let mut h = Sha256::new();
        h.update(seed);
        h.update(&[0u8, 0, 0, 0]);
        let mut expect = [0u8; 32];
        h.finalize(&mut expect);
        assert_eq!(out, expect);
    }
}