Real-World Implementations & Libraries
Overview
Standardizing post-quantum algorithms was the first battle. Shipping them in production software is the war. The distance between a published FIPS standard and a constant-time, side-channel-resistant, FIPS-validated implementation integrated into your TLS stack is measured in years of engineering effort, millions of lines of test vectors, and hard-won lessons about the gap between reference code and production cryptography.
This page maps the PQC implementation ecosystem as it exists today: the research libraries that made early experimentation possible, the production libraries that browsers and cloud providers actually ship, the HSM vendors racing to add hardware-backed PQC support, and the performance realities that determine what you can deploy where. For the protocol-level integration of these libraries, see PQC in Protocols. For migration planning that depends on library readiness, see Migration Strategies & Crypto Agility.
1. Open Quantum Safe (OQS) Project
The Open Quantum Safe project, initiated by Michele Mosca and Douglas Stebila at the University of Waterloo in 2016, has been the single most important catalyst for PQC adoption. OQS provided the first usable, multi-algorithm PQC library and — critically — built integration layers into the TLS and SSH stacks that developers actually use. Without OQS, PQC experimentation would have remained confined to cryptographic researchers running standalone benchmarks.
1.1 liboqs: The Core C Library
liboqs is a C library providing a common API for post-quantum key encapsulation mechanisms (KEMs) and digital signature schemes. Its design philosophy prioritizes breadth and accessibility over production hardening.
Architecture:
┌─────────────────────────────────────────────────┐
│ liboqs API │
│ OQS_KEM_* functions │ OQS_SIG_* functions │
├────────────────────────┼────────────────────────-┤
│ KEM backends │ SIG backends │
│ ┌──────────────────┐ │ ┌──────────────────┐ │
│ │ ML-KEM-512/768/ │ │ │ ML-DSA-44/65/87 │ │
│ │ 1024 │ │ │ │ │
│ ├──────────────────┤ │ ├──────────────────┤ │
│ │ HQC-128/192/256 │ │ │ FN-DSA-512/1024 │ │
│ ├──────────────────┤ │ ├──────────────────┤ │
│ │ BIKE │ │ │ SLH-DSA (all │ │
│ │ │ │ │ parameter sets) │ │
│ ├──────────────────┤ │ ├──────────────────┤ │
│ │ FrodoKEM │ │ │ MAYO, CROSS, │ │
│ │ │ │ │ etc. │ │
│ └──────────────────┘ │ └──────────────────┘ │
├────────────────────────┴────────────────────────-┤
│ Common: RNG abstraction, memory management, │
│ build system (CMake), test harness │
└─────────────────────────────────────────────────┘
API design. The liboqs API is intentionally simple — a flat C API with a small number of functions per primitive type:
// Key Encapsulation
OQS_KEM *kem = OQS_KEM_new(OQS_KEM_alg_ml_kem_768);
OQS_KEM_keypair(kem, public_key, secret_key);
OQS_KEM_encaps(kem, ciphertext, shared_secret, public_key);
OQS_KEM_decaps(kem, shared_secret, ciphertext, secret_key);
OQS_KEM_free(kem);
// Digital Signatures
OQS_SIG *sig = OQS_SIG_new(OQS_SIG_alg_ml_dsa_65);
OQS_SIG_keypair(sig, public_key, secret_key);
OQS_SIG_sign(sig, signature, &sig_len, message, msg_len, secret_key);
OQS_SIG_verify(sig, message, msg_len, signature, sig_len, public_key);
OQS_SIG_free(sig);
Each algorithm object reports its key sizes, ciphertext/signature sizes, and claimed security level through struct fields, enabling generic code that works across algorithms without hardcoded size constants.
Algorithm coverage. As of early 2026, liboqs includes:
- NIST standards: ML-KEM (FIPS 203), ML-DSA (FIPS 204), SLH-DSA (FIPS 205)
- NIST draft standards: FN-DSA (expected FIPS 206), HQC
- Round 4 candidates: BIKE, HQC (pre-standardization versions)
- Additional signatures: Candidates from NIST’s additional signatures on-ramp (MAYO, CROSS, etc.)
- Experimental: FrodoKEM (conservative lattice-based KEM)
Limitations you must understand:
- liboqs is not a production cryptographic library. Its implementations are pulled from upstream reference and optimized sources (PQClean, the NIST submission packages) and may not have undergone the same level of side-channel hardening as production libraries.
- Memory handling follows C conventions — callers are responsible for zeroizing sensitive material.
- The library does not provide hybrid constructions natively — that is the job of the integration layers.
1.2 OQS Integration Layers
The real power of OQS lies not in liboqs itself but in the integration projects that wire PQC into existing protocol stacks.
OQS Provider for OpenSSL 3.x. An OpenSSL provider that makes all liboqs algorithms available through the standard OpenSSL EVP API. Any application linked against OpenSSL 3.x can use PQC algorithms by loading the OQS provider — no source code changes required for applications using the EVP interface correctly.
# Enable the OQS provider in openssl.cnf
[openssl_init]
providers = provider_sect
[provider_sect]
oqs = oqs_sect
[oqs_sect]
activate = 1
module = /usr/lib/oqs-provider/oqsprovider.so
This is significant because it means existing TLS servers (Apache, nginx, HAProxy) can serve PQC-hybrid certificates and negotiate PQC key exchange by changing configuration — not code.
OQS-BoringSSL. A fork of BoringSSL with PQC key exchange and authentication integrated at the TLS 1.3 level. This was historically used for experimentation before Google added native PQC support to upstream BoringSSL.
OQS-OpenSSH. A fork of OpenSSH that adds PQC key exchange and PQC public key authentication. Supports hybrid key exchange (e.g., ECDH + ML-KEM) and PQC host keys. Critical for organizations that need to protect SSH sessions against harvest-now-decrypt-later attacks, particularly for long-lived infrastructure management sessions. See Migration Strategies & Crypto Agility for deployment considerations.
1.3 Language Wrappers
OQS provides bindings for major programming languages, all built on top of liboqs via FFI:
| Language | Project | Binding Type | Notes |
|---|---|---|---|
| Python | liboqs-python | ctypes/CFFI | Pythonic API, pip-installable, good for prototyping |
| Java | liboqs-java | JNI | Integrates with JCA/JCE framework |
| Go | liboqs-go | cgo | Native Go API wrapping liboqs |
| Rust | liboqs-rust | bindgen FFI | Rust-safe wrappers with ownership semantics |
| C++ | liboqs-cpp | Direct C++ wrapper | Thin RAII wrapper around liboqs |
| .NET | liboqs-dotnet | P/Invoke | C# bindings for .NET applications |
Important caveat: These wrappers inherit the “research library” nature of liboqs. They are appropriate for prototyping, testing, and non-production experimentation. For production deployments, use the native PQC support in production-grade libraries (described in Section 3).
2. PQClean: Reference Implementations Done Right
PQClean is a collection of post-quantum cryptographic implementations with a singular focus: clean, portable, constant-time C code that serves as a trustworthy reference baseline.
2.1 Design Philosophy
PQClean occupies a specific niche that no other project fills:
- No build system dependencies: Each implementation is a self-contained directory of
.cand.hfiles. No CMake, no autotools, no Makefiles. You copy the files into your project and compile them. - No external dependencies: No calls to
malloc, no standard library cryptographic functions, no platform-specific intrinsics in the reference (“clean”) implementations. - Constant-time discipline: Reference implementations are written to avoid secret-dependent branches and memory accesses. This is verified by tools like
valgrindwith its--tool=memcheckmode configured to treat secret data as uninitialized. - API conformance: Every implementation conforms to the NIST API (
crypto_kem_keypair,crypto_kem_enc,crypto_kem_decfor KEMs;crypto_sign_keypair,crypto_sign,crypto_sign_openfor signatures).
2.2 Implementation Tiers
PQClean maintains multiple implementation tiers per algorithm:
| Tier | Name | Purpose |
|---|---|---|
| Reference | clean | Portable C, constant-time, no platform-specific optimizations |
| Optimized (x86) | avx2 | Uses AVX2 intrinsics for performance on modern x86_64 CPUs |
| Optimized (ARM) | aarch64 | Uses NEON/SVE intrinsics for ARM platforms |
The separation is critical: the clean implementations prioritize correctness and auditability, while the optimized variants prioritize performance on specific hardware. Both must produce identical outputs for identical inputs — verified by extensive test vector comparison.
2.3 Relationship to Other Projects
PQClean implementations are the upstream source for several other projects:
graph TD
PQClean["PQClean<br/>(Reference Implementations)"]
liboqs["liboqs<br/>(OQS C Library)"]
pqcrypto_rust["pqcrypto<br/>(Rust crate)"]
pqm4["pqm4<br/>(ARM Cortex-M)"]
others["Various production<br/>libraries"]
PQClean --> liboqs
PQClean --> pqcrypto_rust
PQClean --> pqm4
PQClean --> others
liboqs --> oqs_openssl["OQS-OpenSSL Provider"]
liboqs --> oqs_ssh["OQS-OpenSSH"]
liboqs --> oqs_wrappers["Language Wrappers"]
style PQClean fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style liboqs fill:#1a3a5c,stroke:#2d6aa0,color:#ffffff
This architecture means that fixes and improvements in PQClean propagate downstream to liboqs and its integrations — but also that vulnerabilities found in PQClean implementations affect the entire downstream ecosystem.
3. Production Libraries
These are the libraries that actually handle real-world cryptographic operations in browsers, cloud services, and operating systems. Their PQC support varies from shipping-in-production to planned-for-future-release.
3.1 BoringSSL (Google)
BoringSSL is Google’s fork of OpenSSL, used in Chrome, Android, and Google Cloud services. It is the library through which more PQC key exchanges have been performed than any other — by orders of magnitude.
PQC status:
- X25519MLKEM768: Hybrid key exchange combining X25519 (ECDH on Curve25519) with ML-KEM-768. This is the combination used in Chrome for TLS 1.3 connections. Deployed at Google-scale since 2024.
- ML-KEM: Standalone ML-KEM-768 and ML-KEM-1024 support.
- ML-DSA: Support for ML-DSA-65 and ML-DSA-87.
- X25519Kyber768Draft00: The earlier draft version, now deprecated in favor of X25519MLKEM768 following final FIPS 203 publication.
Key engineering decisions:
- BoringSSL implemented ML-KEM from scratch rather than pulling from PQClean or liboqs, applying Google’s own constant-time coding standards and fuzzing infrastructure.
- The hybrid construction is implemented at the TLS layer, not as a generic KEM combiner — key exchange is specifically
X25519MLKEM768as a single named group, not a generic “ECDH + KEM” framework. - BoringSSL does not support the OpenSSL API. Applications using OpenSSL cannot simply swap in BoringSSL. The API surface is deliberately smaller and less forgiving.
What Chrome uses in practice:
When you connect to a Google service (or any server advertising the X25519MLKEM768 key share) with a recent Chrome, the TLS 1.3 handshake uses hybrid post-quantum key exchange. The client sends both an X25519 key share and an ML-KEM-768 encapsulation key in the ClientHello. The combined shared secret provides security against both classical and quantum adversaries. For protocol-level details of this handshake, see PQC in Protocols.
3.2 AWS-LC (libcrypto)
AWS-LC (AWS LibCrypto) is Amazon’s cryptographic library, forked from BoringSSL and hardened for AWS’s production infrastructure. It powers AWS services including S3, KMS, ACM, and the Nitro Enclaves platform.
PQC status:
- ML-KEM: All three parameter sets (512, 768, 1024). Production-ready.
- ML-DSA: All three parameter sets (44, 65, 87). Production-ready.
- FIPS 140-3 validation: AWS-LC has achieved FIPS 140-3 validation (certificate #4816), and AWS has been working to include ML-KEM and ML-DSA within the FIPS boundary. This is significant because FIPS validation for PQC algorithms has been a major blocker for government and regulated-industry adoption.
- Hybrid TLS: X25519MLKEM768 for TLS 1.3 key exchange.
Why AWS-LC matters for enterprises: AWS-LC is the path of least resistance for organizations already running on AWS. The AWS SDK, the AWS CLI, and AWS services themselves use AWS-LC. When AWS enables PQC in a service, the underlying library is already there. For organizations with FIPS requirements (US federal agencies, FedRAMP-authorized SaaS providers), AWS-LC’s FIPS validation path is the fastest route to compliant PQC deployment.
Production hardening beyond BoringSSL:
- Formal verification of critical arithmetic routines using tools like SAW (Software Analysis Workbench) and Coq proofs for parts of the ML-KEM implementation.
- Extensive differential fuzzing against reference implementations.
- Performance optimization for AWS Graviton (ARM) processors, which power an increasing share of AWS compute.
3.3 wolfSSL
wolfSSL targets the embedded and IoT market — constrained devices where OpenSSL’s memory footprint is prohibitive and where FIPS 140-3 validation is a non-negotiable requirement for government/military deployments.
PQC status:
- ML-KEM: All parameter sets, including ML-KEM-512 for constrained environments.
- ML-DSA: All parameter sets.
- SLH-DSA: Stateless hash-based signatures for environments requiring conservative, well-understood security assumptions.
- FIPS 140-3: wolfCrypt (the cryptographic core) has FIPS 140-3 validation, and PQC algorithms are on the certification roadmap. wolfSSL has been one of the fastest movers in expanding FIPS scope to include PQC.
- Hybrid TLS: Support for hybrid key exchange in TLS 1.3.
Embedded focus:
- Configurable at build time to include only the algorithms needed, minimizing code size.
- Tested on bare-metal RTOS environments (FreeRTOS, Zephyr, RIOT) and resource-constrained MCUs.
- Memory allocation can be configured to use static buffers — no
mallocrequired. - ML-KEM-512 keygen + encapsulation runs in under 100KB of RAM on ARM Cortex-M4, making PQC viable on surprisingly constrained hardware.
3.4 Bouncy Castle
Bouncy Castle provides PQC support for the Java and C# ecosystems through its lightweight cryptographic API and JCA/JCE provider.
PQC status (Java):
- ML-KEM: Full support (all parameter sets) via
MLKEMKeyPairGenerator,MLKEMEncapsulator,MLKEMDecapsulator. - ML-DSA: Full support via
MLDSASigner. - SLH-DSA: All parameter sets including SHAKE and SHA-2 variants.
- FN-DSA: Support based on the draft standard.
- HQC: Support based on the NIST submission.
- XMSS/LMS: Stateful hash-based signatures for firmware signing and similar use cases.
- Composite signatures: Support for hybrid classical + PQC signature combinations.
PQC status (C#):
- Mirror of the Java API adapted to .NET conventions. Lags the Java release by 1-2 minor versions.
Why Bouncy Castle matters:
For enterprise Java applications (banking, healthcare, government), Bouncy Castle is often the only viable path to PQC. The standard JDK’s SunJCE provider has been slower to add PQC support, and many organizations cannot wait for Oracle’s timeline. Bouncy Castle’s JCA provider allows PQC algorithms to be used through the standard java.security APIs:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-DSA", "BC");
kpg.initialize(MLDSAParameterSpec.ml_dsa_65);
KeyPair kp = kpg.generateKeyPair();
Signature sig = Signature.getInstance("ML-DSA", "BC");
sig.initSign(kp.getPrivate());
sig.update(message);
byte[] signature = sig.sign();
3.5 OpenSSL 3.5+
OpenSSL remains the most widely deployed cryptographic library in the world, used by the majority of Linux distributions, web servers, and network appliances.
PQC timeline:
- OpenSSL 3.0 (2021): Introduced the provider architecture that makes PQC pluggable — no PQC algorithms shipped.
- OpenSSL 3.2 (2023): No native PQC, but the OQS provider works.
- OpenSSL 3.5 (2025): Native ML-KEM support added to the default provider. X25519MLKEM768 hybrid key exchange available for TLS 1.3.
- OpenSSL 3.6+ (expected): ML-DSA, SLH-DSA support in the default provider. PQC certificate chain validation.
The provider architecture advantage: OpenSSL 3.x’s provider model means PQC support can be delivered as a loadable module rather than requiring recompilation. This is architecturally significant for Linux distributions, which can ship PQC providers as separate packages, and for appliance vendors, who can update PQC support without reflashing the entire firmware.
┌───────────────────────────────────────────────┐
│ Application (EVP API) │
├───────────────────────────────────────────────┤
│ OpenSSL Core │
├──────────┬──────────┬──────────┬──────────────┤
│ Default │ FIPS │ OQS │ Vendor │
│ Provider │ Provider │ Provider│ Provider │
│ (AES, │ (FIPS- │ (PQC │ (HSM-backed │
│ RSA, │ validated│ algos) │ PQC) │
│ ECC, │ subset) │ │ │
│ ML-KEM) │ │ │ │
└──────────┴──────────┴──────────┴──────────────┘
The FIPS complication: OpenSSL’s FIPS provider (which is separately validated) does not yet include PQC algorithms. Organizations that require FIPS-validated cryptography through OpenSSL will need to wait for a FIPS provider update that includes ML-KEM and ML-DSA — or use a different library (AWS-LC, wolfSSL) that has a faster FIPS-with-PQC certification path.
3.6 NSS (Mozilla)
NSS (Network Security Services) is Mozilla’s cryptographic library, used by Firefox, Thunderbird, and various enterprise applications.
PQC status:
- X25519MLKEM768: Hybrid key exchange added to Firefox in late 2024, enabled by default in Firefox 132+. Firefox users connecting to servers that support X25519MLKEM768 automatically get post-quantum key exchange.
- ML-KEM: Standalone support in the NSS library.
- ML-DSA: Under development; not yet shipping in Firefox for certificate verification.
- Certificate chain PQC: Not yet supported — Firefox cannot verify PQC-signed certificates. This mirrors the broader industry status where PQC key exchange is deployed but PQC authentication lags behind.
NSS engineering notes:
- NSS implements ML-KEM independently (not derived from PQClean or liboqs), following Mozilla’s security engineering practices.
- The implementation underwent review by Mozilla’s security team and external auditors before deployment.
- Firefox’s PQC key exchange interoperates with servers using BoringSSL, AWS-LC, and OpenSSL’s PQC support — this interoperability was tested extensively during the IETF standardization of the
X25519MLKEM768key share type.
4. Library Feature Comparison
The following table captures the state of PQC support across major libraries. This is a snapshot — the ecosystem evolves rapidly.
| Feature | liboqs | BoringSSL | AWS-LC | wolfSSL | Bouncy Castle | OpenSSL 3.5+ | NSS |
|---|---|---|---|---|---|---|---|
| ML-KEM | All | 768, 1024 | All | All | All | All | 768 |
| ML-DSA | All | 65, 87 | All | All | All | Planned 3.6 | In progress |
| SLH-DSA | All | No | No | All | All | Planned | No |
| FN-DSA | Draft | No | No | No | Draft | No | No |
| HQC | Yes | No | No | No | Yes | No | No |
| Hybrid KEM | Via integrations | X25519MLKEM768 | X25519MLKEM768 | Yes | Yes | X25519MLKEM768 | X25519MLKEM768 |
| TLS 1.3 PQC | Via OQS-OpenSSL | Native | Native | Native | N/A (not a TLS lib) | Native | Native |
| FIPS 140-3 | No | Module | Yes (with PQC path) | Yes (PQC roadmap) | No | Provider (no PQC yet) | Yes (no PQC yet) |
| Constant-time | Varies by algo | Yes | Yes (formally verified) | Yes | JVM-limited | Yes | Yes |
| Platforms | Linux, macOS, Windows | Linux, macOS, Windows, Android | Linux, macOS, Windows | Embedded, RTOS, all major OS | JVM, .NET | All POSIX, Windows | Linux, macOS, Windows |
| License | MIT | ISC-like | Apache 2.0 | GPLv2 / Commercial | MIT-like | Apache 2.0 | MPL 2.0 |
5. HSM and Hardware Support
Hardware Security Modules (HSMs) present unique challenges for PQC adoption: they must implement algorithms in tamper-resistant hardware, achieve FIPS 140-3 Level 3 certification (which requires the validated algorithms to be within the cryptographic boundary), and support the larger key and signature sizes that PQC demands.
5.1 Vendor Status
Thales Luna HSMs:
- ML-KEM and ML-DSA support available in Luna firmware 7.x.
- Hybrid operations (classical + PQC) supported.
- PKCS#11 interface extended with vendor-specific mechanism types for PQC algorithms.
- FIPS 140-3 Level 3 validation for the HSM platform, with PQC algorithms in the validation scope under the new CMVP guidelines.
Entrust nShield:
- PQC support through the CodeSafe development framework.
- ML-KEM and ML-DSA implementations available.
- FIPS 140-3 Level 3 validated platform; PQC algorithm validation in progress.
- Key management for hybrid certificates (classical + PQC key pairs stored together).
AWS CloudHSM:
- PQC key generation and operations available through the CloudHSM JCE provider and PKCS#11 interface.
- Backed by AWS-LC internally — same PQC implementations as the software library.
- FIPS 140-3 Level 3 validated.
- Particularly relevant for AWS KMS custom key stores and ACM Private CA with PQC certificates.
Azure Managed HSM:
- PQC support announced for Azure Key Vault Managed HSM.
- ML-KEM and ML-DSA for key protection and signing operations.
- Integration with Azure certificate services for PQC certificate lifecycle management.
- Backed by Marvell LiquidSecurity HSMs with PQC firmware.
Google Cloud HSM:
- PQC support through Cloud KMS backed by Cloud HSM.
- ML-KEM for key encapsulation, ML-DSA for digital signatures.
- Integration with Google’s ALTS (Application Layer Transport Security) for service-to-service PQC.
5.2 PKCS#11 PQC Extensions
The PKCS#11 standard (OASIS PKCS#11 3.1+) has been extended to define mechanism types for PQC algorithms:
| Mechanism | PKCS#11 Type | Operations |
|---|---|---|
| ML-KEM | CKM_ML_KEM | Keypair generation, encapsulate, decapsulate |
| ML-DSA | CKM_ML_DSA | Keypair generation, sign, verify |
| SLH-DSA | CKM_SLH_DSA | Keypair generation, sign, verify |
| Hybrid KEM | CKM_ML_KEM_X25519 | Combined ECDH + ML-KEM key establishment |
Practical implication: Applications using the PKCS#11 interface for HSM operations can begin using PQC algorithms through the same API patterns they use for RSA and ECC, provided the HSM firmware supports the new mechanisms. Middleware like p11-kit and PKCS#11 providers for OpenSSL can route PQC operations to HSMs transparently.
5.3 FIPS 140-3 Validation for PQC
The CMVP (Cryptographic Module Validation Program) has been publishing guidance for validating modules that implement the new FIPS standards:
- FIPS 203 (ML-KEM): Algorithm validation through ACVP (Automated Cryptographic Validation Protocol). Test vectors cover keygen, encapsulation, and decapsulation, including edge cases like decapsulation failure (implicit rejection).
- FIPS 204 (ML-DSA): ACVP test vectors for keygen, sign, and verify. Both deterministic and hedged signing modes are tested.
- FIPS 205 (SLH-DSA): ACVP validation available. The stateless nature simplifies validation compared to XMSS/LMS.
Timeline reality: As of early 2026, the CMVP queue for PQC validations is long. Several modules have achieved “in review” status, and early certificates have been issued, but the full validation pipeline is 12-18 months for a typical submission. Organizations planning deployments should factor this queue time into their migration plans.
6. Performance Benchmarks
Performance characteristics of PQC algorithms differ fundamentally from classical cryptography. The following benchmarks are representative of optimized implementations (AVX2 on x86_64, NEON on ARM) running on recent hardware. All cycle counts are approximate and vary with microarchitecture.
6.1 Key Encapsulation: ML-KEM vs. ECDH
| Algorithm | Operation | x86_64 (cycles) | ARM Cortex-A76 (cycles) | WASM (relative) |
|---|---|---|---|---|
| X25519 | Keygen | ~120,000 | ~180,000 | 1.0x baseline |
| X25519 | DH (shared secret) | ~120,000 | ~180,000 | 1.0x |
| ML-KEM-512 | Keygen | ~30,000 | ~55,000 | ~0.4x |
| ML-KEM-512 | Encaps | ~40,000 | ~70,000 | ~0.5x |
| ML-KEM-512 | Decaps | ~35,000 | ~65,000 | ~0.5x |
| ML-KEM-768 | Keygen | ~50,000 | ~90,000 | ~0.7x |
| ML-KEM-768 | Encaps | ~60,000 | ~105,000 | ~0.8x |
| ML-KEM-768 | Decaps | ~55,000 | ~100,000 | ~0.7x |
| ML-KEM-1024 | Keygen | ~70,000 | ~130,000 | ~1.0x |
| ML-KEM-1024 | Encaps | ~85,000 | ~150,000 | ~1.1x |
| ML-KEM-1024 | Decaps | ~80,000 | ~140,000 | ~1.0x |
The performance surprise: ML-KEM is faster than X25519 in raw computational cost. The performance bottleneck for ML-KEM in TLS is not computation but bandwidth — the larger key shares and ciphertexts increase handshake size, which matters on high-latency or low-bandwidth links. For the hybrid X25519MLKEM768, the combined computation is roughly 1.5x the cost of X25519 alone, which is negligible in the context of a TLS handshake.
6.2 Digital Signatures: ML-DSA vs. Classical
| Algorithm | Operation | x86_64 (cycles) | ARM Cortex-A76 (cycles) | WASM (relative) |
|---|---|---|---|---|
| Ed25519 | Keygen | ~60,000 | ~90,000 | 1.0x baseline |
| Ed25519 | Sign | ~65,000 | ~95,000 | 1.0x |
| Ed25519 | Verify | ~180,000 | ~270,000 | 1.0x |
| ECDSA P-256 | Sign | ~100,000 | ~150,000 | ~1.5x |
| ECDSA P-256 | Verify | ~300,000 | ~450,000 | ~1.7x |
| ML-DSA-44 | Keygen | ~150,000 | ~280,000 | ~2.5x |
| ML-DSA-44 | Sign | ~400,000 | ~750,000 | ~6.0x |
| ML-DSA-44 | Verify | ~150,000 | ~280,000 | ~0.8x |
| ML-DSA-65 | Keygen | ~250,000 | ~470,000 | ~4.0x |
| ML-DSA-65 | Sign | ~600,000 | ~1,100,000 | ~9.0x |
| ML-DSA-65 | Verify | ~260,000 | ~480,000 | ~1.4x |
| ML-DSA-87 | Keygen | ~400,000 | ~750,000 | ~6.5x |
| ML-DSA-87 | Sign | ~850,000 | ~1,600,000 | ~13.0x |
| ML-DSA-87 | Verify | ~420,000 | ~790,000 | ~2.3x |
| SLH-DSA-128s | Sign | ~250,000,000 | ~470,000,000 | ~3800x |
| SLH-DSA-128s | Verify | ~5,000,000 | ~9,500,000 | ~28x |
| SLH-DSA-128f | Sign | ~8,000,000 | ~15,000,000 | ~120x |
| SLH-DSA-128f | Verify | ~500,000 | ~950,000 | ~2.8x |
Key observations:
- ML-DSA signing is the bottleneck: Signing is 6-13x slower than Ed25519 depending on the parameter set, due to the rejection sampling loop (the signer must repeatedly sample until the signature norm is small enough). Verification is competitive with or faster than ECDSA.
- SLH-DSA is expensive: The “s” (small) variants produce compact signatures but take hundreds of millions of cycles to sign. The “f” (fast) variants are ~30x faster but produce larger signatures. SLH-DSA is appropriate for firmware signing, code signing, and other offline operations — not for interactive protocols. See Hash-Based Signatures for the underlying construction.
- WASM overhead: WebAssembly implementations are slower because they lack access to AVX2/NEON SIMD instructions. The overhead is most pronounced for lattice operations that benefit heavily from vectorization.
6.3 Memory Footprint
For embedded and IoT deployments, memory matters as much as speed.
| Algorithm | Code Size (ARM Cortex-M4) | Peak RAM | Public Key | Secret Key | CT/Sig |
|---|---|---|---|---|---|
| X25519 | ~8 KB | ~1 KB | 32 B | 32 B | 32 B |
| ML-KEM-512 | ~15 KB | ~10 KB | 800 B | 1,632 B | 768 B |
| ML-KEM-768 | ~15 KB | ~15 KB | 1,184 B | 2,400 B | 1,088 B |
| ML-KEM-1024 | ~15 KB | ~20 KB | 1,568 B | 3,168 B | 1,568 B |
| ML-DSA-44 | ~25 KB | ~40 KB | 1,312 B | 2,560 B | 2,420 B |
| ML-DSA-65 | ~25 KB | ~55 KB | 1,952 B | 4,032 B | 3,309 B |
| ML-DSA-87 | ~25 KB | ~80 KB | 2,592 B | 4,896 B | 4,627 B |
| SLH-DSA-128s | ~10 KB | ~5 KB | 32 B | 64 B | 7,856 B |
| Ed25519 | ~5 KB | ~1 KB | 32 B | 64 B | 64 B |
Embedded viability: ML-KEM-512 is viable on ARM Cortex-M4 class devices (256KB flash, 64KB RAM) with careful implementation. ML-DSA-44 is possible but tight — the 40KB peak RAM during signing consumes a significant fraction of a typical M4’s SRAM. SLH-DSA’s small key sizes are attractive for constrained devices, but the signing time is prohibitive for interactive use cases.
7. Library Selection Decision Tree
Choosing the right PQC library depends on your specific deployment context. The following decision tree captures the primary factors.
graph TD
Start["What is your<br/>deployment target?"]
Start -->|"Web browser/CDN"| Browser["Browser/CDN"]
Start -->|"Cloud service"| Cloud["Cloud Service"]
Start -->|"Enterprise Java/.NET"| Enterprise["Enterprise App"]
Start -->|"Embedded/IoT"| Embedded["Embedded Device"]
Start -->|"Research/Prototyping"| Research["R&D"]
Browser --> BrowserLib["BoringSSL or NSS<br/>(ships with your browser)"]
Cloud -->|"AWS"| AWSLC["AWS-LC<br/>(native, FIPS path)"]
Cloud -->|"GCP"| BoringSSLCloud["BoringSSL<br/>(native to GCP)"]
Cloud -->|"Azure/Other"| OpenSSLCloud["OpenSSL 3.5+<br/>(broad compatibility)"]
Enterprise -->|"Java"| Java["Bouncy Castle<br/>(JCA/JCE provider)"]
Enterprise -->|"C#/.NET"| DotNet["Bouncy Castle C#"]
Enterprise -->|"C/C++"| NativeEnt["AWS-LC or OpenSSL 3.5+"]
Embedded -->|"FIPS required"| WolfSSL["wolfSSL<br/>(FIPS 140-3 path)"]
Embedded -->|"No FIPS"| EmbeddedChoice["wolfSSL or<br/>PQClean direct"]
Research --> LibOQS["liboqs + wrappers<br/>(broadest algorithm coverage)"]
AWSLC -->|"FIPS needed?"| AWSFIPS["AWS-LC FIPS module<br/>(validated)"]
NativeEnt -->|"FIPS needed?"| FIPSChoice{"HSM available?"}
FIPSChoice -->|"Yes"| HSM["HSM vendor SDK<br/>(PKCS#11)"]
FIPSChoice -->|"No"| SoftFIPS["AWS-LC or wolfSSL<br/>(software FIPS)"]
style Start fill:#1a3a5c,stroke:#2d6aa0,color:#ffffff
style BrowserLib fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style AWSLC fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style BoringSSLCloud fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style OpenSSLCloud fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style Java fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style DotNet fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style NativeEnt fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style WolfSSL fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style EmbeddedChoice fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style LibOQS fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style AWSFIPS fill:#5a4a27,stroke:#8c7a3f,color:#ffffff
style HSM fill:#5a4a27,stroke:#8c7a3f,color:#ffffff
style SoftFIPS fill:#5a4a27,stroke:#8c7a3f,color:#ffffff
7.1 Selection Factors Summary
| Factor | Recommended Path |
|---|---|
| FIPS 140-3 required, PQC needed now | AWS-LC (validated), wolfSSL (validated, PQC in scope) |
| FIPS 140-3 required, PQC can wait | OpenSSL FIPS provider (when PQC added) |
| Maximum algorithm coverage | liboqs (research), Bouncy Castle (production Java) |
| Minimum latency / Google ecosystem | BoringSSL |
| Embedded / RTOS / bare metal | wolfSSL, PQClean direct integration |
| Cross-platform C/C++ | OpenSSL 3.5+ (widest platform support) |
| Java enterprise | Bouncy Castle (JCA provider) |
| HSM-backed operations | Vendor SDK + PKCS#11 3.1 |
8. Implementation Pitfalls
Moving from “the algorithm works” to “the implementation is secure” requires navigating a minefield of subtle vulnerabilities. PQC algorithms introduce new classes of implementation risks that classical cryptography developers may not anticipate.
8.1 Side-Channel Vulnerabilities
Timing attacks on lattice operations. The Number Theoretic Transform (NTT), used in ML-KEM and ML-DSA for polynomial multiplication, involves modular arithmetic operations where naive implementations produce variable-time code. Specifically:
- Barrett reduction: The division-like operation used to reduce modulo
qmust be implemented with constant-time shifts and multiplications — not with the CPU’s division instruction, which has data-dependent timing on most architectures. - Rejection sampling in ML-DSA signing: The signer repeatedly samples a random masking vector and checks whether the resulting signature satisfies norm bounds. A naive implementation leaks the number of rejection rounds through timing, potentially revealing information about the secret key. Constant-time implementations must ensure that each signing attempt takes the same time regardless of whether the sample is accepted or rejected — typically by always performing the maximum expected number of rounds and selecting the first valid result.
- Decapsulation failure side channels: ML-KEM uses implicit rejection — when decapsulation detects an invalid ciphertext, it returns a pseudorandom value derived from the secret key and ciphertext rather than an error. The computation paths for valid and invalid ciphertexts must be identical in timing and memory access patterns. Early implementations of Kyber (pre-standardization) had subtle timing differences in the comparison step that were exploitable.
Power analysis on embedded devices. Lattice operations produce distinctive power traces. The NTT’s butterfly operations, coefficient comparisons, and polynomial additions all leak through power consumption. Countermeasures include:
- Masking: Splitting secret values into random shares and performing operations on shares independently. First-order masking of ML-KEM has been demonstrated with ~2x overhead; higher-order masking costs increase rapidly.
- Shuffling: Randomizing the order of NTT butterfly operations.
- Hiding: Adding random dummy operations to obscure the real computation.
8.2 RNG Requirements
PQC algorithms are more dependent on high-quality randomness than classical algorithms:
- ML-KEM keygen: Requires 32 bytes of randomness (seed
dand randomnessz). If the RNG is predictable, the entire key pair is compromised. - ML-DSA signing (hedged mode): Requires fresh randomness per signature to provide resilience against fault attacks. The “deterministic” mode (where the nonce is derived solely from the message and secret key) is simpler but vulnerable to fault injection — an attacker who can cause a computation error during deterministic signing can recover the secret key from two faulty signatures of the same message.
- ML-KEM encapsulation: Requires 32 bytes of randomness. A biased or predictable RNG directly compromises the shared secret.
Practical requirement: Use getrandom(2) on Linux, BCryptGenRandom on Windows, or /dev/urandom with appropriate boot-time entropy. On embedded devices, ensure the hardware RNG is properly seeded before any PQC operation. Userspace PRNGs (even cryptographic ones like ChaCha20-based DRBGs) must be seeded from a hardware entropy source.
8.3 Key Validation
PQC key formats introduce new validation requirements:
- ML-KEM public keys: Must be validated to ensure the polynomial coefficients are in the correct range (
[0, q-1]whereq = 3329). Accepting out-of-range coefficients can lead to decapsulation failures or, worse, enable adaptive attacks where the attacker crafts malformed public keys to extract information about the decapsulator’s secret key. - ML-DSA public keys: Must be validated on import. The matrix
Ais derived deterministically from a seed in the public key, and the vectort1must represent valid coefficients. - SLH-DSA: Public keys are small (32-64 bytes) and are essentially random seeds — minimal validation beyond length checking is needed.
General rule: Always validate public keys received from untrusted sources. Never assume that because a key is the right length, it is well-formed. This applies to keys received in TLS handshakes, X.509 certificates, and API calls.
8.4 Constant-Time Coding Challenges
Writing constant-time code for PQC is harder than for classical algorithms because:
-
Polynomial arithmetic over small moduli: ML-KEM uses
q = 3329, which fits in 16 bits. Operations on 16-bit values using 32-bit or 64-bit instructions can leave residual data in upper bits that leaks through cache behavior. Implementations must be careful about sign extension and zero extension. -
Comparison operations: Checking whether a polynomial coefficient exceeds a threshold (e.g., during compression/decompression in ML-KEM) must use arithmetic comparisons (
(a - b) >> 31to extract the sign bit) rather than conditional branches. -
Memory access patterns: The NTT accesses array elements in a pattern determined by the butterfly structure, not by secret data — this is inherently constant-time. But other operations, like the encoding/decoding of compressed polynomials, can produce secret-dependent access patterns in naive implementations.
-
Compiler interference: Compilers can and do “optimize” constant-time code into branching code. Classic mitigations include:
- Using
volatilebarriers (fragile, compiler-specific) - Assembly implementations for critical sections
- Compiler-specific pragmas to prevent optimization
- Verification tools (e.g.,
ct-verif,dudect,timecop) to detect violations
- Using
Testing constant-time behavior: The gold standard is running the implementation under valgrind with secret inputs marked as “uninitialized” via VALGRIND_MAKE_MEM_UNDEFINED. Valgrind will then flag any branch or memory access that depends on these “uninitialized” (secret) values. This is how PQClean verifies its implementations.
9. Library Ecosystem Map
The following diagram shows how the major PQC libraries relate to each other and to the applications that consume them.
graph TB
subgraph "Standards & References"
NIST["NIST FIPS 203/204/205"]
PQClean["PQClean<br/>(Reference C)"]
end
subgraph "Research & Experimentation"
liboqs["liboqs"]
oqs_openssl["OQS-OpenSSL<br/>Provider"]
oqs_ssh["OQS-OpenSSH"]
oqs_python["liboqs-python"]
end
subgraph "Production Libraries"
BoringSSL["BoringSSL"]
AWSLC["AWS-LC"]
wolfSSL["wolfSSL"]
OpenSSL["OpenSSL 3.5+"]
BouncyCastle["Bouncy Castle"]
NSS["NSS"]
end
subgraph "Applications"
Chrome["Chrome"]
Firefox["Firefox"]
AWS_Services["AWS Services"]
Apache["Apache/nginx"]
Java_Apps["Java Enterprise"]
IoT["IoT/Embedded"]
end
NIST --> PQClean
NIST --> BoringSSL
NIST --> AWSLC
NIST --> wolfSSL
NIST --> OpenSSL
NIST --> BouncyCastle
NIST --> NSS
PQClean --> liboqs
liboqs --> oqs_openssl
liboqs --> oqs_ssh
liboqs --> oqs_python
BoringSSL --> Chrome
BoringSSL --> AWSLC
NSS --> Firefox
AWSLC --> AWS_Services
OpenSSL --> Apache
oqs_openssl --> Apache
BouncyCastle --> Java_Apps
wolfSSL --> IoT
style NIST fill:#5a2727,stroke:#8c3f3f,color:#ffffff
style PQClean fill:#2d5a27,stroke:#4a8c3f,color:#ffffff
style liboqs fill:#1a3a5c,stroke:#2d6aa0,color:#ffffff
style BoringSSL fill:#5a4a27,stroke:#8c7a3f,color:#ffffff
style AWSLC fill:#5a4a27,stroke:#8c7a3f,color:#ffffff
style wolfSSL fill:#5a4a27,stroke:#8c7a3f,color:#ffffff
style OpenSSL fill:#5a4a27,stroke:#8c7a3f,color:#ffffff
style BouncyCastle fill:#5a4a27,stroke:#8c7a3f,color:#ffffff
style NSS fill:#5a4a27,stroke:#8c7a3f,color:#ffffff
10. Practical Integration Guide
10.1 Adding PQC to a TLS Server (OpenSSL 3.5+)
For servers using OpenSSL 3.5+ with native ML-KEM support:
# Generate a hybrid key exchange configuration
# In your openssl.cnf or server configuration:
# Enable X25519MLKEM768 as the preferred key exchange group
# nginx example
ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;
# Apache example
SSLOpenSSLConfCmd Groups X25519MLKEM768:X25519:prime256v1
The key insight: by listing X25519MLKEM768 first, the server prefers post-quantum hybrid key exchange but falls back to classical X25519 or P-256 for clients that do not support PQC. No application changes required — just configuration.
10.2 Adding PQC to a Java Application (Bouncy Castle)
// Register Bouncy Castle as a security provider
Security.addProvider(new BouncyCastleProvider());
Security.addProvider(new BouncyCastlePQCProvider());
// ML-KEM Key Encapsulation
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-KEM", "BCPQC");
kpg.initialize(MLKEMParameterSpec.ml_kem_768);
KeyPair kp = kpg.generateKeyPair();
// Encapsulation (sender side)
KeyGenerator keyGen = KeyGenerator.getInstance("ML-KEM", "BCPQC");
keyGen.init(new KEMGenerateSpec(kp.getPublic(), "AES"));
SecretKeyWithEncapsulation encResult = (SecretKeyWithEncapsulation) keyGen.generateKey();
byte[] encapsulation = encResult.getEncapsulation();
SecretKey sharedSecret = encResult; // Use as AES key
// Decapsulation (recipient side)
KeyGenerator decKeyGen = KeyGenerator.getInstance("ML-KEM", "BCPQC");
decKeyGen.init(new KEMExtractSpec(kp.getPrivate(), encapsulation, "AES"));
SecretKey decSharedSecret = decKeyGen.generateKey();
10.3 Embedded PQC (wolfSSL on FreeRTOS)
#include <wolfssl/wolfcrypt/mlkem.h>
MlKemKey key;
byte pubKey[MLKEM_512_PUBLIC_KEY_SIZE];
byte privKey[MLKEM_512_PRIVATE_KEY_SIZE];
byte ct[MLKEM_512_CIPHERTEXT_SIZE];
byte ss_enc[MLKEM_SS_SIZE];
byte ss_dec[MLKEM_SS_SIZE];
// Initialize
wc_MlKemKey_Init(&key, MLKEM_512, NULL, INVALID_DEVID);
// Generate key pair
wc_MlKemKey_MakeKey(&key, rng);
wc_MlKemKey_ExportPubKey(&key, pubKey, sizeof(pubKey));
// Encapsulate (sender)
wc_MlKemKey_Encapsulate(&key, ct, ss_enc, rng);
// Decapsulate (receiver)
wc_MlKemKey_Decapsulate(&key, ss_dec, ct, sizeof(ct));
// ss_enc == ss_dec (shared secret established)
wc_MlKemKey_Free(&key);
11. Testing and Validation
11.1 ACVP Test Vectors
The NIST Automated Cryptographic Validation Protocol (ACVP) provides the definitive test vectors for validating PQC implementations. Any production implementation must pass ACVP validation for the algorithms it claims to support. ACVP tests cover:
- ML-KEM: KeyGen, Encapsulation, Decapsulation (including implicit rejection behavior on invalid ciphertexts)
- ML-DSA: KeyGen, Sign (deterministic and hedged), Verify
- SLH-DSA: KeyGen, Sign, Verify (all parameter sets)
Libraries like AWS-LC and wolfSSL include ACVP harnesses in their build systems, allowing automated validation runs against NIST’s ACVP server.
11.2 Interoperability Testing
The PQC ecosystem has reached a maturity level where cross-library interoperability is expected and tested:
- TLS interoperability: Chrome (BoringSSL) successfully negotiates X25519MLKEM768 with servers running AWS-LC, OpenSSL 3.5+, and wolfSSL. Mozilla Firefox (NSS) interoperates with the same set.
- Certificate interoperability: ML-DSA-signed certificates generated by one library must be verifiable by all others. The IETF LAMPS working group maintains interoperability test suites for PQC certificates.
- Known incompatibilities: Pre-standard implementations (e.g., early “Kyber” vs. final “ML-KEM”) are not interoperable. The FIPS 203 standardization made small but breaking changes to Kyber (domain separation, key encoding). Ensure all endpoints use the same final standard version. See NIST PQC Standardization Process for the history of these changes.
11.3 Continuous Verification
Production PQC deployments should include:
- Known-Answer Tests (KATs): Run at library initialization (power-on self-test) to verify the implementation produces correct outputs. Required for FIPS 140-3.
- Pairwise consistency tests: After generating a key pair, perform a test encapsulation/decapsulation or sign/verify to ensure the key pair is functional.
- Regression testing against upstream test vectors: When updating library versions, verify against the full ACVP test vector set.
- Side-channel testing: For high-security deployments, periodic evaluation with tools like
dudect(for timing) or electromagnetic probes (for power analysis).
12. Looking Forward
The PQC library ecosystem is in a period of rapid consolidation. The experimental phase — where liboqs and OQS integrations were the only way to test PQC — is giving way to a production phase where major libraries ship native, hardened PQC support. Key trends to watch:
-
FIPS validation completion: The first wave of FIPS 140-3 certificates explicitly covering ML-KEM and ML-DSA are being issued. By mid-2027, most major libraries and HSMs should have PQC within their validated boundary.
-
ML-DSA in certificate chains: Key exchange PQC is deployed; authentication PQC is next. Libraries are adding ML-DSA certificate verification, but the full ecosystem (CAs, browsers, certificate transparency logs) must align before PQC certificates become practical. See PQC in Protocols for the protocol-level requirements.
-
FN-DSA arrival: When FIPS 206 is finalized, libraries will need to add FN-DSA support. Its compact signatures make it attractive for bandwidth-constrained protocols, but the floating-point arithmetic in its sampler presents implementation challenges that the lattice-based algorithms avoid.
-
Performance convergence: Optimized implementations continue to close the gap between PQC and classical algorithms. AVX-512 and ARM SVE2 instruction sets offer new vectorization opportunities for NTT-based algorithms.
-
Composite and hybrid standardization: IETF work on composite key and signature formats (combining classical and PQC algorithms in a single certificate) will require library support for new ASN.1 structures and X.509 extensions.
The critical message for practitioners: do not wait for the ecosystem to be perfect before starting. Begin with hybrid key exchange (X25519MLKEM768) in TLS — the library support is already there in every major stack. Build crypto agility into your architecture now so that swapping algorithms and libraries as the ecosystem matures requires configuration changes, not code rewrites. For a structured approach to this transition, see Migration Strategies & Crypto Agility.