Skip to content

Commit 67b593f

Browse files
committed
Add SealSQ QVault post-quantum TPM support and pqc_ctrl example
1 parent 46d2d33 commit 67b593f

15 files changed

Lines changed: 1108 additions & 13 deletions

File tree

.github/workflows/pqc-build-matrix.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ jobs:
2828
fail-fast: false
2929
matrix:
3030
include:
31+
- name: sealsq-qvault-vendor
32+
wolftpm_config: --enable-pqc --enable-sealsq --disable-fwtpm --disable-examples
3133
- name: cli-mldsa_all-mlkem_all
3234
wolftpm_config: --enable-pqc --enable-mldsa=all --enable-mlkem=all --disable-fwtpm --disable-examples
3335
- name: cli-mldsa_all-mlkem_enc

.github/workflows/pqc-examples.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ jobs:
9595
- name: ML-KEM encap + decap example (standalone)
9696
run: ./examples/pqc/mlkem_encap
9797

98+
- name: PQC control tool (pqc_ctrl.sh suite + argument validation)
99+
# pqc_ctrl.sh runs the full command set and the argument-validation
100+
# negatives, exiting non-zero on any failure. PQC_CTRL_CLEAR is left off
101+
# so the shared Tier-2 fwtpm_server is not wiped mid-sequence; the
102+
# destructive --clear / --pcrextend paths are exercised on hardware.
103+
run: ./examples/pqc/pqc_ctrl.sh
104+
98105
- name: Stop Tier 2 fwtpm_server (free port 2321 for E2E)
99106
run: |
100107
if [ -f /tmp/fwtpm_server.pid ]; then

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ examples/pqc/mldsa_sign
7979
examples/pqc/mlkem_encap
8080
examples/pqc/pqc_mssim_e2e
8181
examples/pqc/gen_pqc_certs
82+
examples/pqc/pqc_ctrl
83+
examples/pqc/mldsa_verify_neg
84+
examples/pqc/mlkem_decap_neg
8285
examples/nvram/extend
8386
examples/nvram/store
8487
examples/nvram/read

README.md

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Portable TPM 2.0 project designed for embedded use.
77

88
* This implementation provides all TPM 2.0 API's in compliance with the specification.
99
* Wrappers provided to simplify Key Generation/Loading, RSA encrypt/decrypt, ECC sign/verify, ECDH, NV, Hashing/HACM, AES, Sealing/Unsealing, Attestation, PCR Extend/Quote and Secure Root of Trust.
10-
* Any TPM 2.0 compliant module is supported. Tested modules include Infineon SLB9670, SLB9672, SLB9673, STMicroelectronics ST33KTPM2XSPI, ST33KTPM2I, ST33TPHF2XSPI, ST33TPHF2XI2C, Microchip ATTPM20, Nations Technologies/NSING Z32H330, NS350, and Nuvoton NPCT650, NPCT750.
10+
* Any TPM 2.0 compliant module is supported. Tested modules include Infineon SLB9670, SLB9672, SLB9673, STMicroelectronics ST33KTPM2XSPI, ST33KTPM2I, ST33TPHF2XSPI, ST33TPHF2XI2C, Microchip ATTPM20, Nations Technologies/NSING Z32H330, NS350, Nuvoton NPCT650, NPCT750, and SealSQ QVault (first TPM with post-quantum ML-DSA/ML-KEM in silicon).
1111
* wolfTPM uses the TPM Interface Specification (TIS) to communicate either over SPI, or using a memory mapped I/O range.
1212
* On Linux, wolfTPM auto-detects between the kernel TPM driver (`/dev/tpmX`) and direct SPI access at runtime - a simple `./configure && make` works with either interface.
1313
* wolfTPM can also use the Linux TPM kernel interface (`/dev/tpmX`) to talk with any physical TPM on SPI, I2C and even LPC bus.
@@ -78,9 +78,12 @@ Supported algorithms:
7878
| Hash-ML-DSA (pre-hash signing) | FIPS 204 | ML-DSA-44 / 65 / 87 with caller hash |
7979
| ML-KEM (key encapsulation) | FIPS 203 | ML-KEM-512 / 768 / 1024 |
8080

81-
The examples run against the in-tree fwTPM server. No shipping hardware
82-
TPM firmware implements v1.85 PQC yet; upgrade paths for discrete chips
83-
are forward-compatible — the same wrapper API targets both.
81+
wolfTPM **officially supports the SealSQ QVault**, the first shipping TPM 2.0
82+
with these v1.85 PQC algorithms in silicon. Build for it with `--enable-sealsq
83+
--enable-pqc`. The same examples and wrapper API also run against the in-tree
84+
fwTPM server for CI or when no hardware is present. See the
85+
[TPM2 Benchmarks](#tpm2-benchmarks) section for measured ML-DSA / ML-KEM
86+
performance on the QVault.
8487

8588
### Building
8689

@@ -144,9 +147,10 @@ make check
144147
```
145148

146149
See [examples/pqc/README.md](examples/pqc/README.md) for per-example
147-
details (`pqc_mssim_e2e`, `mlkem_encap`) and PQC options on the
148-
general-purpose `keygen`/`keyload` tools (`-mldsa`, `-hash_mldsa`,
149-
`-mlkem`).
150+
details — the `pqc_ctrl` control center (every PQC operation plus board
151+
control in one CLI, with `pqc_ctrl.sh` running the full command set),
152+
`pqc_mssim_e2e`, `mlkem_encap`, and PQC options on the general-purpose
153+
`keygen`/`keyload` tools (`-mldsa`, `-hash_mldsa`, `-mlkem`).
150154

151155
For the fwTPM server's PQC internals — the eight v1.85 commands,
152156
primary-key derivation, buffer constants, and spec-interpretation
@@ -238,6 +242,7 @@ Tested with:
238242
* Microchip ATTPM20 module
239243
* Nuvoton NPCT65X or NPCT75x TPM2.0 modules
240244
* Nations Technologies Z32H330 or NS350 TPM 2.0 modules
245+
* SealSQ QVault TPM 2.0 module (SPI, post-quantum ML-DSA / ML-KEM)
241246

242247
#### Device Identification
243248

@@ -283,6 +288,10 @@ Nuvoton NPCT750 TPM2.0
283288
TPM2: Caps 0x30000697, Did 0x00fc, Vid 0x1050, Rid 0x 1
284289
Mfg NTC (0), Vendor NPCT75x"!!4rls, Fw 7.2 (131072), FIPS 140-2 1, CC-EAL4 0
285290

291+
SealSQ QVault TPM 2.0
292+
TPM2: Caps 0x30000797, Did 0x0083, Vid 0x2406, Rid 0x 3
293+
Mfg SEAL (6), Vendor QVault TPM, Fw 2.1 (0x3010303), FIPS 140-3, CC-EAL4 0
294+
286295
## Building
287296

288297
### Building wolfSSL
@@ -346,6 +355,7 @@ make install
346355
--enable-microchip Enable Microchip ATTPM20 Support (default: disabled) - WOLFTPM_MICROCHIP
347356
--enable-nuvoton Enable Nuvoton NPCT65x/NPCT75x Support (default: disabled) - WOLFTPM_NUVOTON
348357
--enable-nations Enable Nations Technology NS350 Support (default: disabled) - WOLFTPM_NATIONS
358+
--enable-sealsq Enable SealSQ QVault post-quantum TPM Support (default: disabled) - WOLFTPM_SEALSQ
349359
350360
--enable-devtpm Enable using Linux kernel driver for /dev/tpmX (default: disabled) - WOLFTPM_LINUX_DEV
351361
Note: With autodetect (default) this is no longer required on Linux;
@@ -594,6 +604,32 @@ ECDSA 256 verify 9 ops took 1.022 sec, avg 113.539 ms, 8.808 ops/se
594604
ECDHE 256 agree 5 ops took 1.161 sec, avg 232.144 ms, 4.308 ops/sec
595605
```
596606

607+
Run on the SealSQ QVault post-quantum TPM (ML-DSA / ML-KEM) on a Raspberry Pi 5
608+
over SPI. These are the first post-quantum TPM benchmarks measured on
609+
shipping-class silicon:
610+
611+
```
612+
./examples/bench/bench
613+
TPM2 Benchmark using Wrapper API's
614+
RNG 10 KB took 1.061 seconds, 9.428 KB/s
615+
AES-256-CBC-enc 57 KB took 1.000 seconds, 56.994 KB/s
616+
SHA256 43 KB took 1.012 seconds, 42.470 KB/s
617+
SHA384 43 KB took 1.024 seconds, 41.984 KB/s
618+
RSA 2048 key gen 3 ops took 20.536 sec, avg 6845.188 ms, 0.146 ops/sec
619+
RSA 2048 Public 71 ops took 1.015 sec, avg 14.289 ms, 69.985 ops/sec
620+
RSA 2048 Private 7 ops took 1.154 sec, avg 164.827 ms, 6.067 ops/sec
621+
ECC 256 key gen 4 ops took 1.170 sec, avg 292.538 ms, 3.418 ops/sec
622+
ECDSA 256 sign 14 ops took 1.019 sec, avg 72.781 ms, 13.740 ops/sec
623+
ECDSA 256 verify 17 ops took 1.031 sec, avg 60.661 ms, 16.485 ops/sec
624+
ECDHE 256 agree 5 ops took 1.030 sec, avg 206.022 ms, 4.854 ops/sec
625+
ML-DSA 65 key gen 8 ops took 16.357 sec, avg 2044.679 ms, 0.489 ops/sec
626+
ML-DSA 65 sign 2 ops took 1.162 sec, avg 581.025 ms, 1.721 ops/sec
627+
ML-DSA 65 verify 7 ops took 1.142 sec, avg 163.118 ms, 6.131 ops/sec
628+
ML-KEM 768 key gen 19 ops took 15.216 sec, avg 800.819 ms, 1.249 ops/sec
629+
ML-KEM 768 encap 5 ops took 1.059 sec, avg 211.777 ms, 4.722 ops/sec
630+
ML-KEM 768 decap 3 ops took 1.276 sec, avg 425.471 ms, 2.350 ops/sec
631+
```
632+
597633
Run on Infineon OPTIGA SLB9672 at 43MHz:
598634

599635
```

configure.ac

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ fi
253253
WOLFTPM_HW_SELECTED=no
254254
for _wt_v in "$enable_infineon" "$enable_st" "$enable_st33" \
255255
"$enable_microchip" "$enable_mchp" \
256-
"$enable_nuvoton" "$enable_nations" \
256+
"$enable_nuvoton" "$enable_nations" "$enable_sealsq" \
257257
"$enable_spi" "$enable_i2c" "$enable_mmio" \
258258
"$enable_devtpm" "$enable_autodetect" \
259259
"$enable_winapi" "$enable_wintbs"; do
@@ -540,6 +540,17 @@ then
540540
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_NATIONS"
541541
fi
542542

543+
# SealSQ QVault TPM (post-quantum ML-DSA/ML-KEM)
544+
AC_ARG_ENABLE([sealsq],
545+
[AS_HELP_STRING([--enable-sealsq],[Enable SealSQ QVault TPM Support (default: disabled)])],
546+
[ ENABLED_SEALSQ=$enableval ],
547+
[ ENABLED_SEALSQ=no ]
548+
)
549+
if test "x$ENABLED_SEALSQ" = "xyes"
550+
then
551+
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_SEALSQ"
552+
fi
553+
543554
# Infineon SLB9670/SLB9672/SLB9673
544555
AC_ARG_ENABLE([infineon],
545556
[AS_HELP_STRING([--enable-infineon],[Enable Infineon SLB9670/SLB9672 TPM Support (default: disabled)])],
@@ -621,7 +632,7 @@ then
621632
# If a module hasn't been selected then enable auto-detection
622633
if test "x$ENABLED_INFINEON" = "xno" && test "x$ENABLED_MCHP" = "xno" && test "x$ENABLED_MICROCHIP" = "xno" && \
623634
test "x$ENABLED_ST" = "xno" && test "x$ENABLED_ST33" = "xno" && test "x$ENABLED_NUVOTON" = "xno" && \
624-
test "x$ENABLED_NATIONS" = "xno"
635+
test "x$ENABLED_NATIONS" = "xno" && test "x$ENABLED_SEALSQ" = "xno"
625636
then
626637
ENABLED_AUTODETECT=yes
627638
fi
@@ -1192,6 +1203,7 @@ echo " * STM ST33: $ENABLED_ST"
11921203
echo " * Microchip ATTPM20: $ENABLED_MICROCHIP"
11931204
echo " * Nuvoton NPCT75x: $ENABLED_NUVOTON"
11941205
echo " * Nations Tech NS350: $ENABLED_NATIONS"
1206+
echo " * SealSQ QVault: $ENABLED_SEALSQ"
11951207

11961208
echo " * fwTPM Server: $ENABLED_FWTPM"
11971209
echo " * fwTPM Only (no client): $ENABLED_FWTPM_ONLY"

examples/pqc/README.md

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
Examples exercising the ML-DSA / ML-KEM post-quantum additions from TCG
44
TPM 2.0 Library Specification v1.85, wrapped by `wolfTPM2_*` API calls.
55

6-
The examples run against the in-tree fwTPM server. No shipping hardware
7-
TPM firmware implements v1.85 PQC yet. See
8-
[docs/FWTPM.md](../../docs/FWTPM.md#tpm-20-v185-post-quantum-support) for
9-
the full fwTPM PQC reference.
6+
These examples run on the SealSQ QVault TPM — the first shipping TPM 2.0 with
7+
v1.85 post-quantum (ML-DSA / ML-KEM) algorithms in silicon — over SPI, and on
8+
the in-tree fwTPM server for CI or when no hardware is present. Build for the
9+
SealSQ part with `--enable-sealsq --enable-pqc`; see
10+
[docs/FWTPM.md](../../docs/FWTPM.md#tpm-20-v185-post-quantum-support) for the
11+
fwTPM PQC reference.
1012

1113
## Building
1214

@@ -67,6 +69,68 @@ All examples expect a running `fwtpm_server` on `127.0.0.1:2321`:
6769
./src/fwtpm/fwtpm_server --clear &
6870
```
6971

72+
### `pqc_ctrl` — PQC control center
73+
74+
One CLI to drive and validate a PQC TPM (SealSQ QVault, or the fwTPM), modeled
75+
on `examples/spdm/spdm_ctrl`: each command runs an operation and controls the
76+
board. Every key operation flushes the transient object table first, so a TPM
77+
with a small object memory (e.g. SealSQ QVault) does not hit
78+
`TPM_RC_OBJECT_MEMORY` when commands are chained.
79+
80+
```
81+
./examples/pqc/pqc_ctrl # --all (default)
82+
./examples/pqc/pqc_ctrl --caps --algs # identify + list supported algorithms
83+
./examples/pqc/pqc_ctrl --mldsa=87 # ML-DSA-87 sign/verify
84+
./examples/pqc/pqc_ctrl --mlkem=1024 # ML-KEM-1024 encap/decap
85+
./examples/pqc/pqc_ctrl --selftest --getrandom=32 --pcrread=0
86+
```
87+
88+
| Command | Description |
89+
|---|---|
90+
| `--caps` | Manufacturer, vendor string, firmware, FIPS mode |
91+
| `--algs` | List the algorithms the TPM reports as supported |
92+
| `--selftest` | `TPM2_SelfTest` |
93+
| `--getrandom[=N]` | N random bytes (default 16) |
94+
| `--pcrread[=idx]` | Read a PCR (SHA-256 bank, falling back to SHA-384) |
95+
| `--pcrextend=idx` | Extend a PCR with a test digest (explicit index required) |
96+
| `--flush` | Flush transient objects (board reset between ops) |
97+
| `--clear` | `TPM2_Clear` — wipes the owner hierarchy |
98+
| `--mldsa[=44/65/87]` | Pure ML-DSA sign/verify (default 65) |
99+
| `--hash-mldsa[=44/65/87]` | Hash-ML-DSA (SHA-256 pre-hash) sign/verify |
100+
| `--mlkem[=512/768/1024]` | ML-KEM encapsulate/decapsulate |
101+
| `--all` | caps + algs + selftest + getrandom + pcrread + every PQC set |
102+
103+
Commands run left-to-right, so they can be chained. Requires `--enable-v185`
104+
(or `--enable-pqc`). Point it at the SealSQ part with `--enable-sealsq`, or at
105+
the fwTPM with `--enable-fwtpm --enable-swtpm`.
106+
107+
Run the whole command set as a pass/fail suite (mirrors
108+
`examples/spdm/spdm_test.sh`). The destructive `--clear` is opt-in via
109+
`PQC_CTRL_CLEAR=1` so the suite never wipes a TPM unexpectedly:
110+
111+
```
112+
./examples/pqc/pqc_ctrl.sh
113+
PQC_CTRL_CLEAR=1 ./examples/pqc/pqc_ctrl.sh # also exercise TPM2_Clear
114+
```
115+
116+
### Benchmarks on SealSQ QVault silicon
117+
118+
Measured with `examples/bench/bench` on a Raspberry Pi 5 driving the QVault over
119+
SPI (the first post-quantum TPM benchmarks on shipping-class silicon):
120+
121+
| Operation | Avg latency | Throughput |
122+
|---|---|---|
123+
| ML-DSA-65 key gen | 2044.7 ms | 0.49 ops/s |
124+
| ML-DSA-65 sign | 581.0 ms | 1.72 ops/s |
125+
| ML-DSA-65 verify | 163.1 ms | 6.13 ops/s |
126+
| ML-KEM-768 key gen | 800.8 ms | 1.25 ops/s |
127+
| ML-KEM-768 encapsulate | 211.8 ms | 4.72 ops/s |
128+
| ML-KEM-768 decapsulate | 425.5 ms | 2.35 ops/s |
129+
130+
Verification is fast (comparable to ECDSA); key generation is a one-off
131+
provisioning cost. See the top-level `README.md` TPM2 Benchmarks section for the
132+
full classical + PQC run.
133+
70134
### `pqc_mssim_e2e`
71135

72136
End-to-end client test over the mssim socket. Two round-trips:

examples/pqc/include.am

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ examples_pqc_gen_pqc_certs_SOURCES = examples/pqc/gen_pqc_certs.c
3434
examples_pqc_gen_pqc_certs_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD)
3535
examples_pqc_gen_pqc_certs_DEPENDENCIES = src/libwolftpm.la
3636

37+
noinst_PROGRAMS += examples/pqc/pqc_ctrl
38+
examples_pqc_pqc_ctrl_SOURCES = examples/pqc/pqc_ctrl.c
39+
examples_pqc_pqc_ctrl_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD)
40+
examples_pqc_pqc_ctrl_DEPENDENCIES = src/libwolftpm.la
41+
3742
EXTRA_DIST += examples/pqc/README.md
43+
EXTRA_DIST += examples/pqc/pqc_ctrl.sh
3844

3945
endif
4046
endif

0 commit comments

Comments
 (0)