use ring::hmac;
const INITIAL_SALT_V1: [u8; 20] = [
0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a,
];
fn hkdf_expand(prk: &[u8], info: &[u8], len: usize) -> Vec<u8> {
let key = hmac::Key::new(hmac::HMAC_SHA256, prk);
let mut out = Vec::with_capacity(len);
let mut t: Vec<u8> = Vec::new();
let mut counter: u8 = 1;
while out.len() < len {
let mut ctx = Vec::with_capacity(t.len() + info.len() + 1);
ctx.extend_from_slice(&t);
ctx.extend_from_slice(info);
ctx.push(counter);
t = hmac::sign(&key, &ctx).as_ref().to_vec();
out.extend_from_slice(&t);
counter = counter.wrapping_add(1);
}
out.truncate(len);
out
}
fn hkdf_expand_label(prk: &[u8], label: &[u8], length: u16) -> Vec<u8> {
let full_label = [b"tls13 ", label].concat();
let mut info = Vec::with_capacity(2 + 1 + full_label.len() + 1);
info.extend_from_slice(&length.to_be_bytes());
info.push(full_label.len() as u8);
info.extend_from_slice(&full_label);
info.push(0);
hkdf_expand(prk, &info, length as usize)
}
#[test]
fn test_initial_key_derivation_matches_aioquic() {
let client_dcid = [0x0e, 0x95, 0x59, 0x96, 0x7f, 0xd8, 0xf3, 0xf3];
let salt_key = hmac::Key::new(hmac::HMAC_SHA256, &INITIAL_SALT_V1);
let initial_secret = hmac::sign(&salt_key, &client_dcid);
let server_recv_secret = hkdf_expand_label(initial_secret.as_ref(), b"client in", 32);
let key = hkdf_expand_label(&server_recv_secret, b"quic key", 16);
let iv = hkdf_expand_label(&server_recv_secret, b"quic iv", 12);
let hp = hkdf_expand_label(&server_recv_secret, b"quic hp", 16);
println!("Initial secret: {:02x?}", initial_secret.as_ref());
println!("Server recv secret: {:02x?}", server_recv_secret);
println!("Server recv KEY: {:02x?}", key);
println!("Server recv IV: {:02x?}", iv);
println!("Server recv HP: {:02x?}", hp);
assert_eq!(
hex::encode(initial_secret.as_ref()),
"e67a9959d8909930a4e5ddb6d73bb125832b223c07e1f4e1ec472bce75e43557",
"initial_secret mismatch"
);
assert_eq!(
hex::encode(&server_recv_secret),
"4db65ca22c95244dc45a1f99e23df86ed501ddc2838f9e26f6c1654a7fdf5496",
"server_recv_secret mismatch"
);
assert_eq!(
hex::encode(&key),
"043887e98cc86ed57739a2f6f86e767c",
"key mismatch"
);
assert_eq!(
hex::encode(&iv),
"cf59f28e8d557c76c529814d",
"iv mismatch"
);
assert_eq!(
hex::encode(&hp),
"9a66a651edbac1b428eea0a5183a3592",
"hp mismatch"
);
}