← Week 3: Hybrid Schemes and TLS

Day 16: X25519 + ML-KEM-768 Hybrid Key Exchange

Phase 3 · July 23, 2026

← Week 3: Hybrid Schemes and TLS

Agenda (2–3 hours)

  • Read (30 min): IETF draft-ietf-tls-ecdhe-mlkem-00 (the concrete X25519Kyber768 spec)
  • Build (120 min): Implement the hybrid KEM combiner in Rust
  • Verify (30 min): Test the security invariants
← Week 3: Hybrid Schemes and TLS

The X25519Kyber768 Construction

IETF defines X25519Kyber768 as a named TLS group (code point 0x11EC).

The hybrid shared secret is:

combined_ss = H("X25519Kyber768" || x25519_ss || mlkem_ss
                || x25519_client_pk || x25519_server_pk
                || mlkem_encap_pk   || mlkem_ciphertext)

The context string ("X25519Kyber768") and all public values are bound into the hash.
This prevents cross-protocol attacks and ensures the two secrets can't be separated.

← Week 3: Hybrid Schemes and TLS

Implementing the Combiner

use aws_lc_rs::{agreement, kem, digest};

pub struct HybridKeyExchange {
    pub shared_secret: Vec<u8>,  // 32 bytes
    pub key_share_bytes: usize,  // bytes transmitted in ClientHello
}

pub fn hybrid_client_encaps(
    x25519_server_pk: &[u8],    // 32 bytes
    mlkem_server_pk: &[u8],     // 1184 bytes
) -> anyhow::Result<(HybridKeyExchange, Vec<u8>)> {
    // 1. Perform X25519 key exchange
    // 2. Perform ML-KEM-768 encapsulation
    // 3. Combine secrets via HKDF or H(context || ss1 || ss2 || ...)
    // 4. Return (shared_secret, ciphertext_to_send)
}
← Week 3: Hybrid Schemes and TLS

X25519 Key Exchange with aws-lc-rs

use aws_lc_rs::agreement::{EphemeralPrivateKey, PublicKey, X25519, agree_ephemeral};

// Client side: generate ephemeral key pair
let client_private = EphemeralPrivateKey::generate(&X25519)?;
let client_public = client_private.compute_public_key()?;

// Server provides its public key; compute shared secret
let server_pk = agreement::UnparsedPublicKey::new(&X25519, server_pk_bytes);
let x25519_ss = agree_ephemeral(
    client_private, &server_pk,
    |key_material| key_material.to_vec()
)?;
// x25519_ss: 32-byte Diffie-Hellman output (not yet suitable as a key)
← Week 3: Hybrid Schemes and TLS

Combining the Secrets

use aws_lc_rs::hkdf;

fn combine_secrets(
    x25519_ss: &[u8],
    mlkem_ss: &[u8],
    context: &[u8],   // e.g., b"X25519MLKEM768"
) -> anyhow::Result<[u8; 32]> {
    // Concatenate then hash (simplified; IETF draft uses a specific construction)
    let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, &[]);
    let prk = salt.extract(&[x25519_ss, mlkem_ss, context].concat());
    let okm = prk.expand(&[context], hkdf::HKDF_SHA256)?;

    let mut combined = [0u8; 32];
    okm.fill(&mut combined)?;
    Ok(combined)
}
← Week 3: Hybrid Schemes and TLS

Security Test: One Side Broken

#[test]
fn test_hybrid_security_x25519_broken() {
    // Simulate: attacker knows x25519_ss (CRQC recovered it)
    // but doesn't know mlkem_ss
    let (_, server_hybrid_ss) = hybrid_server_keygen();
    let (_, client_hybrid_ss) = hybrid_client_encaps(&server_pks);

    // Attacker knows x25519_ss but NOT mlkem_ss
    // They should not be able to recover combined_ss
    // (This is a conceptual test — document it rather than implement)
    assert_eq!(client_hybrid_ss, server_hybrid_ss); // both sides agree
}
← Week 3: Hybrid Schemes and TLS

Challenge Assignment

Implement pqc-demo/src/hybrid.rs:

pub struct HybridKeyPair {
    pub x25519_public: Vec<u8>,
    pub mlkem_public: Vec<u8>,
}

pub fn server_keygen() -> anyhow::Result<(HybridKeyPair, /* private state */ ...)>;
pub fn client_encaps(server_keys: &HybridKeyPair) -> anyhow::Result<(Vec<u8>, Vec<u8>)>;
    // Returns (combined_shared_secret, ciphertext_for_server)
pub fn server_decaps(private_state: .., ciphertext: &[u8]) -> anyhow::Result<Vec<u8>>;

Write a test verifying:

  1. Both sides derive the same combined_shared_secret
  2. A corrupted x25519 contribution → different combined secret
  3. A corrupted ML-KEM contribution → different combined secret
← Week 3: Hybrid Schemes and TLS

Resources

  • IETF draft-ietf-tls-ecdhe-mlkem: datatracker.ietf.org
  • aws-lc-rs agreement module: X25519 key exchange
  • aws-lc-rs hkdf module: for combining secrets
  • IETF draft-irtf-cfrg-hybrid-pke: general hybrid public-key encryption construction