Core Architectural Divergence
OSSL_PROVIDER framework. While this enables pluggable cryptographic modules (vital for legacy government FIPS implementations), it introduces severe abstraction overhead. Every symmetric cipher setup, digest allocation, and key exchange traversal in OpenSSL 3.x passes through provider lookup dispatch tables, reference-counted context lookups, and heap-allocated property queries.
BoringSSL rejects this dynamic modularity entirely. It does not provide ABI stability across releases, disallows third-party dynamic engines, completely excises dead protocols (SSLv2, SSLv3, IDEA, SEED, and unauthenticated cipher suites), and hard-links optimized assembly directly into primitive calls. Thread safety inside BoringSSL relies on minimal lock contention, favoring atomic pointers and per-thread structures over the granular mutex locking found in OpenSSL ticket caches.
Benchmark Methodology and Test Rig
c3-standard-16 compute nodes (16 vCPU Intel Xeon 4th Gen Sapphire Rapids, 64 GB RAM) running Debian 13 (Linux kernel 6.12). Client load was generated from isolated Azure D16s_v5 instances within the same regional metro area over a low-latency interconnect.
Load profiling was executed using h2load and wrk2, simulating 50,000 concurrent long-lived HTTP/2 streams interspersed with continuous new TLS 1.3 handshakes to stress the crypto state machine under CPU and memory pressure.
Workload Scenarios
- Standard Classical TLS 1.3: ECDHE with X25519 key exchange, RSA-3072 and ECDSA P-256 server certificates, AES-128-GCM cipher suite.
- Hybrid Post-Quantum TLS 1.3: ML-KEM-768 combined with X25519 (
X25519MLKEM768), simulating current compliance directives for transit encryption. - Session Resumption Under Load: TLS 1.3 0-RTT and PSK ticket verification across a 4-worker multi-threaded proxy pool.
Production Performance Metrics
| Metric | OpenSSL 3.4.0 (Default Provider) | BoringSSL (Commit 7b8e1f) | Differential |
|---|---|---|---|
| Memory RSS (50k Idle TLS Sessions) | 1.84 GB | 612 MB | -66.7% RSS |
| Memory per Active SSL Context | ~36.8 KB | ~12.2 KB | -66.8% Heap |
| Classical Handshake P95 Latency | 12.4 ms | 7.8 ms | -37.1% |
| Classical Handshake P99 Latency | 28.1 ms | 11.2 ms | -60.1% |
| Hybrid PQ (ML-KEM-768) P95 Latency | 19.8 ms | 12.6 ms | -36.4% |
| Hybrid PQ (ML-KEM-768) P99 Latency | 44.2 ms | 18.9 ms | -57.2% |
| Max Handshake Throughput (Cores Saturated) | 9,420 handshakes/sec | 14,880 handshakes/sec | +57.9% |
CPU Lock Contention (perf mutex spin) | 8.4% | under 0.5% | Negligible |
Memory Footprint and Allocation Profile
SSL struct plus internal EVP_PKEY_CTX allocations). Under 50,000 active streams, the OpenSSL proxy consumed nearly 1.9 GB of RAM purely for state tracking.
BoringSSL drastically compacts the SSL and SSL_SESSION structures. By stripping non-standard extensions and dynamic cipher metadata, BoringSSL maintained the same 50,000 active connection tables at roughly 612 MB. In Kubernetes environments running tight memory cgroups (for instance, Envoy sidecars limited to 1 GB), running OpenSSL under high concurrency routinely triggers cgroup OOM kills during handshake bursts, whereas BoringSSL operates safely within thresholds.
BASH
# Inspecting context allocations via jemalloc profiling during load
jeprof --show_bytes --pdf /usr/local/bin/envoy-boring profile.out > boring_alloc.pdf
jeprof --show_bytes --pdf /usr/local/bin/envoy-openssl profile.out > openssl_alloc.pdfOSSL_PROPERTY_LIST and dynamic provider name resolution tables, whereas BoringSSL showed zero heap allocations during the symmetric crypto phase once the handshake completed.
Post-Quantum Hybrid Handshake Penalties
X25519MLKEM768 increases ClientHello and ServerHello packet sizes from approximately 500 bytes to over 1,500 bytes. This causes handshake payload fragmentation across TCP boundaries, increasing network packet round trips if the initial congestion window (initcwnd) is misconfigured.
Cryptographically, ML-KEM encapsulation and decapsulation require significant polynomial multiplication cycles. BoringSSL leverages highly optimized AVX2 and AVX-512 assembly for ML-KEM-768 primitives on modern x86_64 cores. OpenSSL 3.4 relies on generic C implementations unless explicitly compiled with targeted compiler intrinsics that depend on system-level toolchain capabilities.
Under sustained PQ handshake load, OpenSSL’s handshake P99 latency degraded to 44.2 ms due to thread synchronization locks during cipher context generation. BoringSSL capped P99 latency at 18.9 ms, maintaining consistent performance through lockless session ticket validation.
Building Envoy with BoringSSL vs System OpenSSL
OPENSSL_3_SYSTEM toolchains. Here is how the build configurations diverge in production Bazel workflows.
Production Envoy Bazel Build Configuration with BoringSSL
BASH
# Standard hardened BoringSSL target inside Envoy's workspace
bazel build -c opt \
--define boringssl=fips \
--copt='-march=x86-64-v3' \
--copt='-O3' \
//source/exe:envoy-staticCMAKE
# CMake snippet for statically linking BoringSSL
set(BORINGSSL_DIR '/opt/boringssl')
include_directories(${BORINGSSL_DIR}/include)
add_executable(edge_gateway main.cpp)
target_link_libraries(edge_gateway
${BORINGSSL_DIR}/build/ssl/libssl.a
${BORINGSSL_DIR}/build/crypto/libcrypto.a
pthread
m
)Envoy Gateway TLS Configuration for ML-KEM-768
transport_socket configuration:
YAML
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
'@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_params:
tls_minimum_protocol_version: TLSv1_3
tls_maximum_protocol_version: TLSv1_3
# Prefer hybrid PQ first, falling back to standard X25519
ecdh_curves:
- X25519MLKEM768
- X25519
- P-256
tls_certificates:
- certificate_chain:
filename: '/etc/ssl/certs/edge.bundle.crt'
private_key:
filename: '/etc/ssl/private/edge.key'
session_ticket_keys:
keys:
- filename: '/etc/envoy/secrets/ticket.key'Diagnostic Profiling: Identifying the Bottleneck
perf to distinguish between network packet stalls and cryptographic lock contention.
BASH
# Capture call graph profile across all proxy worker threads for 30 seconds
perf record -F 99 -g -p $(pgrep -d',' -f envoy) -- sleep 30
# Analyze top cryptographic symbols
perf report --sort comm,dso,symbol --stdioTEXT
# OpenSSL 3.4 Profile under contention:
18.42% libcrypto.so.3 [.] ossl_property_query
8.11% libcrypto.so.3 [.] CRYPTO_THREAD_read_lock
6.45% libcrypto.so.3 [.] EVP_PKEY_CTX_new
4.30% libssl.so.3 [.] ssl_parse_clienthello_tlsextTEXT
# BoringSSL Profile under identical load:
28.12% envoy [.] bssl::mlkem::Decapsulate
19.45% envoy [.] bssl::x25519_scalar_mult
12.30% envoy [.] aes_hw_ctr32_encrypt_blocks
4.10% envoy [.] bssl::tls13_process_client_helloCRYPTO_THREAD_read_lock does not appear in BoringSSL’s profile. Handshake dispatch routines access pre-compiled, static lookup tables without querying runtime providers.
Operational Friction and Trade-offs
- No Stable ABI: BoringSSL makes no guarantees across git revisions. You cannot build a shared library
.soonce and rely on system package managers to update it. It must be built as a static dependency embedded directly into your gateway binary. - Patch Management Overhead: Operating BoringSSL means you are responsible for monitoring the Google repository, tracking commits, and orchestrating full container rebuilds and rolling deployments whenever a vulnerability is remediated.
- FIPS 140 Certification Delays: BoringSSL does provide a validated FIPS module (
BoringCrypto), but the certification lag is often longer and more inflexible than commercial or distribution-backed OpenSSL offerings (such as Red Hat Enterprise Linux's FIPS-validated OpenSSL builds). - Missing Legacy APIs: BoringSSL intentionally omits engine mechanisms, legacy PKCS formats, and less-common cryptographic algorithms. If your upstream identity federation layer or enterprise SAML/WS-Trust pipeline depends on antiquated cryptography, BoringSSL will fail to build or run.
