wolfTPM/examples/spdm
Aidan Garske 64f2db9fcb Add SPDM-bound NV policy examples
Add an NV binding example and extend the SPDM control flow to exercise PSK- and certificate-authenticated policy sessions. Document and automate the end-to-end policy checks.
2026-09-15 12:24:05 -07:00
..
README.md Add SPDM-bound NV policy examples 2026-09-15 12:24:05 -07:00
include.am Add SPDM-bound NV policy examples 2026-09-15 12:24:05 -07:00
nv_bind.c Add SPDM-bound NV policy examples 2026-09-15 12:24:05 -07:00
spdm_ctrl.c Add SPDM-bound NV policy examples 2026-09-15 12:24:05 -07:00
spdm_test.sh Add SPDM-bound NV policy examples 2026-09-15 12:24:05 -07:00

README.md

TPM SPDM Setup/Control

This directory contains the SPDM setup and control tool for Nuvoton NPCT75x and Nations NS350 TPMs with wolfTPM.

Overview

The spdm_ctrl tool establishes SPDM secure sessions between the host and a TPM over SPI, enabling AES-256-GCM encrypted bus communication. Identity mode requires the responder key from a trusted provisioning source.

spdm_ctrl and nv_bind are the examples that accept SPDM credentials. Other wolfTPM examples use uncredentialed wolfTPM2_Init() and intentionally return WOLFSPDM_E_BAD_STATE while a TPM is locked in SPDM-only mode; unlock it with spdm_ctrl before running those examples.

Supported hardware:

  • Nuvoton NPCT75x — Identity key mode (ECDHE P-384)
  • Nations NS350 — Identity key mode + PSK mode

For standard SPDM protocol support (spdm-emu, measurements, challenge, etc.), see the wolfSPDM standalone library.

Building

Prerequisites

wolfSSL with crypto algorithms required for SPDM Algorithm Set B:

cd wolfssl
./autogen.sh
./configure --enable-wolftpm --enable-ecc --enable-sha384 --enable-aesgcm --enable-hkdf --enable-sp
make && sudo make install && sudo ldconfig

wolfTPM with Nuvoton SPDM

cd wolfTPM
./autogen.sh
./configure --enable-spdm --enable-nuvoton
make

wolfTPM with Nations SPDM

cd wolfTPM
./autogen.sh
./configure --enable-spdm --enable-nations
make

Setup/Control Commands

Option Description
--vendor=nuvoton|nations Select the identity/vendor adapter explicitly
--enable Enable SPDM on TPM via NTC2_PreConfig (one-time, requires reset)
--disable Disable SPDM on TPM via NTC2_PreConfig (requires reset)
--status Query SPDM status from TPM
--get-pubkey Get TPM's SPDM-Identity P-384 public key
--responder-pubkey <hex> Pin a trusted raw P-384 X||Y key (192 hex characters)
--connect Establish SPDM session (ECDH P-384 handshake)
--caps Read TPM capabilities over the current transport
--session-info Show the TPM's view of the SPDM session (TPM_CAP_SPDM_SESSION_INFO)
--policy-nv Define an NV index guarded by TPM2_PolicyTransportSPDM, then write and read it over the session
--psk <hex> Start a PSK session
--psk-set <psk> <clearauth> Provision a 64-byte PSK and 32-byte ClearAuth
--psk-clear <clearauth> Clear a provisioned PSK
--lock Lock SPDM-only mode (use with --connect)
--unlock Unlock SPDM-only mode (use with --connect)
--tpm-clear Send TPM2_Clear over the current transport

The nv_bind example is a focused, self-contained version of the same idea: it provisions an NV index whose authPolicy is TPM2_PolicyTransportSPDM, stores a secret over an SPDM-PSK session, then shows that the identical read over a plain (non-SPDM) connection is refused with TPM_RC_CHANNEL.

./src/fwtpm/fwtpm_server --spdm-psk --spdm-psk-hex <psk> --clear &
./examples/spdm/nv_bind --psk <psk>

The fwTPM generates a fresh SPDM identity key each time it starts, so on the fwTPM a policy bound to tpmKeyName is only valid for that server lifetime; a hardware TPM holds a persistent identity key, where such a binding is durable. PSK sessions report empty key names, since no asymmetric key authenticated them.

Usage Examples

# One-time setup: enable SPDM + reset TPM
./examples/spdm/spdm_ctrl --enable
# Reset the TPM (see "TPM Reset Pin Control" below)

# Query SPDM status
./examples/spdm/spdm_ctrl --status

# Discover TPM identity key (unauthenticated; do not use as its own trust source)
./examples/spdm/spdm_ctrl --get-pubkey

# Establish SPDM session with a key from trusted provisioning records
./examples/spdm/spdm_ctrl \
    --vendor=nuvoton --responder-pubkey <trusted_p384_x_y_hex> --connect

# Lock SPDM-only mode (connect + lock in one session)
./examples/spdm/spdm_ctrl \
    --responder-pubkey <trusted_p384_x_y_hex> --connect --lock
# Reset the TPM

# Unlock SPDM-only mode
# Reset the TPM
./examples/spdm/spdm_ctrl \
    --responder-pubkey <trusted_p384_x_y_hex> --connect --unlock
# Reset the TPM

TPM Reset Pin Control

SPDM enable/disable and SPDM-only mode changes require a TPM reset to take effect. The reset pin must be connected and controllable by the host.

Important for custom hardware designs: Ensure the TPM reset pin is routed to a host-controllable GPIO. Without reset pin control, SPDM mode changes cannot be applied and recovery from SPDM-only mode is not possible.

The reset line is board specific. On a Raspberry Pi, Nuvoton uses GPIO4 and the ST33KTPM uses GPIO24 (pin 18); confirm your wiring before toggling.

# Assert reset low, release high, wait for TPM startup (Nuvoton GPIO4 shown)
gpioset gpiochip0 4=0 && sleep 0.1 && gpioset gpiochip0 4=1 && sleep 2
# ST33: use line 24 instead of 4

wolfTPM can also drive this from code: build with --enable-hal-reset and call TPM2_IoCb_Reset() (default line: ST33 GPIO24, Nuvoton GPIO4). See hal/README.md.

Automated Test Suite

Runs the full SPDM setup lifecycle on hardware:

export SPDM_RESPONDER_PUBKEY=<trusted_p384_x_y_hex>
./examples/spdm/spdm_test.sh ./examples/spdm/spdm_ctrl nuvoton
./examples/spdm/spdm_test.sh ./examples/spdm/spdm_ctrl nations
./examples/spdm/spdm_test.sh ./examples/spdm/spdm_ctrl nations-psk

The identity-mode hardware runs require SPDM_RESPONDER_PUBKEY from a trusted provisioning source. The PSK run does not use it. The fwtpm-tcg test obtains the freshly generated public key from the local server's protected startup log and passes it through the same pinning interface.

Support

For production use with hardware TPMs and SPDM support, contact support@wolfssl.com.