Build / portability:
- Drop #pragma message in fwtpm_crypto.c (MSVC-incompatible)
- Replace non-ASCII section sign with Sec. across all sources/docs
Configure:
- Add --enable-pqc alias for --enable-v185 (same WOLFTPM_V185 macro)
- Auto-detect: when --enable-fwtpm + wolfCrypt has dilithium.h+mlkem.h
and neither flag is set, configure auto-enables PQC; --disable-pqc
opts out
- Both flags probe the wolfSSL PQC headers and fail at configure time
with a clear hint when missing
Spec / security hardening:
- VerifySequenceComplete now emits TPM_ST_DIGEST_VERIFIED (with hashAlg
metadata) for Hash-ML-DSA tickets, MESSAGE_VERIFIED for Pure ML-DSA
(was mis-tagging digests as messages, breaking PolicyTicket consumers)
- Sign/VerifySequenceComplete: free the slot on TPM_RC_SIGN_CONTEXT_KEY
too, so wrong-key Complete cannot exhaust FWTPM_MAX_SIGN_SEQ slots
(CWE-772 DoS)
- TestParms PQC arms return TPM_RC_PARMS (spec-correct) instead of
TPM_RC_VALUE; reject MLDSA/MLKEM parameter sets not actually compiled
in; parse TPMS_MLKEM_PARMS.symmetric via TPM2_Packet_ParseSymmetric
- GetCapability TPMA_ML_PARAMETER_SETS gates each MLDSA/MLKEM bit on the
per-set wolfCrypt availability macro (subset builds advertise truth)
- TPM2_VerifySignature client parser now defensive: only consume the
v1.85 metaAlg when tag==DIGEST_VERIFIED && hierarchy!=RH_NULL
- VerifyDigestSignature: hard-fail on keyName overflow instead of
silently emitting a ticket missing the name binding
- TPM_GENERATED_VALUE prefix check guarded with rc==0
- Drop dead (void)cmdSize casts in Sign/VerifySequenceStart
- wolfTPM2_EncryptSecret_MLKEM: track wc_InitRng_ex/wc_MlKemKey_Init
success flags so Free is only called on initialized state
- UBSan-v185 sanitizer cflags: explicitly disable signed-integer-overflow
and shift checks (matches the comment about wolfSSL Hash_df 440<<24)
Embedded RAM:
- FWTPM_NV_PUBAREA_EST derives from FWTPM_MAX_MLDSA_PUB_SIZE /
FWTPM_MAX_MLKEM_PUB_SIZE auto-shrink macros (subset builds save NV)
- tpm2_types.h MAX_MLDSA_*/MAX_MLKEM_* stay at worst-case (ABI floor
for TPM2B wire buffers) with comment
Tests:
- Negative test for Hash-MLDSA VerifySeqComplete ticket tag
- Negative test exposing sign-seq slot leak on TPM_RC_SIGN_CONTEXT_KEY
- Roundtrip test for wolfTPM2_SignDigest + VerifyDigestSignature
Documentation:
- README, FWTPM.md, fwtpm/README.md, examples/pqc/README.md mention
both --enable-pqc and --enable-v185 + auto-detect
- README wolfSSL line: --enable-pkcallbacks + WC_RSA_NO_PADDING
- fwtpm/README.md: drop FWTPM_SPEC_* labels (macros never existed),
remove v1.85 Additions table (all 8 commands implemented), update
coverage table to 137/113/24 (82%); note remaining gaps are
inherited v1.59/v1.84 commands, not PQC
- fwtpm_nv.h:52: clarify 2592 vs 2720 math (PQC pub key + header slack)
Fixes 5 v1.85 PR CI/build issues:
1. src/tpm2_wrap.c: add #include <wolfssl/wolfcrypt/mlkem.h> inside the
v185 MLKEM guard. Builds with --disable-fwtpm against wolfSSL with
--enable-mlkem failed because the MLKEM symbol declarations were
only pulled in transitively by src/fwtpm/fwtpm_crypto.c.
2. src/fwtpm/fwtpm_command.c: switch FWTPM_ALLOC_BUF(privKeyDer) to
FWTPM_CALLOC_BUF in 4 sites (Create, Load, LoadExternal, Import,
CreateLoaded). MSan-v185 flagged uninit-value reads in SocketSend
originating from FwCmd_Create's keyedHash branch — when caller
supplies undersized inSensitive material, FwComputeUniqueHash hashed
beyond what was written. Zero-initialising the buffer eliminates the
class of issue.
3. examples/keygen/keygen.c: pass allowExternalMu=NO for MLDSA. The
v1.85 EXT_MU enforcement now correctly rejects allowExternalMu=YES
at object creation per Part 2 §12.2.3.6.
4. .github/workflows/make-test-swtpm.yml: convert v185-pqc-swtpm lane
to build-only. swtpm has no v1.85 PQC, so unit.test PQC blocks fail
on TPM_RC_SIZE; runtime PQC coverage stays in the fwtpm-v185 lane.
5. .github/workflows/sanitizer.yml: UBSan-v185 now uses the same
sanitizer flags as the classical UBSan lane (drops ).
Pre-existing wolfSSL UB at misc.c:117 (440<<24 in Hash_df) only
surfaces under -fsanitize=integer.
Closes 13 findings across two reviews of the v1.85 PQC paths.
Tickets (TPMT_TK_VERIFIED / TPMT_TK_HASHCHECK / TPMT_TK_CREATION):
- FwAppendTicket binds tag (always) and metadata (DIGEST_VERIFIED only)
into the HMAC per Part 2 §10.6.5 Eq (5). Streamed via chunked
wc_HmacUpdate, no temp buffer. All 5 callers updated; the hand-rolled
VerifyDigestSignature path collapses into FwAppendTicket.
- FWTPM_Object gains a hierarchy field, captured at every load/create
site (CreatePrimary, Load, LoadExternal, CreateLoaded). Replaces
hardcoded TPM_RH_OWNER in VerifySignature, VerifySequenceComplete,
VerifyDigestSignature, ContextSave, and Create's creation ticket.
- VerifySequenceComplete snapshots the verified digest before
wc_HashFinal so Hash-ML-DSA tickets bind (digest || keyName) rather
than just keyName — pre-fix, two distinct verified digests on the
same key produced byte-identical tickets (universal reuse).
Authorization:
- Sign/VerifySequenceStart split TPM_RC_KEY (non-signing key, e.g.
ML-KEM) from TPM_RC_SCHEME (signing key, scheme unsupported) using
TPMA_OBJECT_sign per Part 3 §17.5.1 / §17.6.1.
- SignDigest restricted-key path validates TPMT_TK_HASHCHECK HMAC
per Part 3 §20.7.1 instead of blanket-rejecting; x509sign keeps
the TPM_RC_ATTRIBUTES short-circuit.
- Decapsulate, SignDigest, SignSequenceComplete reject NO_SESSIONS
with TPM_RC_AUTH_MISSING (Auth Role: USER, Tables 62/124/126).
Restricted-key TPM_GENERATED_VALUE check:
- FWTPM_SignSeq.firstBytes[4] populated by SequenceUpdate covers the
Hash-ML-DSA path where bytes are otherwise consumed by hashCtx;
topped-up from the Complete trailing buffer for Pure-MLDSA one-shot.
Closes the Update-then-empty-Complete bypass.
Client-side (src/tpm2.c):
- TPM2_VerifySequenceComplete defensively dispatches on validation.tag
for TPMU_TK_VERIFIED_META, mirroring TPM2_VerifyDigestSignature.
Other:
- TPM2_Packet_AppendSensitive caps mldsa/mlkem .size to buffer length.
- pqc_mssim_e2e.c zeroizes ss1/ss2 on cleanup.
- Untrack examples/pqc/pqc_mssim_e2e (libtool wrapper with hardcoded
/home/aidangarske path; .gitignore already covered it).
- #pragma message at WOLFTPM_V185 build-time flagging that the PQC
primary-key KDFa labels are interpretation pending TCG Part 4 v1.85;
suppressible via -DWOLFTPM_V185_LABELS_ACK.
Tests: 11 new fixtures in tests/fwtpm_unit_tests.c, 4 existing tests
updated to assert new spec-mandated RCs. fwtpm_unit.test reports 105
passing, zero failures.
Main README:
- New ## Post-Quantum Cryptography (v1.85) section between fwTPM and
TPM 2.0 Overview. Covers supported algorithms (ML-DSA-44/65/87,
Hash-ML-DSA, ML-KEM-512/768/1024), exact wolfSSL + wolfTPM build
config (--enable-dilithium --enable-mlkem --enable-experimental ...
for wolfSSL; --enable-fwtpm --enable-v185 for wolfTPM), and a
make check pointer.
- Existing feature-list bullet at line 41 now points to the new
section instead of directly to docs/FWTPM.md.
examples/pqc/README.md:
- Rewrite around three audience splits: (1) build steps, (2) run
everything with make check, (3) per-example details.
- New sections for mlkem_encap and the -mldsa/-hash_mldsa/-mlkem
options on examples/keygen/keygen.
- Drop stale --enable-swtpm reference (wrong flag; caused reviewer
confusion).
- Point users at the existing tests/fwtpm_check.sh and
tests/pqc_mssim_e2e.sh for targeted reruns without the full classical
suite.
Documentation split (no duplication):
- Top-level README - build + I just want to run it
- examples/pqc/README.md per-example usage
- docs/FWTPM.md#tpm-20-v185-post-quantum-support -> server internals
(commands, primary-key derivation, buffer constants, spec
interpretation decisions)
examples/keygen/keygen:
- New -mldsa[=44|65|87], -hash_mldsa[=44|65|87], -mlkem[=512|768|1024]
options alongside existing -rsa/-ecc/-sym/-keyedhash. Dispatches to
wolfTPM2_GetKeyTemplate_{MLDSA,HASH_MLDSA,MLKEM}, then CreateKey under
the SRK parent. AIK template path correctly rejects PQC (AIKs are
RSA/ECC only per TCG).
- Param-set parser defaults: MLDSA-65, MLKEM-768, SHA-256 pre-hash for
Hash-ML-DSA.
examples/pqc/mlkem_encap (new):
- CreatePrimary MLKEM (512/768/1024) then Encapsulate + Decapsulate,
asserting the two shared secrets match byte-for-byte. Companion to
pqc_mssim_e2e but focused on the KEM wrappers alone.
examples/run_examples.sh:
- Detects WOLFTPM_V185 from config.h, runs keygen+keyload round-trip
for all 9 PQC variants (same pattern used by RSA/ECC blocks above).
All 9 pass against fwtpm_server.
Cross-process PQC validation: new examples/pqc/pqc_mssim_e2e exercises
wolfTPM2_* client wrappers against a running fwtpm_server over the
mssim (SWTPM) socket transport. Two round-trips in one binary:
- MLKEM-768 Encap/Decap: asserts ciphertext = 1088 bytes and the two
derived shared secrets are byte-identical.
- HashMLDSA-65 SignDigest/Verify: asserts signature = 3309 bytes and
the validation ticket carries TPM_ST_DIGEST_VERIFIED.
tests/pqc_mssim_e2e.sh spawns fwtpm_server, waits for TCP readiness,
runs the client, and cleans up. Proves client marshaling + mssim
framing + fwtpm_server unmarshaling + PQC handler dispatch agree over
a real socket between two separately-compiled processes — orthogonal
to the in-process fwtpm_unit.test suite.
Infrastructure:
- configure.ac: new AM_CONDITIONAL BUILD_V185 so the example only
builds when --enable-v185 is passed (matches the pattern of
BUILD_SWTPM, BUILD_DEVTPM, etc.).
- examples/include.am, examples/pqc/include.am: register the example.
Output polish: replaced 85 inconsistent calls with a
fwtpm_pass(name, is_pqc) helper that produces aligned
columns. PQC tests are tagged [PQC] so they're visually distinct
from the classical suite at a glance. All 86 existing tests still pass.