How to Test Your Post-Quantum Cryptography Implementation
Deploying post-quantum cryptography is only half the battle. Testing that your implementation works correctly, interoperates with other systems, and performs acceptably under load is where most teams spend the majority of their PQC migration effort. This guide covers practical testing approaches, from quick browser-based checks to full TLS handshake capture and analysis.
Testing strategy overview
PQC testing breaks into four categories, each with different tools and objectives:
- Correctness testing: Does your implementation produce valid outputs? Do KATs (Known Answer Tests) pass?
- Interoperability testing: Can your client talk to other servers? Can other clients talk to your server?
- Performance testing: What is the latency impact? How do larger key sizes affect throughput?
- Regression testing: After updates, does everything still work?
Tool 1: The OQS interop test server
The Open Quantum Safe project operates a public interoperability test server at test.openquantumsafe.org. This is the single most useful resource for quick PQC testing.
What it provides
The test server runs multiple TLS server instances, each on a different port, configured with a specific post-quantum signature algorithm for server authentication. All server certificates are signed by a common CA using conventional RSA cryptography, so your client only needs to trust the OQS test CA to validate the chain.
How to use it
Step 1: Download the OQS test CA certificate
curl -O https://test.openquantumsafe.org/CA.crt
Step 2: Connect with OpenSSL (using oqs-provider)
# Test ML-KEM-768 key exchange with ML-DSA-65 server auth
openssl s_client -connect test.openquantumsafe.org:6042 \
-CAfile CA.crt \
-groups mlkem768
Step 3: Connect with curl (if built with OQS support)
curl --cacert CA.crt \
--curves mlkem768 \
https://test.openquantumsafe.org:6042/
Available test ports
The OQS server offers dozens of ports, each with a different algorithm combination. Key ports include:
| Port | Server Signature | Key Exchange |
|---|---|---|
| 6042 | ML-DSA-65 | ML-KEM-768 |
| 6043 | ML-DSA-87 | ML-KEM-1024 |
| 6044 | ML-DSA-44 | ML-KEM-512 |
| 6050 | SLH-DSA-SHA2-128f | ML-KEM-768 |
| 6060 | Falcon-512 | ML-KEM-768 |
Check the full list at test.openquantumsafe.org for the current port assignments, as they change when new algorithms are added.
Limitations
- The server uses RSA as the CA signature, so you are testing server leaf certificate PQC algorithms, not a full PQ chain
- Availability depends on OQS infrastructure; it is a research project, not an SLA-backed service
- Only linux/amd64 Docker images are provided if you want to run your own local copy
Tool 2: Browser developer tools
Modern browsers that support hybrid PQ key exchange provide visibility into which key agreement algorithm was used for a given connection.
Chrome / Chromium
- Open DevTools (F12 or Cmd+Option+I)
- Navigate to the Security tab
- Click on the page origin under “Main origin”
- Look at “Key exchange” in the connection details
For a PQ-protected connection, you will see:
Key exchange: X25519MLKEM768
For a classical-only connection:
Key exchange: X25519
Chrome command-line flags for testing
# Force specific key exchange groups
chrome --enable-features=PostQuantumKeyAgreement
# Disable PQ to test fallback behavior
chrome --disable-features=PostQuantumKeyAgreement
Firefox
- Click the lock icon in the address bar
- Click “Connection secure” then “More Information”
- In the Security tab, look at “Technical Details”
Firefox shows the full cipher suite, which includes the key exchange group:
TLS_AES_256_GCM_SHA384, 256 bit keys, X25519MLKEM768
Edge
Same process as Chrome (Edge is Chromium-based). The Security tab in DevTools shows identical information.
What browsers tell you (and what they do not)
Browser dev tools confirm which key exchange algorithm was negotiated. They do not tell you:
- Whether the server offered PQ algorithms (only what was selected)
- The certificate signature algorithm (check the certificate details separately)
- Whether the connection used hybrid mode versus pure PQ
For deeper inspection, you need packet capture tools.
Tool 3: Wireshark TLS handshake capture
Wireshark (version 4.2+) can decode post-quantum TLS handshakes and display the key exchange groups, signature algorithms, and certificate chains in detail.
Capture setup
# Capture on port 443 with sufficient snaplen for large PQ handshakes
tshark -i eth0 -f "tcp port 443" -w pq-capture.pcap
# Or with Wireshark GUI: set capture filter to "tcp port 443"
Important: PQ handshakes are larger than classical ones. ML-KEM-768 adds approximately 1,184 bytes (public key) + 1,088 bytes (ciphertext) to the handshake. ML-DSA-65 certificates add approximately 1,952 bytes (public key) + 3,309 bytes (signature). Ensure your snaplen is sufficient (use default of 262144 bytes).
Display filters for PQ algorithms
# Show only ClientHello messages with PQ key share groups
tls.handshake.extensions.supported_group == 0x0768
# Show only ServerHello messages with PQ key exchange
tls.handshake.extensions.key_share.group == 0x0768
# Filter for hybrid key exchange (X25519MLKEM768)
tls.handshake.extensions.supported_group == 0x4588
Note: The IANA code point for X25519MLKEM768 is 0x4588 (assigned in RFC 9936). Pure ML-KEM-768 uses 0x0768.
Analyzing a PQ handshake
In a successful hybrid PQ handshake, you will see:
-
ClientHello:
supported_groupsextension includes X25519MLKEM768 (0x4588). Thekey_shareextension contains both a 32-byte X25519 share and an 1184-byte ML-KEM-768 public key. -
ServerHello:
key_shareextension contains the server’s X25519 share (32 bytes) and ML-KEM-768 ciphertext (1088 bytes). -
Certificate: Contains the server certificate. Currently this uses classical signatures (ECDSA or RSA) because PQ certificates are not yet in production WebPKI.
-
CertificateVerify: The server signature over the handshake transcript. Currently classical.
Identifying problems
Common issues visible in packet captures:
- ClientHello too large: If the PQ key share causes the ClientHello to exceed typical MTU, you may see TCP fragmentation or middlebox interference
- Server does not select PQ group: The server’s supported_groups do not include the PQ option, or its TLS library does not support PQ key exchange
- Handshake failure after ServerHello: May indicate a decapsulation failure or key share format mismatch
Tool 4: OpenSSL s_client and s_server
OpenSSL 3.5 (with built-in PQ support) or OpenSSL 3.x with oqs-provider lets you test PQ TLS connections from the command line.
Testing as a client
# Connect to a server and request ML-KEM-768
openssl s_client -connect example.com:443 \
-groups X25519MLKEM768 \
-sigalgs ml-dsa-65
# Show the negotiated parameters
# Look for "Server Temp Key: ML-KEM-768" in output
Running a test server
# Generate ML-DSA-65 key and self-signed cert
openssl genpkey -algorithm ml-dsa-65 -out server.key
openssl req -new -x509 -key server.key -out server.crt \
-days 365 -subj "/CN=pqc-test.local"
# Start server with PQ key exchange
openssl s_server -key server.key -cert server.crt \
-groups X25519MLKEM768:mlkem768:X25519 \
-accept 4433
Verifying algorithm negotiation
The s_client output includes a line showing the negotiated key exchange:
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server Temp Key: ML-KEM-768, 1184 bits
If you see “X25519, 253 bits” instead, the PQ negotiation failed and the connection fell back to classical key exchange.
Tool 5: Docker-based PQC TLS lab
For repeatable testing in isolated environments, Docker-based labs provide a complete PQC TLS stack.
Using the OQS Docker images
# Run OQS-OpenSSL server
docker run --platform linux/amd64 -p 4433:4433 \
openquantumsafe/oqs-openssl3:latest \
openssl s_server -cert /certs/server.crt \
-key /certs/server.key \
-groups mlkem768 -accept 4433
# Connect from another container
docker run --platform linux/amd64 --rm \
openquantumsafe/oqs-openssl3:latest \
openssl s_client -connect host.docker.internal:4433 \
-groups mlkem768
Platform note for Apple Silicon
The OQS Docker images are published for linux/amd64 only. On Apple Silicon Macs (M1/M2/M3/M4), you must add --platform linux/amd64 to all Docker commands. This runs under QEMU emulation, which is slower but functionally correct for testing purposes.
Tool 6: Automated interop testing with pytest
For CI/CD integration, build automated PQC interop tests:
import subprocess
import pytest
PQ_GROUPS = ["X25519MLKEM768", "mlkem768", "mlkem1024"]
@pytest.mark.parametrize("group", PQ_GROUPS)
def test_pq_handshake(group, pq_server):
"""Verify that PQ key exchange completes successfully."""
result = subprocess.run(
[
"openssl", "s_client",
"-connect", f"localhost:{pq_server.port}",
"-groups", group,
"-CAfile", "test-ca.crt",
],
input=b"Q\n",
capture_output=True,
timeout=10,
)
assert result.returncode == 0
assert b"Server Temp Key: ML-KEM" in result.stdout
def test_fallback_when_pq_unavailable(classical_server):
"""Verify graceful fallback to X25519 when server has no PQ."""
result = subprocess.run(
[
"openssl", "s_client",
"-connect", f"localhost:{classical_server.port}",
"-groups", "X25519MLKEM768:X25519",
"-CAfile", "test-ca.crt",
],
input=b"Q\n",
capture_output=True,
timeout=10,
)
assert result.returncode == 0
assert b"Server Temp Key: X25519" in result.stdout
Tool 7: Cloudflare PQ checker
Cloudflare operates a post-quantum test page that shows whether your browser successfully negotiated a PQ key exchange:
- Visit
pq.cloudflareresearch.com(or check any Cloudflare-proxied site) - The page displays the key agreement algorithm your browser used
- If you see “X25519MLKEM768”, your browser and the connection support PQ key exchange
This is the quickest way to verify that your browser environment supports post-quantum TLS without any setup.
Performance testing methodology
When benchmarking PQC performance, measure these metrics:
Handshake latency
# Measure TLS handshake time with PQ
time openssl s_client -connect server:443 \
-groups X25519MLKEM768 </dev/null 2>/dev/null
# Compare with classical-only
time openssl s_client -connect server:443 \
-groups X25519 </dev/null 2>/dev/null
Bandwidth overhead
| Component | Classical (X25519 + ECDSA) | Hybrid PQ (X25519MLKEM768 + ECDSA) |
|---|---|---|
| ClientHello key share | 32 bytes | 32 + 1,184 = 1,216 bytes |
| ServerHello key share | 32 bytes | 32 + 1,088 = 1,120 bytes |
| Total handshake overhead | baseline | +2,272 bytes |
Load testing
Use tools like h2load or custom scripts to test PQ TLS under concurrent connections:
# h2load with PQ (requires PQ-capable build)
h2load -n 10000 -c 100 https://pq-server:443/
Common testing pitfalls
-
Forgetting to test fallback: Always verify that your client gracefully falls back to classical key exchange when connecting to servers without PQ support. List PQ groups first but include X25519 as a fallback.
-
Ignoring middlebox interference: Corporate proxies, firewalls, and CDN edge nodes may strip or reject large ClientHello messages. Test through your actual network path, not just localhost.
-
Only testing happy path: Test with mismatched versions, corrupted key shares, and expired certificates. PQ implementations should fail safely.
-
Not measuring cold-start latency: The first PQ handshake may be slower due to key generation. Measure both first-connection and subsequent-connection latency.
-
Testing only key exchange: PQ key exchange is the most deployed feature today, but also test PQ certificate validation (using self-signed ML-DSA certs) to prepare for future WebPKI changes.
Testing checklist
Use this checklist for PQC deployment validation:
- Browser DevTools confirm X25519MLKEM768 key exchange
- Wireshark capture shows correct PQ key share in ClientHello
- Server selects PQ group when offered
- Graceful fallback to X25519 when PQ unavailable
- No handshake failures through corporate proxy/firewall
- TLS handshake completes under 200ms (typical network)
- PQ handshake overhead is less than 50ms versus classical
- OQS test server connection succeeds
- Self-signed ML-DSA certificate validates in test environment
- Load test shows acceptable throughput under PQ
- Monitoring/alerting covers PQ negotiation failures
FAQ
Q: Do I need special tools to test PQ key exchange?
For basic testing, no. Modern Chrome and Firefox already negotiate X25519MLKEM768 by default when the server supports it. Use browser DevTools to confirm. For deeper testing (packet inspection, interop, CI/CD), you need OpenSSL 3.5 or OQS tools.
Q: How do I test PQ if my server does not support it yet?
Use the OQS test server at test.openquantumsafe.org, Cloudflare’s PQ-enabled edge (any Cloudflare site), or spin up a local test server using OpenSSL 3.5 with PQ groups enabled.
Q: What does a failed PQ negotiation look like?
The connection does not fail. It silently falls back to classical key exchange (X25519 or P-256). You will only notice by checking DevTools or packet captures. This is by design: hybrid negotiation ensures backward compatibility.
Q: Can Wireshark decode PQ TLS handshakes?
Yes, Wireshark 4.2+ recognizes PQ key exchange groups and displays them correctly in the TLS handshake dissector. You can filter by specific group code points.
Q: How much larger is a PQ TLS handshake?
With X25519MLKEM768 hybrid key exchange and a classical ECDSA certificate, the handshake is approximately 2.3 KB larger than a purely classical handshake. If ML-DSA certificates are used in the future, add approximately 5 KB more.
Q: Should I test on Apple Silicon or Intel?
Test on both if your deployment targets both. OQS Docker images require x86 emulation on Apple Silicon, which affects performance measurements but not correctness. For accurate benchmarks, use native x86 hardware or compile natively on ARM.
Q: How do I automate PQ testing in CI/CD?
Use OpenSSL s_client in pytest or shell scripts to verify PQ negotiation against your test servers. Check for “Server Temp Key: ML-KEM” in the output. Run these tests in your pipeline after every TLS configuration change.
Sources
- Open Quantum Safe: Test Server — Public interoperability test server for PQC TLS connections
- Cloudflare Radar: Post-Quantum Encryption — Browser PQ check tool and server PQ support verification
- Cloudflare: PQC Support documentation — Browser version support for X25519MLKEM768 (Chrome 131+, Firefox 132+)
- Open Quantum Safe: oqs-provider — OpenSSL 3.x provider for testing PQC algorithms
- RFC 9954: Hybrid Key Exchange in TLS 1.3 — Standard defining X25519MLKEM768 hybrid key exchange
- Wireshark: TLS dissector documentation — PQ handshake analysis in Wireshark 4.2+