Skip to content

Post-Quantum Cryptography

wolfProvider supports the NIST post-quantum standards through the OpenSSL 3 EVP interface. PQC support is opt-in so applications that do not need these algorithms are unaffected.

Requirements

  • wolfSSL v5.9.2-stable or later
  • OpenSSL 3.6 or later
  • A wolfSSL build with the required PQC algorithms enabled

The packaged Debian build is not currently available with PQC because its system OpenSSL is older than OpenSSL 3.6.

Supported Algorithms

Algorithm Standard Supported Parameter Sets
ML-KEM FIPS 203 ML-KEM-512, ML-KEM-768, ML-KEM-1024
ML-DSA FIPS 204 ML-DSA-44, ML-DSA-65, ML-DSA-87
SLH-DSA with SHA-2 FIPS 205 SLH-DSA-SHA2-128s, SLH-DSA-SHA2-128f, SLH-DSA-SHA2-192s, SLH-DSA-SHA2-192f, SLH-DSA-SHA2-256s, SLH-DSA-SHA2-256f
SLH-DSA with SHAKE FIPS 205 SLH-DSA-SHAKE-128s, SLH-DSA-SHAKE-128f, SLH-DSA-SHAKE-192s, SLH-DSA-SHAKE-192f, SLH-DSA-SHAKE-256s, SLH-DSA-SHAKE-256f
LMS RFC 8554 / NIST SP 800-208 LMS and LMOTS public-key verification

ML-KEM supports key generation, encapsulation, decapsulation, raw key import/export, and public/private key encoding. ML-DSA supports key generation, pure and pre-hash signing, verification, context strings, and key encoding. SLH-DSA supports key generation, pure signing, verification, context strings, and key encoding. LMS supports public-key import and one-shot signature verification only. Private-key import, signing, and key generation are not exposed. This follows OpenSSL's LMS key-type contract, which is verification-only, and is also a deliberate wolfProvider design choice: it avoids exposing stateful LMS private-key operations through an interface that cannot enforce LMS leaf-use (one-time signature) state, even though wolfCrypt itself supports LMS signing and key generation.

Provider Architecture

Applications use the standard OpenSSL 3 EVP interfaces. wolfProvider registers OpenSSL key management implementations for each parameter set, a KEM implementation for ML-KEM, and signature implementations for ML-DSA and SLH-DSA. The provider translates those operations to the corresponding wolfCrypt APIs and keeps the wolfCrypt key object inside the OpenSSL EVP_PKEY.

Family OpenSSL Interfaces Main Operations
ML-KEM EVP_PKEY, EVP_PKEY_CTX, KEM Key generation, encapsulation, decapsulation, key import/export
ML-DSA EVP_PKEY, EVP_MD_CTX, signature Key generation, pure and pre-hash sign/verify, key import/export
SLH-DSA EVP_PKEY, EVP_MD_CTX, signature Key generation, pure sign/verify, key import/export
LMS EVP_PKEY, EVP_MD_CTX, signature Public-key import/export and one-shot verification

ML-KEM, ML-DSA, and SLH-DSA keys support raw public and private key parameters as well as DER and PEM encoding. Public keys use SubjectPublicKeyInfo and private keys use PKCS#8. Encrypted PKCS#8 private key output is also supported. The signature implementations expose the algorithm identifiers needed for X.509 certificate and request operations. LMS instead uses OpenSSL's raw XDR public-key representation and exposes no private key.

OpenSSL's LMS provider contract represents one LMS tree rather than a general multi-level HSS hierarchy. Its raw public keys and signatures omit the four-byte HSS level-count field. wolfProvider adds the required single-level HSS wrapper only when calling wolfCrypt and removes it again at the OpenSSL boundary, preserving OpenSSL-compatible bytes for applications.

Sizes and Security Categories

ML-KEM always produces a 32-byte shared secret:

Parameter Set NIST Category Public Key Private Key Ciphertext
ML-KEM-512 1 800 bytes 1,632 bytes 768 bytes
ML-KEM-768 3 1,184 bytes 2,400 bytes 1,088 bytes
ML-KEM-1024 5 1,568 bytes 3,168 bytes 1,568 bytes

ML-DSA signatures and keys have fixed sizes for each parameter set:

Parameter Set NIST Category Public Key Private Key Signature
ML-DSA-44 2 1,312 bytes 2,560 bytes 2,420 bytes
ML-DSA-65 3 1,952 bytes 4,032 bytes 3,309 bytes
ML-DSA-87 5 2,592 bytes 4,896 bytes 4,627 bytes

For SLH-DSA, SHA-2 and SHAKE parameter sets at the same level have the same sizes. The s variants favor smaller signatures, while the f variants favor faster signing:

Parameter Sets NIST Category Public Key Private Key Signature
128s 1 32 bytes 64 bytes 7,856 bytes
128f 1 32 bytes 64 bytes 17,088 bytes
192s 3 48 bytes 96 bytes 16,224 bytes
192f 3 48 bytes 96 bytes 35,664 bytes
256s 5 64 bytes 128 bytes 29,792 bytes
256f 5 64 bytes 128 bytes 49,856 bytes

Signing and Generation Parameters

ML-DSA and SLH-DSA accept the OpenSSL signature context-string parameter, with the FIPS 204 and FIPS 205 maximum of 255 bytes. Both support deterministic and randomized signing through OpenSSL signature parameters. ML-DSA additionally supports HashML-DSA pre-hash signing and external representative input.

The key generation implementations accept the OpenSSL ML-KEM, ML-DSA, and SLH-DSA seed parameters. These deterministic paths are used by known-answer and ACVP testing. Normal application key generation and randomized signing use wolfCrypt's random number generator.

OpenSSL presents one-shot and streaming digest-sign entry points to applications. wolfProvider incrementally hashes HashML-DSA input. Pure ML-DSA and SLH-DSA require the complete message; SLH-DSA therefore buffers streaming input and enforces a 64 MiB message limit.

Platform Optimizations

wolfProvider calls the native wolfCrypt implementations, so it benefits from the optimized code selected by the wolfSSL build without requiring a separate provider-specific acceleration layer. Available wolfCrypt PQC speedups include x86-64 vectorized implementations and assembly paths for ARM32, AArch64, ARMv7-M, and ARMv7E-M. Optimized SHA-2, SHA-3, and SHAKE operations also accelerate the hash work used by ML-KEM, ML-DSA, SLH-DSA, and the configured LMS parameter families.

The exact path is compiler, processor, and wolfSSL configuration dependent. Applications should use the wolfCrypt benchmark on the target system to confirm which implementation was selected and measure the relevant parameter sets. See the wolfSSL PQC documentation and ML-KEM acceleration overview for configuration and benchmark details.

Building

Post-quantum algorithms require OpenSSL 3.6 or later. Set OPENSSL_TAG to the latest patched OpenSSL 3.6.x release (not 3.6.0, which has known vulnerabilities) so the build does not use an outdated default:

The build script can configure OpenSSL, wolfSSL, and wolfProvider together:

OPENSSL_TAG=openssl-3.6.2 ./scripts/build-wolfprovider.sh --enable-pqc

--enable-pqc enables ML-KEM, ML-DSA and SLH-DSA. LMS remains an independent option, and each family can also be selected separately:

./scripts/build-wolfprovider.sh --enable-mlkem
./scripts/build-wolfprovider.sh --enable-mldsa
./scripts/build-wolfprovider.sh --enable-slhdsa
./scripts/build-wolfprovider.sh --enable-lms

For a manual build, configure wolfSSL with the matching algorithm options, then configure wolfProvider:

# In the wolfSSL source directory, add the required options to the
# normal wolfSSL configuration.
cd /path/to/wolfssl
./configure --enable-mlkem --enable-mldsa --enable-slhdsa=yes,sha2 \
    --enable-lms=verify-only,sha256-192,shake256
make
sudo make install

# In the wolfProvider source directory, configure against OpenSSL 3.6 or later.
cd /path/to/wolfProvider
./configure --enable-pqc --enable-lms \
    --with-openssl=/path/to/openssl \
    --with-wolfssl=/path/to/wolfssl
make
sudo make install

PQC code is not compiled unless --enable-pqc or an individual PQC option is passed to wolfProvider, even when wolfSSL has the algorithms enabled.

Because wolfProvider only verifies LMS, build wolfSSL with --enable-lms=verify-only,sha256-192,shake256 so LMS signing and key generation are left out of the build entirely rather than compiled and unused. The build-wolfprovider.sh --enable-lms flow already configures wolfSSL this way.

Using wolfProvider

For production deployments, the recommended configuration is replace-default mode. It makes wolfProvider the OpenSSL default and prevents operations from silently falling back to OpenSSL's built-in provider:

OPENSSL_TAG=openssl-3.6.2 ./scripts/build-wolfprovider.sh --replace-default \
    --enable-pqc --enable-lms

Applications may also load wolfProvider as a normal provider. This mode is useful for interoperability and migration, but it does not guarantee that every operation is handled by wolfProvider: OpenSSL can select its default provider when wolfProvider does not implement an operation or when the configuration is not applied.

Use the repository's provider configuration to load wolfProvider. The configuration activates libwolfprov; point OPENSSL_CONF at that file:

export OPENSSL_CONF=/path/to/wolfProvider/provider.conf

If wolfProvider was installed outside OpenSSL's module search path, also set OPENSSL_MODULES to the directory containing libwolfprov.so:

export OPENSSL_MODULES=/path/to/wolfprovider/lib

Run commands with the configuration active. Because this configuration activates only libwolfprov, OpenSSL does not implicitly activate its own default provider, so operations that wolfProvider does not support will fail rather than silently falling back, unless an application also activates the default provider (or unless replace-default mode is used):

OPENSSL_CONF=/path/to/wolfProvider/provider.conf \
    openssl list -kem-algorithms
OPENSSL_CONF=/path/to/wolfProvider/provider.conf \
    openssl list -signature-algorithms

OpenSSL EVP Example

The maintained examples/pqc_openssl_example.c program demonstrates the three NIST FIPS 203 through FIPS 205 algorithm families through the OpenSSL EVP API:

  • ML-KEM-768 key generation, encapsulation, and decapsulation
  • ML-DSA-65 key generation, signing, and verification
  • SLH-DSA-SHA2-128f key generation, signing, and verification

The example is built (as a noinst_PROGRAMS target, not an Automake test) when wolfProvider is configured with any PQC family; wolfProvider's CI runs it as a separate step rather than as part of make check. Compile-time guards run only the enabled families, so the same source also demonstrates ML-KEM-only, ML-DSA-only, and SLH-DSA-only builds. After building, it can be run directly from the wolfProvider root:

./examples/pqc_openssl_example

LMS is not part of this example because OpenSSL exposes only verification and a useful example would require a fixed public key and signature. The repository instead validates LMS with OpenSSL's 320 fixed verification vectors and focused provider unit tests.

Build Macros

The configure options add the corresponding request macros:

Configure Option wolfProvider Request Macro wolfSSL Capability Macro
--enable-mlkem WOLFPROV_HAVE_MLKEM WOLFSSL_HAVE_MLKEM
--enable-mldsa WOLFPROV_HAVE_MLDSA WOLFSSL_HAVE_MLDSA
--enable-slhdsa WOLFPROV_HAVE_SLHDSA WOLFSSL_HAVE_SLHDSA
--enable-lms WOLFPROV_HAVE_LMS WOLFSSL_HAVE_LMS

--enable-pqc enables ML-KEM, ML-DSA and SLH-DSA. LMS is enabled separately with --enable-lms. After configuration validates the wolfSSL capabilities, wolfProvider uses WP_HAVE_MLKEM, WP_HAVE_MLDSA, WP_HAVE_SLHDSA, and WP_HAVE_LMS internally to compile and register the available implementations.

TLS 1.3

wolfProvider advertises these ML-KEM TLS groups:

  • MLKEM512
  • MLKEM768
  • MLKEM1024
  • X25519MLKEM768
  • SecP256r1MLKEM768
  • SecP384r1MLKEM1024

It also advertises the mldsa44, mldsa65, and mldsa87 TLS 1.3 signature schemes. SLH-DSA is supported through EVP but is not advertised as a TLS signature scheme.

Validation

The wolfCrypt Post Quantum v7.0.0 implementation has NIST CAVP algorithm validation under certificate A8437. The certificate covers LMS signature verification; ML-KEM key generation, encapsulation, and decapsulation; ML-DSA key generation, signature generation, and signature verification; and SLH-DSA key generation, signature generation, and signature verification. It also covers the prerequisite SHA, SHAKE, HMAC, and DRBG implementations identified by the NIST record.

This validation applies to the identified wolfCrypt version and operating environment. A wolfProvider application must use the corresponding validated wolfCrypt distribution and follow its security policy when a validated deployment is required.

CAVP validates the algorithm implementations. It is distinct from validation of a complete cryptographic module under FIPS 140-3, so using an open source or FIPS-ready wolfSSL build does not by itself create a FIPS-validated application.

Validated FIPS and FIPS-Ready builds currently reject PQC (ML-KEM, ML-DSA, SLH-DSA) and LMS: combining a FIPS or FIPS-Ready wolfSSL build with these algorithms is not yet supported, pending the module-specific CAST integration. Use PQC and LMS with non-FIPS wolfSSL builds only until that integration is available. This restriction is enforced by scripts/build-wolfprovider.sh; a hand-run ./configure does not currently reject the combination, so use the build script (or avoid the combination) for FIPS builds.

PQC has several independent test layers:

  • Unit and example tests cover key generation, import/export, encoding, encapsulation, decapsulation, signing, verification, malformed inputs, and X.509 operations where applicable. LMS unit tests specifically cover raw public-key import/export, XDR decoding, selection handling, malformed input, and rejection of unsupported stateful operations.
  • OpenSSL EVP known-answer tests run the ML-KEM, ML-DSA, SLH-DSA, and LMS vector files against wolfProvider. The LMS vector contains 320 verification cases.
  • The PQC interoperability test compares wolfProvider with OpenSSL's default provider and the direct wolfSSL APIs. It also tests ML-KEM and hybrid TLS groups plus ML-DSA certificate authentication.
  • The nginx OSP workflow tests ML-KEM and hybrid TLS 1.3 key exchange with ML-DSA authentication.
  • The pinned libacvp (Cisco v2.3.1) OSP workflow builds wolfProvider with --enable-pqc, runs its APP_PQC_HANDLER group, and then its complete unit suite, exercising the OpenSSL ACVP handlers for ML-KEM and ML-DSA in both replace-default and non-replace modes.

The version, nginx, and libacvp matrices cover wolfSSL master and the latest eligible stable release. OSP tests run in replace-default and non-replace modes, with normal and forced-failure cases. PQC KAT and version coverage runs for pull requests, while the longer OSP integration workflows are label-selected and run nightly. The KAT matrix also includes an LMS-only row in addition to the combined PQC configurations.

Further Reading

For the current deployment model and complete build guidance, see the wolfProvider README and the wolfProvider Integration Guide. The integration guide covers replace-default builds, provider configuration, testing, and troubleshooting. For validated deployments, use the FIPS Integration Guide, which explains the replace-default requirements and security-policy considerations.

The repository's replace-default build workflow, provider configuration, PQC EVP example, and PQC KAT runner are maintained alongside the implementation and provide the most current examples of supported options and validation.