zenith-net 0.1.0

Zenith 网络地址与传输层抽象:L2-L4 协议解析、TCP/UDP/QUIC 状态机、来源准入引擎、单队列 Worker 数据面循环
//! Quick test for initial key derivation comparison with aioquic reference
//! Run: cargo test -p zenith-net --test initial_key_test

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() {
    // From server-side debug log: client_dcid = [0e, 95, 59, 96, 7f, d8, f3, f3]
    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);

    // server recv = client in
    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);

    // Expected (from aioquic reference):
    // Initial secret: e67a9959d8909930a4e5ddb6d73bb125832b223c07e1f4e1ec472bce75e43557
    // Server recv secret: 4db65ca22c95244dc45a1f99e23df86ed501ddc2838f9e26f6c1654a7fdf5496
    // Server recv KEY: 043887e98cc86ed57739a2f6f86e767c
    // Server recv IV:  cf59f28e8d557c76c529814d
    // Server recv HP:  9a66a651edbac1b428eea0a5183a3592

    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"
    );
}