use hkdf::Hkdf;
use oxicrypto_core::CryptoError;
const LABEL_PREFIX: &[u8] = b"tls13 ";
const MAX_HKDF_LABEL_LEN: usize = 2 + 1 + 255 + 1 + 255;
fn encode_hkdf_label<'a>(
length: u16,
label: &[u8],
context: &[u8],
out: &'a mut [u8; MAX_HKDF_LABEL_LEN],
) -> Result<&'a [u8], CryptoError> {
let label_len = LABEL_PREFIX
.len()
.checked_add(label.len())
.ok_or(CryptoError::BadInput)?;
if label_len > 255 || context.len() > 255 {
return Err(CryptoError::BadInput);
}
let mut pos = 0usize;
out[pos] = (length >> 8) as u8;
out[pos + 1] = (length & 0xff) as u8;
pos += 2;
out[pos] = label_len as u8;
pos += 1;
out[pos..pos + LABEL_PREFIX.len()].copy_from_slice(LABEL_PREFIX);
pos += LABEL_PREFIX.len();
out[pos..pos + label.len()].copy_from_slice(label);
pos += label.len();
out[pos] = context.len() as u8;
pos += 1;
out[pos..pos + context.len()].copy_from_slice(context);
pos += context.len();
Ok(&out[..pos])
}
pub fn hkdf_expand_label_sha256(
prk: &[u8],
label: &[u8],
context: &[u8],
okm_out: &mut [u8],
) -> Result<(), CryptoError> {
if okm_out.is_empty() {
return Err(CryptoError::BadInput);
}
let length = u16::try_from(okm_out.len()).map_err(|_| CryptoError::BadInput)?;
let mut buf = [0u8; MAX_HKDF_LABEL_LEN];
let info = encode_hkdf_label(length, label, context, &mut buf)?;
let hk = Hkdf::<sha2::Sha256>::from_prk(prk).map_err(|_| CryptoError::InvalidKey)?;
hk.expand(info, okm_out)
.map_err(|_| CryptoError::Internal("HKDF-Expand-Label (SHA-256) failed"))?;
Ok(())
}
pub fn hkdf_expand_label_sha384(
prk: &[u8],
label: &[u8],
context: &[u8],
okm_out: &mut [u8],
) -> Result<(), CryptoError> {
if okm_out.is_empty() {
return Err(CryptoError::BadInput);
}
let length = u16::try_from(okm_out.len()).map_err(|_| CryptoError::BadInput)?;
let mut buf = [0u8; MAX_HKDF_LABEL_LEN];
let info = encode_hkdf_label(length, label, context, &mut buf)?;
let hk = Hkdf::<sha2::Sha384>::from_prk(prk).map_err(|_| CryptoError::InvalidKey)?;
hk.expand(info, okm_out)
.map_err(|_| CryptoError::Internal("HKDF-Expand-Label (SHA-384) failed"))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hkdf_sha256_extract;
fn hex_decode(s: &str) -> Vec<u8> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).expect("valid hex"))
.collect()
}
#[test]
fn encode_hkdf_label_quic_key() {
let mut buf = [0u8; MAX_HKDF_LABEL_LEN];
let info = encode_hkdf_label(16, b"quic key", &[], &mut buf).expect("encode");
let mut expected = vec![0x00, 0x10, 0x0e];
expected.extend_from_slice(b"tls13 quic key");
expected.push(0x00);
assert_eq!(info, &expected[..]);
}
#[test]
fn encode_hkdf_label_with_context() {
let mut buf = [0u8; MAX_HKDF_LABEL_LEN];
let ctx = [0xaa, 0xbb, 0xcc];
let info = encode_hkdf_label(32, b"derived", &ctx, &mut buf).expect("encode");
let mut expected = vec![0x00, 0x20, b"tls13 derived".len() as u8];
expected.extend_from_slice(b"tls13 derived");
expected.push(0x03);
expected.extend_from_slice(&ctx);
assert_eq!(info, &expected[..]);
}
#[test]
fn label_too_long_errors() {
let mut buf = [0u8; MAX_HKDF_LABEL_LEN];
let long = [b'x'; 250]; assert_eq!(
encode_hkdf_label(16, &long, &[], &mut buf),
Err(CryptoError::BadInput)
);
}
#[test]
fn empty_output_errors() {
let prk = [0u8; 32];
assert_eq!(
hkdf_expand_label_sha256(&prk, b"x", &[], &mut []),
Err(CryptoError::BadInput)
);
}
#[test]
fn rfc9001_a1_client_initial_keys() {
let salt = hex_decode("38762cf7f55934b34d179ae6a4c80cadccbb7f0a");
let dcid = hex_decode("8394c8f03e515708");
let initial_secret = hkdf_sha256_extract(&salt, &dcid);
assert_eq!(
initial_secret.to_vec(),
hex_decode("7db5df06e7a69e432496adedb00851923595221596ae2ae9fb8115c1e9ed0a44"),
"initial_secret mismatch"
);
let mut client_secret = [0u8; 32];
hkdf_expand_label_sha256(&initial_secret, b"client in", &[], &mut client_secret)
.expect("client in");
assert_eq!(
client_secret.to_vec(),
hex_decode("c00cf151ca5be075ed0ebfb5c80323c42d6b7db67881289af4008f1f6c357aea"),
"client_initial_secret mismatch"
);
let mut key = [0u8; 16];
hkdf_expand_label_sha256(&client_secret, b"quic key", &[], &mut key).expect("quic key");
assert_eq!(
key.to_vec(),
hex_decode("1f369613dd76d5467730efcbe3b1a22d"),
"client quic key mismatch"
);
let mut iv = [0u8; 12];
hkdf_expand_label_sha256(&client_secret, b"quic iv", &[], &mut iv).expect("quic iv");
assert_eq!(
iv.to_vec(),
hex_decode("fa044b2f42a3fd3b46fb255c"),
"client quic iv mismatch"
);
let mut hp = [0u8; 16];
hkdf_expand_label_sha256(&client_secret, b"quic hp", &[], &mut hp).expect("quic hp");
assert_eq!(
hp.to_vec(),
hex_decode("9f50449e04a0e810283a1e9933adedd2"),
"client quic hp mismatch"
);
}
#[test]
fn rfc9001_a1_server_initial_keys() {
let salt = hex_decode("38762cf7f55934b34d179ae6a4c80cadccbb7f0a");
let dcid = hex_decode("8394c8f03e515708");
let initial_secret = hkdf_sha256_extract(&salt, &dcid);
let mut server_secret = [0u8; 32];
hkdf_expand_label_sha256(&initial_secret, b"server in", &[], &mut server_secret)
.expect("server in");
assert_eq!(
server_secret.to_vec(),
hex_decode("3c199828fd139efd216c155ad844cc81fb82fa8d7446fa7d78be803acdda951b"),
"server_initial_secret mismatch"
);
let mut key = [0u8; 16];
hkdf_expand_label_sha256(&server_secret, b"quic key", &[], &mut key).expect("quic key");
assert_eq!(
key.to_vec(),
hex_decode("cf3a5331653c364c88f0f379b6067e37"),
"server quic key mismatch"
);
let mut iv = [0u8; 12];
hkdf_expand_label_sha256(&server_secret, b"quic iv", &[], &mut iv).expect("quic iv");
assert_eq!(
iv.to_vec(),
hex_decode("0ac1493ca1905853b0bba03e"),
"server quic iv mismatch"
);
let mut hp = [0u8; 16];
hkdf_expand_label_sha256(&server_secret, b"quic hp", &[], &mut hp).expect("quic hp");
assert_eq!(
hp.to_vec(),
hex_decode("c206b8d9b9f0f37644430b490eeaa314"),
"server quic hp mismatch"
);
}
#[test]
fn hkdf_expand_label_sha384_deterministic() {
let prk = crate::hkdf_sha384_extract(b"salt", b"ikm");
let mut a = [0u8; 48];
let mut b = [0u8; 48];
hkdf_expand_label_sha384(&prk, b"derived", &[], &mut a).expect("a");
hkdf_expand_label_sha384(&prk, b"derived", &[], &mut b).expect("b");
assert_eq!(a, b);
assert_ne!(a, [0u8; 48]);
}
}