Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions .github/workflows/pqc-build-matrix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
name: PQC Build Matrix (v1.85 trimming)

on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]
types: [opened, synchronize, reopened, ready_for_review]
repository_dispatch:
types: [nightly-trigger]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

# Exhaustive build of every ML-DSA x ML-KEM trimming combination, for both the
# client library (--disable-fwtpm) and the fwTPM server (--enable-fwtpm), to
# prove each per-operation gate compiles and links (lib + server + unit tests).
# The (mldsa=no, mlkem=no) combo is intentionally absent: it is a configure
# error (no PQC algorithm) verified by the reject-no-pqc job below. Runtime
# make check on the full op set is covered by pqc-examples.yml.
jobs:
build:
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
include:
- name: cli-mldsa_all-mlkem_all
wolftpm_config: --enable-pqc --enable-mldsa=all --enable-mlkem=all --disable-fwtpm --disable-examples
- name: cli-mldsa_all-mlkem_enc
wolftpm_config: --enable-pqc --enable-mldsa=all --enable-mlkem=enc --disable-fwtpm --disable-examples
- name: cli-mldsa_all-mlkem_dec
wolftpm_config: --enable-pqc --enable-mldsa=all --enable-mlkem=dec --disable-fwtpm --disable-examples
- name: cli-mldsa_all-mlkem_no
wolftpm_config: --enable-pqc --enable-mldsa=all --enable-mlkem=no --disable-fwtpm --disable-examples
- name: cli-mldsa_signonly-mlkem_all
wolftpm_config: --enable-pqc --enable-mldsa=sign-only --enable-mlkem=all --disable-fwtpm --disable-examples
- name: cli-mldsa_signonly-mlkem_enc
wolftpm_config: --enable-pqc --enable-mldsa=sign-only --enable-mlkem=enc --disable-fwtpm --disable-examples
- name: cli-mldsa_signonly-mlkem_dec
wolftpm_config: --enable-pqc --enable-mldsa=sign-only --enable-mlkem=dec --disable-fwtpm --disable-examples
- name: cli-mldsa_signonly-mlkem_no
wolftpm_config: --enable-pqc --enable-mldsa=sign-only --enable-mlkem=no --disable-fwtpm --disable-examples
- name: cli-mldsa_verifyonly-mlkem_all
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=all --disable-fwtpm --disable-examples
- name: cli-mldsa_verifyonly-mlkem_enc
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=enc --disable-fwtpm --disable-examples
- name: cli-mldsa_verifyonly-mlkem_dec
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=dec --disable-fwtpm --disable-examples
- name: cli-mldsa_verifyonly-mlkem_no
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=no --disable-fwtpm --disable-examples
- name: cli-mldsa_no-mlkem_all
wolftpm_config: --enable-pqc --enable-mldsa=no --enable-mlkem=all --disable-fwtpm --disable-examples
- name: cli-mldsa_no-mlkem_enc
wolftpm_config: --enable-pqc --enable-mldsa=no --enable-mlkem=enc --disable-fwtpm --disable-examples
- name: cli-mldsa_no-mlkem_dec
wolftpm_config: --enable-pqc --enable-mldsa=no --enable-mlkem=dec --disable-fwtpm --disable-examples
- name: fw-mldsa_all-mlkem_all
wolftpm_config: --enable-pqc --enable-mldsa=all --enable-mlkem=all --enable-fwtpm --disable-examples
- name: fw-mldsa_all-mlkem_enc
wolftpm_config: --enable-pqc --enable-mldsa=all --enable-mlkem=enc --enable-fwtpm --disable-examples
- name: fw-mldsa_all-mlkem_dec
wolftpm_config: --enable-pqc --enable-mldsa=all --enable-mlkem=dec --enable-fwtpm --disable-examples
- name: fw-mldsa_all-mlkem_no
wolftpm_config: --enable-pqc --enable-mldsa=all --enable-mlkem=no --enable-fwtpm --disable-examples
- name: fw-mldsa_signonly-mlkem_all
wolftpm_config: --enable-pqc --enable-mldsa=sign-only --enable-mlkem=all --enable-fwtpm --disable-examples
- name: fw-mldsa_signonly-mlkem_enc
wolftpm_config: --enable-pqc --enable-mldsa=sign-only --enable-mlkem=enc --enable-fwtpm --disable-examples
- name: fw-mldsa_signonly-mlkem_dec
wolftpm_config: --enable-pqc --enable-mldsa=sign-only --enable-mlkem=dec --enable-fwtpm --disable-examples
- name: fw-mldsa_signonly-mlkem_no
wolftpm_config: --enable-pqc --enable-mldsa=sign-only --enable-mlkem=no --enable-fwtpm --disable-examples
- name: fw-mldsa_verifyonly-mlkem_all
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=all --enable-fwtpm --disable-examples
- name: fw-mldsa_verifyonly-mlkem_enc
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=enc --enable-fwtpm --disable-examples
- name: fw-mldsa_verifyonly-mlkem_dec
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=dec --enable-fwtpm --disable-examples
- name: fw-mldsa_verifyonly-mlkem_no
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=no --enable-fwtpm --disable-examples
- name: fw-mldsa_no-mlkem_all
wolftpm_config: --enable-pqc --enable-mldsa=no --enable-mlkem=all --enable-fwtpm --disable-examples
- name: fw-mldsa_no-mlkem_enc
wolftpm_config: --enable-pqc --enable-mldsa=no --enable-mlkem=enc --enable-fwtpm --disable-examples
- name: fw-mldsa_no-mlkem_dec
wolftpm_config: --enable-pqc --enable-mldsa=no --enable-mlkem=dec --enable-fwtpm --disable-examples
- name: cli-disable-pqc
wolftpm_config: --disable-pqc --disable-fwtpm --disable-examples
- name: cli-v185-full
wolftpm_config: --enable-v185 --disable-fwtpm --disable-examples
- name: cli-verifyencap-nohash
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=enc --disable-hash-mldsa --disable-fwtpm --disable-examples
- name: fw-disable-pqc
wolftpm_config: --disable-pqc --enable-fwtpm --disable-examples
- name: fw-v185-full
wolftpm_config: --enable-v185 --enable-fwtpm --disable-examples
- name: fw-verifyencap-nohash
wolftpm_config: --enable-pqc --enable-mldsa=verify-only --enable-mlkem=enc --disable-hash-mldsa --enable-fwtpm --disable-examples

steps:
- name: Checkout wolfTPM
uses: actions/checkout@v4

- name: Setup wolfSSL with PQC
uses: ./.github/actions/setup-wolfssl
with:
configure-flags: >-
--enable-wolftpm --enable-pkcallbacks --enable-keygen
--enable-dilithium --enable-mlkem --enable-experimental --enable-harden
--enable-aescfb
cflags: -DWC_RSA_NO_PADDING

- name: Build wolfTPM (${{ matrix.name }})
run: |
./autogen.sh
./configure ${{ matrix.wolftpm_config }}
make -j"$(nproc)"

- name: Show resolved PQC options
run: grep -E "WOLFTPM_(V185|PQC|NO_ML|NO_HASH|MLDSA|MLKEM)" wolftpm/options.h || true

- name: Upload failure logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: pqc-build-${{ matrix.name }}-logs
path: |
config.log
retention-days: 5

reject-no-pqc:
# --enable-pqc with both algorithms off must FAIL at configure with a clear
# message (no PQC algorithm). Verifies the guard, not just that it builds.
if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout wolfTPM
uses: actions/checkout@v4
- name: Setup wolfSSL with PQC
uses: ./.github/actions/setup-wolfssl
with:
configure-flags: >-
--enable-wolftpm --enable-pkcallbacks --enable-keygen
--enable-dilithium --enable-mlkem --enable-experimental --enable-harden
--enable-aescfb
cflags: -DWC_RSA_NO_PADDING
- name: configure must reject mldsa=no + mlkem=no
run: |
./autogen.sh
if ./configure --enable-pqc --enable-mldsa=no --enable-mlkem=no \
--disable-fwtpm --disable-examples 2>err.log; then
echo "ERROR: configure accepted a no-PQC-algorithm build"; exit 1
fi
grep -q "leaves no PQC algorithm" err.log \
|| { echo "ERROR: wrong/no error message"; cat err.log; exit 1; }
echo "PASS: rejected with the expected message"
16 changes: 16 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Release Notes

## wolfTPM Release 4.1.0 (TBD)

**Detail**

* Added fine-grained post-quantum build macros so the v1.85 PQC footprint can be
trimmed to the operations actually used. New layered gates: `WOLFTPM_PQC`
(lean ML-DSA / ML-KEM, the new meaning of `--enable-pqc`) sits under the full
`WOLFTPM_V185`; `WOLFTPM_MLDSA` / `WOLFTPM_MLKEM` per algorithm; and
`WOLFTPM_MLDSA_SIGN` / `WOLFTPM_MLDSA_VERIFY` / `WOLFTPM_MLKEM_ENCAP` /
`WOLFTPM_MLKEM_DECAP` / `WOLFTPM_HASH_MLDSA` per operation, with matching
`WOLFTPM_NO_*` opt-outs resolved in `wolftpm/tpm2_pqc.h`.
* Added `--enable-mldsa[=all|sign-only|verify-only|no]`,
`--enable-mlkem[=all|enc|dec|no]`, and `--disable-hash-mldsa` configure flags
(mirroring the wolfSSL syntax). `--enable-pqc` now selects the lean PQC subset
and excludes the non-PQC v1.85 spec code; `--enable-v185` is unchanged (full).

## wolfTPM Release 4.0.0 (Apr 22, 2026)

**Summary**
Expand Down
41 changes: 36 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Portable TPM 2.0 project designed for embedded use.
* Support for HMAC Sessions.
* Support for reading Endorsement certificates (EK Credential Profile).
* Includes a portable firmware TPM 2.0 implementation (fwTPM, also known as fTPM / swtpm) for embedded platforms without a discrete TPM chip. See [Firmware TPM (fwTPM / fTPM / swtpm)](#firmware-tpm-fwtpm--ftpm--swtpm) below.
* **Post-quantum cryptography support** via TPM 2.0 Library Specification v1.85: ML-DSA (FIPS 204) signing and ML-KEM (FIPS 203) key encapsulation, enabled with `--enable-pqc` (alias for `--enable-v185`). Auto-detected when `--enable-fwtpm` is built against a wolfCrypt that has ML-DSA + ML-KEM. Both the client library and the fwTPM server implement the eight new v1.85 PQC commands. See [Post-Quantum Cryptography (v1.85)](#post-quantum-cryptography-v185) below.
* **Post-quantum cryptography support** via TPM 2.0 Library Specification v1.85: ML-DSA (FIPS 204) signing and ML-KEM (FIPS 203) key encapsulation, enabled with `--enable-v185` (full v1.85) or the leaner `--enable-pqc` (ML-DSA / ML-KEM only), with per-operation trimming via `--enable-mldsa`/`--enable-mlkem`. Auto-detected when `--enable-fwtpm` is built against a wolfCrypt that has ML-DSA + ML-KEM. Both the client library and the fwTPM server implement the eight new v1.85 PQC commands. See [Post-Quantum Cryptography (v1.85)](#post-quantum-cryptography-v185) below.
* **SPDM attestation support** (DMTF DSP0274) over the TCG SPDM-over-TPM binding, with a TCG certificate handshake and a DSP0274 pre-shared-key (PSK) handshake, enabled with `--enable-spdm`. The fwTPM server includes an SPDM 1.3 responder so the stack can be exercised end-to-end in CI without discrete silicon. See [SPDM Attestation](#spdm-attestation) below.

Note: See [examples/README.md](examples/README.md) for details on using the examples.
Expand Down Expand Up @@ -101,10 +101,41 @@ sudo make install
make
```

`--enable-pqc` is an alias for `--enable-v185`; both turn on the same
WOLFTPM_V185 build flag. If you omit them but `--enable-fwtpm` is set
and wolfCrypt has ML-DSA + ML-KEM available, configure auto-detects
PQC and enables it. Pass `--disable-pqc` to opt out explicitly.
`--enable-v185` turns on the full v1.85 build (`WOLFTPM_V185`): the PQC
algorithms plus the non-PQC v1.85 spec additions. `--enable-pqc` turns on
just the lean PQC subset (`WOLFTPM_PQC`) — ML-DSA / ML-KEM only — which is
smaller for deployments that do not need the rest of v1.85. If you omit both
but `--enable-fwtpm` is set and wolfCrypt has ML-DSA + ML-KEM available,
configure auto-detects and enables full v1.85. Pass `--disable-pqc` to opt
out explicitly.

#### Trimming the PQC footprint

To compile only the operations you call (smaller binary, no malloc needed),
mirror the wolfSSL flags:

```
# ML-DSA verify-only + ML-KEM encapsulate-only (no sign, no decapsulate)
./configure --enable-pqc --enable-mldsa=verify-only --enable-mlkem=enc
```

| Flag | Values | Drops |
|------|--------|-------|
| `--enable-mldsa` | `all` (default) / `sign-only` / `verify-only` / `no` | the unselected ML-DSA operation |
| `--enable-mlkem` | `all` (default) / `enc` / `dec` / `no` | the unselected ML-KEM operation |
| `--disable-hash-mldsa` | — | pre-hash ML-DSA key support |

These map to `WOLFTPM_NO_MLDSA_SIGN`, `WOLFTPM_NO_MLKEM_DECAP`, etc., which
embedded integrators can also pass directly via `CFLAGS` without autotools.
Existing `--enable-v185` builds are unaffected (every operation defaults on).
Disabling both algorithms (`--enable-mldsa=no --enable-mlkem=no`) is a configure
error — use `--disable-pqc` to build without any post-quantum support.

The same flags also trim the fwTPM server: `--enable-fwtpm
--enable-mldsa=verify-only` builds a server that implements only ML-DSA verify
(the sign command handlers, dispatch entries, and crypto are compiled out).
fwTPM always builds the full v1.85 spec surface, so the trims apply on top of
`WOLFTPM_V185`.

### Running the examples

Expand Down
98 changes: 82 additions & 16 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -787,20 +787,43 @@ AC_ARG_ENABLE([v185],
[ ENABLED_V185=detect ]
)
AC_ARG_ENABLE([pqc],
[AS_HELP_STRING([--enable-pqc],[Alias for --enable-v185 (post-quantum: ML-DSA / ML-KEM)])],
[AS_HELP_STRING([--enable-pqc],[Enable the lean post-quantum subset (ML-DSA / ML-KEM only, excludes the non-PQC v1.85 spec code that --enable-v185 adds)])],
[ ENABLED_PQC=$enableval ],
[ ENABLED_PQC=detect ]
)

# An explicit "yes" on either flag wins. An explicit "no" on either
# disables. Mixed (e.g. --enable-pqc + --disable-v185) treats explicit
# "no" as the safer choice and disables.
# Per-algorithm / per-operation trimming, mirroring the wolfSSL flag syntax.
# These only take effect when PQC (or v1.85) is enabled; default is everything.
AC_ARG_ENABLE([mldsa],
[AS_HELP_STRING([--enable-mldsa@<:@=all|sign-only|verify-only|no@:>@],[Limit wolfTPM ML-DSA support (default: all)])],
[ ENABLED_TPM_MLDSA=$enableval ],
[ ENABLED_TPM_MLDSA=all ]
)
AC_ARG_ENABLE([mlkem],
[AS_HELP_STRING([--enable-mlkem@<:@=all|enc|dec|no@:>@],[Limit wolfTPM ML-KEM support (default: all)])],
[ ENABLED_TPM_MLKEM=$enableval ],
[ ENABLED_TPM_MLKEM=all ]
)
AC_ARG_ENABLE([hash-mldsa],
[AS_HELP_STRING([--disable-hash-mldsa],[Drop pre-hash ML-DSA key support (default: enabled with ML-DSA)])],
[ ENABLED_TPM_HASH_MLDSA=$enableval ],
[ ENABLED_TPM_HASH_MLDSA=yes ]
)

# Resolve the entry points into one mode: no / pqc (lean) / v185 (full).
# --enable-v185 => full v1.85 (non-PQC spec code + PQC), also defines PQC
# --enable-pqc => lean PQC subset only (smaller)
# An explicit "no" on either disables. --enable-v185 wins over --enable-pqc.
ENABLED_PQC_MODE=no
if test "x$ENABLED_V185" = "xno" || test "x$ENABLED_PQC" = "xno"
then
ENABLED_V185=no
elif test "x$ENABLED_V185" = "xyes" || test "x$ENABLED_PQC" = "xyes"
ENABLED_PQC_MODE=no
elif test "x$ENABLED_V185" = "xyes"
then
ENABLED_PQC_MODE=v185
elif test "x$ENABLED_PQC" = "xyes"
then
ENABLED_V185=yes
ENABLED_PQC_MODE=pqc
else
# Neither flag specified — try auto-detect, but only when the natural
# consumer (fwTPM + wolfCrypt) is being built. Without fwTPM there is
Expand Down Expand Up @@ -832,17 +855,21 @@ else
if test "x$WOLFTPM_HAVE_MLDSA_FN" = "xyes" && \
test "x$WOLFTPM_HAVE_MLKEM_FN" = "xyes"
then
AC_MSG_NOTICE([wolfCrypt ML-DSA + ML-KEM detected; auto-enabling --enable-v185 (use --disable-v185 or --disable-pqc to opt out)])
ENABLED_V185=yes
else
ENABLED_V185=no
AC_MSG_NOTICE([wolfCrypt ML-DSA + ML-KEM detected; auto-enabling --enable-v185 (use --disable-v185/--disable-pqc to opt out, or --enable-pqc for the lean PQC subset)])
ENABLED_PQC_MODE=v185
fi
else
ENABLED_V185=no
fi
fi

if test "x$ENABLED_V185" = "xyes"
# fwTPM server source is gated on WOLFTPM_V185; the lean PQC subset targets
# the client library. If building fwTPM, promote lean PQC to full v1.85.
if test "x$ENABLED_PQC_MODE" = "xpqc" && test "x$ENABLED_FWTPM" = "xyes"
then
AC_MSG_NOTICE([fwTPM requires full v1.85; promoting --enable-pqc to --enable-v185])
ENABLED_PQC_MODE=v185
fi

if test "x$ENABLED_PQC_MODE" != "xno"
then
# Explicit opt-in: re-probe so we fail at configure time (with a
# clear hint about wolfSSL flags) rather than deep inside the compile
Expand Down Expand Up @@ -874,9 +901,41 @@ then
[AC_MSG_RESULT([no])
AC_MSG_ERROR([wolfTPM PQC requires wolfSSL >= v5.8.0-stable. Please upgrade wolfSSL.])])

AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_V185"
if test "x$ENABLED_PQC_MODE" = "xv185"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_V185"
else
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_PQC"
fi

# Per-algorithm / per-operation trimming (emit WOLFTPM_NO_* opt-outs).
case "x$ENABLED_TPM_MLDSA" in
xall|xyes) ;;
xno) AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_NO_MLDSA" ;;
xsign-only) AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_NO_MLDSA_VERIFY" ;;
xverify-only) AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_NO_MLDSA_SIGN" ;;
*) AC_MSG_ERROR([invalid --enable-mldsa=$ENABLED_TPM_MLDSA (use all|sign-only|verify-only|no)]) ;;
esac
case "x$ENABLED_TPM_MLKEM" in
xall|xyes) ;;
xno) AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_NO_MLKEM" ;;
xenc) AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_NO_MLKEM_DECAP" ;;
xdec) AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_NO_MLKEM_ENCAP" ;;
*) AC_MSG_ERROR([invalid --enable-mlkem=$ENABLED_TPM_MLKEM (use all|enc|dec|no)]) ;;
esac
# v1.85 is a post-quantum spec; enabling it with no PQC algorithm at all
# is contradictory — point the user at --disable-pqc instead.
if test "x$ENABLED_TPM_MLDSA" = "xno" && test "x$ENABLED_TPM_MLKEM" = "xno"
then
AC_MSG_ERROR([--enable-pqc/--enable-v185 with both --enable-mldsa=no and --enable-mlkem=no leaves no PQC algorithm; use --disable-pqc to build without post-quantum support])
fi
if test "x$ENABLED_TPM_HASH_MLDSA" = "xno"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_NO_HASH_MLDSA"
fi
fi
AM_CONDITIONAL([BUILD_V185], [test "x$ENABLED_V185" = "xyes"])
AM_CONDITIONAL([BUILD_V185], [test "x$ENABLED_PQC_MODE" = "xv185"])
AM_CONDITIONAL([BUILD_PQC], [test "x$ENABLED_PQC_MODE" != "xno"])

# HARDEN FLAGS
AX_HARDEN_CC_COMPILER_FLAGS
Expand Down Expand Up @@ -1075,3 +1134,10 @@ echo " * fwTPM TIS/SHM: $ENABLED_FWTPM_TIS"
echo " * Runtime Module Detection: $ENABLED_AUTODETECT"
echo " * Firmware Upgrade Support: $ENABLED_FIRMWARE"
echo " * SPDM Support: $ENABLED_SPDM"
echo " * Post-Quantum (v185/pqc): $ENABLED_PQC_MODE"
if test "x$ENABLED_PQC_MODE" != "xno"
then
echo " ML-DSA: $ENABLED_TPM_MLDSA"
echo " ML-KEM: $ENABLED_TPM_MLKEM"
echo " Hash-ML-DSA: $ENABLED_TPM_HASH_MLDSA"
fi
Loading
Loading