initial commit

Signed-off-by: Kamal Tufekcic <kamal@lo.sh>
This commit is contained in:
Kamal Tufekcic 2026-04-02 23:48:10 +03:00
commit d73755a275
No known key found for this signature in database
165830 changed files with 568244 additions and 0 deletions

View file

@ -0,0 +1,161 @@
//! HKDF-SHA3-256 (RFC 5869 construction with SHA3-256).
use crate::error::{Error, Result};
use hkdf::Hkdf;
use sha3::Sha3_256;
/// Perform HKDF-SHA3-256 extract-and-expand.
///
/// Derives `out.len()` bytes of keying material.
/// Maximum output length: 255 * 32 = 8160 bytes.
///
/// # Errors
///
/// Returns `InvalidLength` if `out` is empty or exceeds 8160 bytes.
///
/// # Security
///
/// The caller is responsible for zeroizing the `out` buffer when the derived
/// key material is no longer needed. The `hkdf` crate's internal PRK is
/// **not** zeroized on drop (`hkdf 0.13.0-rc.5` has no `zeroize` feature).
/// Mitigation: `Hkdf` is a short-lived local — PRK lifetime is bounded to
/// this function scope, and `panic = "abort"` prevents unwinding.
pub fn hkdf_sha3_256(salt: &[u8], ikm: &[u8], info: &[u8], out: &mut [u8]) -> Result<()> {
if out.is_empty() {
return Err(Error::InvalidLength {
expected: 1,
got: 0,
});
}
// HKDF-Expand maximum: 255 × HashLen (SHA3-256 = 32 bytes) = 8160 bytes.
if out.len() > 255 * 32 {
return Err(Error::InvalidLength {
expected: 255 * 32,
got: out.len(),
});
}
let hk = Hkdf::<Sha3_256>::new(Some(salt), ikm);
// Length already validated above — expand cannot fail.
hk.expand(info, out)
.expect("HKDF output length already validated");
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
// Test vectors: RFC 5869 input patterns computed with SHA3-256 via Python 3
// hashlib HKDF implementation (provenance: manual HMAC-SHA3-256-based
// extract+expand, verified against this Rust `hkdf`+`sha3` crate output).
#[test]
fn case1() {
let ikm = [0x0b; 22];
let salt = hex!("000102030405060708090a0b0c");
let info = hex!("f0f1f2f3f4f5f6f7f8f9");
let expected = hex!(
"0c5160501d65021deaf2c14f5abce04c"
"5bd2635abceeba61c2edb6e8ed726749"
"00557728f2c9f2c4c179"
);
let mut out = [0u8; 42];
hkdf_sha3_256(&salt, &ikm, &info, &mut out).unwrap();
assert_eq!(out, expected);
}
#[test]
fn case2() {
let ikm: Vec<u8> = (0x00..=0x4f).collect();
let salt: Vec<u8> = (0x60..=0xaf).collect();
let info: Vec<u8> = (0xb0..=0xff).collect();
let expected = hex!(
"3dc251e66c75da6560405ec5ac10e17d"
"851eedfbfdc13feafbec16964c25d021"
"bd971465a3e9c615f27769019e3f0407"
"d84986fb0ba24e729c99834624baa21c"
"b623dc0098f430d52e18bbdf694df4ed"
"d8b2"
);
let mut out = [0u8; 82];
hkdf_sha3_256(&salt, &ikm, &info, &mut out).unwrap();
assert_eq!(out, expected);
}
#[test]
fn case3() {
let ikm = [0x0b; 22];
let salt = b"";
let info = b"";
let expected = hex!(
"bc1342cdd75c05e8b0c3ae609ce44106"
"84d197232875073499b30cdfe2de2853"
"c1c1bed63d725e885e78"
);
let mut out = [0u8; 42];
hkdf_sha3_256(salt, &ikm, info, &mut out).unwrap();
assert_eq!(out, expected);
}
#[test]
fn output_32_bytes() {
let mut out = [0u8; 32];
hkdf_sha3_256(b"salt", b"ikm", b"info", &mut out).unwrap();
assert!(out.iter().any(|&b| b != 0));
}
#[test]
fn output_64_bytes() {
let mut out = [0u8; 64];
hkdf_sha3_256(b"salt", b"ikm", b"info", &mut out).unwrap();
assert!(out.iter().any(|&b| b != 0));
}
#[test]
fn output_1_byte() {
let mut out = [0u8; 1];
hkdf_sha3_256(b"salt", b"ikm", b"info", &mut out).unwrap();
assert_ne!(out[0], 0u8);
}
#[test]
fn max_output() {
let mut out = vec![0u8; 255 * 32];
hkdf_sha3_256(b"salt", b"ikm", b"info", &mut out).unwrap();
assert!(out.iter().any(|&b| b != 0));
}
#[test]
fn max_output_kat() {
// KAT for T(255) boundary — verifies the final HMAC iteration of
// HKDF-Expand produces correct output. Reference: Python 3.12+
// hmac + hashlib (sha3_256).
let salt = [0u8; 32];
let ikm = b"test input keying material";
let info = b"test info";
let mut out = vec![0u8; 8160];
hkdf_sha3_256(&salt, ikm, info, &mut out).unwrap();
assert_eq!(
&out[..32],
&hex_literal::hex!("fe631a871e206c76b2328b44340b951ec80635132cef2b633d9fffbed3b03af6")
);
assert_eq!(
&out[8128..],
&hex_literal::hex!("8e358e5b1b40fcdeb1a51bda1f65c3d76908111a55738dd5d5e6991541aa8b4c")
);
}
#[test]
fn empty_output_returns_error() {
let mut out: [u8; 0] = [];
assert!(hkdf_sha3_256(b"salt", b"ikm", b"info", &mut out).is_err());
}
#[test]
fn oversized_output_returns_error() {
let mut out = vec![0u8; 255 * 32 + 1];
assert!(hkdf_sha3_256(b"salt", b"ikm", b"info", &mut out).is_err());
}
}