OpenSSL 3.5.0 (released April 8, 2025) introduced significant changes that impact TLS handshake performance. These changes carry forward to all 3.5.x and 3.6.x releases. Our benchmarks compare the latest versions of the 3.4.x, 3.5.x, and 3.6.x series to analyze the performance impact.
1,281
Commits between versions
1,326
Files changed
~13%
Handshake performance reduction
Key Finding: The observed handshake performance reduction is because OpenSSL 3.5.x pre-computes two key shares by default (ML-KEM-768 + X25519) on every TLS 1.3 connection. This happens even when the server doesn't support ML-KEM. This is a deliberate security enhancement to prepare for quantum-safe cryptography, not a bug.
📋 Tested OpenSSL Versions
This benchmark suite tests the following OpenSSL versions to provide comprehensive performance comparisons across the entire supported release series:
Version
Series
Release Date
Key Features
Support Status
1.1.1w
1.1.1 (LTS)
Sep 2023
Final 1.x release, baseline for comparisons
EOL (Sep 2023)
3.0.15
3.0 (LTS)
Sep 2024
Provider architecture, FIPS module
Supported until Sep 2026
3.1.7
3.1
Sep 2024
Performance improvements, bug fixes
Maintenance
3.2.3
3.2 (LTS)
Sep 2024
Client QUIC, HPKE, major performance recovery
Supported until Nov 2026
3.3.2
3.3
Sep 2024
Continued QUIC improvements, QLOG
Maintenance
3.4.0
3.4
Oct 2024
Peak traditional performance, pre-PQC
Current stable
3.5.3
3.5
Sep 2025
ML-KEM/PQC, server QUIC, 0-RTT
Current feature
Version Selection Rationale: We test the latest patch release of each major version series to ensure fair comparisons. LTS (Long-Term Support) versions receive priority as they're most commonly deployed in production environments.
Quick Version Comparison
1.1.1w
Baseline
Direct API No providers
3.0.x
Provider Era
New architecture ~20% overhead
3.2.x
Recovery
QUIC client Performance boost
3.4.x
Peak
Best traditional performance
3.5.x+
Quantum-Ready
ML-KEM/PQC Server QUIC
Performance Journey
The chart below shows how TLS handshake performance evolved across versions:
1.1.1w 19,557
3.0.15 16,719
3.1.7 17,150
3.2.3 17,892
3.3.2 17,107
3.4.0 18,806
3.5.3 16,300
TLS 1.3 New Handshakes per Second (higher is better)
Key Observations:
3.0.x introduced regression due to provider architecture overhead
3.2.x-3.4.x recovered through optimization work
3.5.x trades performance for security with quantum-resistant defaults
The Primary Cause: Default TLS Keyshare Changes
We traced the performance difference to a specific commit in the OpenSSL source code that changed how TLS 1.3 key shares are generated by default.
The Critical Code Change: Commit 63a70d6 by Viktor Dukhovni (Feb 25, 2025)
Generate a key share upfront - Pre-compute key material in the ClientHello
CPU cost on EVERY connection
?
Optional - Skip if group is not available
Minimal
/
Tuple separator - Groups in a preference order
None
The Key Insight: The * prefix in ?*X25519MLKEM768 means OpenSSL now pre-computes an ML-KEM key pair for EVERY TLS 1.3 ClientHello, even if the server doesn't support ML-KEM!
Why This Hurts Performance
What Happens on EVERY Connection (3.5.x default)
Generate ML-KEM-768 key pair - This is a lattice-based crypto operation (~1000+ CPU cycles)
Generate X25519 key pair - Standard ECDH (fast)
Send both key shares in ClientHello
If server doesn't support ML-KEM, the ML-KEM computation is completely wasted
What Happened Before (3.4.x)
Generate X25519 key pair - Only one operation
Send single key share in ClientHello
Why Did They Do This? The trade-off is intentional:
Benefit: Avoids an extra round-trip (HRR) when connecting to ML-KEM-supporting servers
Cost: Every connection pays the ML-KEM CPU cost upfront
This is a security-first decision: preparing for quantum-safe cryptography
GitHub Reference
View the exact commit that introduced this change:
Note: Benchmark data is automatically updated when new tests are run. The values above represent typical differences between the 3.4.x and 3.5.x+ series. See summary.json for the latest raw data.
Analysis: The handshake performance reduction (~13%) is expected and correlates with the additional ML-KEM operations during key exchange. RSA and ECDSA operations show minimal change (-1% to -2%), which is within normal variance.
Finding: The openssl speed command now properly tests AEAD ciphers by computing and validating authentication tags, which is more realistic but slower
Impact: AES-GCM benchmark results between 3.4.x and 3.5.x are not directly comparable due to this methodology change
Why our AES-GCM numbers look different: This is NOT a performance regression. The 3.5.x benchmarks now properly measure AEAD operations including tag computation/verification. Previous benchmarks were testing unrealistic scenarios.
If post-quantum security is a priority, accept the ~13% handshake overhead as the cost of future-proofing
For high-throughput servers where PQC isn't required, configure traditional ECDH keyshares
Consider the bandwidth impact: ML-KEM keys are ~37x larger than ECDH P-256 keys
⚠️ QUIC + Post-Quantum Challenge: The large key sizes of ML-KEM create significant complications for QUIC (HTTP/3), which relies on UDP packets with strict MTU constraints (~1,200 bytes). A hybrid ML-KEM+X25519 key share is ~2,336 bytes—exceeding the entire QUIC Initial packet limit!
To use traditional ECDH-only key exchange in 3.5.x (matching 3.4.x behavior):
SSL_CTX_set1_groups_list(ctx, "X25519:P-256:P-384");
// Or via openssl.cnf:
// Groups = X25519:P-256:P-384
Verify This Hypothesis
We provide a test script to verify that ML-KEM default keyshares are the primary cause of the performance difference. The test compares three configurations of the latest OpenSSL 3.5.x:
Test Configurations
Default (ML-KEM preferred): Standard 3.5.x build with default keyshares (X25519MLKEM768:X25519)
ECDH-only groups: Same build, but with traditional keyshares (X25519:P-256:P-384)
no-ml-kem build: 3.5.x compiled without ML-KEM support entirely
Expected Results: If the hypothesis is correct:
Configuration 1 (default) should be ~13% slower than Configuration 2 (ECDH-only)
Configuration 2 should perform similar to OpenSSL 3.4.x
Configuration 3 should also be similar to Configuration 2
Run the Test
# From the benchmark repository root:
npm run test:mlkem-hypothesis
# Or run the script directly:
bash scripts/test-mlkem-hypothesis.sh
# This will:
# 1. Download and build latest OpenSSL 3.5.x with default settings
# 2. Build OpenSSL 3.5.x with no-ml-kem option
# 3. Run TLS handshake benchmarks on all configurations
# 4. Output a comparison showing the performance difference
OpenSSL Configure Option
The key configure option to disable ML-KEM support is:
./Configure no-ml-kem
This will compile OpenSSL without ML-KEM (Kyber) support, effectively making it behave like 3.4.x for TLS keyshares.