From 550cea93ab20f095e654750b533b3c2ed2b290ec Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 23 Jun 2026 14:59:36 -0700 Subject: [PATCH 1/5] Add transparent TPM_RC_RETRY resubmit at command chokepoint --- ChangeLog.md | 7 +++ src/tpm2.c | 123 ++++++++++++++++++++++++++++++------------ src/tpm2_packet.c | 15 ++++++ src/tpm2_wrap.c | 16 ++++++ tests/unit_tests.c | 88 ++++++++++++++++++++++++++++++ wolftpm/tpm2.h | 33 ++++++++++++ wolftpm/tpm2_packet.h | 2 + wolftpm/tpm2_types.h | 12 +++++ wolftpm/tpm2_wrap.h | 28 ++++++++++ 9 files changed, 289 insertions(+), 35 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index ec1c2483..55dbde10 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -27,6 +27,13 @@ client wrappers, DA/noDA/lockout/self-heal/persistence unit tests, an `examples/management/da_check` end-to-end example, and the `tests/fwtpm_da_retry.sh` CI harness. +* Added transparent `TPM_RC_RETRY` handling so commands are automatically + resubmitted when the TPM reports it is momentarily busy (for example + persisting the daUsed flag on first auth use of a non-noDA AIK/SUDI key), + matching the TCG ESYS behavior. Resubmit count defaults to `WOLFTPM_MAX_RETRIES` + (3) and is configurable at runtime via `wolfTPM2_SetCommandRetries` / + `TPM2_SetCommandRetries`; define `WOLFTPM_NO_RETRY` or set the count to 0 to + disable and return `TPM_RC_RETRY` to the caller. ## wolfTPM Release 4.0.0 (Apr 22, 2026) diff --git a/src/tpm2.c b/src/tpm2.c index aa95dbf0..d35bc8b6 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -467,6 +467,60 @@ static TPM_RC TPM2_SPDM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) } #endif /* WOLFTPM_SPDM */ +/* Submit the finalized command in packet (length cmdSz) and parse the + * response, returning the TPM response code. On TPM_RC_RETRY the TPM is + * momentarily busy (e.g. persisting the daUsed flag on first auth use of a + * non-noDA key) and asks for the identical command to be resubmitted; the + * error response is header-only, so restoring the command header and resending + * up to ctx->retries times recovers transparently. */ +static TPM_RC TPM2_TransmitWithRetry(TPM2_CTX* ctx, TPM2_Packet* packet, + UINT32 cmdSz) +{ + TPM_RC rc; +#ifndef WOLFTPM_NO_RETRY + byte cmdHdr[TPM2_HEADER_SIZE]; + int origSize = packet->size; + int retries = ctx->retries; + + XMEMCPY(cmdHdr, packet->buf, TPM2_HEADER_SIZE); +#endif + + for (;;) { + /* send command requires packet->pos to be the total command length */ + packet->pos = cmdSz; + + #ifdef WOLFTPM_SPDM + rc = TPM2_SPDM_SendCommand(ctx, packet); + if (rc >= 0) { + if (rc != TPM_RC_SUCCESS) + return rc; /* SPDM active but failed, do not retry cleartext */ + } + else /* rc < 0: SPDM not active, use normal transport */ + #endif + { + rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet); + } + if (rc != 0) + return rc; /* transport error */ + + /* parse response header and extract the TPM response code */ + rc = TPM2_Packet_Parse(rc, packet); + + #ifndef WOLFTPM_NO_RETRY + if (TPM2_Packet_RetryRestore(rc, &retries, packet, cmdHdr, origSize)) { + #ifdef DEBUG_WOLFTPM + printf("TPM_RC_RETRY: resubmitting command, %d retries left\n", + retries); + #endif + continue; + } + #endif + break; + } + + return rc; +} + static TPM_RC TPM2_SendCommandAuth(TPM2_CTX* ctx, TPM2_Packet* packet, CmdInfo_t* info) { @@ -507,26 +561,8 @@ static TPM_RC TPM2_SendCommandAuth(TPM2_CTX* ctx, TPM2_Packet* packet, return rc; } - /* reset packet->pos to total command length (send command requires it) */ - packet->pos = cmdSz; - - /* submit command and wait for response */ -#ifdef WOLFTPM_SPDM - rc = TPM2_SPDM_SendCommand(ctx, packet); - if (rc >= 0) { - if (rc != TPM_RC_SUCCESS) - return rc; /* SPDM active but failed, do not retry cleartext */ - } - else /* rc < 0: SPDM not active, use normal transport */ -#endif - { - rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet); - } - if (rc != 0) - return rc; - - /* parse response */ - rc = TPM2_Packet_Parse(rc, packet); + /* submit command and wait for response, auto-resubmitting on TPM_RC_RETRY */ + rc = TPM2_TransmitWithRetry(ctx, packet, cmdSz); respSz = packet->size; /* restart the unmarshalling position */ @@ -559,22 +595,10 @@ static TPM_RC TPM2_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) if (ctx == NULL || packet == NULL) return BAD_FUNC_ARG; -#ifdef WOLFTPM_SPDM - rc = TPM2_SPDM_SendCommand(ctx, packet); - if (rc >= 0) { - if (rc != TPM_RC_SUCCESS) - return rc; /* SPDM active but failed, do not retry cleartext */ - 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); - if (rc != 0) - return rc; + /* submit command and wait for response, auto-resubmitting on TPM_RC_RETRY */ + rc = TPM2_TransmitWithRetry(ctx, packet, (UINT32)packet->pos); - return TPM2_Packet_Parse(rc, packet); + return rc; } #ifndef WOLFTPM2_NO_WOLFCRYPT @@ -711,6 +735,33 @@ TPM_RC TPM2_SetHalIoCb(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx) return rc; } +TPM_RC TPM2_SetCommandRetries(TPM2_CTX* ctx, int retries) +{ + TPM_RC rc; + + if (ctx == NULL || retries < 0) { + return BAD_FUNC_ARG; + } + + rc = TPM2_AcquireLock(ctx); + if (rc == TPM_RC_SUCCESS) { + ctx->retries = retries; + + TPM2_ReleaseLock(ctx); + } + + return rc; +} + +int TPM2_GetCommandRetries(TPM2_CTX* ctx) +{ + if (ctx == NULL) { + return BAD_FUNC_ARG; + } + /* atomic int read, no lock needed; the setter takes the lock */ + return ctx->retries; +} + /* If timeoutTries <= 0 then it will not try and startup chip and will * use existing default locality */ TPM_RC TPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, @@ -724,6 +775,8 @@ TPM_RC TPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, XMEMSET(ctx, 0, sizeof(TPM2_CTX)); + ctx->retries = WOLFTPM_MAX_RETRIES; + #ifndef WOLFTPM2_NO_WOLFCRYPT rc = TPM2_WolfCrypt_Init(); if (rc != 0) diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 1c0125d1..43b8b4b5 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -1625,6 +1625,21 @@ TPM_RC TPM2_Packet_Parse(TPM_RC rc, TPM2_Packet* packet) return rc; } +int TPM2_Packet_RetryRestore(TPM_RC rc, int* retries, TPM2_Packet* packet, + const byte* cmdHdr, int origSize) +{ + if (rc != TPM_RC_RETRY || retries == NULL || *retries <= 0 || + packet == NULL || packet->buf == NULL || cmdHdr == NULL) { + return 0; + } + (*retries)--; + /* A TPM_RC_RETRY response is header-only, so the command body survives; + * restore the clobbered header and the buffer size for an identical resend */ + XMEMCPY(packet->buf, cmdHdr, TPM2_HEADER_SIZE); + packet->size = origSize; + return 1; +} + int TPM2_Packet_Finalize(TPM2_Packet* packet, TPM_ST tag, TPM_CC cc) { word32 cmdSz = packet->pos; /* get total packet size */ diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index c9039de5..2b8f6552 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -750,6 +750,22 @@ int wolfTPM2_GetTpmDevId(WOLFTPM2_DEV* dev) return (int)dev->ctx.did_vid; /* return something besides INVALID_DEVID */ } +int wolfTPM2_SetCommandRetries(WOLFTPM2_DEV* dev, int retries) +{ + if (dev == NULL) { + return BAD_FUNC_ARG; + } + return TPM2_SetCommandRetries(&dev->ctx, retries); +} + +int wolfTPM2_GetCommandRetries(WOLFTPM2_DEV* dev) +{ + if (dev == NULL) { + return BAD_FUNC_ARG; + } + return TPM2_GetCommandRetries(&dev->ctx); +} + int wolfTPM2_SelfTest(WOLFTPM2_DEV* dev) { int rc; diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 22e1d9bd..21819134 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2515,6 +2515,92 @@ static void test_wolfTPM2_VerifyHashTicket_DigestSize(void) #endif } +/* Transparent TPM_RC_RETRY resubmit is configurable. Verify the default seeded + * by init, the setter/getter round-trip, and rejection of bad arguments. */ +static void test_wolfTPM2_CommandRetries(void) +{ + int rc; + WOLFTPM2_DEV dev; + + XMEMSET(&dev, 0, sizeof(dev)); + + rc = wolfTPM2_SetCommandRetries(NULL, 1); + AssertIntEQ(rc, BAD_FUNC_ARG); + rc = wolfTPM2_GetCommandRetries(NULL); + AssertIntEQ(rc, BAD_FUNC_ARG); + rc = wolfTPM2_SetCommandRetries(&dev, -1); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* retries is seeded before any HAL IO setup, so the default holds even on + * builds without a default IO callback where init returns an error */ + (void)TPM2_Init_minimal(&dev.ctx); + AssertIntEQ(wolfTPM2_GetCommandRetries(&dev), WOLFTPM_MAX_RETRIES); + + rc = wolfTPM2_SetCommandRetries(&dev, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(wolfTPM2_GetCommandRetries(&dev), 0); + + rc = wolfTPM2_SetCommandRetries(&dev, 7); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(wolfTPM2_GetCommandRetries(&dev), 7); + + TPM2_Cleanup(&dev.ctx); + + printf("Test TPM Wrapper:\tCommandRetries config:\t\tPassed\n"); +} + +/* Exercise the TPM_RC_RETRY resubmit bookkeeping directly: header/size restore, + * command-body preservation, retry-count decrement, and surfacing TPM_RC_RETRY + * once the count is exhausted - the parts of the loop most likely to regress. */ +static void test_TPM2_Packet_RetryRestore(void) +{ + TPM2_Packet packet; + byte buf[TPM2_HEADER_SIZE + 4]; + byte hdr[TPM2_HEADER_SIZE]; + int retries; + + XMEMSET(hdr, 0xAA, sizeof(hdr)); + XMEMSET(buf, 0x00, sizeof(buf)); + buf[TPM2_HEADER_SIZE] = 0xBB; /* command body byte, must survive a resend */ + packet.buf = buf; + packet.pos = 0; + packet.size = TPM2_HEADER_SIZE; /* shrunk by a prior TPM2_Packet_Parse */ + + /* A non-retry response must not resubmit and must leave state untouched */ + retries = 2; + AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_SUCCESS, &retries, &packet, + hdr, (int)sizeof(buf)), 0); + AssertIntEQ(retries, 2); + AssertIntEQ(packet.size, TPM2_HEADER_SIZE); + + /* RETRY with budget: resubmit, header + size restored, body preserved */ + AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, &retries, &packet, + hdr, (int)sizeof(buf)), 1); + AssertIntEQ(retries, 1); + AssertIntEQ(packet.size, (int)sizeof(buf)); + AssertIntEQ(buf[0], 0xAA); + AssertIntEQ(buf[TPM2_HEADER_SIZE], 0xBB); + + /* Second RETRY exhausts the budget */ + AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, &retries, &packet, + hdr, (int)sizeof(buf)), 1); + AssertIntEQ(retries, 0); + + /* Budget exhausted: RETRY is surfaced to the caller, no resubmit */ + AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, &retries, &packet, + hdr, (int)sizeof(buf)), 0); + AssertIntEQ(retries, 0); + + /* NULL guards */ + retries = 1; + AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, NULL, &packet, + hdr, (int)sizeof(buf)), 0); + AssertIntEQ(TPM2_Packet_RetryRestore(TPM_RC_RETRY, &retries, NULL, + hdr, (int)sizeof(buf)), 0); + + printf("Test TPM Wrapper:\tRetryRestore logic:\t\tPassed\n"); +} + /* wolfTPM2_NVCreateAuthPolicy must derive nameAlg from authPolicySz so * the policy digest hash matches the index's nameAlg. Bug-mode hardcoded * SHA-256 nameAlg, which made SHA-384/SHA-512 policies unsatisfiable. @@ -5699,6 +5785,8 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_GetKeyTemplate_ex_nameAlg(); test_wolfTPM2_SignHashScheme_DigestSize(); test_wolfTPM2_VerifyHashTicket_DigestSize(); + test_wolfTPM2_CommandRetries(); + test_TPM2_Packet_RetryRestore(); test_wolfTPM2_NVCreateAuthPolicy_NameAlg(); test_wolfTPM2_GetKeyTemplate_KeyedHash_Scheme(); test_wolfTPM2_LoadEccPublicKey_Ex(); diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 12f81067..e142287c 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -2229,6 +2229,9 @@ typedef struct TPM2_CTX { void* spdmCtx; /* Pointer to WOLFTPM2_SPDM_CTX when session active */ unsigned int spdmOnlyDetected:1; /* TPM_RC_DISABLED from Startup */ #endif + + /* Additional resubmit attempts on TPM_RC_RETRY (0 disables) */ + int retries; } TPM2_CTX; @@ -3773,6 +3776,36 @@ WOLFTPM_API TPM_RC TPM2_ChipStartup(TPM2_CTX* ctx, int timeoutTries); */ WOLFTPM_API TPM_RC TPM2_SetHalIoCb(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx); +/*! + \ingroup TPM2_Proprietary + \brief Sets the number of times a command is transparently resubmitted on TPM_RC_RETRY + \brief The TPM returns TPM_RC_RETRY when momentarily busy (for example persisting the daUsed flag on first auth use of a non-noDA key). Defaults to WOLFTPM_MAX_RETRIES; set to 0 to disable and have TPM_RC_RETRY returned to the caller. + + \return TPM_RC_SUCCESS: successful + \return BAD_FUNC_ARG: the TPM2 context is NULL or retries is negative + + \param ctx pointer to a TPM2_CTX struct + \param retries number of additional resubmit attempts on TPM_RC_RETRY (0 disables) + + \sa TPM2_GetCommandRetries + \sa wolfTPM2_SetCommandRetries +*/ +WOLFTPM_API TPM_RC TPM2_SetCommandRetries(TPM2_CTX* ctx, int retries); + +/*! + \ingroup TPM2_Proprietary + \brief Returns the number of times a command is transparently resubmitted on TPM_RC_RETRY + + \return the configured retry count on success + \return BAD_FUNC_ARG: the TPM2 context is a NULL pointer + + \param ctx pointer to a TPM2_CTX struct + + \sa TPM2_SetCommandRetries + \sa wolfTPM2_SetCommandRetries +*/ +WOLFTPM_API int TPM2_GetCommandRetries(TPM2_CTX* ctx); + /*! \ingroup TPM2_Proprietary \brief Sets the structure holding the TPM Authorizations. diff --git a/wolftpm/tpm2_packet.h b/wolftpm/tpm2_packet.h index 88f6a52f..5c052c1b 100644 --- a/wolftpm/tpm2_packet.h +++ b/wolftpm/tpm2_packet.h @@ -241,6 +241,8 @@ WOLFTPM_LOCAL void TPM2_Packet_ParseAttest(TPM2_Packet* packet, TPMS_ATTEST* out WOLFTPM_LOCAL TPM_RC TPM2_Packet_Parse(TPM_RC rc, TPM2_Packet* packet); WOLFTPM_LOCAL int TPM2_Packet_Finalize(TPM2_Packet* packet, TPM_ST tag, TPM_CC cc); +WOLFTPM_TEST_API int TPM2_Packet_RetryRestore(TPM_RC rc, int* retries, + TPM2_Packet* packet, const byte* cmdHdr, int origSize); WOLFTPM_LOCAL int TPM2_GetCmdAuthCount(TPM2_CTX* ctx, const CmdInfo_t* info); diff --git a/wolftpm/tpm2_types.h b/wolftpm/tpm2_types.h index f8ab3fa4..7ebf9578 100644 --- a/wolftpm/tpm2_types.h +++ b/wolftpm/tpm2_types.h @@ -571,6 +571,18 @@ typedef int64_t INT64; #define TPM_SPI_WAIT_RETRY 50 #endif +/* Number of times to transparently resubmit a command on TPM_RC_RETRY. + * The TPM returns TPM_RC_RETRY when momentarily busy (e.g. persisting the + * daUsed flag on first auth use of a non-noDA key). Set to 0, or define + * WOLFTPM_NO_RETRY, to disable and pass TPM_RC_RETRY back to the caller. */ +#ifdef WOLFTPM_NO_RETRY + #undef WOLFTPM_MAX_RETRIES + #define WOLFTPM_MAX_RETRIES 0 +#endif +#ifndef WOLFTPM_MAX_RETRIES +#define WOLFTPM_MAX_RETRIES 3 +#endif + #ifndef MAX_SYM_BLOCK_SIZE #define MAX_SYM_BLOCK_SIZE 20 #endif diff --git a/wolftpm/tpm2_wrap.h b/wolftpm/tpm2_wrap.h index 8978f0ba..84a5fdf0 100644 --- a/wolftpm/tpm2_wrap.h +++ b/wolftpm/tpm2_wrap.h @@ -341,6 +341,34 @@ WOLFTPM_API int wolfTPM2_Cleanup_ex(WOLFTPM2_DEV* dev, int doShutdown); */ WOLFTPM_API int wolfTPM2_GetTpmDevId(WOLFTPM2_DEV* dev); +/*! + \ingroup wolfTPM2_Wrappers + \brief Sets the number of times a command is transparently resubmitted on TPM_RC_RETRY + \brief The TPM returns TPM_RC_RETRY when momentarily busy (for example persisting the daUsed flag on first auth use of a non-noDA key such as an AIK or SUDI key). wolfTPM resends the identical command up to this many times. Defaults to WOLFTPM_MAX_RETRIES; set to 0 to disable and have TPM_RC_RETRY returned to the caller. + + \return TPM_RC_SUCCESS: successful + \return BAD_FUNC_ARG: dev is NULL or retries is negative + + \param dev pointer to a populated structure of WOLFTPM2_DEV type + \param retries number of additional resubmit attempts on TPM_RC_RETRY (0 disables) + + \sa wolfTPM2_GetCommandRetries +*/ +WOLFTPM_API int wolfTPM2_SetCommandRetries(WOLFTPM2_DEV* dev, int retries); + +/*! + \ingroup wolfTPM2_Wrappers + \brief Returns the number of times a command is transparently resubmitted on TPM_RC_RETRY + + \return the configured retry count on success + \return BAD_FUNC_ARG: dev is a NULL pointer + + \param dev pointer to a populated structure of WOLFTPM2_DEV type + + \sa wolfTPM2_SetCommandRetries +*/ +WOLFTPM_API int wolfTPM2_GetCommandRetries(WOLFTPM2_DEV* dev); + /*! \ingroup wolfTPM2_Wrappers \brief Asks the TPM to perform its self test From 720d90285aa1a87f9ff0d46e1a08e8bfbc01adca Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 23 Jun 2026 15:48:26 -0700 Subject: [PATCH 2/5] Address review: guard retry code with WOLFTPM_NO_RETRY, drop wrappers, document option --- ChangeLog.md | 6 +++--- README.md | 2 ++ src/tpm2.c | 4 ++++ src/tpm2_packet.c | 2 ++ src/tpm2_wrap.c | 16 ---------------- tests/unit_tests.c | 24 ++++++++++++++---------- wolftpm/tpm2.h | 6 ++++-- wolftpm/tpm2_packet.h | 2 ++ wolftpm/tpm2_wrap.h | 28 ---------------------------- 9 files changed, 31 insertions(+), 59 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 55dbde10..eb1d5699 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -31,9 +31,9 @@ resubmitted when the TPM reports it is momentarily busy (for example persisting the daUsed flag on first auth use of a non-noDA AIK/SUDI key), matching the TCG ESYS behavior. Resubmit count defaults to `WOLFTPM_MAX_RETRIES` - (3) and is configurable at runtime via `wolfTPM2_SetCommandRetries` / - `TPM2_SetCommandRetries`; define `WOLFTPM_NO_RETRY` or set the count to 0 to - disable and return `TPM_RC_RETRY` to the caller. + (3) and is configurable at runtime via `TPM2_SetCommandRetries`; define + `WOLFTPM_NO_RETRY` or set the count to 0 to disable and return `TPM_RC_RETRY` + to the caller. ## wolfTPM Release 4.0.0 (Apr 22, 2026) diff --git a/README.md b/README.md index bbe164bc..417d6deb 100644 --- a/README.md +++ b/README.md @@ -363,6 +363,8 @@ WOLFTPM2_USE_SW_ECDHE Disables use of TPM for ECC ephemeral key generation and WOLFTPM2_ECC_DEFAULT_CURVE Default ECC curve for wrapper key templates that request P256 (SRK/AIK/general ECC). Defaults to TPM_ECC_NIST_P256, or the smallest enabled curve meeting ECC_MIN_KEY_SZ. Override e.g. -DWOLFTPM2_ECC_DEFAULT_CURVE=TPM_ECC_NIST_P384. TLS_BENCH_MODE Enables TLS benchmarking mode. NO_TPM_BENCH Disables the TPM benchmarking example. +WOLFTPM_MAX_RETRIES Number of times a command is transparently resubmitted when the TPM returns TPM_RC_RETRY (momentarily busy, e.g. persisting the daUsed flag on first auth use of a non-noDA AIK/SUDI key). Defaults to 3. Adjust at runtime with TPM2_SetCommandRetries(). +WOLFTPM_NO_RETRY Compiles out the TPM_RC_RETRY auto-resubmit handling; TPM_RC_RETRY is returned to the caller for manual handling. ``` Note: For the I2C support on Raspberry Pi you may need to enable I2C. Here are the steps: diff --git a/src/tpm2.c b/src/tpm2.c index d35bc8b6..f2b6a117 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -735,6 +735,7 @@ TPM_RC TPM2_SetHalIoCb(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx) return rc; } +#ifndef WOLFTPM_NO_RETRY TPM_RC TPM2_SetCommandRetries(TPM2_CTX* ctx, int retries) { TPM_RC rc; @@ -761,6 +762,7 @@ int TPM2_GetCommandRetries(TPM2_CTX* ctx) /* atomic int read, no lock needed; the setter takes the lock */ return ctx->retries; } +#endif /* !WOLFTPM_NO_RETRY */ /* If timeoutTries <= 0 then it will not try and startup chip and will * use existing default locality */ @@ -775,7 +777,9 @@ TPM_RC TPM2_Init_ex(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx, XMEMSET(ctx, 0, sizeof(TPM2_CTX)); +#ifndef WOLFTPM_NO_RETRY ctx->retries = WOLFTPM_MAX_RETRIES; +#endif #ifndef WOLFTPM2_NO_WOLFCRYPT rc = TPM2_WolfCrypt_Init(); diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 43b8b4b5..d44bf5a7 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -1625,6 +1625,7 @@ TPM_RC TPM2_Packet_Parse(TPM_RC rc, TPM2_Packet* packet) return rc; } +#ifndef WOLFTPM_NO_RETRY int TPM2_Packet_RetryRestore(TPM_RC rc, int* retries, TPM2_Packet* packet, const byte* cmdHdr, int origSize) { @@ -1639,6 +1640,7 @@ int TPM2_Packet_RetryRestore(TPM_RC rc, int* retries, TPM2_Packet* packet, packet->size = origSize; return 1; } +#endif /* !WOLFTPM_NO_RETRY */ int TPM2_Packet_Finalize(TPM2_Packet* packet, TPM_ST tag, TPM_CC cc) { diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 2b8f6552..c9039de5 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -750,22 +750,6 @@ int wolfTPM2_GetTpmDevId(WOLFTPM2_DEV* dev) return (int)dev->ctx.did_vid; /* return something besides INVALID_DEVID */ } -int wolfTPM2_SetCommandRetries(WOLFTPM2_DEV* dev, int retries) -{ - if (dev == NULL) { - return BAD_FUNC_ARG; - } - return TPM2_SetCommandRetries(&dev->ctx, retries); -} - -int wolfTPM2_GetCommandRetries(WOLFTPM2_DEV* dev) -{ - if (dev == NULL) { - return BAD_FUNC_ARG; - } - return TPM2_GetCommandRetries(&dev->ctx); -} - int wolfTPM2_SelfTest(WOLFTPM2_DEV* dev) { int rc; diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 21819134..056c081e 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2515,34 +2515,35 @@ static void test_wolfTPM2_VerifyHashTicket_DigestSize(void) #endif } +#ifndef WOLFTPM_NO_RETRY /* Transparent TPM_RC_RETRY resubmit is configurable. Verify the default seeded * by init, the setter/getter round-trip, and rejection of bad arguments. */ -static void test_wolfTPM2_CommandRetries(void) +static void test_TPM2_CommandRetries(void) { int rc; WOLFTPM2_DEV dev; XMEMSET(&dev, 0, sizeof(dev)); - rc = wolfTPM2_SetCommandRetries(NULL, 1); + rc = TPM2_SetCommandRetries(NULL, 1); AssertIntEQ(rc, BAD_FUNC_ARG); - rc = wolfTPM2_GetCommandRetries(NULL); + rc = TPM2_GetCommandRetries(NULL); AssertIntEQ(rc, BAD_FUNC_ARG); - rc = wolfTPM2_SetCommandRetries(&dev, -1); + rc = TPM2_SetCommandRetries(&dev.ctx, -1); AssertIntEQ(rc, BAD_FUNC_ARG); /* retries is seeded before any HAL IO setup, so the default holds even on * builds without a default IO callback where init returns an error */ (void)TPM2_Init_minimal(&dev.ctx); - AssertIntEQ(wolfTPM2_GetCommandRetries(&dev), WOLFTPM_MAX_RETRIES); + AssertIntEQ(TPM2_GetCommandRetries(&dev.ctx), WOLFTPM_MAX_RETRIES); - rc = wolfTPM2_SetCommandRetries(&dev, 0); + rc = TPM2_SetCommandRetries(&dev.ctx, 0); AssertIntEQ(rc, TPM_RC_SUCCESS); - AssertIntEQ(wolfTPM2_GetCommandRetries(&dev), 0); + AssertIntEQ(TPM2_GetCommandRetries(&dev.ctx), 0); - rc = wolfTPM2_SetCommandRetries(&dev, 7); + rc = TPM2_SetCommandRetries(&dev.ctx, 7); AssertIntEQ(rc, TPM_RC_SUCCESS); - AssertIntEQ(wolfTPM2_GetCommandRetries(&dev), 7); + AssertIntEQ(TPM2_GetCommandRetries(&dev.ctx), 7); TPM2_Cleanup(&dev.ctx); @@ -2600,6 +2601,7 @@ static void test_TPM2_Packet_RetryRestore(void) printf("Test TPM Wrapper:\tRetryRestore logic:\t\tPassed\n"); } +#endif /* !WOLFTPM_NO_RETRY */ /* wolfTPM2_NVCreateAuthPolicy must derive nameAlg from authPolicySz so * the policy digest hash matches the index's nameAlg. Bug-mode hardcoded @@ -5785,8 +5787,10 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_GetKeyTemplate_ex_nameAlg(); test_wolfTPM2_SignHashScheme_DigestSize(); test_wolfTPM2_VerifyHashTicket_DigestSize(); - test_wolfTPM2_CommandRetries(); +#ifndef WOLFTPM_NO_RETRY + test_TPM2_CommandRetries(); test_TPM2_Packet_RetryRestore(); +#endif test_wolfTPM2_NVCreateAuthPolicy_NameAlg(); test_wolfTPM2_GetKeyTemplate_KeyedHash_Scheme(); test_wolfTPM2_LoadEccPublicKey_Ex(); diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index e142287c..44a900a1 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -2230,8 +2230,10 @@ typedef struct TPM2_CTX { unsigned int spdmOnlyDetected:1; /* TPM_RC_DISABLED from Startup */ #endif +#ifndef WOLFTPM_NO_RETRY /* Additional resubmit attempts on TPM_RC_RETRY (0 disables) */ int retries; +#endif } TPM2_CTX; @@ -3776,6 +3778,7 @@ WOLFTPM_API TPM_RC TPM2_ChipStartup(TPM2_CTX* ctx, int timeoutTries); */ WOLFTPM_API TPM_RC TPM2_SetHalIoCb(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCtx); +#ifndef WOLFTPM_NO_RETRY /*! \ingroup TPM2_Proprietary \brief Sets the number of times a command is transparently resubmitted on TPM_RC_RETRY @@ -3788,7 +3791,6 @@ WOLFTPM_API TPM_RC TPM2_SetHalIoCb(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCt \param retries number of additional resubmit attempts on TPM_RC_RETRY (0 disables) \sa TPM2_GetCommandRetries - \sa wolfTPM2_SetCommandRetries */ WOLFTPM_API TPM_RC TPM2_SetCommandRetries(TPM2_CTX* ctx, int retries); @@ -3802,9 +3804,9 @@ WOLFTPM_API TPM_RC TPM2_SetCommandRetries(TPM2_CTX* ctx, int retries); \param ctx pointer to a TPM2_CTX struct \sa TPM2_SetCommandRetries - \sa wolfTPM2_SetCommandRetries */ WOLFTPM_API int TPM2_GetCommandRetries(TPM2_CTX* ctx); +#endif /* !WOLFTPM_NO_RETRY */ /*! \ingroup TPM2_Proprietary diff --git a/wolftpm/tpm2_packet.h b/wolftpm/tpm2_packet.h index 5c052c1b..4c14c316 100644 --- a/wolftpm/tpm2_packet.h +++ b/wolftpm/tpm2_packet.h @@ -241,8 +241,10 @@ WOLFTPM_LOCAL void TPM2_Packet_ParseAttest(TPM2_Packet* packet, TPMS_ATTEST* out WOLFTPM_LOCAL TPM_RC TPM2_Packet_Parse(TPM_RC rc, TPM2_Packet* packet); WOLFTPM_LOCAL int TPM2_Packet_Finalize(TPM2_Packet* packet, TPM_ST tag, TPM_CC cc); +#ifndef WOLFTPM_NO_RETRY WOLFTPM_TEST_API int TPM2_Packet_RetryRestore(TPM_RC rc, int* retries, TPM2_Packet* packet, const byte* cmdHdr, int origSize); +#endif WOLFTPM_LOCAL int TPM2_GetCmdAuthCount(TPM2_CTX* ctx, const CmdInfo_t* info); diff --git a/wolftpm/tpm2_wrap.h b/wolftpm/tpm2_wrap.h index 84a5fdf0..8978f0ba 100644 --- a/wolftpm/tpm2_wrap.h +++ b/wolftpm/tpm2_wrap.h @@ -341,34 +341,6 @@ WOLFTPM_API int wolfTPM2_Cleanup_ex(WOLFTPM2_DEV* dev, int doShutdown); */ WOLFTPM_API int wolfTPM2_GetTpmDevId(WOLFTPM2_DEV* dev); -/*! - \ingroup wolfTPM2_Wrappers - \brief Sets the number of times a command is transparently resubmitted on TPM_RC_RETRY - \brief The TPM returns TPM_RC_RETRY when momentarily busy (for example persisting the daUsed flag on first auth use of a non-noDA key such as an AIK or SUDI key). wolfTPM resends the identical command up to this many times. Defaults to WOLFTPM_MAX_RETRIES; set to 0 to disable and have TPM_RC_RETRY returned to the caller. - - \return TPM_RC_SUCCESS: successful - \return BAD_FUNC_ARG: dev is NULL or retries is negative - - \param dev pointer to a populated structure of WOLFTPM2_DEV type - \param retries number of additional resubmit attempts on TPM_RC_RETRY (0 disables) - - \sa wolfTPM2_GetCommandRetries -*/ -WOLFTPM_API int wolfTPM2_SetCommandRetries(WOLFTPM2_DEV* dev, int retries); - -/*! - \ingroup wolfTPM2_Wrappers - \brief Returns the number of times a command is transparently resubmitted on TPM_RC_RETRY - - \return the configured retry count on success - \return BAD_FUNC_ARG: dev is a NULL pointer - - \param dev pointer to a populated structure of WOLFTPM2_DEV type - - \sa wolfTPM2_SetCommandRetries -*/ -WOLFTPM_API int wolfTPM2_GetCommandRetries(WOLFTPM2_DEV* dev); - /*! \ingroup wolfTPM2_Wrappers \brief Asks the TPM to perform its self test From e9b3add5f9e720fecdf5d9f06220e463f1750fb5 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 23 Jun 2026 16:20:34 -0700 Subject: [PATCH 3/5] Make TPM_RC_RETRY auto-resubmit opt-in (default 0) per review --- ChangeLog.md | 15 ++++++++------- README.md | 4 ++-- wolftpm/tpm2.h | 2 +- wolftpm/tpm2_types.h | 10 ++++++---- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index eb1d5699..0f4b1bd5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -27,13 +27,14 @@ client wrappers, DA/noDA/lockout/self-heal/persistence unit tests, an `examples/management/da_check` end-to-end example, and the `tests/fwtpm_da_retry.sh` CI harness. -* Added transparent `TPM_RC_RETRY` handling so commands are automatically - resubmitted when the TPM reports it is momentarily busy (for example - persisting the daUsed flag on first auth use of a non-noDA AIK/SUDI key), - matching the TCG ESYS behavior. Resubmit count defaults to `WOLFTPM_MAX_RETRIES` - (3) and is configurable at runtime via `TPM2_SetCommandRetries`; define - `WOLFTPM_NO_RETRY` or set the count to 0 to disable and return `TPM_RC_RETRY` - to the caller. +* Added optional transparent `TPM_RC_RETRY` handling so commands can be + automatically resubmitted when the TPM reports it is momentarily busy (for + example persisting the daUsed flag on first auth use of an externally + provisioned non-noDA AIK/SUDI key). Disabled by default to preserve the raw + TPM response code; opt in via `TPM2_SetCommandRetries` at runtime or + `-DWOLFTPM_MAX_RETRIES=N` at build time. Define `WOLFTPM_NO_RETRY` to compile + the handling out entirely. wolfTPM's own key templates set `noDA` and never + trigger it. ## wolfTPM Release 4.0.0 (Apr 22, 2026) diff --git a/README.md b/README.md index 417d6deb..b155c7e0 100644 --- a/README.md +++ b/README.md @@ -363,8 +363,8 @@ WOLFTPM2_USE_SW_ECDHE Disables use of TPM for ECC ephemeral key generation and WOLFTPM2_ECC_DEFAULT_CURVE Default ECC curve for wrapper key templates that request P256 (SRK/AIK/general ECC). Defaults to TPM_ECC_NIST_P256, or the smallest enabled curve meeting ECC_MIN_KEY_SZ. Override e.g. -DWOLFTPM2_ECC_DEFAULT_CURVE=TPM_ECC_NIST_P384. TLS_BENCH_MODE Enables TLS benchmarking mode. NO_TPM_BENCH Disables the TPM benchmarking example. -WOLFTPM_MAX_RETRIES Number of times a command is transparently resubmitted when the TPM returns TPM_RC_RETRY (momentarily busy, e.g. persisting the daUsed flag on first auth use of a non-noDA AIK/SUDI key). Defaults to 3. Adjust at runtime with TPM2_SetCommandRetries(). -WOLFTPM_NO_RETRY Compiles out the TPM_RC_RETRY auto-resubmit handling; TPM_RC_RETRY is returned to the caller for manual handling. +WOLFTPM_MAX_RETRIES Default number of times a command is transparently resubmitted when the TPM returns TPM_RC_RETRY (momentarily busy, e.g. persisting the daUsed flag on first auth use of an externally provisioned non-noDA AIK/SUDI key). Disabled by default (0); opt in with TPM2_SetCommandRetries() at runtime or -DWOLFTPM_MAX_RETRIES=N at build time. wolfTPM's own key templates set noDA and never trigger it. +WOLFTPM_NO_RETRY Compiles out the TPM_RC_RETRY auto-resubmit handling entirely; TPM_RC_RETRY is returned to the caller for manual handling. ``` Note: For the I2C support on Raspberry Pi you may need to enable I2C. Here are the steps: diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 44a900a1..8d381423 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -3782,7 +3782,7 @@ WOLFTPM_API TPM_RC TPM2_SetHalIoCb(TPM2_CTX* ctx, TPM2HalIoCb ioCb, void* userCt /*! \ingroup TPM2_Proprietary \brief Sets the number of times a command is transparently resubmitted on TPM_RC_RETRY - \brief The TPM returns TPM_RC_RETRY when momentarily busy (for example persisting the daUsed flag on first auth use of a non-noDA key). Defaults to WOLFTPM_MAX_RETRIES; set to 0 to disable and have TPM_RC_RETRY returned to the caller. + \brief The TPM returns TPM_RC_RETRY when momentarily busy (for example persisting the daUsed flag on first auth use of a non-noDA key). Disabled by default (WOLFTPM_MAX_RETRIES is 0); pass a count > 0 to enable transparent resubmit. A count of 0 returns TPM_RC_RETRY to the caller. \return TPM_RC_SUCCESS: successful \return BAD_FUNC_ARG: the TPM2 context is NULL or retries is negative diff --git a/wolftpm/tpm2_types.h b/wolftpm/tpm2_types.h index 7ebf9578..42e10502 100644 --- a/wolftpm/tpm2_types.h +++ b/wolftpm/tpm2_types.h @@ -571,16 +571,18 @@ typedef int64_t INT64; #define TPM_SPI_WAIT_RETRY 50 #endif -/* Number of times to transparently resubmit a command on TPM_RC_RETRY. +/* Default number of times to transparently resubmit a command on TPM_RC_RETRY. * The TPM returns TPM_RC_RETRY when momentarily busy (e.g. persisting the - * daUsed flag on first auth use of a non-noDA key). Set to 0, or define - * WOLFTPM_NO_RETRY, to disable and pass TPM_RC_RETRY back to the caller. */ + * daUsed flag on first auth use of a non-noDA key). Disabled by default (0) to + * keep the raw TPM response code; opt in at runtime with TPM2_SetCommandRetries + * or at build time with -DWOLFTPM_MAX_RETRIES=N. Define WOLFTPM_NO_RETRY to + * compile the handling out entirely. */ #ifdef WOLFTPM_NO_RETRY #undef WOLFTPM_MAX_RETRIES #define WOLFTPM_MAX_RETRIES 0 #endif #ifndef WOLFTPM_MAX_RETRIES -#define WOLFTPM_MAX_RETRIES 3 +#define WOLFTPM_MAX_RETRIES 0 #endif #ifndef MAX_SYM_BLOCK_SIZE From 13de985c7eecd026f2bbb5c90cd7dd1d1a7bb7b8 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 14:31:41 -0700 Subject: [PATCH 4/5] Split TPM2_TransmitCommand by WOLFTPM_NO_RETRY and drop WithRetry name per review --- src/tpm2.c | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/tpm2.c b/src/tpm2.c index f2b6a117..0ef93fe0 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -467,23 +467,52 @@ static TPM_RC TPM2_SPDM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) } #endif /* WOLFTPM_SPDM */ +#ifdef WOLFTPM_NO_RETRY +/* Submit the finalized command in packet (length cmdSz) and parse the + * response, returning the TPM response code. */ +static TPM_RC TPM2_TransmitCommand(TPM2_CTX* ctx, TPM2_Packet* packet, + UINT32 cmdSz) +{ + TPM_RC rc; + + /* send command requires packet->pos to be the total command length */ + packet->pos = cmdSz; + +#ifdef WOLFTPM_SPDM + rc = TPM2_SPDM_SendCommand(ctx, packet); + if (rc >= 0) { + if (rc != TPM_RC_SUCCESS) + return rc; /* SPDM active but failed, do not retry cleartext */ + } + else /* rc < 0: SPDM not active, use normal transport */ +#endif + { + rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet); + } + if (rc != 0) + return rc; /* transport error */ + + /* parse response header and extract the TPM response code */ + rc = TPM2_Packet_Parse(rc, packet); + + return rc; +} +#else /* Submit the finalized command in packet (length cmdSz) and parse the * response, returning the TPM response code. On TPM_RC_RETRY the TPM is * momentarily busy (e.g. persisting the daUsed flag on first auth use of a * non-noDA key) and asks for the identical command to be resubmitted; the * error response is header-only, so restoring the command header and resending * up to ctx->retries times recovers transparently. */ -static TPM_RC TPM2_TransmitWithRetry(TPM2_CTX* ctx, TPM2_Packet* packet, +static TPM_RC TPM2_TransmitCommand(TPM2_CTX* ctx, TPM2_Packet* packet, UINT32 cmdSz) { TPM_RC rc; -#ifndef WOLFTPM_NO_RETRY byte cmdHdr[TPM2_HEADER_SIZE]; int origSize = packet->size; int retries = ctx->retries; XMEMCPY(cmdHdr, packet->buf, TPM2_HEADER_SIZE); -#endif for (;;) { /* send command requires packet->pos to be the total command length */ @@ -506,7 +535,6 @@ static TPM_RC TPM2_TransmitWithRetry(TPM2_CTX* ctx, TPM2_Packet* packet, /* parse response header and extract the TPM response code */ rc = TPM2_Packet_Parse(rc, packet); - #ifndef WOLFTPM_NO_RETRY if (TPM2_Packet_RetryRestore(rc, &retries, packet, cmdHdr, origSize)) { #ifdef DEBUG_WOLFTPM printf("TPM_RC_RETRY: resubmitting command, %d retries left\n", @@ -514,12 +542,12 @@ static TPM_RC TPM2_TransmitWithRetry(TPM2_CTX* ctx, TPM2_Packet* packet, #endif continue; } - #endif break; } return rc; } +#endif /* WOLFTPM_NO_RETRY */ static TPM_RC TPM2_SendCommandAuth(TPM2_CTX* ctx, TPM2_Packet* packet, CmdInfo_t* info) @@ -561,8 +589,8 @@ static TPM_RC TPM2_SendCommandAuth(TPM2_CTX* ctx, TPM2_Packet* packet, return rc; } - /* submit command and wait for response, auto-resubmitting on TPM_RC_RETRY */ - rc = TPM2_TransmitWithRetry(ctx, packet, cmdSz); + /* submit command and parse the response */ + rc = TPM2_TransmitCommand(ctx, packet, cmdSz); respSz = packet->size; /* restart the unmarshalling position */ @@ -595,8 +623,8 @@ static TPM_RC TPM2_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) if (ctx == NULL || packet == NULL) return BAD_FUNC_ARG; - /* submit command and wait for response, auto-resubmitting on TPM_RC_RETRY */ - rc = TPM2_TransmitWithRetry(ctx, packet, (UINT32)packet->pos); + /* submit command and parse the response */ + rc = TPM2_TransmitCommand(ctx, packet, (UINT32)packet->pos); return rc; } From ddddacd3740641a5f7756eda8d7d270848c08a7f Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 15:16:27 -0700 Subject: [PATCH 5/5] fwTPM: prevent SPDM-active failure from downgrading to cleartext and dedup transport dispatch --- src/tpm2.c | 55 +++++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/src/tpm2.c b/src/tpm2.c index 0ef93fe0..610acaa5 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -452,7 +452,13 @@ static TPM_RC TPM2_SPDM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) packet->buf, packet->pos, tpmResp, &tpmRespSz); if (rc != 0) { TPM2_ForceZero(tpmResp, sizeof(tpmResp)); - return rc; + #ifdef DEBUG_WOLFTPM + printf("SPDM secured exchange failed: %d\n", rc); + #endif + /* SPDM is active: never downgrade to cleartext. Map the (negative) + * transport error to a positive TPM RC so the caller treats it as a + * hard failure instead of "SPDM not active". */ + return TPM_RC_FAILURE; } if (tpmRespSz > MAX_RESPONSE_SIZE) { @@ -467,6 +473,25 @@ static TPM_RC TPM2_SPDM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) } #endif /* WOLFTPM_SPDM */ +/* Send the finalized command in packet over the SPDM secured channel when a + * session is active, otherwise over the raw transport. Once SPDM is active an + * exchange failure is returned as-is and never falls back to a cleartext send, + * so a forced SPDM failure cannot downgrade the link. */ +static TPM_RC TPM2_DispatchCommand(TPM2_CTX* ctx, TPM2_Packet* packet) +{ + TPM_RC rc; + +#ifdef WOLFTPM_SPDM + rc = TPM2_SPDM_SendCommand(ctx, packet); + if (rc >= 0) + return rc; /* SPDM active: success or hard failure, no cleartext */ + /* rc < 0: SPDM not active, use normal transport */ +#endif + rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet); + + return rc; +} + #ifdef WOLFTPM_NO_RETRY /* Submit the finalized command in packet (length cmdSz) and parse the * response, returning the TPM response code. */ @@ -478,19 +503,9 @@ static TPM_RC TPM2_TransmitCommand(TPM2_CTX* ctx, TPM2_Packet* packet, /* send command requires packet->pos to be the total command length */ packet->pos = cmdSz; -#ifdef WOLFTPM_SPDM - rc = TPM2_SPDM_SendCommand(ctx, packet); - if (rc >= 0) { - if (rc != TPM_RC_SUCCESS) - return rc; /* SPDM active but failed, do not retry cleartext */ - } - else /* rc < 0: SPDM not active, use normal transport */ -#endif - { - rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet); - } + rc = TPM2_DispatchCommand(ctx, packet); if (rc != 0) - return rc; /* transport error */ + return rc; /* transport or SPDM error */ /* parse response header and extract the TPM response code */ rc = TPM2_Packet_Parse(rc, packet); @@ -518,19 +533,9 @@ static TPM_RC TPM2_TransmitCommand(TPM2_CTX* ctx, TPM2_Packet* packet, /* send command requires packet->pos to be the total command length */ packet->pos = cmdSz; - #ifdef WOLFTPM_SPDM - rc = TPM2_SPDM_SendCommand(ctx, packet); - if (rc >= 0) { - if (rc != TPM_RC_SUCCESS) - return rc; /* SPDM active but failed, do not retry cleartext */ - } - else /* rc < 0: SPDM not active, use normal transport */ - #endif - { - rc = (TPM_RC)INTERNAL_SEND_COMMAND(ctx, packet); - } + rc = TPM2_DispatchCommand(ctx, packet); if (rc != 0) - return rc; /* transport error */ + return rc; /* transport or SPDM error */ /* parse response header and extract the TPM response code */ rc = TPM2_Packet_Parse(rc, packet);