use crate::internal_alloc::Vec;
use noxtls_core::{Error, Result};
use super::{noxtls_hmac_sha256, noxtls_hmac_sha384, HmacSha256Context};
#[must_use]
pub fn noxtls_hkdf_extract_sha256(salt: &[u8], ikm: &[u8]) -> [u8; 32] {
let effective_salt = if salt.is_empty() {
&[0_u8; 32][..]
} else {
salt
};
noxtls_hmac_sha256(effective_salt, ikm)
}
pub fn noxtls_hkdf_expand_sha256(prk: &[u8], info: &[u8], len: usize) -> Result<Vec<u8>> {
if prk.len() < 32 {
return Err(Error::InvalidLength("hkdf prk must be at least 32 bytes"));
}
if len > 32 * 255 {
return Err(Error::InvalidLength("hkdf output length exceeds RFC limit"));
}
let mut okm = Vec::with_capacity(len);
let hmac = HmacSha256Context::new(prk);
let mut t = [0_u8; 32];
let mut has_prev = false;
let n = len.div_ceil(32);
for idx in 1..=n {
let counter = [idx as u8];
t = if has_prev {
hmac.finalize_parts(&[&t[..], info, &counter])
} else {
hmac.finalize_parts(&[info, &counter])
};
has_prev = true;
let take = (len - okm.len()).min(t.len());
okm.extend_from_slice(&t[..take]);
}
Ok(okm)
}
#[must_use]
pub fn noxtls_hkdf_extract_sha384(salt: &[u8], ikm: &[u8]) -> [u8; 48] {
let effective_salt = if salt.is_empty() {
&[0_u8; 48][..]
} else {
salt
};
noxtls_hmac_sha384(effective_salt, ikm)
}
pub fn noxtls_hkdf_expand_sha384(prk: &[u8], info: &[u8], len: usize) -> Result<Vec<u8>> {
if prk.len() < 48 {
return Err(Error::InvalidLength("hkdf prk must be at least 48 bytes"));
}
if len > 48 * 255 {
return Err(Error::InvalidLength("hkdf output length exceeds RFC limit"));
}
let mut okm = Vec::with_capacity(len);
let mut t = Vec::new();
let n = len.div_ceil(48);
for idx in 1..=n {
let mut msg = Vec::with_capacity(t.len() + info.len() + 1);
msg.extend_from_slice(&t);
msg.extend_from_slice(info);
msg.push(idx as u8);
t = noxtls_hmac_sha384(prk, &msg).to_vec();
okm.extend_from_slice(&t);
}
okm.truncate(len);
Ok(okm)
}
#[cfg(test)]
mod tests {
use super::{noxtls_hkdf_expand_sha256, noxtls_hkdf_extract_sha256};
use crate::internal_alloc::Vec;
fn decode_hex(hex: &str) -> Vec<u8> {
assert_eq!(hex.len() % 2, 0, "hex string must have even length");
(0..hex.len())
.step_by(2)
.map(|index| {
u8::from_str_radix(&hex[index..index + 2], 16)
.expect("hex test vector should be valid")
})
.collect()
}
#[test]
fn hkdf_sha256_matches_rfc5869_case_1() {
let ikm = vec![0x0b; 22];
let salt = decode_hex("000102030405060708090a0b0c");
let info = decode_hex("f0f1f2f3f4f5f6f7f8f9");
let expected_prk =
decode_hex("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5");
let expected_okm = decode_hex(
"3cb25f25faacd57a90434f64d0362f2a\
2d2d0a90cf1a5a4c5db02d56ecc4c5bf\
34007208d5b887185865",
);
let prk = noxtls_hkdf_extract_sha256(&salt, &ikm);
assert_eq!(prk.as_slice(), expected_prk.as_slice());
let okm = noxtls_hkdf_expand_sha256(&prk, &info, 42).expect("expand");
assert_eq!(okm.as_slice(), expected_okm.as_slice());
}
}