wolfTPM fTPM v1.85 Post-Quantum

Full Release Overview

wolfTPM’s firmware TPM (fTPM) is available with full TCG TPM 2.0 Library Specification v1.85 post-quantum support. This release adds the eight new v1.85 commands, the ML-DSA and ML-KEM algorithm structures, and supporting infrastructure (examples, unit tests, NIST ACVP known-answer-tests, a libFuzzer harness) to wolfSSL’s portable software TPM 2.0 implementation. The work merged in wolfTPM PR #445.

This post is the technical companion to the wolfTPM client library and fTPM release announcements. It covers exactly what shipped, where to find it in the source tree, and which embedded targets it runs on today.

What the v1.85 Specification Adds

TCG TPM 2.0 Library Specification v1.85 introduces the post-quantum surface to TPM 2.0: three new algorithm identifiers, eight new commands, and the wire formats and structures to support them.

Algorithm Identifier Use
ML-KEM TPM_ALG_MLKEM (0x00A0) Key encapsulation, decrypt-only keys
Pure ML-DSA TPM_ALG_MLDSA (0x00A1) Message signing
Hash-ML-DSA TPM_ALG_HASH_MLDSA (0x00A2) Pre-hashed signing

Eight New Commands Implemented in fTPM

Every v1.85 post-quantum command is implemented in src/fwtpm/fwtpm_command.c with full TPM 2.0 authorization, NV persistence, and parameter encryption support.

Command CC Purpose
TPM2_Encapsulate 0x000001A7 ML-KEM encapsulation; returns shared secret + ciphertext
TPM2_Decapsulate 0x000001A8 ML-KEM decapsulation; recovers shared secret from ciphertext
TPM2_SignSequenceStart 0x000001AA Begin ML-DSA sign sequence
TPM2_SignSequenceComplete 0x000001A4 Finalize sign sequence with message buffer
TPM2_VerifySequenceStart 0x000001A9 Begin ML-DSA verify sequence
TPM2_VerifySequenceComplete 0x000001A3 Finalize verify sequence; returns TPMT_TK_VERIFIED
TPM2_SignDigest 0x000001A6 One-shot digest sign (Hash-ML-DSA)
TPM2_VerifyDigestSignature 0x000001A5 Verify digest signature; returns TPMT_TK_VERIFIED

Parameter Set Coverage

The full NIST-standardized parameter range is supported on both client and server.

Algorithm Standard Parameter Sets
ML-DSA FIPS 204 ML-DSA-44, ML-DSA-65, ML-DSA-87
ML-KEM FIPS 203 ML-KEM-512, ML-KEM-768, ML-KEM-1024

Post-Quantum Primary Key Derivation

PQC primary keys derive deterministically from the hierarchy seed using the same KDFa-based model TPM 2.0 already uses for RSA and ECC primary keys.

  • ML-DSA: KDFa(nameAlg, seed, “MLDSA”, hashUnique) produces a 32-byte Xi, which is passed to wc_dilithium_make_key_from_seed. Wire format stores the 32-byte Xi per TCG Part 2 Table 210.
  • Hash-ML-DSA: Same expansion model with label “HASH_MLDSA”.
  • ML-KEM: KDFa(nameAlg, seed, “MLKEM”, hashUnique) produces a 64-byte (d||z) seed, which is passed to wc_MlKemKey_MakeKeyWithRandom. Wire format stores the 64-byte seed per TCG Part 2 Table 206.

This preserves the cold-boot recovery model of TPM 2.0: the same seed regenerates the same primary key bit-for-bit across reboots.

New Example Programs

Three new programs in examples/pqc/ exercise the post-quantum path end-to-end against a running fTPM server.

Program Description
pqc_mssim_e2e Round-trips both algorithms over mssim socket transport. ML-KEM-768 CreatePrimary + Encapsulate + Decapsulate (asserts ciphertext is 1088 bytes, shared secrets match), then Hash-ML-DSA-65 CreatePrimary + SignDigest + VerifyDigestSignature (asserts signature is 3309 bytes, validation ticket is TPM_ST_DIGEST_VERIFIED).
mlkem_encap ML-KEM encapsulation/decapsulation round-trip with selectable parameter set (-mlkem=512|768|1024).
mldsa_sign Pure ML-DSA sign + verify with selectable parameter set (-mldsa=44|65|87). Demonstrates one-shot signing per Part 3 Sec. 17.5 and streaming verify per Sec. 20.3.

The existing keygen and keyload examples also accept new flags (-mldsa, -hash_mldsa, -mlkem) for primary key creation and persistence.

Test Coverage

This release ships 132 fTPM unit tests in tests/fwtpm_unit_tests.c and 83 client unit tests in tests/unit_tests.c. Roughly 30 fTPM tests and 18 client tests are post-quantum-specific.

fTPM PQC Unit Tests (in-process)

  • CreatePrimary(MLKEM-768), CreatePrimary(MLDSA-65)
  • CreateLoaded(MLDSA-65), CreateLoaded(MLKEM-768)
  • MLKEM Encap/Decap Roundtrip, MLDSA Sign/Verify Sequence
  • MLDSA SignDigest/Verify Roundtrip
  • Encap/Decap ECC DHKEM (P-256/HKDF-SHA256), P-384/HKDF-SHA384 variants
  • FwEncryptSeed/FwDecryptSeed MLKEM Roundtrip
  • Classical sign/verify regression tests (RSA, ECDSA, HMAC) over the new sequence/digest commands
  • Negative-path tests for scheme mismatch, digest-size mismatch, NULL-scheme rejection, handle-auth enforcement

Client Wrapper PQC Tests (over mssim socket)

  • ML-DSA Sign Sequence, Verify Sequence, Verify Seq data-chain
  • Hash-ML-DSA SignSeqUpdate streaming, Hash-ML-DSA SignDigest roundtrip
  • ML-DSA SignSeqStart no-session, Sign Seq w/ key auth
  • ML-KEM Encapsulate, Decapsulate, Round Trip
  • EncryptSecret MLKEM, PQC Key Templates, Signature PQC serialize, Public PQC roundtrip

NIST ACVP Known-Answer Tests

tests/pqc_kat_vectors.h (802 lines) ships pinned vectors used to validate the implementation against NIST’s published ACVP answer keys.

  • ML-DSA-44 verify, against NIST ACVP and wolfSSL internal vectors
  • ML-DSA-44 keygen determinism
  • ML-KEM-512 encapsulation with pinned randomness, against NIST ACVP
  • ML-KEM-512 keygen determinism
  • LoadExternal of a NIST ACVP ML-DSA-44 public key through the fwTPM command handler

A KAT regression in any of these tests would indicate a wire-format or algorithm-implementation drift, not just a wrapper bug.

Fuzzing

tests/fuzz/fwtpm_fuzz.c is a libFuzzer harness for FWTPM_ProcessCommand. It feeds raw byte sequences directly into the fTPM command processor, exercising every parser, dispatcher, and authorization path without any wolfTPM-client-side wrapping.

Component Details
Build ./configure –enable-fwtpm –enable-fuzz CFLAGS=”-fsanitize=fuzzer-no-link,address -g” LDFLAGS=”-fsanitize=fuzzer,address”
Dictionary tests/fuzz/tpm2.dict: 97 entries of TPM 2.0 command codes, algorithm identifiers, response tags, and structure markers
Corpus generator tests/fuzz/gen_corpus.py (212 lines): synthesizes seed inputs covering classical and PQC paths
CI fuzz.yml runs the harness on every PR (smoke run) and weekly (extended run) under AddressSanitizer

Buffer Sizing Under v1.85

Post-quantum signatures and public keys are dramatically larger than their classical equivalents. fTPM auto-sizes at compile time based on which PQC parameter sets are enabled.

Symbol Classical DSA-44+KEM-512 DSA-65+KEM-768 DSA-87+KEM-1024
FWTPM_TIS_FIFO_SIZE 4096 4096 8192 8192
FWTPM_MAX_COMMAND_SIZE 4096 4096 8192 8192
FWTPM_MAX_PUB_BUF 512 1408 2048 2720
FWTPM_MAX_DER_SIG_BUF 256 2536 3373 4736

ML-DSA-44-only and ML-KEM-only builds stay at 4096-byte FIFOs. The 8192 lift only applies when ML-DSA-65 or ML-DSA-87 is enabled, because their signatures do not fit a 4096-byte response with TPM headers.

Embedded Targets

The fTPM is portable C built on wolfCrypt, with no dynamic allocation in hot paths and WOLFTPM_SMALL_STACK support for constrained environments. The following HAL drivers ship with wolfTPM and are usable today as the transport layer for the fTPM server.

HAL Source
Linux (/dev/spidev*, /dev/tpm0) hal/tpm_io_linux.c
STMicro Cube (STM32 family) hal/tpm_io_st.c
Atmel ASF hal/tpm_io_atmel.c
Infineon TriCore hal/tpm_io_infineon.c
Microchip hal/tpm_io_microchip.c
Xilinx hal/tpm_io_xilinx.c
QNX hal/tpm_io_qnx.c
Espressif (ESP32) hal/tpm_io_espressif.c
Zephyr hal/tpm_io_zephyr.c
BareBox hal/tpm_io_barebox.c
U-Boot hal/tpm_io_uboot.c
Generic MMIO hal/tpm_io_mmio.c

Anywhere wolfTPM runs, fTPM with v1.85 post-quantum support runs.

STM32H5 Reference Port
A complete STM32H5 Cortex-M33 with TrustZone (CMSE) reference port is available in the wolfTPM examples repository. It runs the fTPM server on the STM32H5, exposes the SWTPM protocol over UART, and accepts commands from a host-side wolfTPM client built with –enable-swtpm=uart.
This means a development board can run a fully post-quantum TPM 2.0 server today: ML-DSA signing, ML-KEM key encapsulation, NV persistence, and attestation primitives, with no discrete TPM chip on the board.

Open Source Availability

wolfTPM is dual-licensed under GPLv2+ for open source use and commercially for proprietary integration. The fTPM source, the eight v1.85 PQC command handlers, all unit tests, all examples, the NIST ACVP KAT vectors, and the libFuzzer harness are in the public wolfSSL/wolfTPM repository on GitHub.
This release makes wolfTPM the first openly available, freely usable software TPM 2.0 implementation that ships v1.85 post-quantum support. Other TPM 2.0 simulators in common use, including the TCG-controlled reference implementation, do not currently expose v1.85 commands publicly. Products that need to evaluate, integrate, or ship post-quantum TPM functionality today can do so against fTPM without an NDA, without TCG membership, and without waiting for the discrete-TPM vendor rollout.

Build and Run

wolfSSL (ML-DSA and ML-KEM in wolfCrypt):

# wolfSSL with PQC + keygen
./configure --enable-wolftpm --enable-pkcallbacks \
            --enable-keygen --enable-dilithium \
            --enable-mlkem --enable-experimental \
            --enable-harden CFLAGS="-DWC_RSA_NO_PADDING"
make && sudo make install

wolfTPM:

# wolfTPM with fwTPM + v1.85
./configure --enable-fwtpm --enable-pqc
make
make check

Run only the PQC end-to-end test (fastest PQC-focused check):

./tests/pqc_mssim_e2e.sh

CI Coverage

Continuous integration runs the full PQC matrix on every PR and on a nightly schedule.

  • pqc-examples.yml: wolfSSL + wolfTPM PQC build, examples sweep, mssim E2E
  • fwtpm-test.yml: fTPM matrix (10+ build variants including PQC, no-PQC, small-stack)
  • fuzz.yml: libFuzzer smoke run per-PR, extended weekly run under AddressSanitizer
  • sanitizer.yml: AddressSanitizer / UndefinedBehaviorSanitizer / MemorySanitizer

Documentation

  • Specification mapping and per-command spec citations: docs/FWTPM.md
  • Example programs: examples/pqc/README.md
  • Embedded port reference (STM32H5): src/fwtpm/ports/README.md

If you have questions about any of the above, please contact us at facts@wolfssl.com or call us at +1 425 245 8247.

Download wolfSSL Now