Quantum computing isn't here yet. But the data you encrypt today will still exist when it arrives. An attacker recording your traffic now can decrypt it later with a quantum computer — this is called "harvest now, decrypt later."
GhostWire implements ML-KEM-768 (formerly CRYSTALS-Kyber) as a post-quantum key exchange, hybridized with classical X25519. Here's why and how.
Why Post-Quantum Now?
The timeline is uncertain. Estimates range from 5 to 30 years for a cryptographically relevant quantum computer. But:
- Classified intelligence suggests nation-states are already harvesting encrypted traffic
- NIST finalized ML-KEM in 2024 — the standard is ready
- The performance cost is small — ML-KEM-768 adds ~1ms to key exchange
Waiting is not a strategy. Implementing now means your protocol is ready when quantum computers arrive.
ML-KEM-768: The Basics
ML-KEM is a Key Encapsulation Mechanism (KEM). It works differently from traditional key exchange:
Keygen: (public_key, secret_key) = ML-KEM.Keygen()
Encaps: (ciphertext, shared_secret) = ML-KEM.Encaps(public_key)
Decaps: shared_secret = ML-KEM.Decaps(ciphertext, secret_key)
The sender generates a random shared secret, encrypts it with the receiver's public key, and sends the ciphertext. The receiver decrypts to get the same shared secret. No back-and-forth needed — it's a one-shot operation.
Hybrid Approach
ML-KEM-768 alone isn't enough. If it's broken (mathematically or implementation-wise), you have no fallback. The solution is hybrid key exchange:
use x25519_dalek::{EphemeralSecret, PublicKey as X25519PublicKey};
use ml_kem::{MlKem768, Encaps, Decaps, Encapsulate, Decapsulate};
use ml_kem::kpem::KemCore;
fn hybrid_key_exchange(
x25519_secret: &EphemeralSecret,
x25519_peer: &X25519PublicKey,
mlkem_pk: &MlKem768::PublicKey,
) -> ([u8; 32], Vec<u8>) {
// Classical key exchange
let classical = x25519_secret.diffie_hellman(x25519_peer);
// Post-quantum encapsulation
let (ciphertext, pq_secret) = mlkem_pk.encapsulate(&mut OsRng)
.expect("encapsulation should not fail");
// Combine both secrets
let mut combined = [0u8; 64];
combined[..32].copy_from_slice(classical.as_bytes());
combined[32..].copy_from_slice(&pq_secret);
// Derive final key
let final_key = hkdf_expand(&combined, b"ghostwire-hybrid-v1");
(final_key, ciphertext.to_vec())
}
Why concatenate? Because an attacker needs to break both X25519 and ML-KEM-768 to get the key. If ML-KEM falls to quantum computing, X25519 still protects you (until quantum computers break that too). If ML-KEM has a mathematical weakness, X25519 is unaffected.
The Key Sizes
This is where post-quantum gets real:
| X25519 | ML-KEM-768 | Combined | |
|---|---|---|---|
| Public key | 32 bytes | 1,184 bytes | 1,216 bytes |
| Ciphertext | 32 bytes | 1,088 bytes | 1,120 bytes |
| Shared secret | 32 bytes | 32 bytes | 32 bytes |
The public key is 37x larger. The ciphertext is 34x larger. This matters for mesh networks where bandwidth is limited (especially over LoRa, which has ~250 byte packets).
Our solution: Key exchange happens once per session (or when keys rotate). The bulk data (messages, files) uses the derived 32-byte key with XChaCha20-Poly1305. The large keys are only on the wire during handshake.
Performance
Benchmarked on an x86-64 machine:
| Operation | Time |
|---|---|
| ML-KEM-768 Keygen | 15μs |
| ML-KEM-768 Encaps | 28μs |
| ML-KEM-768 Decaps | 31μs |
| X25519 Keygen | 12μs |
| X25519 ECDH | 45μs |
| Total hybrid | ~75μs |
75 microseconds for post-quantum secure key exchange. That's fast enough to do on every connection.
On ESP32-S3 (no hardware acceleration):
| Operation | Time |
|---|---|
| ML-KEM-768 Keygen | 280μs |
| ML-KEM-768 Encaps | 520μs |
| ML-KEM-768 Decaps | 580μs |
| Total hybrid | ~1.2ms |
1.2ms on embedded hardware. Acceptable for a key exchange that happens once per session.
The Crate
We're using the ml-kem crate from RustCrypto:
[dependencies]
ml-kem = "0.2"
x25519-dalek = { version = "2", features = ["static_secrets"] }
The crate implements NIST FIPS 203 (ML-KEM). It's audited, constant-time, and no-std compatible (works on ESP32).
What's Next
In the next article, we'll cover AI-routed mesh networking — how GhostWire uses a 4-layer routing system (LightGBM → Gemma 4 → GNN → PRoPHET) to find optimal paths through a dynamic mesh.
GitHub: github.com/Phantomojo/GhostWire-secure-mesh-communication
Website: ghostwire.cc
Built from Nairobi, under RVC.
Top comments (0)