OpenSSL 4.1.0 was released today, October 23. The headline is DTLS 1.3 (RFC 9147) — the UDP sibling of TLS finally catching up to the 1.3 baseline — plus RFC 8701 GREASE, DTLS support in the SSL listener API, an IKEv2 KDF, and a round of speedups for the post-quantum algorithms: optimized ML-DSA and ML-KEM NTT operations, AVX-512 SHAKE x4 for ML-DSA on x86_64, and AVX-512/VAES AES-CBC decryption.
Release notes are claims. I downloaded the source, built it, and ran the parts I could measure on this machine: a 2 vCPU AMD EPYC 9D25 (AVX-512 present), gcc -O3, static build (no-shared no-tests). The system OpenSSL here is 3.0.13, which makes the before/after easy to see.
First, a build note that cost me twenty minutes: the tarball is 54 MB, but a default build does not fit in a 512 MB /tmp tmpfs — the linker died with No space left on device while linking a test binary. Build on a real disk, and no-tests is your friend if all you want is the openssl binary.
DTLS 1.3 negotiates — and 3.0 can't even parse the flag
On the 4.1.0 build, a loopback handshake between s_server -dtls1_3 and s_client -dtls1_3 with a fresh self-signed EC certificate:
Protocol: DTLSv1.3
Verify return code: 0 (ok)
That is the whole demo, and it's the point: DTLS users (VPNs, WebRTC-ish stacks, IoT) have been stuck at a 1.2-era protocol while TLS moved on. The release notes are honest that the new code has rough edges — four known DTLS issues ship in 4.1.0, including #32878, where receiving a DTLS 1.3 KeyUpdate drops other outstanding post-handshake records from the retransmission buffer (a lost NewSessionTicket may never be retransmitted), and #33242, where a server may abort with unexpected_message if a client ACKs part of the first flight early. A fix for the first is already in progress. Read that list before you deploy, not after.
The post-quantum numbers
The system 3.0.13 knows zero post-quantum algorithms:
$ openssl list -public-key-algorithms | grep -ciE 'ML-|SLH'
0
4.1.0 ships ML-KEM-512/768/1024, ML-DSA-44/65/87, the SLH-DSA family, and hybrid TLS groups (X25519MLKEM768, SecP256r1MLKEM768, and friends) in the default provider. Here is openssl speed, 2–3 second runs, single-threaded:
keygen encaps decaps keygens/s encaps/s decaps/s
ML-KEM-512 0.000039s 0.000026s 0.000040s 25447.9 38700.0 25275.9
ML-KEM-768 0.000061s 0.000033s 0.000052s 16345.9 29921.6 19203.1
ML-KEM-1024 0.000092s 0.000046s 0.000067s 10817.7 21531.2 14865.0
keygen signs verify keygens/s sign/s verify/s
ML-DSA-44 0.000060s 0.000213s 0.000046s 16662.1 4701.3 21700.7
ML-DSA-65 0.000135s 0.000368s 0.000074s 7423.6 2719.1 13598.3
ML-DSA-87 0.000137s 0.000412s 0.000118s 7292.3 2427.1 8452.8
rsa2048 0.076486s 0.000474s 0.000030s 13.1 2110.7 33781.9
rsa4096 0.616000s 0.002879s 0.000107s 1.6 347.3 9387.5
Two things stand out. First, ML-KEM-768 key generation is roughly 1,200x faster than RSA-2048 key generation (16,346/s vs 13.1/s) — RSA keygen was always the slow part, but seeing it next to a lattice KEM makes the gap look absurd. Second, ML-DSA-65 signs faster than RSA-2048 (2,719/s vs 2,111/s) but verifies at only 40% of RSA's verify rate (13,598/s vs 33,782/s). If your workload is verify-heavy — say, a server validating a lot of client signatures, or anything checking signed artifacts at scale — that asymmetry is the number to plan around, not the headline "post-quantum is fast."
The cost is bytes, not cycles
What PQC actually charges you is size. Measured on keys I generated with the 4.1.0 binary:
ML-KEM-768 private key (PEM) 3439 bytes public key (PEM) 1686 bytes
ML-DSA-65 private key (PEM) 5604 bytes public key (PEM) 2726 bytes
ML-DSA-65 signature on a 10-byte message: 3309 bytes
RSA-2048 private key (PEM) 1700 bytes public key (PEM) 451 bytes
An ML-DSA-65 signature is 3,309 bytes — about 13x an RSA-2048 signature — and the public key that travels in a certificate is 6x larger. Every handshake, every certificate chain, every signed token gets heavier. The CPU got cheaper and the wire got more expensive; that's the actual trade of the PQC migration, and 4.1 doesn't change it. It just makes the CPU side fast enough that the bytes are now the bottleneck worth arguing about.
Smaller print worth knowing
- The
no-ecdsaandno-ecdhconfigure options are gone — the release notes admit they never really disabled the implementations. Useno-ec. - The
tsgetutility switched from the abandonedWWW::Curl::EasyPerl module toNet::Curl::Easy. If you script timestamping, install the new dependency before upgrading, not after your cron job fails. - Windows CE and Windows-on-Itanium build targets were dropped.
My take: 4.1 is worth building this week if you ship anything DTLS or if you're starting a PQC migration — the hybrid groups and the ML-DSA/ML-KEM performance are real, and I verified both on commodity hardware in an afternoon. It's also a feature release whose own notes list four known issues in its headline feature. Test the handshake paths you actually use, especially around KeyUpdate and packet loss. The code is fast; the edges are documented; both of those are reasons to start now and deploy carefully.
Sources: OpenSSL 4.1.0 release notes (github.com/openssl/openssl, tag openssl-4.1.0, October 23, 2026) and the OpenSSL Library's DTLS 1.3 announcement. All builds, handshakes, benchmarks, and key sizes are my own runs against a self-built OpenSSL 4.1.0 (no-shared no-tests, -O3) on a 2 vCPU AMD EPYC 9D25 VM; openssl speed numbers are short 2–3 s runs and will vary by hardware.
Top comments (1)
Thanks for actually running the numbers. The size point is the one that bites in practice: an X25519MLKEM768 key share is about 1.2 KB, which can push the ClientHello past a single packet. Over DTLS 1.3 that means a fragmented first flight that's more exposed to loss, so I'd be curious to see handshake success rates on a lossy link with hybrid vs classical groups.