🔐 PQXDH Protocol Simulator

Post-Quantum Extended Diffie-Hellman Key Exchange

About PQXDH

PQXDH is Signal's hybrid post-quantum key agreement protocol that combines classical X25519 elliptic curve cryptography with ML-KEM-1024 (formerly CRYSTALS-Kyber), a NIST-standardized post-quantum key encapsulation mechanism. This provides protection against both classical and quantum computer attacks.

Key Features: Post-quantum forward secrecy • Quantum-resistant • NIST FIPS 203 compliant • Production-ready (Signal Messenger)

⚛️ How ML-KEM Works: Lattice-Based Cryptography Deep Dive

🔬 Understanding Lattice-Based Cryptography

ML-KEM (Module-Lattice Key Encapsulation Mechanism) is based on the Learning With Errors (LWE) problem, a mathematical problem that is believed to be hard for both classical and quantum computers to solve.

💡 The Core Idea: While elliptic curve cryptography (like X25519) relies on the discrete logarithm problem that quantum computers can break using Shor's algorithm, lattice-based crypto relies on finding a short vector in a high-dimensional lattice—a problem that remains hard even for quantum computers!

📐 The LWE Problem (Simplified)

The security of ML-KEM is based on this mathematical challenge:

Given: A random vector a and the result b = a·s + e (mod q)
where:
s = secret key (small values)
e = small random error (Gaussian noise)
q = prime modulus

Challenge: Find s given only a and b

The small error e "hides" the secret key s. Without e, this would be easy to solve (simple linear algebra). With e, it becomes extremely difficult—even for quantum computers!

🔑 ML-KEM Key Generation (Step-by-Step)

Example with small numbers (dimension = 4, modulus = 17):

Step 1: Generate secret key s (small random values)
s = [1, 0, 1, 1] ← binary values for security
Step 2: Generate random lattice vector a
a = [12, 5, 8, 15] ← uniformly random mod 17
Step 3: Sample small error e (Gaussian distribution)
e = [1, -1, 0, 1] ← small noise, typically σ ≈ 3.2
Step 4: Compute public key b = a·s + e (mod 17)
• a·s = 12×1 + 5×0 + 8×1 + 15×1 = 12 + 0 + 8 + 15 = 35
• a·s + e = 35 + 1 = 36
• b = 36 mod 17 = 2

Public Key: (a, b) = ([12, 5, 8, 15], 2)
Secret Key: s = [1, 0, 1, 1]

📨 Encapsulation (Sending a Secret)

To share a secret with someone, we use their public key (a, b):

Step 1: Generate random vector r = [1, 1, 0, 1]
Step 2: Generate small errors e1, e2
Step 3: Create ciphertext:
• c1 = a·r + e1 (mod q) ← "encrypts" the randomness
• c2 = b·r + e2 + message×(q/2) (mod q) ← carries the message
Step 4: Derive shared secret from randomness r

🔓 Decapsulation (Recovering the Secret)

The recipient uses their secret key s to recover the shared secret:

Compute: c2 - c1·s (mod q)

This removes the noise and recovers: message×(q/2)
Rounding gives us the message bit, which allows deriving the same shared secret!

🛡️ Why Quantum Computers Can't Break This

Shor's Algorithm (breaks RSA, DH, ECC):
• Exploits mathematical structure in factoring and discrete logarithms
• Uses quantum Fourier transform to find periods efficiently
Doesn't apply to lattice problems!

Lattice Problems (basis of ML-KEM):
• No known quantum algorithm provides exponential speedup
• Grover's algorithm only gives √n speedup (not enough)
• Best known attacks remain exponential even for quantum computers
• Security based on geometry and high-dimensional space, not number theory

🎯 ML-KEM-1024 Parameters

Lattice Dimension (k × n) 4 × 256 = 1024 coefficients
Modulus (q) 3329 (prime)
Error Distribution (σ) Centered binomial distribution, η = 2
Public Key Size 1568 bytes
Ciphertext Size 1568 bytes
Security Level 256-bit quantum security (NIST Level 5)

📚 Further Reading: The actual ML-KEM uses polynomial rings and module lattices for efficiency, but the core security principle remains the same: adding small errors to linear equations makes them exponentially hard to solve!

🦀 Implementation Comparison: JavaScript (ML-KEM) vs Rust (LWE)

🔬 Two Implementations, Same Mathematical Foundation

This PQXDH simulation uses JavaScript to demonstrate ML-KEM (Kyber), while the Slow Lynx Cryptography Discovery project includes a Rust implementation of educational LWE encryption. Both are based on the same lattice cryptography principles!

Aspect JavaScript (This Page) Rust (Slow Lynx)
Implementation EducationalMLKEM class LatticeKey struct
Purpose ML-KEM-1024 demonstration in PQXDH protocol Educational LWE encryption with multiple security levels
Security Levels NIST Level 5 (256-bit quantum) Toy (80-bit), Standard (128-bit), High (192-bit), Military (256-bit)
Dimension 4 × 256 = 1024 128, 256, 512, or 1024 (configurable)
Modulus q = 3329 (ML-KEM standard) q = 7681, 12289, 16381, or 32749
Error Sampling Centered binomial (η=2) Discrete Gaussian approximation (σ=3.2)
Environment Browser (HTML/JavaScript) Command-line (Rust CLI)
Encryption Capability KEM (Key Encapsulation Mechanism) Bit-by-bit, 4-bit (nibble), and 8-bit (byte) encryption

📝 Code Comparison: Key Generation

Both implementations follow the same core algorithm: b = a·s + e (mod q)

JavaScript (This Page):
// Key Generation: b = a·s + e (mod q)
generateKeyPair() {
    // Generate public lattice vector 'a' (uniformly random)
    const a = new Uint8Array(this.publicKeySize);
    crypto.getRandomValues(a);

    // Generate secret key 's' (small binary values)
    const s = new Uint8Array(this.publicKeySize);
    for (let i = 0; i < s.length; i++) {
        s[i] = Math.random() < 0.5 ? 0 : 1;
    }

    // Sample small error 'e' (centered binomial)
    const e = this.sampleSmallError(this.publicKeySize);

    // Compute b = a·s + e (mod q)
    const b = new Uint8Array(this.publicKeySize);
    for (let i = 0; i < this.publicKeySize; i++) {
        let sum = 0;
        for (let j = 0; j < Math.min(8, this.publicKeySize - i); j++) {
            sum += a[i + j] * s[j];
        }
        b[i] = (sum + e[i]) % 256;
    }

    return { publicKey: a, secretKey: s };
}
Rust (Slow Lynx - lattice_crypto.rs):
// Key Generation: b = a·s + e (mod q)
pub fn generate(rng: &CspRng, params: &LatticeParams)
    -> Result<LatticeKeyPair, &'static str> {
    let n = params.dimension;
    let q = params.modulus;

    // Generate secret key s (binary values {0, 1})
    let mut s = vec![0u32; n];
    for i in 0..n {
        s[i] = rng.next_u32()? % 2;
    }

    // Generate random lattice vector a
    let mut a = vec![0u32; n];
    for i in 0..n {
        a[i] = rng.next_u32()? % q;
    }

    // Sample small error e (Gaussian approximation)
    let mut e = vec![0u32; n];
    for i in 0..n {
        e[i] = sample_discrete_gaussian(rng, params.error_stddev, q)?;
    }

    // Compute b = a*s + e (mod q) - element-wise
    let mut b = vec![0u32; n];
    for i in 0..n {
        let mut sum = 0u64;
        sum = (a[i] as u64 * s[i] as u64) % q as u64;
        b[i] = ((sum + e[i] as u64) % q as u64) as u32;
    }

    Ok(LatticeKeyPair { public_key: (a, b), secret_key: s })
}

🎯 Key Similarities

Both implementations share the same structure:
1. Generate random lattice vector a (uniformly distributed mod q)
2. Generate secret key s (small binary values)
3. Sample small errors e from a noise distribution
4. Compute b = a·s + e (mod q)
5. Public key = (a, b), Secret key = s

The LWE security assumption: Given (a, b), it's computationally hard to recover s, even for quantum computers!

🔧 Parameter Design Comparison

The Rust implementation offers configurable security levels through parameter presets:

Security Level Dimension (n) Modulus (q) Error (σ) Security
Toy 128 7681 3.2 ~80-bit
Standard 256 12289 3.2 ~128-bit
High 512 16381 3.2 ~192-bit
Military 1024 32749 3.2 ~256-bit

🔗 Cryptography Education Toolkit

Slow Butterfly Simulation Browser-based cryptography simulations (this page!) - DH, ECDH, PQXDH, ML-KEM
Slow Lynx Cryptography Discovery Rust CLI: CSPRNG analysis, LWE implementation, crypto library scanner
Slow Owl Packet Observer Real-time network analyzer: observe hybrid PQ (X25519Kyber768) in live traffic
🎓 Complete Learning Journey:
1. Theory (This Page): Understand ML-KEM/LWE with visual examples and step-by-step math
2. Implementation (Slow Lynx): Explore configurable Rust LWE with different security levels
3. Real-World (Slow Owl): Observe actual hybrid PQ key exchanges in network traffic
4. Result: Deep understanding from mathematical foundations to production deployment

💡 Design Philosophy: The JavaScript version prioritizes browser accessibility and protocol demonstration (ML-KEM in PQXDH). The Rust version emphasizes configurable parameters, comprehensive testing, and integration with a CSPRNG for cryptographic research. Both teach the same core LWE concepts from different angles!

Security Comparison: X3DH vs PQXDH

Feature X3DH (Classical) PQXDH (Hybrid)
Primary Key Exchange X25519 (Curve25519) X25519 + ML-KEM-1024
Quantum Resistance ❌ Vulnerable to Shor's Algorithm ✅ Quantum-Resistant
Forward Secrecy ✅ Yes (classical) ✅ Yes (post-quantum)
Protection Timeline Until ~2035 (quantum threat) 2025+ (current protection)
NIST Standardized NIST SP 800-186 NIST FIPS 203 (Aug 2024)
Key Size 32 bytes (256-bit) 32 bytes + 1568 bytes (KEM)
Performance ~1ms ~10-20ms
Production Use Signal (2016-2023) Signal (2023+)
⚠️ Educational Prototype Notice: This is a simplified educational demonstration using browser-based cryptography. The actual PQXDH protocol includes additional security measures, authentication, and key validation. For production use, refer to Signal's official specification and use audited cryptographic libraries.