How to Test Your Post-Quantum Cryptography Implementation

Published

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:

  1. Correctness testing: Does your implementation produce valid outputs? Do KATs (Known Answer Tests) pass?
  2. Interoperability testing: Can your client talk to other servers? Can other clients talk to your server?
  3. Performance testing: What is the latency impact? How do larger key sizes affect throughput?
  4. 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:

PortServer SignatureKey Exchange
6042ML-DSA-65ML-KEM-768
6043ML-DSA-87ML-KEM-1024
6044ML-DSA-44ML-KEM-512
6050SLH-DSA-SHA2-128fML-KEM-768
6060Falcon-512ML-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

  1. Open DevTools (F12 or Cmd+Option+I)
  2. Navigate to the Security tab
  3. Click on the page origin under “Main origin”
  4. 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

  1. Click the lock icon in the address bar
  2. Click “Connection secure” then “More Information”
  3. 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:

  1. ClientHello: supported_groups extension includes X25519MLKEM768 (0x4588). The key_share extension contains both a 32-byte X25519 share and an 1184-byte ML-KEM-768 public key.

  2. ServerHello: key_share extension contains the server’s X25519 share (32 bytes) and ML-KEM-768 ciphertext (1088 bytes).

  3. Certificate: Contains the server certificate. Currently this uses classical signatures (ECDSA or RSA) because PQ certificates are not yet in production WebPKI.

  4. 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:

  1. Visit pq.cloudflareresearch.com (or check any Cloudflare-proxied site)
  2. The page displays the key agreement algorithm your browser used
  3. 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

ComponentClassical (X25519 + ECDSA)Hybrid PQ (X25519MLKEM768 + ECDSA)
ClientHello key share32 bytes32 + 1,184 = 1,216 bytes
ServerHello key share32 bytes32 + 1,088 = 1,120 bytes
Total handshake overheadbaseline+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

  1. 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.

  2. 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.

  3. Only testing happy path: Test with mismatched versions, corrupted key shares, and expired certificates. PQ implementations should fail safely.

  4. Not measuring cold-start latency: The first PQ handshake may be slower due to key generation. Measure both first-connection and subsequent-connection latency.

  5. 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