Skip to content

wolfTPM SPDM support (tested with Nuvoton NPCT75x)#458

Open
aidangarske wants to merge 4 commits intowolfSSL:masterfrom
aidangarske:add-full-spdm-support
Open

wolfTPM SPDM support (tested with Nuvoton NPCT75x)#458
aidangarske wants to merge 4 commits intowolfSSL:masterfrom
aidangarske:add-full-spdm-support

Conversation

@aidangarske
Copy link
Member

@aidangarske aidangarske commented Feb 20, 2026

Description

Migrates the standalone wolfSPDM library into wolfTPM as an in-tree spdm/
subdirectory and adds full SPDM support for both Nuvoton NPCT75x and
Nations NS350 TPMs. Eliminates the external dependency — a single
--enable-spdm configure flag builds everything.

wolfSPDM Library (spdm/)

  • SPDM 1.3 (DSP0274) with Algorithm Set B (P-384, SHA-384, AES-256-GCM, HKDF)
  • Standard mode: works with libspdm spdm-emu responder for testing
  • TCG SPDM Binding: vendor-defined commands (GET_PUBK, GIVE_PUB, SPDMONLY,
    GET_STS_, TPM2_CMD, PSK_SET_, PSK_CLR_)
  • Identity key session: GET_VERSION, GET_CAPABILITIES, NEGOTIATE_ALGORITHMS,
    GET_PUBK, KEY_EXCHANGE, GIVE_PUB, FINISH
  • PSK session: GET_VERSION, GET_CAPABILITIES, NEGOTIATE_ALGORITHMS,
    PSK_EXCHANGE, PSK_FINISH
  • Secured messaging: AES-256-GCM AEAD with sequence number tracking
  • Static memory mode (zero-malloc) and dynamic mode (--enable-spdm-dynamic-mem)
  • Unit test suite (spdm/test/unit_test.c)

Nuvoton NPCT75x Support

  • NTC2_PreConfig vendor commands for SPDM enable/disable
  • SPDM-only mode: lock/unlock enforcement over identity key sessions
  • Auto-SPDM: transparent session establishment when TPM is in SPDM-only mode
  • Tested: 6/6 hardware tests (status, connect, lock, unit test, unlock, caps)

Nations NS350 Support

PSK Mode

  • PSK_SET/PSK_CLEAR vendor commands with 32-byte ClearAuth (TCG PC Client spec)
  • PSK_EXCHANGE/PSK_FINISH session establishment (Salt_0 = 0xFF for PSK mode)
  • GET_STATUS with response parsing (PSKSet, SPDMOnly, spec version)
  • SPDM_ONLY lock/unlock for PSK mode
  • Tested: 10/10 PSK lifecycle tests (provision, connect, clear, re-provision)

Identity Key Mode

  • Shared TCG SPDM binding protocol with Nuvoton
  • TPM2_VendorSpdmIdentityKeySet for identity key provisioning
  • TODO: Re-test on firmware 0.1.0.15 (digest changed to SHA-384(TPMT_PUBLIC))

wolfTPM Integration

  • 13+ new wolfTPM2_Spdm*() wrapper API functions in tpm2_wrap.h
  • Transparent SPDM transport in TPM2_SendCommand() — all TPM commands
    automatically encrypted when SPDM session is active
  • TIS I/O callback for routing SPDM through SPI/I2C TPM FIFO
  • Auto-generated ephemeral P-384 key pair for mutual authentication

SPDM Demo (examples/spdm/)

  • spdm_demo.c: Nuvoton and Nations hardware modes
  • spdm_test.sh: Automated tests for nuvoton, nations, and nations-psk modes

TCG SPDM Vendor Commands

VdCode Command Nuvoton Nations
GET_PUBK Get Public Key Yes TODO
GIVE_PUB Give Public Key Yes TODO
TPM2_CMD TPM Command Yes Yes
GET_STS_ Get Status Yes Yes
SPDMONLY SPDM-Only Mode Yes Yes
PSK_SET_ PSK Set N/A Yes
PSK_CLR_ PSK Clear N/A Yes

Test Plan

  • Build with --enable-spdm --enable-nuvoton
  • Build with --enable-spdm --enable-nations
  • Build without --enable-spdm (no breakage)
  • 26/26 unit tests PASS
  • 6/6 Nuvoton hardware tests PASS
  • 10/12 Nations PSK hardware tests PASS. (two still need testing)
  • Nations identity key mode on firmware 0.1.0.15
  • Copilot review

This comment was marked as outdated.

This comment was marked as outdated.

This comment was marked as outdated.

@aidangarske aidangarske requested a review from dgarske February 20, 2026 21:58
@dgarske dgarske assigned dgarske and wolfSSL-Bot and unassigned aidangarske Feb 21, 2026
Copilot AI review requested due to automatic review settings February 24, 2026 16:26

This comment was marked as off-topic.

This comment was marked as outdated.

@dgarske dgarske removed their assignment Feb 24, 2026
Copilot AI review requested due to automatic review settings February 26, 2026 18:20

This comment was marked as outdated.

@aidangarske aidangarske force-pushed the add-full-spdm-support branch from b684e06 to e56719c Compare February 26, 2026 18:25
Copy link
Contributor

@dgarske dgarske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very nice and almost ready for merge. I provided you some feedback directly and I know you are out Monday, so no rush.

  - 18/18 emulator tests PASS (6 tests x 3 versions: 1.2, 1.3, 1.4)
  1. Removed OpaqueDataLength(2) from CHALLENGE request for SPDM 1.3+ — spec says only RequesterContext(8), no OpaqueDataLength in request
  2. Removed OpaqueDataLength(2) from signed GET_MEASUREMENTS request for SPDM 1.3+ — same issue
  - spdm/README.md: Added supported versions table (spdm-emu: 1.2/1.3/1.4, Nuvoton: 1.3), updated protocol flow diagram, added --ver flag to demo options,
  added wolfSPDM_SetMaxVersion() to API table, updated emulator section to mention 18-test multi-version coverage
  - Addressed PR review feedback
Copilot AI review requested due to automatic review settings March 3, 2026 19:28

This comment was marked as outdated.

@aidangarske aidangarske requested a review from dgarske March 3, 2026 20:00
@dgarske dgarske assigned dgarske and unassigned aidangarske Mar 5, 2026
Copy link
Contributor

@dgarske dgarske left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please work on code size reduction (possibly moving the spdm-emu stuff to another report). Also please work on the TPM SPDM PSK use case.

@dgarske dgarske assigned aidangarske and unassigned dgarske Mar 6, 2026
Copilot AI review requested due to automatic review settings March 13, 2026 01:05
@aidangarske aidangarske force-pushed the add-full-spdm-support branch from f43ecbb to 57c6e83 Compare March 13, 2026 01:05
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 39 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

… lines)

  Security fixes:
  - Mandatory responder signature verification in KEY_EXCHANGE_RSP (was conditional skip)
  - Sensitive stack buffer zeroing (wc_ForceZero) in BuildFinish, ParseKeyExchangeRsp,
    wolfSPDM_Finish for keys, HMAC, and signature data
  - TCG integer underflow guard in ParseTcgClearMessage (msgSize < header check)
  - BuildIV: removed dead code duplicate branches, collapsed to single 8-byte XOR path

  Code quality:
  - Cascade error handling (rc == WOLFSPDM_SUCCESS pattern) across spdm_msg.c,
    spdm_session.c, spdm_secured.c for single-cleanup-path safety
  - wolfSPDM_BuildVendorDefined: added spdmVersion parameter (was hardcoded 0x13)
  - spdm_error.h: added spdm_types.h include for standalone WOLFSPDM_API definition
  - spdm.h: added stack usage documentation (~22KB context, ~20KB call chain)

  Codebase reduction (-4,301 net lines):
  - Condensed spdm_demo.c (382->256 lines), spdm_test.sh (143->70 lines)
  - Removed verbose banner comments and redundant section headers across all files
  - Consolidated unit tests with shared helpers, removed duplicate test patterns
  - Removed spdm-emu-test.yml CI workflow (moved to standalone wolfSPDM repo)
  - Streamlined README documentation

  Test results:
  - 26/26 unit tests PASS
  - 6/6 Nuvoton hardware tests PASS (spdm_test.sh)
@aidangarske aidangarske force-pushed the add-full-spdm-support branch from 57c6e83 to d58702e Compare March 13, 2026 17:50
@aidangarske aidangarske requested a review from dgarske March 13, 2026 18:08
@aidangarske aidangarske changed the title wolfSPDM migration to wolfTPM/spdm with Nuvoton and spdm-emu support wolfSPDM migration to wolfTPM/spdm with Nuvoton NPCT75x Mar 13, 2026
@dgarske dgarske removed their assignment Mar 17, 2026
@dgarske dgarske changed the title wolfSPDM migration to wolfTPM/spdm with Nuvoton NPCT75x wolfTPM SPDM support (tested with Nuvoton NPCT75x) Mar 18, 2026
  - Nations NS350 PSK mode: PSK_SET, PSK_CLEAR, PSK_EXCHANGE, PSK_FINISH,
    GET_STATUS, SPDM_ONLY with 32-byte ClearAuth per TCG PC Client spec
  - Salt_0 = 0xFF for PSK mode HKDF-Extract (vs 0x00 for identity key mode)
  - NATIONS_PSK mode checks in spdm_secured.c encrypt/decrypt paths
  - GET_STATUS response parsing fix (PSKSet byte was not being read)
  - Demo cleanup order: TPM2_Shutdown before SPDM disconnect
  - 32-byte ClearAuth enforcement on PSK_SET and PSK_CLEAR
  - spdm_test.sh nations-psk: 12-step PSK lifecycle test with NSING reference data
  - spdm/README.md: TCG commands section, Nations PSK docs, validation status
  - Removed unused inline PSK_SET, SetPskSetPayload, ConnectNationsPskProvision
  - SPDM 1.3+ OpaqueDataLength fix for CHALLENGE and GET_MEASUREMENTS requests

  Tested: 10/10 Nations PSK tests PASS, 6/6 Nuvoton tests PASS
Copy link

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #458

Scan targets checked: wolftpm-bugs, wolftpm-src
Findings: 1

Info (1)

Incomplete review: src/*.c diffs truncated

File: hal/tpm_io_linux.c:85-87
Function: N/A
Category: HAL implementation flaws

The diff provided was truncated before reaching the in-scope src/tpm2.c, src/tpm2_packet.c, src/tpm2_spdm.c, src/tpm2_swtpm.c, and src/tpm2_wrap.c changes. The only visible in-scope change is in hal/tpm_io_linux.c which adds a compile-time SPI chip-select constant TPM2_SPI_DEV_CS "0" for WOLFTPM_NATIONS, identical to the existing Nuvoton configuration. This change is benign. The out-of-scope spdm/src/ library code (visible in the diff) demonstrates good security practices: wc_ForceZero for key material, wc_ecc_check_key for peer key validation, proper buffer bounds checking, and state machine enforcement. A complete review of src/tpm2_spdm.c and src/tpm2_wrap.c is recommended as these files contain the integration layer between wolfTPM and the SPDM subsystem.

#elif defined(WOLFTPM_NATIONS)
    /* Nations Technology NS350 uses CE0 */
    #define TPM2_SPI_DEV_CS "0"

Recommendation: Re-run this security review with the complete diff (including src/tpm2.c, src/tpm2_packet.c, src/tpm2_spdm.c, src/tpm2_swtpm.c, and src/tpm2_wrap.c) to ensure full coverage of the in-scope files.


This review was generated automatically by Fenrir. Findings are non-blocking.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 42 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +490 to +494
if (rc < 0) /* SPDM not active, use normal transport */
#endif
{
rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet);
}
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In TPM2_SendCommandAuth(), the SPDM fallback condition uses if (rc < 0) to decide that SPDM is inactive. However, wolfTPM2_SPDM_SecuredExchange/wolfSPDM return negative error codes on real failures (e.g., decrypt/HMAC/IO errors), so this will incorrectly fall back to sending the same command over the clear transport after an SPDM failure. Use a dedicated sentinel for "SPDM not active" (e.g., return -1 only for that case and check rc == -1 here), and treat any other non-zero as a hard error (no cleartext retry).

Suggested change
if (rc < 0) /* SPDM not active, use normal transport */
#endif
{
rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet);
}
if (rc == -1) { /* SPDM not active, use normal transport */
rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet);
}
else if (rc != 0) {
/* SPDM is active but the secured exchange failed: do not retry in clear */
return rc;
}
#else
rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet);
#endif

Copilot uses AI. Check for mistakes.
Comment on lines +524 to 532
#ifdef WOLFTPM_SPDM
rc = TPM2_SPDM_SendCommand(ctx, packet);
if (rc == TPM_RC_SUCCESS)
return TPM2_Packet_Parse(rc, packet);
/* rc < 0 means SPDM not active, fall through to normal transport */
#endif

/* submit command and wait for response */
rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet);
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In TPM2_SendCommand(), if TPM2_SPDM_SendCommand() returns an SPDM error (non-zero), the code currently falls through to INTERNAL_SEND_COMMAND and will resend the command in cleartext. This can leak commands/authorization after an SPDM failure and can also cause duplicate execution. Handle SPDM outcomes explicitly: return immediately on any error that is not the "SPDM not active" sentinel, and only fall back to INTERNAL_SEND_COMMAND when SPDM is not configured/connected.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants