fwTPM Post-Quantum Support (TPM 2.0 v1.85)¶
The fwTPM implements the post-quantum additions from TCG TPM 2.0 Library Specification v1.85, using wolfCrypt's FIPS 203 (ML-KEM) and FIPS 204 (ML-DSA) modules. This brings post-quantum keys, signing, and key encapsulation to platforms that have no TPM silicon. These v1.85 commands raise the implemented count from 103 to 111 commands. For the library-wide post-quantum view, see Post-Quantum.
Enable it with --enable-pqc, which the fwTPM build promotes to the full --enable-v185, at configure time. It is also auto-detected when --enable-fwtpm is built against a wolfCrypt that has both ML-DSA and ML-KEM available. Both flags set the internal WOLFTPM_V185 macro that gates the implementation. Pass --disable-pqc to opt out when auto-detect would otherwise enable it.
Algorithms¶
| Alg | Parameter Sets | Use |
|---|---|---|
TPM_ALG_MLKEM (0x00A0) |
ML-KEM-512 / 768 / 1024 | Key encapsulation (decrypt-only keys) |
TPM_ALG_MLDSA (0x00A1) |
ML-DSA-44 / 65 / 87 | Pure ML-DSA message signing |
TPM_ALG_HASH_MLDSA (0x00A2) |
HashML-DSA-44 / 65 / 87 | Pre-hashed ML-DSA signing |
Commands¶
The eight v1.85 PQC commands live in src/fwtpm/fwtpm_command.c:
| Command | CC | Purpose |
|---|---|---|
TPM2_Encapsulate |
0x000001A7 |
ML-KEM encapsulation, returns sharedSecret and ciphertext |
TPM2_Decapsulate |
0x000001A8 |
ML-KEM decapsulation from ciphertext (requires USER auth) |
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 (HashML-DSA or ext-mu ML-DSA) |
TPM2_VerifyDigestSignature |
0x000001A5 |
Verify digest signature |
Primary Key Derivation¶
PQC primary keys follow the same deterministic derivation model as RSA and ECC: hierarchy seed and template, then a KDFa-derived seed, then FIPS 203 or FIPS 204 key expansion.
- ML-DSA:
KDFa(nameAlg, seed, "MLDSA", hashUnique)gives a 32-byte Xi.wc_MlDsaKey_MakeKeyFromSeedturns that into the public key and the expanded private key. The wire format stores only the 32-byte Xi per TCG Part 2 Table 210. - HashML-DSA: the label is
"HASH_MLDSA", with the same seed size and expansion. - ML-KEM:
KDFa(nameAlg, seed, "MLKEM", hashUnique)gives a 64-byte value (d followed by z).wc_MlKemKey_MakeKeyWithRandomturns that into the encapsulation and decapsulation keys. The wire format stores only the 64-byte seed per TCG Part 2 Table 206.
Note
These label strings are an interpretation. TCG Part 4 v185, which would specify them normatively, is unpublished. They are subject to change if a later release candidate or Part 4 v185 prescribes different labels.
Sign and Verify Sequences¶
Pure ML-DSA sequences are streamable on both sign and verify, so TPM2_SequenceUpdate is accepted. TPM_RC_ONE_SHOT_SIGNATURE applies to multi-pass schemes such as EdDSA, not to pure ML-DSA. A caller can also pass the whole message through the buffer parameter of TPM2_SignSequenceComplete. Verify sequences accumulate the message through TPM2_SequenceUpdate, because TPM2_VerifySequenceComplete has no buffer parameter.
HashML-DSA sequences (both sign and verify) use wolfCrypt's wc_HashAlg context to stream the message into the key's hash algorithm. TPM2_SignSequenceComplete finalizes the hash and calls wc_MlDsaKey_SignCtxHash.
Signature wire formats differ per spec Part 2 Table 217:
- Pure ML-DSA:
TPM2B_SIGNATURE_MLDSA, laid out assigAlg + size + bytes - HashML-DSA:
TPMS_SIGNATURE_HASH_MLDSA, laid out assigAlg + hashAlg + size + bytes
Buffer Constants¶
Under WOLFTPM_V185, buffers are lifted to fit ML-DSA-87 signatures (4627 bytes) and public keys (2592 bytes):
| Symbol | v1.38 | v1.85 |
|---|---|---|
FWTPM_MAX_COMMAND_SIZE |
4096 | 8192 |
FWTPM_MAX_PUB_BUF |
512 | 2720 |
FWTPM_MAX_DER_SIG_BUF |
256 | 4736 |
FWTPM_MAX_KEM_CT_BUF |
n/a | 1600 |
FWTPM_TIS_FIFO_SIZE |
4096 | 8192 |
FWTPM_NV_PUBAREA_EST |
600 | 2720 |
These are the worst-case values. The defaults shrink at compile time to match the parameter sets wolfCrypt was built with. See "v1.85 Embedded RAM Impact" in Building for the per-parameter-set table.
Limitations and Scope¶
The v1.85 commands are implemented for post-quantum keys only. Non-PQC key types are rejected with TPM_RC_KEY or TPM_RC_SCHEME, even when the v1.85 spec defines the commands generically:
TPM2_EncapsulateandTPM2_Decapsulate: ML-KEM only. ECC DHKEM (the Table 100ecdharm with a non-NULL KDF) is not implemented.TPM2_SignSequenceStart,TPM2_VerifySequenceStart,TPM2_SignSequenceComplete, andTPM2_VerifySequenceComplete: ML-DSA and HashML-DSA only. Classical schemes (RSASSA, RSAPSS, ECDSA, SM2, ECSCHNORR, HMAC) that the spec also permits through these commands are not supported.TPM2_SignDigestandTPM2_VerifyDigestSignature: ML-DSA and HashML-DSA only. Classical digest signing (RSASSA, RSAPSS, ECDSA) over these new commands is not supported. Use the existingTPM2_SignandTPM2_VerifySignaturecommands for those schemes.
Deferred and Out of Scope¶
Three v1.85 features are deferred, each for a documented reason:
- ML-KEM-salted sessions. Part 3 Sec.11.1 (
TPM2_StartAuthSession) does not describe an ML-KEM bullet alongside the RSA-OAEP and ECDH paths, even though Part 2 Sec.11.4.2 Table 222 defines themlkemarm ofTPMU_ENCRYPTED_SECRET. Part 4 v185, which would specify this normatively, is not yet published. Current behavior:TPM2_StartAuthSessionreturnsTPM_RC_KEYfor an ML-KEM tpmKey. Revisit when Part 4 v185 is published. - External-mu ML-DSA signing. wolfCrypt has no mu-direct sign API. Part 2 Sec.12.2.3.7 says "512-byte external Mu", but FIPS 204 Algorithm 7 Line 6 produces 64 bytes (SHAKE256 output). This is pending a wolfCrypt API addition and TCG errata confirmation. Current behavior:
TPM_RC_SCHEMEfor ext-mu paths, andTPM_RC_EXT_MUfor Pure ML-DSA keys withoutallowExternalMu. - ECC KEM arm of Encapsulate and Decapsulate. Part 2 Sec.10.3.13 Table 100 has both
mlkemandecdharms, but the table note allows implementations to modify the union based on the algorithms they support. The fwTPM supports themlkemarm only.
Test Coverage¶
tests/fwtpm_unit_tests.c includes ten PQC tests that exercise the full path:
- CreatePrimary for ML-KEM-768 and ML-DSA-65
- Full Encapsulate and Decapsulate round-trip (shared secret byte match)
- HashML-DSA SignDigest and VerifyDigestSignature round-trip
- Pure ML-DSA sign sequence and verify sequence round-trip
- Dual-source known-answer tests (NIST ACVP and wolfSSL internal vectors) for ML-DSA-44 verify, ML-DSA-44 keygen determinism, ML-KEM-512 encapsulation with pinned randomness, and ML-KEM-512 keygen determinism
- LoadExternal of a NIST ACVP ML-DSA-44 public key through the fwTPM handler