http://docker:8085ML-KEM-1024 encapsulation establishes a shared secret between peer and gateway. Both derive the same AES-256-GCM session key without ever transmitting it.
ML-KEM-1024 (FIPS 203, Level 5)Gateway signs the handshake transcript with ML-DSA-65. Peers verify to ensure they negotiated with the real gateway, not a MITM.
ML-DSA-65 (FIPS 204, Level 3)Every packet through the tunnel is AES-256-GCM encrypted using the KEM-derived session key. Decrypted on the other side. Same model as WireGuard ChaCha20/AES-GCM.
AES-256-GCM (with ML-KEM-derived key)Periodic re-encapsulation generates a fresh session key. The old key is zeroised immediately. Bounds the window of a compromised session key — PQ forward secrecy.
ML-KEM-1024 (FIPS 203, Level 5)This service simulates a WireGuard-style post-quantum VPN gateway. It demonstrates the exact handshake and tunnel-encryption patterns your VPN infrastructure will use after migrating to the Qudo JNI provider.
Handshake: Peer encapsulates to gateway's ML-KEM-1024 public key (provider.kemEncapsulate(gatewayPubKey, "ML-KEM-1024")). Both sides derive the same 256-bit AES session key. Gateway proves its identity with an ML-DSA-65 signature on the handshake transcript.
Tunnel: All traffic is AES-256-GCM encrypted with the KEM-derived key. Each packet gets a fresh random IV.
Rekey: On-demand new KEM encapsulation. Old session key is zeroised — forward secrecy per rekey interval.
/api/vpn/gateway-info
Get gateway identity + ML-KEM-1024 public key
/api/vpn/register-peer
Register a new VPN peer
/api/vpn/handshake
Perform PQC handshake — establish session key
/api/vpn/verify-handshake
Verify gateway identity (peer-side check)
/api/vpn/send-packet
Send encrypted data through tunnel
/api/vpn/rekey
Rotate session key (fresh KEM encapsulation)
/api/vpn/disconnect
Tear down tunnel and zerosise session key
/api/vpn/tunnels
List all active tunnels + traffic stats
/api/vpn/health
Service health
Migrate your VPN/IPSec/WireGuard infrastructure to quantum-safe key exchange and tunnel encryption using the Qudo JNI provider.
A PQ-VPN gateway mirrors WireGuard's handshake model, replacing classical X25519 DH with ML-KEM-1024 encapsulation and adding ML-DSA-65 for gateway identity proof.
provider.generateKeyPair("ML-KEM-1024")provider.kemEncapsulate(gatewayPub, "ML-KEM-1024")provider.kemDecapsulate(ct, gatewayPriv, "ML-KEM-1024")provider.verify(transcript, sig, gatewayPub, "ML-DSA-65")provider.sign(transcript, identityKey, "ML-DSA-65")kemEncapsulate()Walk through a full PQ-VPN session using the API Test tab above. Follow this order:
tunnelId from step 2). Response includes the gateway's ML-DSA-65 signature, transcript, and identity pubkey.signature, transcript, and gatewayIdentityPublicKey from step 3 to independently verify the gateway is genuine (detects MITM).Check GET /api/vpn/tunnels at any point to see packet/byte counters and rekey history.
Three components change. Everything else (routing, firewall rules, DNS, client apps) stays the same.
Measured via Qudo JNI on Apple M-series, single-thread. Run Benchmark for your hardware.
| Operation | Algorithm | Latency | Wire Size |
|---|---|---|---|
| KEM keygen | ML-KEM-1024 | ~0.15ms | Pub: 1,568B |
| Encapsulate | ML-KEM-1024 | ~0.12ms | CT: 1,568B |
| Decapsulate | ML-KEM-1024 | ~0.12ms | — |
| Identity sign | ML-DSA-65 | ~0.5ms | Sig: 3,309B |
| Identity verify | ML-DSA-65 | ~0.2ms | — |
| Tunnel encrypt | AES-256-GCM | <0.01ms | +28B overhead (IV+tag) |
Total handshake overhead vs classical WireGuard: ~0.8ms extra (KEM encap/decap + sig). On a 50ms RTT link, this is invisible. The KEM ciphertext (1,568B) adds one extra packet at most.
| Pitfall | Impact | Fix |
|---|---|---|
| Reusing KEM ciphertext | Shared secret replay — attacker can decrypt traffic | Fresh kemEncapsulate() on every handshake/rekey. Never cache ciphertext. |
| Not zeroising old session key | Compromised memory leaks past traffic | Arrays.fill(oldKey, (byte) 0) immediately after rekey/disconnect |
| Skipping identity verification | MITM can substitute their own KEM pubkey | Always verify ML-DSA-65 signature on the handshake transcript. Pin the gateway pubkey in config. |
| MTU too low for KEM ciphertext | Fragmentation during handshake | ML-KEM-1024 ciphertext is 1,568B. Fits in one standard 1500-MTU frame. But on tunnels with overhead (GRE/IPsec), increase outer MTU or allow fragmentation during handshake only. |
| Rekey interval too long | Session key exposure window grows | WireGuard rekeys every 2 min / 2^64 bytes. Apply the same policy with PQC — ML-KEM encapsulation is cheap enough. |
provider.kemEncapsulate(pubKey, "ML-KEM-768") — same API, different string.This simulator demonstrates the cryptographic patterns. A production PQ-VPN will additionally need:
| Feature | Playground | Production |
|---|---|---|
| Two-sided handshake | Both sides run server-side in one API call | Peer runs kemEncapsulate() locally, sends only kemCiphertext (1,568B) to gateway. Same Qudo JNI calls, different process boundary. |
| Hybrid mode | Pure ML-KEM-1024 only | Combine X25519 + ML-KEM-768 shared secrets (XOR or HKDF) for backwards compatibility during transition. Same kemEncapsulate() call — add a classical DH alongside it. |
| Automatic rekey | On-demand via /rekey |
Timer-based (WireGuard: every 2 min) or byte-count-based (after 264 bytes). Wrap the same kemEncapsulate() in a scheduled task. |
| MTU / fragmentation | Not simulated | ML-KEM-1024 ciphertext (1,568B) fits one 1500-MTU frame. On tunnels with encapsulation overhead (GRE, IPsec), set inner MTU to ~1400 or allow handshake fragmentation. MSS clamping recommended. |
| Peer-to-peer mesh | Client ↔ gateway only | For mesh, each peer pair runs an independent handshake. The same kemEncapsulate() / kemDecapsulate() pattern applies — just N×(N-1)/2 of them. |
| Throughput testing | /send-packet with optional "size": N (up to 64KB) |
For sustained throughput benchmarks, use the Benchmark page — AES-256-GCM speed doesn't change with PQC (only the handshake does). |
kemEncapsulate, kemDecapsulate, sign, verify). The protocol scaffolding changes; the crypto calls don't.
Analyze any endpoint's cryptographic configuration. Enter a host:port to scan its TLS setup and identify what's quantum-safe vs what needs migration.