"""
ch15_pq_mlkem_demo.py
Vol II · Chapter 15 — ML-KEM-768 Key Encapsulation
====================================================
Demonstrates the full ML-KEM-768 key encapsulation and
decapsulation cycle — the mechanism underlying the
X25519MLKEM768 hybrid TLS 1.3 cipher suite used to protect
the settlement message bus.

Requirements:
    pip install liboqs-python

Run once after installation to verify ML-KEM is enabled:
    python3 -c "import oqs; print(oqs.get_enabled_kem_mechanisms())"
"""

import oqs
import time

KEM_ALG = "ML-KEM-768"


# ── Key sizes ─────────────────────────────────────────────────────────────────

print("=" * 60)
print(f"ML-KEM-768 Key Encapsulation  (FIPS 203)")
print("=" * 60)

# ── Step 1: CSD generates a key pair (the "server" in TLS terms) ──────────────
with oqs.KeyEncapsulation(KEM_ALG) as csd:
    csd_public_key  = csd.generate_keypair()
    csd_private_key = csd.export_secret_key()

print(f"CSD public key:   {len(csd_public_key):,} bytes")   # 1,184
print(f"CSD private key:  {len(csd_private_key):,} bytes")  # 2,400

# ── Step 2: Participant encapsulates a shared secret ─────────────────────────
# (Participant has received the CSD's public key via its certificate)
with oqs.KeyEncapsulation(KEM_ALG) as participant:
    ciphertext, shared_secret_sender = participant.encap_secret(csd_public_key)

print(f"Ciphertext:       {len(ciphertext):,} bytes")              # 1,088
print(f"Shared secret:    {len(shared_secret_sender)} bytes")      # 32

# ── Step 3: CSD decapsulates to recover the shared secret ────────────────────
with oqs.KeyEncapsulation(KEM_ALG) as csd:
    csd.import_secret_key(csd_private_key)
    shared_secret_receiver = csd.decap_secret(ciphertext)

assert shared_secret_sender == shared_secret_receiver, "Shared secrets do not match!"
print(f"Secrets match:    True")
print(f"Secret (hex):     {shared_secret_sender.hex()[:32]}…")


# ── Size comparison ───────────────────────────────────────────────────────────

print()
print("Size comparison with classical ECDH (X25519):")
print(f"  X25519    public key:     32 bytes")
print(f"  X25519    ciphertext:     32 bytes  (ephemeral public key)")
print(f"  ML-KEM-768 public key: {len(csd_public_key):,} bytes  (×{len(csd_public_key)//32})")
print(f"  ML-KEM-768 ciphertext: {len(ciphertext):,} bytes  (×{len(ciphertext)//32})")
print()
print("In a hybrid X25519MLKEM768 TLS handshake, the ClientKeyShare")
print(f"extension carries both: 32 + {len(ciphertext):,} = {32 + len(ciphertext):,} bytes.")


# ── Performance benchmark ─────────────────────────────────────────────────────

print()
print("=" * 60)
print("Performance Benchmark")
print("=" * 60)

N = 500

# X25519 benchmark (via cryptography library — optional)
try:
    from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
    t0 = time.perf_counter()
    for _ in range(N):
        priv = X25519PrivateKey.generate()
        pub  = priv.public_key()
        priv.exchange(pub)
    x_time = time.perf_counter() - t0
    print(f"X25519 exchange:   {N/x_time:>8,.0f} op/s  ({x_time/N*1000:.3f} ms/op)")
except ImportError:
    print("X25519 benchmark skipped (install 'cryptography' package)")

# ML-KEM-768 benchmark
with oqs.KeyEncapsulation(KEM_ALG) as kem:
    pub = kem.generate_keypair()
    priv = kem.export_secret_key()

t0 = time.perf_counter()
for _ in range(N):
    with oqs.KeyEncapsulation(KEM_ALG) as k:
        ct, _ = k.encap_secret(pub)
mlkem_enc = time.perf_counter() - t0

t0 = time.perf_counter()
with oqs.KeyEncapsulation(KEM_ALG) as k:
    k.import_secret_key(priv)
    for _ in range(N):
        k.decap_secret(ct)
mlkem_dec = time.perf_counter() - t0

print(f"ML-KEM-768 encap:  {N/mlkem_enc:>8,.0f} op/s  ({mlkem_enc/N*1000:.3f} ms/op)")
print(f"ML-KEM-768 decap:  {N/mlkem_dec:>8,.0f} op/s  ({mlkem_dec/N*1000:.3f} ms/op)")
print()
print("For a settlement platform at 100,000 TLS connections/hour (~28/s),")
print("ML-KEM-768 decap throughput exceeds the requirement by a wide margin.")
