mirror of https://github.com/wolfSSL/wolfTPM.git
Add SealSQ QVault TPM post-quantum support and pqc_ctrl example
parent
46d2d337a4
commit
b57d851156
|
|
@ -28,6 +28,8 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: sealsq-qvault-vendor
|
||||
wolftpm_config: --enable-pqc --enable-sealsq --disable-fwtpm --disable-examples
|
||||
- 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
|
||||
|
|
|
|||
|
|
@ -95,6 +95,13 @@ jobs:
|
|||
- name: ML-KEM encap + decap example (standalone)
|
||||
run: ./examples/pqc/mlkem_encap
|
||||
|
||||
- name: PQC control tool (pqc_ctrl.sh suite + argument validation)
|
||||
# pqc_ctrl.sh runs the full command set and the argument-validation
|
||||
# negatives, exiting non-zero on any failure. PQC_CTRL_CLEAR is left off
|
||||
# so the shared Tier-2 fwtpm_server is not wiped mid-sequence; the
|
||||
# destructive --clear / --pcrextend paths are exercised on hardware.
|
||||
run: ./examples/pqc/pqc_ctrl.sh
|
||||
|
||||
- name: Stop Tier 2 fwtpm_server (free port 2321 for E2E)
|
||||
run: |
|
||||
if [ -f /tmp/fwtpm_server.pid ]; then
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ examples/pqc/mldsa_sign
|
|||
examples/pqc/mlkem_encap
|
||||
examples/pqc/pqc_mssim_e2e
|
||||
examples/pqc/gen_pqc_certs
|
||||
examples/pqc/pqc_ctrl
|
||||
examples/pqc/mldsa_verify_neg
|
||||
examples/pqc/mlkem_decap_neg
|
||||
examples/nvram/extend
|
||||
examples/nvram/store
|
||||
examples/nvram/read
|
||||
|
|
|
|||
50
README.md
50
README.md
|
|
@ -7,7 +7,7 @@ Portable TPM 2.0 project designed for embedded use.
|
|||
|
||||
* This implementation provides all TPM 2.0 API's in compliance with the specification.
|
||||
* 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.
|
||||
* 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.
|
||||
* 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 TPM (first TPM with post-quantum ML-DSA/ML-KEM in silicon).
|
||||
* wolfTPM uses the TPM Interface Specification (TIS) to communicate either over SPI, or using a memory mapped I/O range.
|
||||
* 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.
|
||||
* 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:
|
|||
| Hash-ML-DSA (pre-hash signing) | FIPS 204 | ML-DSA-44 / 65 / 87 with caller hash |
|
||||
| ML-KEM (key encapsulation) | FIPS 203 | ML-KEM-512 / 768 / 1024 |
|
||||
|
||||
The examples run against the in-tree fwTPM server. No shipping hardware
|
||||
TPM firmware implements v1.85 PQC yet; upgrade paths for discrete chips
|
||||
are forward-compatible — the same wrapper API targets both.
|
||||
wolfTPM **officially supports the SealSQ QVault TPM**, the first shipping TPM 2.0
|
||||
with these v1.85 PQC algorithms in silicon. Build for it with `--enable-sealsq
|
||||
--enable-pqc`. The same examples and wrapper API also run against the in-tree
|
||||
fwTPM server for CI or when no hardware is present. See the
|
||||
[TPM2 Benchmarks](#tpm2-benchmarks) section for measured ML-DSA / ML-KEM
|
||||
performance on the QVault TPM.
|
||||
|
||||
### Building
|
||||
|
||||
|
|
@ -144,9 +147,10 @@ make check
|
|||
```
|
||||
|
||||
See [examples/pqc/README.md](examples/pqc/README.md) for per-example
|
||||
details (`pqc_mssim_e2e`, `mlkem_encap`) and PQC options on the
|
||||
general-purpose `keygen`/`keyload` tools (`-mldsa`, `-hash_mldsa`,
|
||||
`-mlkem`).
|
||||
details — the `pqc_ctrl` control center (every PQC operation plus board
|
||||
control in one CLI, with `pqc_ctrl.sh` running the full command set),
|
||||
`pqc_mssim_e2e`, `mlkem_encap`, and PQC options on the general-purpose
|
||||
`keygen`/`keyload` tools (`-mldsa`, `-hash_mldsa`, `-mlkem`).
|
||||
|
||||
For the fwTPM server's PQC internals — the eight v1.85 commands,
|
||||
primary-key derivation, buffer constants, and spec-interpretation
|
||||
|
|
@ -238,6 +242,7 @@ Tested with:
|
|||
* Microchip ATTPM20 module
|
||||
* Nuvoton NPCT65X or NPCT75x TPM2.0 modules
|
||||
* Nations Technologies Z32H330 or NS350 TPM 2.0 modules
|
||||
* SealSQ QVault TPM 2.0 module (SPI, post-quantum ML-DSA / ML-KEM)
|
||||
|
||||
#### Device Identification
|
||||
|
||||
|
|
@ -283,6 +288,10 @@ Nuvoton NPCT750 TPM2.0
|
|||
TPM2: Caps 0x30000697, Did 0x00fc, Vid 0x1050, Rid 0x 1
|
||||
Mfg NTC (0), Vendor NPCT75x"!!4rls, Fw 7.2 (131072), FIPS 140-2 1, CC-EAL4 0
|
||||
|
||||
SealSQ QVault TPM 2.0
|
||||
TPM2: Caps 0x30000797, Did 0x0083, Vid 0x2406, Rid 0x 3
|
||||
Mfg SEAL (6), Vendor QVault TPM, Fw 2.1 (0x3010303), FIPS 140-3, CC-EAL4 0
|
||||
|
||||
## Building
|
||||
|
||||
### Building wolfSSL
|
||||
|
|
@ -346,6 +355,7 @@ make install
|
|||
--enable-microchip Enable Microchip ATTPM20 Support (default: disabled) - WOLFTPM_MICROCHIP
|
||||
--enable-nuvoton Enable Nuvoton NPCT65x/NPCT75x Support (default: disabled) - WOLFTPM_NUVOTON
|
||||
--enable-nations Enable Nations Technology NS350 Support (default: disabled) - WOLFTPM_NATIONS
|
||||
--enable-sealsq Enable SealSQ QVault post-quantum TPM Support (default: disabled) - WOLFTPM_SEALSQ
|
||||
|
||||
--enable-devtpm Enable using Linux kernel driver for /dev/tpmX (default: disabled) - WOLFTPM_LINUX_DEV
|
||||
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
|
|||
ECDHE 256 agree 5 ops took 1.161 sec, avg 232.144 ms, 4.308 ops/sec
|
||||
```
|
||||
|
||||
Run on the SealSQ QVault post-quantum TPM (ML-DSA / ML-KEM) on a Raspberry Pi 5
|
||||
over SPI. These are the first post-quantum TPM benchmarks measured on
|
||||
shipping-class silicon:
|
||||
|
||||
```
|
||||
./examples/bench/bench
|
||||
TPM2 Benchmark using Wrapper API's
|
||||
RNG 10 KB took 1.061 seconds, 9.428 KB/s
|
||||
AES-256-CBC-enc 57 KB took 1.000 seconds, 56.994 KB/s
|
||||
SHA256 43 KB took 1.012 seconds, 42.470 KB/s
|
||||
SHA384 43 KB took 1.024 seconds, 41.984 KB/s
|
||||
RSA 2048 key gen 3 ops took 20.536 sec, avg 6845.188 ms, 0.146 ops/sec
|
||||
RSA 2048 Public 71 ops took 1.015 sec, avg 14.289 ms, 69.985 ops/sec
|
||||
RSA 2048 Private 7 ops took 1.154 sec, avg 164.827 ms, 6.067 ops/sec
|
||||
ECC 256 key gen 4 ops took 1.170 sec, avg 292.538 ms, 3.418 ops/sec
|
||||
ECDSA 256 sign 14 ops took 1.019 sec, avg 72.781 ms, 13.740 ops/sec
|
||||
ECDSA 256 verify 17 ops took 1.031 sec, avg 60.661 ms, 16.485 ops/sec
|
||||
ECDHE 256 agree 5 ops took 1.030 sec, avg 206.022 ms, 4.854 ops/sec
|
||||
ML-DSA 65 key gen 8 ops took 16.357 sec, avg 2044.679 ms, 0.489 ops/sec
|
||||
ML-DSA 65 sign 2 ops took 1.162 sec, avg 581.025 ms, 1.721 ops/sec
|
||||
ML-DSA 65 verify 7 ops took 1.142 sec, avg 163.118 ms, 6.131 ops/sec
|
||||
ML-KEM 768 key gen 19 ops took 15.216 sec, avg 800.819 ms, 1.249 ops/sec
|
||||
ML-KEM 768 encap 5 ops took 1.059 sec, avg 211.777 ms, 4.722 ops/sec
|
||||
ML-KEM 768 decap 3 ops took 1.276 sec, avg 425.471 ms, 2.350 ops/sec
|
||||
```
|
||||
|
||||
Run on Infineon OPTIGA SLB9672 at 43MHz:
|
||||
|
||||
```
|
||||
|
|
|
|||
16
configure.ac
16
configure.ac
|
|
@ -253,7 +253,7 @@ fi
|
|||
WOLFTPM_HW_SELECTED=no
|
||||
for _wt_v in "$enable_infineon" "$enable_st" "$enable_st33" \
|
||||
"$enable_microchip" "$enable_mchp" \
|
||||
"$enable_nuvoton" "$enable_nations" \
|
||||
"$enable_nuvoton" "$enable_nations" "$enable_sealsq" \
|
||||
"$enable_spi" "$enable_i2c" "$enable_mmio" \
|
||||
"$enable_devtpm" "$enable_autodetect" \
|
||||
"$enable_winapi" "$enable_wintbs"; do
|
||||
|
|
@ -540,6 +540,17 @@ then
|
|||
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_NATIONS"
|
||||
fi
|
||||
|
||||
# SealSQ QVault TPM (post-quantum ML-DSA/ML-KEM)
|
||||
AC_ARG_ENABLE([sealsq],
|
||||
[AS_HELP_STRING([--enable-sealsq],[Enable SealSQ QVault TPM Support (default: disabled)])],
|
||||
[ ENABLED_SEALSQ=$enableval ],
|
||||
[ ENABLED_SEALSQ=no ]
|
||||
)
|
||||
if test "x$ENABLED_SEALSQ" = "xyes"
|
||||
then
|
||||
AM_CFLAGS="$AM_CFLAGS -DWOLFTPM_SEALSQ"
|
||||
fi
|
||||
|
||||
# Infineon SLB9670/SLB9672/SLB9673
|
||||
AC_ARG_ENABLE([infineon],
|
||||
[AS_HELP_STRING([--enable-infineon],[Enable Infineon SLB9670/SLB9672 TPM Support (default: disabled)])],
|
||||
|
|
@ -621,7 +632,7 @@ then
|
|||
# If a module hasn't been selected then enable auto-detection
|
||||
if test "x$ENABLED_INFINEON" = "xno" && test "x$ENABLED_MCHP" = "xno" && test "x$ENABLED_MICROCHIP" = "xno" && \
|
||||
test "x$ENABLED_ST" = "xno" && test "x$ENABLED_ST33" = "xno" && test "x$ENABLED_NUVOTON" = "xno" && \
|
||||
test "x$ENABLED_NATIONS" = "xno"
|
||||
test "x$ENABLED_NATIONS" = "xno" && test "x$ENABLED_SEALSQ" = "xno"
|
||||
then
|
||||
ENABLED_AUTODETECT=yes
|
||||
fi
|
||||
|
|
@ -1192,6 +1203,7 @@ echo " * STM ST33: $ENABLED_ST"
|
|||
echo " * Microchip ATTPM20: $ENABLED_MICROCHIP"
|
||||
echo " * Nuvoton NPCT75x: $ENABLED_NUVOTON"
|
||||
echo " * Nations Tech NS350: $ENABLED_NATIONS"
|
||||
echo " * SealSQ QVault TPM: $ENABLED_SEALSQ"
|
||||
|
||||
echo " * fwTPM Server: $ENABLED_FWTPM"
|
||||
echo " * fwTPM Only (no client): $ENABLED_FWTPM_ONLY"
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@
|
|||
Examples exercising the ML-DSA / ML-KEM post-quantum additions from TCG
|
||||
TPM 2.0 Library Specification v1.85, wrapped by `wolfTPM2_*` API calls.
|
||||
|
||||
The examples run against the in-tree fwTPM server. No shipping hardware
|
||||
TPM firmware implements v1.85 PQC yet. See
|
||||
[docs/FWTPM.md](../../docs/FWTPM.md#tpm-20-v185-post-quantum-support) for
|
||||
the full fwTPM PQC reference.
|
||||
These examples run on the SealSQ QVault TPM — the first shipping TPM 2.0 with
|
||||
v1.85 post-quantum (ML-DSA / ML-KEM) algorithms in silicon — over SPI, and on
|
||||
the in-tree fwTPM server for CI or when no hardware is present. Build for the
|
||||
SealSQ part with `--enable-sealsq --enable-pqc`; see
|
||||
[docs/FWTPM.md](../../docs/FWTPM.md#tpm-20-v185-post-quantum-support) for the
|
||||
fwTPM PQC reference.
|
||||
|
||||
## Building
|
||||
|
||||
|
|
@ -67,6 +69,68 @@ All examples expect a running `fwtpm_server` on `127.0.0.1:2321`:
|
|||
./src/fwtpm/fwtpm_server --clear &
|
||||
```
|
||||
|
||||
### `pqc_ctrl` — PQC control center
|
||||
|
||||
One CLI to drive and validate a PQC TPM (SealSQ QVault TPM, or the fwTPM),
|
||||
modeled on `examples/spdm/spdm_ctrl`: each command runs an operation and
|
||||
controls the board. Every key operation flushes the transient object table
|
||||
first, so a TPM with a small object memory (e.g. SealSQ QVault TPM) does not
|
||||
hit `TPM_RC_OBJECT_MEMORY` when commands are chained.
|
||||
|
||||
```
|
||||
./examples/pqc/pqc_ctrl # --all (default)
|
||||
./examples/pqc/pqc_ctrl --caps --algs # identify + list supported algorithms
|
||||
./examples/pqc/pqc_ctrl --mldsa=87 # ML-DSA-87 sign/verify
|
||||
./examples/pqc/pqc_ctrl --mlkem=1024 # ML-KEM-1024 encap/decap
|
||||
./examples/pqc/pqc_ctrl --selftest --getrandom=32 --pcrread=0
|
||||
```
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `--caps` | Manufacturer, vendor string, firmware, FIPS mode |
|
||||
| `--algs` | List the algorithms the TPM reports as supported |
|
||||
| `--selftest` | `TPM2_SelfTest` |
|
||||
| `--getrandom[=N]` | N random bytes (default 16) |
|
||||
| `--pcrread[=idx]` | Read a PCR (SHA-256 bank, falling back to SHA-384) |
|
||||
| `--pcrextend=idx` | Extend a PCR with a test digest (explicit index required) |
|
||||
| `--flush` | Flush transient objects (board reset between ops) |
|
||||
| `--clear` | `TPM2_Clear` — wipes the owner hierarchy |
|
||||
| `--mldsa[=44/65/87]` | Pure ML-DSA sign/verify (default 65) |
|
||||
| `--hash-mldsa[=44/65/87]` | Hash-ML-DSA (SHA-256 pre-hash) sign/verify |
|
||||
| `--mlkem[=512/768/1024]` | ML-KEM encapsulate/decapsulate |
|
||||
| `--all` | caps + algs + selftest + getrandom + pcrread + every PQC set |
|
||||
|
||||
Commands run left-to-right, so they can be chained. Requires `--enable-v185`
|
||||
(or `--enable-pqc`). Point it at the SealSQ part with `--enable-sealsq`, or at
|
||||
the fwTPM with `--enable-fwtpm --enable-swtpm`.
|
||||
|
||||
Run the whole command set as a pass/fail suite (mirrors
|
||||
`examples/spdm/spdm_test.sh`). The destructive `--clear` is opt-in via
|
||||
`PQC_CTRL_CLEAR=1` so the suite never wipes a TPM unexpectedly:
|
||||
|
||||
```
|
||||
./examples/pqc/pqc_ctrl.sh
|
||||
PQC_CTRL_CLEAR=1 ./examples/pqc/pqc_ctrl.sh # also exercise TPM2_Clear
|
||||
```
|
||||
|
||||
### Benchmarks on SealSQ QVault TPM silicon
|
||||
|
||||
Measured with `examples/bench/bench` on a Raspberry Pi 5 driving the QVault TPM
|
||||
over SPI (the first post-quantum TPM benchmarks on shipping-class silicon):
|
||||
|
||||
| Operation | Avg latency | Throughput |
|
||||
|---|---|---|
|
||||
| ML-DSA-65 key gen | 2044.7 ms | 0.49 ops/s |
|
||||
| ML-DSA-65 sign | 581.0 ms | 1.72 ops/s |
|
||||
| ML-DSA-65 verify | 163.1 ms | 6.13 ops/s |
|
||||
| ML-KEM-768 key gen | 800.8 ms | 1.25 ops/s |
|
||||
| ML-KEM-768 encapsulate | 211.8 ms | 4.72 ops/s |
|
||||
| ML-KEM-768 decapsulate | 425.5 ms | 2.35 ops/s |
|
||||
|
||||
Verification is fast (comparable to ECDSA); key generation is a one-off
|
||||
provisioning cost. See the top-level `README.md` TPM2 Benchmarks section for the
|
||||
full classical + PQC run.
|
||||
|
||||
### `pqc_mssim_e2e`
|
||||
|
||||
End-to-end client test over the mssim socket. Two round-trips:
|
||||
|
|
|
|||
|
|
@ -34,7 +34,13 @@ examples_pqc_gen_pqc_certs_SOURCES = examples/pqc/gen_pqc_certs.c
|
|||
examples_pqc_gen_pqc_certs_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD)
|
||||
examples_pqc_gen_pqc_certs_DEPENDENCIES = src/libwolftpm.la
|
||||
|
||||
noinst_PROGRAMS += examples/pqc/pqc_ctrl
|
||||
examples_pqc_pqc_ctrl_SOURCES = examples/pqc/pqc_ctrl.c
|
||||
examples_pqc_pqc_ctrl_LDADD = src/libwolftpm.la $(LIB_STATIC_ADD)
|
||||
examples_pqc_pqc_ctrl_DEPENDENCIES = src/libwolftpm.la
|
||||
|
||||
EXTRA_DIST += examples/pqc/README.md
|
||||
EXTRA_DIST += examples/pqc/pqc_ctrl.sh
|
||||
|
||||
endif
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,808 @@
|
|||
/* pqc_ctrl.c
|
||||
*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfTPM.
|
||||
*
|
||||
* wolfTPM is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfTPM is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
/* Example: single control/validation tool for the TPM 2.0 v1.85 post-quantum
|
||||
* algorithms (ML-DSA, Hash-ML-DSA, ML-KEM), modeled on examples/spdm/spdm_ctrl.
|
||||
* One CLI runs each operation and controls the board. Each key operation
|
||||
* reclaims loaded transient objects first, so a TPM with a small transient-
|
||||
* object table (e.g. SealSQ QVault TPM) does not hit TPM_RC_OBJECT_MEMORY
|
||||
* when key operations are chained. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <wolftpm/tpm2_wrap.h>
|
||||
#include <hal/tpm_io.h>
|
||||
#include <examples/tpm_test.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && \
|
||||
defined(WOLFTPM_MLDSA_SIGN) && defined(WOLFTPM_MLDSA_VERIFY) && \
|
||||
defined(WOLFTPM_MLKEM_ENCAP) && defined(WOLFTPM_MLKEM_DECAP) && \
|
||||
defined(WOLFTPM_HASH_MLDSA)
|
||||
|
||||
static const byte gPqcMsg[] = "wolfTPM PQC control example message";
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Expected usage:\n");
|
||||
printf("./examples/pqc/pqc_ctrl [command(s)]\n");
|
||||
printf("Commands run in the given order; default (no args) is --all:\n");
|
||||
printf("* --caps Print TPM caps (vendor, FW, FIPS)\n");
|
||||
printf("* --algs List supported algorithms\n");
|
||||
printf("* --selftest Run TPM2_SelfTest\n");
|
||||
printf("* --getrandom[=N] Get N random bytes (default 16)\n");
|
||||
printf("* --pcrread[=idx] Read PCR index (default 0)\n");
|
||||
printf("* --pcrextend=idx Extend PCR (explicit index required)\n");
|
||||
printf("* --flush Flush transient objects (board reset)\n");
|
||||
printf("* --clear TPM2_Clear (wipes owner hierarchy!)\n");
|
||||
printf("* --mldsa[=44|65|87] Pure ML-DSA sign/verify (default 65)\n");
|
||||
printf("* --hash-mldsa[=44|65|87] Hash-ML-DSA (SHA-256) sign/verify\n");
|
||||
printf("* --mlkem[=512|768|1024] ML-KEM encapsulate/decapsulate\n");
|
||||
printf("* --all caps + every parameter set of each op\n");
|
||||
}
|
||||
|
||||
/* Empty value means "bare flag, use default set"; any other non-matching
|
||||
* value is rejected rather than silently defaulted. A NULL ps validates only. */
|
||||
static int parseMldsaSet(const char* val, TPMI_MLDSA_PARAMETER_SET* ps)
|
||||
{
|
||||
TPMI_MLDSA_PARAMETER_SET set;
|
||||
|
||||
if (val[0] == '\0' || XSTRCMP(val, "65") == 0) {
|
||||
set = TPM_MLDSA_65;
|
||||
}
|
||||
else if (XSTRCMP(val, "44") == 0) {
|
||||
set = TPM_MLDSA_44;
|
||||
}
|
||||
else if (XSTRCMP(val, "87") == 0) {
|
||||
set = TPM_MLDSA_87;
|
||||
}
|
||||
else {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
if (ps != NULL) {
|
||||
*ps = set;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parseMlkemSet(const char* val, TPMI_MLKEM_PARAMETER_SET* ps)
|
||||
{
|
||||
TPMI_MLKEM_PARAMETER_SET set;
|
||||
|
||||
if (val[0] == '\0' || XSTRCMP(val, "768") == 0) {
|
||||
set = TPM_MLKEM_768;
|
||||
}
|
||||
else if (XSTRCMP(val, "512") == 0) {
|
||||
set = TPM_MLKEM_512;
|
||||
}
|
||||
else if (XSTRCMP(val, "1024") == 0) {
|
||||
set = TPM_MLKEM_1024;
|
||||
}
|
||||
else {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
if (ps != NULL) {
|
||||
*ps = set;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* mldsaName(TPMI_MLDSA_PARAMETER_SET ps)
|
||||
{
|
||||
return (ps == TPM_MLDSA_44) ? "44" : (ps == TPM_MLDSA_87) ? "87" : "65";
|
||||
}
|
||||
|
||||
static const char* mlkemName(TPMI_MLKEM_PARAMETER_SET ps)
|
||||
{
|
||||
return (ps == TPM_MLKEM_512) ? "512" : (ps == TPM_MLKEM_1024) ? "1024" :
|
||||
"768";
|
||||
}
|
||||
|
||||
/* Reclaim the transient object table by flushing exactly the handles the TPM
|
||||
* reports as loaded. Parts with a small object table (e.g. SealSQ QVault TPM)
|
||||
* hit TPM_RC_OBJECT_MEMORY when a key plus a sign/verify sequence object are
|
||||
* live at once. wolfTPM2_UnloadHandles_AllTransient() only covers
|
||||
* MAX_HANDLE_NUM (3) fixed handles, so query the actual loaded set instead. */
|
||||
static int pqc_flush_transient(WOLFTPM2_DEV* dev)
|
||||
{
|
||||
int rc;
|
||||
word32 i;
|
||||
GetCapability_In in;
|
||||
GetCapability_Out out;
|
||||
FlushContext_In flushCtx;
|
||||
|
||||
(void)dev; /* operates on the active TPM context */
|
||||
XMEMSET(&in, 0, sizeof(in));
|
||||
XMEMSET(&out, 0, sizeof(out));
|
||||
XMEMSET(&flushCtx, 0, sizeof(flushCtx));
|
||||
in.capability = TPM_CAP_HANDLES;
|
||||
in.property = TRANSIENT_FIRST;
|
||||
in.propertyCount = MAX_CAP_HANDLES;
|
||||
rc = TPM2_GetCapability(&in, &out);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
return rc;
|
||||
}
|
||||
if (out.capabilityData.capability != TPM_CAP_HANDLES) {
|
||||
return TPM_RC_FAILURE;
|
||||
}
|
||||
for (i = 0; i < out.capabilityData.data.handles.count; i++) {
|
||||
flushCtx.flushHandle = out.capabilityData.data.handles.handle[i];
|
||||
(void)TPM2_FlushContext(&flushCtx);
|
||||
}
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
|
||||
static int do_caps(WOLFTPM2_DEV* dev)
|
||||
{
|
||||
int rc;
|
||||
WOLFTPM2_CAPS caps;
|
||||
|
||||
XMEMSET(&caps, 0, sizeof(caps));
|
||||
|
||||
rc = wolfTPM2_GetCapabilities(dev, &caps);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("GetCapabilities failed 0x%x: %s\n",
|
||||
rc, wolfTPM2_GetRCString(rc));
|
||||
return rc;
|
||||
}
|
||||
printf("Caps: Mfg %s, Vendor %s, Fw %u.%u, FIPS %s\n",
|
||||
caps.mfgStr, caps.vendorStr, caps.fwVerMajor, caps.fwVerMinor,
|
||||
TPM2_GetCapsFipsStr(caps.fips140_3, caps.fips140_2));
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const char* algName(TPM_ALG_ID alg)
|
||||
{
|
||||
switch (alg) {
|
||||
case TPM_ALG_RSA: return "RSA";
|
||||
case TPM_ALG_SHA1: return "SHA1";
|
||||
case TPM_ALG_SHA256: return "SHA256";
|
||||
case TPM_ALG_SHA384: return "SHA384";
|
||||
case TPM_ALG_SHA512: return "SHA512";
|
||||
case TPM_ALG_AES: return "AES";
|
||||
case TPM_ALG_ECC: return "ECC";
|
||||
case TPM_ALG_HMAC: return "HMAC";
|
||||
case TPM_ALG_KEYEDHASH: return "KEYEDHASH";
|
||||
#ifdef WOLFTPM_PQC
|
||||
case TPM_ALG_MLKEM: return "ML-KEM";
|
||||
case TPM_ALG_MLDSA: return "ML-DSA";
|
||||
case TPM_ALG_HASH_MLDSA: return "Hash-ML-DSA";
|
||||
#endif
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int do_algs(WOLFTPM2_DEV* dev)
|
||||
{
|
||||
int rc;
|
||||
word32 i;
|
||||
word32 count;
|
||||
const char* name;
|
||||
TPM_ALG_ID alg;
|
||||
GetCapability_In in;
|
||||
GetCapability_Out out;
|
||||
|
||||
(void)dev;
|
||||
XMEMSET(&in, 0, sizeof(in));
|
||||
XMEMSET(&out, 0, sizeof(out));
|
||||
in.capability = TPM_CAP_ALGS;
|
||||
in.property = TPM_ALG_FIRST; /* spec-conformant start; 0 is TPM_RC_VALUE */
|
||||
/* MAX_CAP_ALGS covers the whole algorithm range in one request. Paging is
|
||||
* avoided deliberately: TPM_CAP_ALGS advances by a property cursor that
|
||||
* assumes ascending IDs, but some parts return the list unordered. */
|
||||
in.propertyCount = MAX_CAP_ALGS;
|
||||
|
||||
rc = TPM2_GetCapability(&in, &out);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("GetCapability(ALGS) failed 0x%x: %s\n",
|
||||
rc, wolfTPM2_GetRCString(rc));
|
||||
return rc;
|
||||
}
|
||||
if (out.capabilityData.capability != TPM_CAP_ALGS) {
|
||||
printf("GetCapability(ALGS) returned unexpected capability 0x%x\n",
|
||||
(unsigned)out.capabilityData.capability);
|
||||
return TPM_RC_FAILURE;
|
||||
}
|
||||
count = out.capabilityData.data.algorithms.count;
|
||||
printf("Supported algorithms (%u):\n", (unsigned)count);
|
||||
for (i = 0; i < count; i++) {
|
||||
alg = out.capabilityData.data.algorithms.algProperties[i].alg;
|
||||
name = algName(alg);
|
||||
if (name != NULL) {
|
||||
printf(" 0x%04x %s\n", (unsigned)alg, name);
|
||||
}
|
||||
else {
|
||||
printf(" 0x%04x\n", (unsigned)alg);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_selftest(WOLFTPM2_DEV* dev)
|
||||
{
|
||||
int rc = wolfTPM2_SelfTest(dev);
|
||||
if (rc == TPM_RC_SUCCESS)
|
||||
printf("PASS SelfTest\n");
|
||||
else
|
||||
printf("FAIL SelfTest 0x%x: %s\n", rc, wolfTPM2_GetRCString(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_getrandom(WOLFTPM2_DEV* dev, int len)
|
||||
{
|
||||
int rc;
|
||||
int i;
|
||||
byte buf[64];
|
||||
|
||||
if (len <= 0 || len > (int)sizeof(buf)) {
|
||||
printf("FAIL GetRandom invalid length %d (max %d)\n",
|
||||
len, (int)sizeof(buf));
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
XMEMSET(buf, 0, sizeof(buf));
|
||||
|
||||
rc = wolfTPM2_GetRandom(dev, buf, (word32)len);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("FAIL GetRandom 0x%x: %s\n", rc, wolfTPM2_GetRCString(rc));
|
||||
return rc;
|
||||
}
|
||||
printf("Random %d bytes: ", len);
|
||||
for (i = 0; i < len; i++)
|
||||
printf("%02x", buf[i]);
|
||||
printf("\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Read a PCR, trying the SHA-256 bank first then SHA-384 (a PQC part such as
|
||||
* SealSQ QVault TPM allocates only the SHA-384 bank). */
|
||||
static int do_pcrread(WOLFTPM2_DEV* dev, int idx)
|
||||
{
|
||||
int rc;
|
||||
int i;
|
||||
int hashAlg = TPM_ALG_SHA256;
|
||||
int digestSz = 0;
|
||||
byte digest[TPM_MAX_DIGEST_SIZE];
|
||||
|
||||
XMEMSET(digest, 0, sizeof(digest));
|
||||
digestSz = (int)sizeof(digest);
|
||||
rc = wolfTPM2_ReadPCR(dev, idx, hashAlg, digest, &digestSz);
|
||||
/* Fall back to SHA-384 when SHA-256 errors or returns an empty bank. */
|
||||
if (rc != TPM_RC_SUCCESS || digestSz == 0) {
|
||||
hashAlg = TPM_ALG_SHA384;
|
||||
digestSz = (int)sizeof(digest);
|
||||
rc = wolfTPM2_ReadPCR(dev, idx, hashAlg, digest, &digestSz);
|
||||
}
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("FAIL PCR%d read 0x%x: %s\n",
|
||||
idx, rc, wolfTPM2_GetRCString(rc));
|
||||
return rc;
|
||||
}
|
||||
if (digestSz == 0) {
|
||||
printf("FAIL PCR%d read no digest returned (no allocated bank)\n", idx);
|
||||
return TPM_RC_FAILURE;
|
||||
}
|
||||
printf("PCR%d (%s): ", idx, hashAlg == TPM_ALG_SHA384 ? "SHA384" : "SHA256");
|
||||
for (i = 0; i < digestSz; i++)
|
||||
printf("%02x", digest[i]);
|
||||
printf("\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* PCR extend is irreversible, so extend exactly one bank: the TPM's first
|
||||
* bank that actually has PCRs allocated (a part may allocate only SHA-384).
|
||||
* Never fall back to a second extend, which would touch two banks. */
|
||||
static int do_pcrextend(WOLFTPM2_DEV* dev, int idx)
|
||||
{
|
||||
int rc;
|
||||
int hashAlg;
|
||||
int digestSz;
|
||||
word32 b;
|
||||
TPML_PCR_SELECTION* banks;
|
||||
GetCapability_In in;
|
||||
GetCapability_Out out;
|
||||
byte digest[TPM_MAX_DIGEST_SIZE];
|
||||
|
||||
XMEMSET(&in, 0, sizeof(in));
|
||||
XMEMSET(&out, 0, sizeof(out));
|
||||
in.capability = TPM_CAP_PCRS;
|
||||
in.property = 0;
|
||||
in.propertyCount = HASH_COUNT; /* return every assigned bank, not just one */
|
||||
rc = TPM2_GetCapability(&in, &out);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("FAIL PCR%d extend 0x%x: %s\n",
|
||||
idx, rc, wolfTPM2_GetRCString(rc));
|
||||
return rc;
|
||||
}
|
||||
banks = &out.capabilityData.data.assignedPCR;
|
||||
if (out.capabilityData.capability != TPM_CAP_PCRS || banks->count == 0) {
|
||||
printf("FAIL PCR%d extend no assigned PCR bank\n", idx);
|
||||
return TPM_RC_FAILURE;
|
||||
}
|
||||
|
||||
/* Pick the first bank that has the requested PCR index allocated. */
|
||||
hashAlg = TPM_ALG_ERROR;
|
||||
for (b = 0; b < banks->count && hashAlg == TPM_ALG_ERROR; b++) {
|
||||
if ((idx / 8) < (int)banks->pcrSelections[b].sizeofSelect &&
|
||||
(banks->pcrSelections[b].pcrSelect[idx / 8] &
|
||||
(1 << (idx % 8))) != 0) {
|
||||
hashAlg = banks->pcrSelections[b].hash;
|
||||
}
|
||||
}
|
||||
if (hashAlg == TPM_ALG_ERROR) {
|
||||
printf("FAIL PCR%d extend index not allocated in any bank\n", idx);
|
||||
return TPM_RC_FAILURE;
|
||||
}
|
||||
digestSz = TPM2_GetHashDigestSize((TPMI_ALG_HASH)hashAlg);
|
||||
if (digestSz <= 0 || digestSz > (int)sizeof(digest)) {
|
||||
printf("FAIL PCR%d extend unsupported bank 0x%x\n", idx, hashAlg);
|
||||
return TPM_RC_FAILURE;
|
||||
}
|
||||
|
||||
XMEMSET(digest, 0x11, sizeof(digest));
|
||||
rc = wolfTPM2_ExtendPCR(dev, idx, hashAlg, digest, digestSz);
|
||||
if (rc == TPM_RC_SUCCESS) {
|
||||
printf("PASS PCR%d extend (bank 0x%x)\n", idx, hashAlg);
|
||||
}
|
||||
else {
|
||||
printf("FAIL PCR%d extend 0x%x: %s\n",
|
||||
idx, rc, wolfTPM2_GetRCString(rc));
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_clear(WOLFTPM2_DEV* dev)
|
||||
{
|
||||
int rc;
|
||||
|
||||
printf("WARNING: TPM2_Clear wipes the owner hierarchy and persistent keys\n");
|
||||
rc = wolfTPM2_Clear(dev);
|
||||
if (rc == TPM_RC_SUCCESS)
|
||||
printf("PASS Clear (TPM reset to defaults)\n");
|
||||
else
|
||||
printf("FAIL Clear 0x%x: %s\n", rc, wolfTPM2_GetRCString(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_mldsa(WOLFTPM2_DEV* dev, TPMI_MLDSA_PARAMETER_SET ps)
|
||||
{
|
||||
int rc;
|
||||
WOLFTPM2_KEY key;
|
||||
TPMT_PUBLIC tpl;
|
||||
TPM_HANDLE seq = 0;
|
||||
TPMT_TK_VERIFIED validation;
|
||||
FlushContext_In flushCtx;
|
||||
byte* sig = NULL;
|
||||
int sigSz = MAX_MLDSA_SIG_SIZE;
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
XMEMSET(&tpl, 0, sizeof(tpl));
|
||||
XMEMSET(&validation, 0, sizeof(validation));
|
||||
XMEMSET(&flushCtx, 0, sizeof(flushCtx));
|
||||
|
||||
(void)pqc_flush_transient(dev);
|
||||
|
||||
sig = (byte*)XMALLOC(MAX_MLDSA_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (sig == NULL)
|
||||
return MEMORY_E;
|
||||
|
||||
rc = wolfTPM2_GetKeyTemplate_MLDSA(&tpl,
|
||||
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
||||
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
||||
TPMA_OBJECT_noDA, ps, 0);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
rc = wolfTPM2_CreatePrimaryKey(dev, &key, TPM_RH_OWNER, &tpl, NULL, 0);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
rc = wolfTPM2_SignSequenceStart(dev, &key, NULL, 0, &seq);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
rc = wolfTPM2_SignSequenceComplete(dev, seq, &key,
|
||||
gPqcMsg, (int)sizeof(gPqcMsg) - 1, sig, &sigSz);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
seq = 0; /* Complete consumed the sequence object */
|
||||
|
||||
rc = wolfTPM2_VerifySequenceStart(dev, &key, NULL, 0, &seq);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
rc = wolfTPM2_VerifySequenceUpdate(dev, seq,
|
||||
gPqcMsg, (int)sizeof(gPqcMsg) - 1);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
rc = wolfTPM2_VerifySequenceComplete(dev, seq, &key, NULL, 0,
|
||||
sig, sigSz, &validation);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
seq = 0; /* Complete consumed the sequence object */
|
||||
|
||||
if (validation.tag != TPM_ST_MESSAGE_VERIFIED) {
|
||||
printf("ML-DSA-%s verify: unexpected ticket tag 0x%x\n",
|
||||
mldsaName(ps), (unsigned)validation.tag);
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
|
||||
exit:
|
||||
if (rc == TPM_RC_SUCCESS) {
|
||||
printf("PASS ML-DSA-%-3s sign+verify (sig %d bytes)\n",
|
||||
mldsaName(ps), sigSz);
|
||||
}
|
||||
else {
|
||||
printf("FAIL ML-DSA-%-3s 0x%x: %s\n",
|
||||
mldsaName(ps), rc, wolfTPM2_GetRCString(rc));
|
||||
}
|
||||
if (seq != 0) {
|
||||
flushCtx.flushHandle = seq;
|
||||
(void)TPM2_FlushContext(&flushCtx);
|
||||
}
|
||||
wolfTPM2_UnloadHandle(dev, &key.handle);
|
||||
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_hash_mldsa(WOLFTPM2_DEV* dev, TPMI_MLDSA_PARAMETER_SET ps)
|
||||
{
|
||||
int rc;
|
||||
WOLFTPM2_KEY key;
|
||||
TPMT_PUBLIC tpl;
|
||||
TPMT_TK_VERIFIED validation;
|
||||
byte digest[TPM_SHA256_DIGEST_SIZE];
|
||||
byte* sig = NULL;
|
||||
int sigSz = MAX_MLDSA_SIG_SIZE;
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
XMEMSET(&tpl, 0, sizeof(tpl));
|
||||
XMEMSET(&validation, 0, sizeof(validation));
|
||||
XMEMSET(digest, 0xAA, sizeof(digest));
|
||||
|
||||
(void)pqc_flush_transient(dev);
|
||||
|
||||
sig = (byte*)XMALLOC(MAX_MLDSA_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (sig == NULL)
|
||||
return MEMORY_E;
|
||||
|
||||
rc = wolfTPM2_GetKeyTemplate_HASH_MLDSA(&tpl,
|
||||
TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
||||
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
||||
TPMA_OBJECT_noDA, ps, TPM_ALG_SHA256);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
rc = wolfTPM2_CreatePrimaryKey(dev, &key, TPM_RH_OWNER, &tpl, NULL, 0);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
rc = wolfTPM2_SignDigest(dev, &key, digest, (int)sizeof(digest),
|
||||
NULL, 0, sig, &sigSz);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
rc = wolfTPM2_VerifyDigestSignature(dev, &key, digest, (int)sizeof(digest),
|
||||
sig, sigSz, NULL, 0, &validation);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
if (validation.tag != TPM_ST_DIGEST_VERIFIED) {
|
||||
printf("HashML-DSA-%s verify: unexpected ticket tag 0x%x\n",
|
||||
mldsaName(ps), (unsigned)validation.tag);
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
|
||||
exit:
|
||||
if (rc == TPM_RC_SUCCESS) {
|
||||
printf("PASS HashML-DSA-%-3s signdigest+verify (sig %d bytes)\n",
|
||||
mldsaName(ps), sigSz);
|
||||
}
|
||||
else {
|
||||
printf("FAIL HashML-DSA-%-3s 0x%x: %s\n",
|
||||
mldsaName(ps), rc, wolfTPM2_GetRCString(rc));
|
||||
}
|
||||
wolfTPM2_UnloadHandle(dev, &key.handle);
|
||||
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int do_mlkem(WOLFTPM2_DEV* dev, TPMI_MLKEM_PARAMETER_SET ps)
|
||||
{
|
||||
int rc;
|
||||
WOLFTPM2_KEY key;
|
||||
TPMT_PUBLIC tpl;
|
||||
byte ss1[64];
|
||||
byte ss2[64];
|
||||
int ss1Sz = (int)sizeof(ss1);
|
||||
int ss2Sz = (int)sizeof(ss2);
|
||||
byte* ct = NULL;
|
||||
int ctSz = MAX_MLKEM_CT_SIZE;
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
XMEMSET(&tpl, 0, sizeof(tpl));
|
||||
XMEMSET(ss1, 0, sizeof(ss1));
|
||||
XMEMSET(ss2, 0, sizeof(ss2));
|
||||
|
||||
(void)pqc_flush_transient(dev);
|
||||
|
||||
ct = (byte*)XMALLOC(MAX_MLKEM_CT_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (ct == NULL)
|
||||
return MEMORY_E;
|
||||
|
||||
rc = wolfTPM2_GetKeyTemplate_MLKEM(&tpl,
|
||||
TPMA_OBJECT_decrypt | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent |
|
||||
TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
|
||||
TPMA_OBJECT_noDA, ps);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
rc = wolfTPM2_CreatePrimaryKey(dev, &key, TPM_RH_OWNER, &tpl, NULL, 0);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
rc = wolfTPM2_Encapsulate(dev, &key, ct, &ctSz, ss1, &ss1Sz);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
rc = wolfTPM2_Decapsulate(dev, &key, ct, ctSz, ss2, &ss2Sz);
|
||||
if (rc != TPM_RC_SUCCESS) goto exit;
|
||||
|
||||
if (ss1Sz <= 0 || ss1Sz != ss2Sz || XMEMCMP(ss1, ss2, ss1Sz) != 0) {
|
||||
printf("ML-KEM-%s: decapsulated secret does not match\n",
|
||||
mlkemName(ps));
|
||||
rc = TPM_RC_FAILURE;
|
||||
}
|
||||
|
||||
exit:
|
||||
if (rc == TPM_RC_SUCCESS) {
|
||||
printf("PASS ML-KEM-%-3s encap+decap (ct %d bytes)\n",
|
||||
mlkemName(ps), ctSz);
|
||||
}
|
||||
else {
|
||||
printf("FAIL ML-KEM-%-3s 0x%x: %s\n",
|
||||
mlkemName(ps), rc, wolfTPM2_GetRCString(rc));
|
||||
}
|
||||
wc_ForceZero(ss1, sizeof(ss1));
|
||||
wc_ForceZero(ss2, sizeof(ss2));
|
||||
wolfTPM2_UnloadHandle(dev, &key.handle);
|
||||
XFREE(ct, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Run one --all step and keep the first non-zero return code. */
|
||||
static void allStep(int rc, int* firstErr)
|
||||
{
|
||||
if (rc != 0 && *firstErr == 0) {
|
||||
*firstErr = rc;
|
||||
}
|
||||
}
|
||||
|
||||
static int do_all(WOLFTPM2_DEV* dev)
|
||||
{
|
||||
int firstErr = 0;
|
||||
|
||||
printf("=== TPM control + PQC full matrix ===\n");
|
||||
allStep(do_caps(dev), &firstErr);
|
||||
allStep(do_algs(dev), &firstErr);
|
||||
allStep(do_selftest(dev), &firstErr);
|
||||
allStep(do_getrandom(dev, 16), &firstErr);
|
||||
allStep(do_pcrread(dev, 0), &firstErr);
|
||||
allStep(do_mldsa(dev, TPM_MLDSA_44), &firstErr);
|
||||
allStep(do_mldsa(dev, TPM_MLDSA_65), &firstErr);
|
||||
allStep(do_mldsa(dev, TPM_MLDSA_87), &firstErr);
|
||||
allStep(do_hash_mldsa(dev, TPM_MLDSA_44), &firstErr);
|
||||
allStep(do_hash_mldsa(dev, TPM_MLDSA_65), &firstErr);
|
||||
allStep(do_hash_mldsa(dev, TPM_MLDSA_87), &firstErr);
|
||||
allStep(do_mlkem(dev, TPM_MLKEM_512), &firstErr);
|
||||
allStep(do_mlkem(dev, TPM_MLKEM_768), &firstErr);
|
||||
allStep(do_mlkem(dev, TPM_MLKEM_1024), &firstErr);
|
||||
return firstErr;
|
||||
}
|
||||
|
||||
/* Match "name" (bare flag, sets val="") or "name=VALUE" (sets val=VALUE).
|
||||
* Returns 0 when arg is a different option, so a missing '=' such as
|
||||
* "--pcrextend16" does not match and is reported as unrecognized. */
|
||||
static int matchOpt(const char* arg, const char* name, const char** val)
|
||||
{
|
||||
word32 nameLen = (word32)XSTRLEN(name);
|
||||
|
||||
if (XSTRNCMP(arg, name, nameLen) != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (arg[nameLen] == '\0') {
|
||||
*val = "";
|
||||
return 1;
|
||||
}
|
||||
if (arg[nameLen] == '=') {
|
||||
if (arg[nameLen + 1] == '\0') {
|
||||
return 0; /* "name=" with no value is not a valid option */
|
||||
}
|
||||
*val = arg + nameLen + 1;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return 1 if val is all digits and parses to a number in [lo, hi]. An empty
|
||||
* string returns 0, so callers decide whether a bare flag is acceptable. */
|
||||
static int validNum(const char* val, int lo, int hi)
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
|
||||
/* Reject empty and over-long inputs (guards XATOI against overflow, which
|
||||
* could otherwise wrap a huge value back into [lo, hi]). */
|
||||
if (val[0] == '\0' || XSTRLEN(val) > 3) {
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; val[i] != '\0'; i++) {
|
||||
if (val[i] < '0' || val[i] > '9') {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
n = XATOI(val);
|
||||
return (n >= lo && n <= hi);
|
||||
}
|
||||
|
||||
/* Handle one option. When validateOnly is set, only check that the option is
|
||||
* recognized and its value is valid — no TPM access, dev may be NULL; otherwise
|
||||
* execute it. This is the single source of truth for the option set, shared by
|
||||
* the pre-execution validation pass and the execution pass, so there is no
|
||||
* duplicated option table. Returns 0 on success, BAD_FUNC_ARG for an unknown
|
||||
* option or bad value, or a TPM return code from the executed operation. */
|
||||
static int handleOpt(WOLFTPM2_DEV* dev, const char* arg, int validateOnly)
|
||||
{
|
||||
int rc;
|
||||
const char* val;
|
||||
TPMI_MLDSA_PARAMETER_SET ms;
|
||||
TPMI_MLKEM_PARAMETER_SET ks;
|
||||
|
||||
if (XSTRCMP(arg, "--caps") == 0) {
|
||||
return validateOnly ? 0 : do_caps(dev);
|
||||
}
|
||||
if (XSTRCMP(arg, "--algs") == 0) {
|
||||
return validateOnly ? 0 : do_algs(dev);
|
||||
}
|
||||
if (XSTRCMP(arg, "--selftest") == 0) {
|
||||
return validateOnly ? 0 : do_selftest(dev);
|
||||
}
|
||||
if (XSTRCMP(arg, "--clear") == 0) {
|
||||
return validateOnly ? 0 : do_clear(dev);
|
||||
}
|
||||
if (XSTRCMP(arg, "--all") == 0) {
|
||||
return validateOnly ? 0 : do_all(dev);
|
||||
}
|
||||
if (XSTRCMP(arg, "--flush") == 0) {
|
||||
if (validateOnly) {
|
||||
return 0;
|
||||
}
|
||||
rc = pqc_flush_transient(dev);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("FAIL flush 0x%x: %s\n", rc, wolfTPM2_GetRCString(rc));
|
||||
return rc;
|
||||
}
|
||||
printf("Flushed transient objects\n");
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
if (matchOpt(arg, "--getrandom", &val)) {
|
||||
if (val[0] != '\0' && !validNum(val, 1, 64)) {
|
||||
printf("Error: --getrandom length must be 1..64: %s\n", arg);
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
return validateOnly ? 0 :
|
||||
do_getrandom(dev, val[0] == '\0' ? 16 : XATOI(val));
|
||||
}
|
||||
if (matchOpt(arg, "--pcrread", &val)) {
|
||||
if (val[0] != '\0' && !validNum(val, 0, 23)) {
|
||||
printf("Error: --pcrread index must be 0..23: %s\n", arg);
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
return validateOnly ? 0 : do_pcrread(dev, XATOI(val));
|
||||
}
|
||||
if (matchOpt(arg, "--pcrextend", &val)) {
|
||||
/* extend is irreversible: require an explicit in-range index */
|
||||
if (!validNum(val, 0, 23)) {
|
||||
printf("Error: --pcrextend needs an explicit PCR 0..23: %s\n", arg);
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
return validateOnly ? 0 : do_pcrextend(dev, XATOI(val));
|
||||
}
|
||||
if (matchOpt(arg, "--hash-mldsa", &val)) {
|
||||
if (parseMldsaSet(val, &ms) != 0) {
|
||||
printf("Error: invalid ML-DSA parameter set: %s\n", arg);
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
return validateOnly ? 0 : do_hash_mldsa(dev, ms);
|
||||
}
|
||||
if (matchOpt(arg, "--mldsa", &val)) {
|
||||
if (parseMldsaSet(val, &ms) != 0) {
|
||||
printf("Error: invalid ML-DSA parameter set: %s\n", arg);
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
return validateOnly ? 0 : do_mldsa(dev, ms);
|
||||
}
|
||||
if (matchOpt(arg, "--mlkem", &val)) {
|
||||
if (parseMlkemSet(val, &ks) != 0) {
|
||||
printf("Error: invalid ML-KEM parameter set: %s\n", arg);
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
return validateOnly ? 0 : do_mlkem(dev, ks);
|
||||
}
|
||||
printf("Error: Unrecognized option: %s\n", arg);
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
static int pqc_ctrl_run(int argc, char* argv[])
|
||||
{
|
||||
int rc = 0;
|
||||
int opRc;
|
||||
int i;
|
||||
WOLFTPM2_DEV dev;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (XSTRCMP(argv[i], "-?") == 0 || XSTRCMP(argv[i], "-h") == 0 ||
|
||||
XSTRCMP(argv[i], "--help") == 0) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validation pass: reject a bad argument set before touching the TPM or
|
||||
* running any op, so a typo cannot run an earlier --clear or PCR extend. */
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (handleOpt(NULL, argv[i], 1) != 0) {
|
||||
usage();
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
XMEMSET(&dev, 0, sizeof(dev));
|
||||
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
|
||||
if (rc != TPM_RC_SUCCESS) {
|
||||
printf("wolfTPM2_Init failed 0x%x: %s\n",
|
||||
rc, wolfTPM2_GetRCString(rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* No arguments: run the full control + PQC matrix. */
|
||||
if (argc <= 1) {
|
||||
rc = do_all(&dev);
|
||||
}
|
||||
|
||||
/* Execution pass: every argument was validated above. */
|
||||
for (i = 1; i < argc; i++) {
|
||||
opRc = handleOpt(&dev, argv[i], 0);
|
||||
if (opRc != 0 && rc == 0) {
|
||||
rc = opRc;
|
||||
}
|
||||
}
|
||||
|
||||
wolfTPM2_Cleanup(&dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* !WOLFTPM2_NO_WRAPPER && PQC ops */
|
||||
|
||||
#ifndef NO_MAIN_DRIVER
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
#if !defined(WOLFTPM2_NO_WRAPPER) && \
|
||||
defined(WOLFTPM_MLDSA_SIGN) && defined(WOLFTPM_MLDSA_VERIFY) && \
|
||||
defined(WOLFTPM_MLKEM_ENCAP) && defined(WOLFTPM_MLKEM_DECAP) && \
|
||||
defined(WOLFTPM_HASH_MLDSA)
|
||||
int rc = pqc_ctrl_run(argc, argv);
|
||||
return (rc == 0) ? 0 : 1;
|
||||
#else
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
printf("pqc_ctrl requires --enable-v185 (ML-DSA + ML-KEM + Hash-ML-DSA)\n");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
#endif /* NO_MAIN_DRIVER */
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
#!/bin/bash
|
||||
# pqc_ctrl.sh - PQC control-center command tests (SealSQ QVault TPM / fwTPM)
|
||||
#
|
||||
# Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfTPM.
|
||||
#
|
||||
# wolfTPM is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# wolfTPM is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
|
||||
# Exercises every pqc_ctrl command against a PQC TPM. Point it at the SealSQ
|
||||
# QVault TPM part (built with --enable-sealsq --enable-pqc) or the fwTPM (built
|
||||
# with --enable-fwtpm --enable-swtpm, with fwtpm_server listening).
|
||||
|
||||
PQC_CTRL="${1:-./examples/pqc/pqc_ctrl}"
|
||||
PASS=0 FAIL=0 TOTAL=0
|
||||
|
||||
if [ -t 1 ]; then
|
||||
GREEN='\033[0;32m' RED='\033[0;31m' NC='\033[0m'
|
||||
else
|
||||
GREEN='' RED='' NC=''
|
||||
fi
|
||||
|
||||
# run_test NAME EXPECT CMD...
|
||||
# EXPECT is text that must appear in the output ("" requires only exit 0).
|
||||
run_test() {
|
||||
local name="$1"; local expect="$2"; shift 2
|
||||
local out rc
|
||||
TOTAL=$((TOTAL + 1))
|
||||
echo "[$TOTAL] $name"
|
||||
out=$("$@" 2>&1); rc=$?
|
||||
if [ $rc -eq 0 ] && { [ -z "$expect" ] || echo "$out" | grep -q "$expect"; }; then
|
||||
echo -e " ${GREEN}PASS${NC}"; PASS=$((PASS + 1))
|
||||
else
|
||||
echo -e " ${RED}FAIL${NC} (rc=$rc)"; FAIL=$((FAIL + 1))
|
||||
echo "$out" | tail -3 | sed 's/^/ /'
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
# run_neg NAME CMD... — a bad argument set must exit non-zero without running
|
||||
# any operation. Passes only when the tool rejects it.
|
||||
run_neg() {
|
||||
local name="$1"; shift
|
||||
local out rc
|
||||
TOTAL=$((TOTAL + 1))
|
||||
echo "[$TOTAL] $name (must reject)"
|
||||
out=$("$@" 2>&1); rc=$?
|
||||
# Must exit non-zero AND print a validation error, so the test proves the
|
||||
# argument was rejected — not that the tool failed for some other reason
|
||||
# (e.g. no TPM available).
|
||||
if [ $rc -ne 0 ] && echo "$out" | grep -q "Error"; then
|
||||
echo -e " ${GREEN}PASS${NC}"; PASS=$((PASS + 1))
|
||||
else
|
||||
echo -e " ${RED}FAIL${NC} (rc=$rc, no validation error)"; FAIL=$((FAIL + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
if [ ! -x "$PQC_CTRL" ]; then
|
||||
echo "Error: $PQC_CTRL not found."
|
||||
echo "Build with: ./configure --enable-sealsq --enable-pqc && make"
|
||||
echo "Usage: $0 [path-to-pqc_ctrl]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "=== PQC Control Center Tests ==="
|
||||
echo "Tool: $PQC_CTRL"
|
||||
echo ""
|
||||
|
||||
# Board control / management commands
|
||||
run_test "Capabilities" "Vendor" "$PQC_CTRL" --caps
|
||||
run_test "Algorithm list" "ML-KEM" "$PQC_CTRL" --algs
|
||||
run_test "Self test" "SelfTest" "$PQC_CTRL" --selftest
|
||||
run_test "Get random (32 bytes)" "Random 32" "$PQC_CTRL" --getrandom=32
|
||||
run_test "PCR read (index 0)" "PCR0" "$PQC_CTRL" --pcrread=0
|
||||
run_test "Flush transient" "Flushed" "$PQC_CTRL" --flush
|
||||
|
||||
# Pure ML-DSA — every parameter set
|
||||
for PS in 44 65 87; do
|
||||
run_test "ML-DSA-$PS sign/verify" "ML-DSA-$PS" "$PQC_CTRL" --mldsa=$PS
|
||||
done
|
||||
|
||||
# Hash-ML-DSA (SHA-256 pre-hash) — every parameter set
|
||||
for PS in 44 65 87; do
|
||||
run_test "Hash-ML-DSA-$PS sign/verify" "HashML-DSA-$PS" \
|
||||
"$PQC_CTRL" --hash-mldsa=$PS
|
||||
done
|
||||
|
||||
# ML-KEM — every parameter set
|
||||
for PS in 512 768 1024; do
|
||||
run_test "ML-KEM-$PS encap/decap" "ML-KEM-$PS" "$PQC_CTRL" --mlkem=$PS
|
||||
done
|
||||
|
||||
# Full matrix in a single process (expect a real op line so a not-compiled-in
|
||||
# stub, which exits 0 with a "requires --enable-v185" message, cannot pass).
|
||||
run_test "Full matrix (--all)" "ML-KEM-1024" "$PQC_CTRL" --all
|
||||
|
||||
# Irreversible state-modifying commands (a PCR extend cannot be undone without a
|
||||
# TPM reset; TPM2_Clear wipes the owner hierarchy). Both are off by default so
|
||||
# the suite never changes TPM state unexpectedly; opt in with PQC_CTRL_CLEAR=1.
|
||||
if [ "${PQC_CTRL_CLEAR:-0}" = "1" ]; then
|
||||
run_test "PCR extend (index 16)" "PCR16 extend" "$PQC_CTRL" --pcrextend=16
|
||||
run_test "TPM clear" "Clear" "$PQC_CTRL" --clear
|
||||
fi
|
||||
|
||||
# Argument-validation negatives: each must be rejected before any op runs.
|
||||
run_neg "Invalid ML-DSA set" "$PQC_CTRL" --mldsa=abc
|
||||
run_neg "Out-of-range getrandom" "$PQC_CTRL" --getrandom=999
|
||||
run_neg "Bare pcrextend" "$PQC_CTRL" --pcrextend
|
||||
run_neg "Unknown option" "$PQC_CTRL" --bogus --clear
|
||||
|
||||
echo ""
|
||||
echo "=== Results: $TOTAL total, $PASS passed, $FAIL failed ==="
|
||||
if [ $FAIL -eq 0 ]; then
|
||||
echo -e "${GREEN}ALL TESTS PASSED${NC}"; exit 0
|
||||
else
|
||||
echo -e "${RED}$FAIL TEST(S) FAILED${NC}"; exit 1
|
||||
fi
|
||||
|
|
@ -376,6 +376,13 @@ if [ $ENABLE_V185 -eq 1 ]; then
|
|||
[ $RESULT -ne 0 ] && echo -e "mlkem_encap mlkem=$PS failed! $RESULT" && exit 1
|
||||
done
|
||||
|
||||
echo -e "PQC control tool (pqc_ctrl board commands)"
|
||||
# Board-control commands only; the ML-DSA/ML-KEM matrix is already covered
|
||||
# by the standalone example loops above, so do not re-run it here.
|
||||
./examples/pqc/pqc_ctrl --caps --algs --selftest --getrandom=16 >> $TPMPWD/run.out 2>&1
|
||||
RESULT=$?
|
||||
[ $RESULT -ne 0 ] && echo -e "pqc_ctrl board commands failed! $RESULT" && exit 1
|
||||
|
||||
echo -e "PQC primary key (create_primary -mldsa)"
|
||||
for PS in 44 65 87; do
|
||||
./examples/keygen/create_primary -mldsa=$PS -oh >> $TPMPWD/run.out 2>&1
|
||||
|
|
|
|||
|
|
@ -92,6 +92,9 @@
|
|||
#elif defined(WOLFTPM_NATIONS)
|
||||
/* Nations Technology NS350 uses CE0 */
|
||||
#define TPM2_SPI_DEV_CS "0"
|
||||
#elif defined(WOLFTPM_SEALSQ)
|
||||
/* SealSQ QVault TPM uses CE0 */
|
||||
#define TPM2_SPI_DEV_CS "0"
|
||||
#else
|
||||
/* OPTIGA SLB9670/SLB9762 and LetsTrust TPM use CE1 */
|
||||
#define TPM2_SPI_DEV_CS "1"
|
||||
|
|
|
|||
|
|
@ -861,6 +861,10 @@ static int wolfTPM2_ParseCapabilities(WOLFTPM2_CAPS* caps,
|
|||
caps->mfg = TPM_MFG_NATIONTECH;
|
||||
caps->req_wait_state = 1;
|
||||
}
|
||||
else if (XMEMCMP(&caps->mfgStr, "SEAL", 4) == 0) {
|
||||
caps->mfg = TPM_MFG_SEALSQ;
|
||||
caps->req_wait_state = 1;
|
||||
}
|
||||
break;
|
||||
case TPM_PT_VENDOR_STRING_1:
|
||||
case TPM_PT_VENDOR_STRING_2:
|
||||
|
|
|
|||
|
|
@ -4386,6 +4386,7 @@ typedef enum {
|
|||
TPM_VENDOR_MCHP = 0x1114,
|
||||
TPM_VENDOR_NUVOTON = 0x1050,
|
||||
TPM_VENDOR_NATIONTECH = 0x1B4E,
|
||||
TPM_VENDOR_SEALSQ = 0x2406,
|
||||
} TPM_Vendor_t;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -474,6 +474,17 @@ typedef int64_t INT64;
|
|||
/* Max: 43MHz */
|
||||
#define TPM2_SPI_MAX_HZ TPM2_SPI_MAX_HZ_NUVOTON
|
||||
#endif
|
||||
#elif defined(WOLFTPM_SEALSQ)
|
||||
/* SealSQ QVault TPM */
|
||||
/* Requires wait state support */
|
||||
#ifndef WOLFTPM_CHECK_WAIT_STATE
|
||||
#define WOLFTPM_CHECK_WAIT_STATE
|
||||
#endif
|
||||
#define TPM2_SPI_MAX_HZ_SEALSQ 33000000
|
||||
#ifndef TPM2_SPI_MAX_HZ
|
||||
/* Max: 33MHz */
|
||||
#define TPM2_SPI_MAX_HZ TPM2_SPI_MAX_HZ_SEALSQ
|
||||
#endif
|
||||
#else
|
||||
/* Infineon OPTIGA SLB9670/SLB9672/SLB9673 */
|
||||
#ifdef WOLFTPM_SLB9670
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ typedef enum WOLFTPM2_MFG {
|
|||
TPM_MFG_MCHP,
|
||||
TPM_MFG_NUVOTON,
|
||||
TPM_MFG_NATIONTECH,
|
||||
TPM_MFG_SEALSQ,
|
||||
} WOLFTPM2_MFG;
|
||||
|
||||
typedef struct WOLFTPM2_CAPS {
|
||||
|
|
|
|||
Loading…
Reference in New Issue