Benchmarking rustls 0.23.31 vs OpenSSL 3.5.15 vs BoringSSL on x86_64

2025-07-31

System configuration

We ran the benchmarks on a bare-metal server with the following characteristics:

Versions

The benchmarking tool used for both OpenSSL and BoringSSL was openssl-bench 82b86b22.

This was built from source with its makefile.

BoringSSL

The tested version of BoringSSL is 0.20250701.0, which was the most recent point on master when we started these measurements.

BoringSSL was built from source with CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Release.

OpenSSL

The tested version of OpenSSL is 3.5.1, which was the latest release at the time of writing.

OpenSSL was built from source with ./Configure ; make -j12.

Rustls

The tested version of rustls is 0.23.31, which was the latest release at the time of writing. This was used with aws-lc-rs 1.13.1 / aws-lc-sys 0.29.0.

Measurements

BoringSSL was tested with this command:

~/bench/openssl-bench
$ BENCH_MULTIPLIER=16 setarch -R make measure BORINGSSL=1

OpenSSL was tested with this command:

~/bench/openssl-bench
$ BENCH_MULTIPLIER=16 setarch -R make measure

rustls was tested with this command:

~/bench/rustls
$ BENCH_MULTIPLIER=16 setarch -R make -f admin/bench-measure.mk measure

Results

Transfer measurements are in megabytes per second. Handshake units are handshakes per second.

BoringSSL 0.20250701.0OpenSSL 3.5.1rustls 0.23.31
transfer, 1.2, aes-128-gcm, sending8575.276565.228074.82
transfer, 1.2, aes-128-gcm, receiving6986.817219.677952.68
transfer, 1.3, aes-256-gcm, sending7739.616093.277628.68
transfer, 1.3, aes-256-gcm, receiving6421.366472.37407.83
BoringSSL 0.20250701.0OpenSSL 3.5.1rustls 0.23.31
full handshakes, 1.2, rsa, client5375.063251.548206.33
full handshakes, 1.2, rsa, server1447.3321692857.81
full handshakes, 1.2, ecdsa, client3454.892195.554345.05
full handshakes, 1.2, ecdsa, server9096.445178.0213618.81
full handshakes, 1.3, rsa, client3125.362222.214187.28
full handshakes, 1.3, rsa, server1285.881714.242273.13
full handshakes, 1.3, ecdsa, client2344.761650.562884.83
full handshakes, 1.3, ecdsa, server5113.833183.266229.71
BoringSSL 0.20250701.0OpenSSL 3.5.1rustls 0.23.31
resumed handshakes, 1.2, client47,509.519,936.565,617.35
resumed handshakes, 1.2, server46,561.821,043.174,771.51
resumed handshakes, 1.3, client4695.793574.865614.4
resumed handshakes, 1.3, server5803.033771.286623.94

graph of transfer speeds

graph of full handshakes

graph of resumed handshakes

Notable changes since last time

Post-quantum key exchange

OpenSSL and rustls now use X25519MLKEM768 post-quantum key exchange by default. BoringSSL is configured to do the same. This applies to all TLS1.3 handshakes.

oldnew
BoringSSL 76968bb3➡️BoringSSL 0.20250701.0
full handshakes, 1.3, rsa, client4813.91 hs/s1.54x slower3125.36 hs/s
OpenSSL 3.3.2➡️OpenSSL 3.5.1
full handshakes, 1.3, rsa, client2788.76 hs/s1.25x slower2222.21 hs/s
rustls 0.23.15➡️rustls 0.23.31
full handshakes, 1.3, rsa, client6803.93 hs/s1.62x slower4187.28 hs/s

BoringSSL AVX-512 AES-GCM

BoringSSL now has AVX512-accelerated AES-GCM. Since last time, that looks like:

oldnew
BoringSSL 76968bb3➡️BoringSSL 0.20250701.0
transfer, 1.2, aes-128-gcm, sending5043.04 MB/s1.7x faster8575.27 MB/s

rustls extension optimizations

We spent some time improving our internal representation for TLS extensions. This applied to clients and servers, and all TLS versions. But it's most visible here in TLS1.2 performance because there aren't any cryptography changes masking it.

oldnew
rustls 0.23.15➡️rustls 0.23.31
resumed handshakes, 1.2, client64,722.55 hs/s1.02x faster65,617.35 hs/s
resumed handshakes, 1.2, server71,149.91 hs/s1.05x faster74,771.51 hs/s