From 4656a2c4748960a3d1cb55da31fc29a5aee49f48 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 15 Sep 2026 17:07:29 -0700 Subject: [PATCH 1/5] Add native helpers to query algorithm and PCR bank support --- src/tpm2.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++ src/tpm2_wrap.c | 32 ++---------------- tests/unit_tests.c | 50 ++++++++++++++++++++++++++++ wolftpm/tpm2.h | 40 ++++++++++++++++++++++ 4 files changed, 174 insertions(+), 30 deletions(-) diff --git a/src/tpm2.c b/src/tpm2.c index 95e70a4f..7c081af1 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -7816,6 +7816,88 @@ void TPM2_PrintPublicArea(const TPM2B_PUBLIC* pub) } #endif /* DEBUG_WOLFTPM */ +/* TPM_CAP_ALGS returns algorithms with ID >= property, so a match at index 0 + * means implemented. Fails closed: *isSupported is 0 on any error. */ +int TPM2_IsAlgSupported(TPM_ALG_ID alg, int* isSupported) +{ + int rc; + GetCapability_In in; + GetCapability_Out out; + TPML_ALG_PROPERTY* algs; + + if (isSupported == NULL) { + return BAD_FUNC_ARG; + } + *isSupported = 0; + + XMEMSET(&in, 0, sizeof(in)); + XMEMSET(&out, 0, sizeof(out)); + in.capability = TPM_CAP_ALGS; + in.property = alg; + in.propertyCount = 1; + rc = TPM2_GetCapability(&in, &out); + if (rc != TPM_RC_SUCCESS) { + return rc; /* query failure, distinct from "not supported" */ + } + /* union - confirm the capability asked for */ + if (out.capabilityData.capability != TPM_CAP_ALGS) { + return TPM_RC_VALUE; + } + + algs = &out.capabilityData.data.algorithms; + if (algs->count >= 1 && algs->algProperties[0].alg == alg) { + *isSupported = 1; + } + return TPM_RC_SUCCESS; +} + +/* Implementing a hash and allocating a bank for it are separate: a TPM may + * offer SHA-1 while allocating no SHA-1 bank, and a selection naming an + * unallocated bank is rejected. Ask before TPM2_SetupPCRSel(). Fails closed. */ +int TPM2_IsPcrBankAllocated(TPM_ALG_ID hashAlg, int pcrIndex, int* isAllocated) +{ + int rc; + word32 i; + GetCapability_In in; + GetCapability_Out out; + TPML_PCR_SELECTION* banks; + + if (isAllocated == NULL) { + return BAD_FUNC_ARG; + } + *isAllocated = 0; + if (pcrIndex < 0) { + return BAD_FUNC_ARG; + } + + XMEMSET(&in, 0, sizeof(in)); + XMEMSET(&out, 0, sizeof(out)); + in.capability = TPM_CAP_PCRS; + in.property = 0; + in.propertyCount = HASH_COUNT; /* all assigned banks */ + rc = TPM2_GetCapability(&in, &out); + if (rc != TPM_RC_SUCCESS) { + return rc; + } + if (out.capabilityData.capability != TPM_CAP_PCRS) { + return TPM_RC_VALUE; + } + + banks = &out.capabilityData.data.assignedPCR; + for (i = 0; i < banks->count; i++) { + if (banks->pcrSelections[i].hash != hashAlg) { + continue; + } + if ((pcrIndex / 8) < (int)banks->pcrSelections[i].sizeofSelect && + (banks->pcrSelections[i].pcrSelect[pcrIndex / 8] & + (1 << (pcrIndex % 8))) != 0) { + *isAllocated = 1; + break; + } + } + return TPM_RC_SUCCESS; +} + /******************************************************************************/ /* --- END Helpful API's -- */ /******************************************************************************/ diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 2a947640..fe68a7c5 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -1250,15 +1250,9 @@ int wolfTPM2_GetCapabilities(WOLFTPM2_DEV* dev, WOLFTPM2_CAPS* cap) * Returns TPM_RC_SUCCESS with *isSupported set to 1 (supported) or 0 (not * supported); on any failure a non-zero rc is returned and *isSupported is set * to 0 so a caller that ignores the rc fails closed. - * Queries TPM_CAP_ALGS: the TPM returns algorithms with ID >= property, so a - * match at index 0 for a single-property query means it is implemented. */ + * Delegates to TPM2_IsAlgSupported(); dev is validated but unused. */ int wolfTPM2_IsAlgSupported(WOLFTPM2_DEV* dev, TPM_ALG_ID alg, int* isSupported) { - int rc; - GetCapability_In in; - GetCapability_Out out; - TPML_ALG_PROPERTY* algs; - if (isSupported == NULL) { return BAD_FUNC_ARG; } @@ -1267,29 +1261,7 @@ int wolfTPM2_IsAlgSupported(WOLFTPM2_DEV* dev, TPM_ALG_ID alg, int* isSupported) if (dev == NULL) { return BAD_FUNC_ARG; } - XMEMSET(&in, 0, sizeof(in)); - XMEMSET(&out, 0, sizeof(out)); - in.capability = TPM_CAP_ALGS; - in.property = alg; - in.propertyCount = 1; - rc = TPM2_GetCapability(&in, &out); - if (rc != TPM_RC_SUCCESS) { - return rc; /* query failure, distinct from "not supported" */ - } - /* capabilityData.data is a union - confirm the TPM answered with the - * capability we asked for before reading the algorithm member, so a - * non-conforming response cannot be reinterpreted as an algorithm - * property. */ - if (out.capabilityData.capability != TPM_CAP_ALGS) { - return TPM_RC_VALUE; - } - /* The TPM returns algorithms with ID >= property; a match at index 0 - * means the requested algorithm is implemented. */ - algs = &out.capabilityData.data.algorithms; - if (algs->count >= 1 && algs->algProperties[0].alg == alg) { - *isSupported = 1; - } - return TPM_RC_SUCCESS; + return TPM2_IsAlgSupported(alg, isSupported); } int wolfTPM2_GetHandles(TPM_HANDLE handle, TPML_HANDLE* handles) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index d216a87c..9bbcdef0 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1241,6 +1241,55 @@ static void test_wolfTPM2_IsAlgSupported(void) #endif /* WOLFTPM_SWTPM */ } +/* TPM2_IsPcrBankAllocated: argument validation always, plus a live query on + * the simulator. Mirrors test_wolfTPM2_IsAlgSupported. */ +static void test_TPM2_IsPcrBankAllocated(void) +{ + int isAllocated = 1; /* seeded true to prove the error paths clear it */ +#if defined(WOLFTPM_SWTPM) + int rc; + WOLFTPM2_DEV dev; +#endif + + /* NULL out-param */ + AssertIntEQ(TPM2_IsPcrBankAllocated(TPM_ALG_SHA256, 0, NULL), + BAD_FUNC_ARG); + /* negative index must fail and must not leave the out-param set */ + AssertIntEQ(TPM2_IsPcrBankAllocated(TPM_ALG_SHA256, -1, &isAllocated), + BAD_FUNC_ARG); + AssertIntEQ(isAllocated, 0); + +#if defined(WOLFTPM_SWTPM) + XMEMSET(&dev, 0, sizeof(dev)); + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + AssertIntEQ(rc, 0); + + /* Every TPM 2.0 part allocates a SHA2-256 bank covering PCR 0 */ + isAllocated = 0; + AssertIntEQ(TPM2_IsPcrBankAllocated(TPM_ALG_SHA256, 0, &isAllocated), + TPM_RC_SUCCESS); + AssertIntEQ(isAllocated, 1); + + /* A hash no bank uses reports not-allocated, with a success rc because + * the query itself worked - the distinction this API exists to make. */ + isAllocated = 1; + AssertIntEQ(TPM2_IsPcrBankAllocated((TPM_ALG_ID)0x7FFF, 0, &isAllocated), + TPM_RC_SUCCESS); + AssertIntEQ(isAllocated, 0); + + /* An index beyond the PCR count is not allocated in any bank */ + isAllocated = 1; + AssertIntEQ(TPM2_IsPcrBankAllocated(TPM_ALG_SHA256, 250, &isAllocated), + TPM_RC_SUCCESS); + AssertIntEQ(isAllocated, 0); + + wolfTPM2_Cleanup(&dev); + printf("Test PcrBank: %-40s Passed\n", "Args + Query:"); +#else + printf("Test PcrBank: %-40s Passed\n", "Arg Validation:"); +#endif /* WOLFTPM_SWTPM */ +} + /* Success path for wolfTPM2_PolicyOR: satisfy one branch of a real two-branch * OR on a live policy session and confirm the TPM's running policy digest * matches the offline computation. Simulator only. */ @@ -9309,6 +9358,7 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_FirmwareUpgrade_ex_session(); #endif test_wolfTPM2_IsAlgSupported(); + test_TPM2_IsPcrBankAllocated(); test_wolfTPM2_PolicyOR_success(); #if defined(WOLFTPM_MLDSA) && defined(WOLFTPM_MLKEM) /* Run non-TPM-dependent tests first */ diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 531b99b7..c91398c2 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -4094,6 +4094,46 @@ WOLFTPM_API void TPM2_SetupPCRSel(TPML_PCR_SELECTION* pcr, TPM_ALG_ID alg, WOLFTPM_API void TPM2_SetupPCRSelArray(TPML_PCR_SELECTION* pcr, TPM_ALG_ID alg, byte* pcrArray, word32 pcrArraySz); +/*! + \ingroup TPM2_Proprietary + \brief Report whether the TPM implements a given algorithm + + \note Queries TPM_CAP_ALGS. Fails closed: *isSupported is 0 on any error, + so a query failure cannot be mistaken for "supported". + + \return TPM_RC_SUCCESS: query completed; *isSupported is 1 or 0 + \return BAD_FUNC_ARG: isSupported is NULL + + \param alg the algorithm identifier to test (for example TPM_ALG_SHA512) + \param isSupported output, set to 1 if implemented by the TPM, else 0 + + \sa TPM2_IsPcrBankAllocated +*/ +WOLFTPM_API int TPM2_IsAlgSupported(TPM_ALG_ID alg, int* isSupported); + +/*! + \ingroup TPM2_Proprietary + \brief Report whether a PCR index is allocated in a bank of a given hash + + \note Queries TPM_CAP_PCRS. Implementing a hash and allocating a bank for + it are separate: a TPM may offer SHA-1 while allocating no SHA-1 + bank, and a selection naming an unallocated bank is rejected. Ask + before building a selection with TPM2_SetupPCRSel(). Fails closed: + *isAllocated is 0 on any error. + + \return TPM_RC_SUCCESS: query completed; *isAllocated is 1 or 0 + \return BAD_FUNC_ARG: isAllocated is NULL or pcrIndex is negative + + \param hashAlg the PCR bank hash algorithm (for example TPM_ALG_SHA256) + \param pcrIndex the PCR index to test + \param isAllocated output, set to 1 if allocated in that bank, else 0 + + \sa TPM2_SetupPCRSel + \sa TPM2_IsAlgSupported +*/ +WOLFTPM_API int TPM2_IsPcrBankAllocated(TPM_ALG_ID hashAlg, int pcrIndex, + int* isAllocated); + /*! \ingroup TPM2_Proprietary \brief Get a human readable string for any TPM 2.0 return code From 6655c7548a64e919397fae2d4c7e2056d394bfa8 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 15 Sep 2026 17:07:29 -0700 Subject: [PATCH 2/5] Reject oversized signatures before TPM2_VerifySignature --- examples/pqc/pqc_ctrl.c | 23 ++++++++ src/tpm2_wrap.c | 120 ++++++++++++++++++++++++++++++++++++++++ wolftpm/tpm2.h | 4 ++ 3 files changed, 147 insertions(+) diff --git a/examples/pqc/pqc_ctrl.c b/examples/pqc/pqc_ctrl.c index 2e41c1bd..c4d9c1e2 100644 --- a/examples/pqc/pqc_ctrl.c +++ b/examples/pqc/pqc_ctrl.c @@ -430,6 +430,15 @@ static int do_mldsa(WOLFTPM2_DEV* dev, TPMI_MLDSA_PARAMETER_SET ps) if (rc != TPM_RC_SUCCESS) goto exit; rc = wolfTPM2_VerifySequenceComplete(dev, seq, &key, NULL, 0, sig, sigSz, &validation); + if (rc == BUFFER_E) { + /* Same oversize case do_hash_mldsa() reports: signing worked but the + * TPM cannot take the signature back, so skip rather than fail. */ + printf("SKIP ML-DSA-%-3s signed %d bytes, not verified: " + "signature exceeds this TPM's input buffer\n", + mldsaName(ps), sigSz); + rc = TPM_RC_SUCCESS; + goto exit_quiet; + } if (rc != TPM_RC_SUCCESS) goto exit; seq = 0; /* Complete consumed the sequence object */ @@ -448,6 +457,9 @@ static int do_mldsa(WOLFTPM2_DEV* dev, TPMI_MLDSA_PARAMETER_SET ps) printf("FAIL ML-DSA-%-3s 0x%x: %s\n", mldsaName(ps), rc, wolfTPM2_GetRCString(rc)); } +exit_quiet: + /* The size guard runs before the sequence is consumed, so seq is still + * live on BUFFER_E and must be flushed here like any other exit. */ if (seq != 0) { flushCtx.flushHandle = seq; (void)TPM2_FlushContext(&flushCtx); @@ -493,6 +505,16 @@ static int do_hash_mldsa(WOLFTPM2_DEV* dev, TPMI_MLDSA_PARAMETER_SET ps) rc = wolfTPM2_VerifyDigestSignature(dev, &key, digest, (int)sizeof(digest), sig, sigSz, NULL, 0, &validation); + if (rc == BUFFER_E) { + /* Too large for this TPM to accept back for on-TPM verification. + * Signing worked but nothing checked it, so skip, not pass. Only + * BUFFER_E means oversize; a query error still reports FAIL. */ + printf("SKIP HashML-DSA-%-3s signed %d bytes, not verified: " + "signature exceeds this TPM's input buffer\n", + mldsaName(ps), sigSz); + rc = TPM_RC_SUCCESS; + goto exit_quiet; + } if (rc != TPM_RC_SUCCESS) goto exit; if (validation.tag != TPM_ST_DIGEST_VERIFIED) { @@ -510,6 +532,7 @@ static int do_hash_mldsa(WOLFTPM2_DEV* dev, TPMI_MLDSA_PARAMETER_SET ps) printf("FAIL HashML-DSA-%-3s 0x%x: %s\n", mldsaName(ps), rc, wolfTPM2_GetRCString(rc)); } +exit_quiet: wolfTPM2_UnloadHandle(dev, &key.handle); XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER); return rc; diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index fe68a7c5..c4f1ae3f 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -5724,6 +5724,109 @@ int wolfTPM2_SignHash(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, } +/* Reject a signature the TPM cannot accept as a parameter. Some parts stop + * responding until a hardware reset rather than erroring, so this must not be + * left to the TPM. Two limits apply: TPM_PT_INPUT_BUFFER bounds the parameter + * and TPM_PT_MAX_COMMAND_SIZE bounds the whole command, so the capability read + * is skipped only when the signature plus overhead fits inside + * TPM_MIN_INPUT_BUFFER, the floor every conformant TPM meets. That still + * covers every ECC signature and RSA up to 4096. Above it the limit must be + * established, so this fails closed. Returns TPM_RC_SUCCESS if it fits, + * BUFFER_E if it provably does not, and the query error otherwise so callers + * can tell the two apart. */ + +/* Bytes that share the command with the signature: 10-byte header, up to two + * 4-byte handles, a 4-byte auth-area size, a password session (~9) or an HMAC + * session with a nonce and digest (~75), the digest TPM2B (up to 66), and the + * TPMT_SIGNATURE tag/alg/size fields (~8). Rounded up with margin. Only used + * to reserve room against TPM_PT_MAX_COMMAND_SIZE, so an over-estimate can + * reject a signature that would just fit; override if that ever bites. */ +#ifndef TPM_SIG_CMD_OVERHEAD +#define TPM_SIG_CMD_OVERHEAD 176 +#endif + +static int wolfTPM2_CheckSigInputBuffer(int sigSz) +{ + int rc; + GetCapability_In in; + GetCapability_Out out; + TPML_TAGGED_TPM_PROPERTY* props; + UINT32 inputBuffer; + + if (sigSz < 0) { + return BUFFER_E; + } + /* Overhead is included: a signature that fits the parameter floor could + * still overflow a TPM whose command limit equals that floor. */ + if ((UINT32)sigSz + TPM_SIG_CMD_OVERHEAD <= TPM_MIN_INPUT_BUFFER) { + return TPM_RC_SUCCESS; + } + + XMEMSET(&in, 0, sizeof(in)); + XMEMSET(&out, 0, sizeof(out)); + in.capability = TPM_CAP_TPM_PROPERTIES; + in.property = TPM_PT_INPUT_BUFFER; + in.propertyCount = 1; + rc = TPM2_GetCapability(&in, &out); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFTPM + printf("Signature size check: cannot read TPM_PT_INPUT_BUFFER " + "(0x%x), refusing a %d byte signature\n", rc, sigSz); + #endif + return rc; /* query failure, distinct from a genuine oversize */ + } + /* union - confirm the capability and property asked for */ + props = &out.capabilityData.data.tpmProperties; + if (out.capabilityData.capability != TPM_CAP_TPM_PROPERTIES || + props->count == 0 || + props->tpmProperty[0].property != TPM_PT_INPUT_BUFFER) { + #ifdef DEBUG_WOLFTPM + printf("Signature size check: unexpected capability response, " + "refusing a %d byte signature\n", sigSz); + #endif + return TPM_RC_VALUE; /* not an oversize; the TPM answered wrongly */ + } + + inputBuffer = props->tpmProperty[0].value; + if (inputBuffer == 0) { + return TPM_RC_VALUE; /* nonsensical limit, treat as unreadable */ + } + if ((UINT32)sigSz > inputBuffer) { + #ifdef DEBUG_WOLFTPM + printf("Signature size check: signature %d bytes exceeds the TPM's " + "%u byte input buffer\n", sigSz, (unsigned int)inputBuffer); + #endif + return BUFFER_E; + } + + /* Also check the whole command fits; some parts report both the same. + * Not reporting this limit is not penalised, the check above bounded it. */ + XMEMSET(&in, 0, sizeof(in)); + XMEMSET(&out, 0, sizeof(out)); + in.capability = TPM_CAP_TPM_PROPERTIES; + in.property = TPM_PT_MAX_COMMAND_SIZE; + in.propertyCount = 1; + rc = TPM2_GetCapability(&in, &out); + if (rc == TPM_RC_SUCCESS && + out.capabilityData.capability == TPM_CAP_TPM_PROPERTIES) { + props = &out.capabilityData.data.tpmProperties; + if (props->count > 0 && + props->tpmProperty[0].property == TPM_PT_MAX_COMMAND_SIZE && + props->tpmProperty[0].value > 0 && + (UINT32)sigSz + TPM_SIG_CMD_OVERHEAD > + props->tpmProperty[0].value) { + #ifdef DEBUG_WOLFTPM + printf("Signature size check: signature %d bytes plus overhead " + "exceeds the TPM's %u byte command limit\n", sigSz, + (unsigned int)props->tpmProperty[0].value); + #endif + return BUFFER_E; + } + } + + return TPM_RC_SUCCESS; +} + /* sigAlg: TPM_ALG_RSASSA, TPM_ALG_RSAPSS, TPM_ALG_ECDSA or TPM_ALG_ECDAA */ /* hashAlg: TPM_ALG_SHA1, TPM_ALG_SHA256, TPM_ALG_SHA384 or TPM_ALG_SHA512 */ int wolfTPM2_VerifyHashTicket(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, @@ -5741,6 +5844,11 @@ int wolfTPM2_VerifyHashTicket(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, return BAD_FUNC_ARG; } + rc = wolfTPM2_CheckSigInputBuffer(sigSz); + if (rc != TPM_RC_SUCCESS) { + return rc; + } + if (key->pub.publicArea.type == TPM_ALG_ECC) { if (sigAlg == TPM_ALG_NULL) sigAlg = key->pub.publicArea.parameters.eccDetail.scheme.scheme; @@ -6170,6 +6278,13 @@ int wolfTPM2_VerifySequenceComplete(WOLFTPM2_DEV* dev, return BAD_FUNC_ARG; } + /* Before the sequence is advanced: bailing out after SequenceUpdate + * would leave the sequence slot allocated. */ + rc = wolfTPM2_CheckSigInputBuffer(sigSz); + if (rc != TPM_RC_SUCCESS) { + return rc; + } + /* Validate per-key-type sigSz BEFORE the internal SequenceUpdate * call. Otherwise we advance the TPM-side sequence and then bail out * before Complete, leaving the slot allocated until the caller @@ -6457,6 +6572,11 @@ int wolfTPM2_VerifyDigestSignature(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, return BAD_FUNC_ARG; } + rc = wolfTPM2_CheckSigInputBuffer(sigSz); + if (rc != TPM_RC_SUCCESS) { + return rc; + } + XMEMSET(&verifyDigestSigIn, 0, sizeof(verifyDigestSigIn)); verifyDigestSigIn.keyHandle = key->handle.hndl; verifyDigestSigIn.digest.size = (UINT16)digestSz; diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index c91398c2..3a7cdc17 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -738,6 +738,10 @@ typedef enum { } TPM_PT_T; typedef UINT32 TPM_PT; +/* Smallest TPM_PT_INPUT_BUFFER a conformant TPM may report (TCG Part 2), so a + * parameter at or below it fits without reading the capability. */ +#define TPM_MIN_INPUT_BUFFER 1024 + /* PCR Property Tag */ typedef enum { TPM_PT_PCR_FIRST = 0x00000000, From 4fe76e2ad9025fa69fd0af1de61910ba653fe96f Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 15 Sep 2026 17:07:29 -0700 Subject: [PATCH 3/5] Skip example paths for features the TPM does not implement --- examples/native/native_test.c | 70 ++++++++++++++++++++++++----------- examples/wrap/wrap_test.c | 37 +++++++++++------- tests/unit_tests.c | 25 +++++++++++++ wolftpm/tpm2.h | 40 +++++++++++++------- 4 files changed, 123 insertions(+), 49 deletions(-) diff --git a/examples/native/native_test.c b/examples/native/native_test.c index 11d7c0ad..a596dee8 100644 --- a/examples/native/native_test.c +++ b/examples/native/native_test.c @@ -339,6 +339,7 @@ int TPM2_Native_Test(void* userCtx) int TPM2_Native_TestArgs(void* userCtx, int argc, char *argv[]) { int rc; + int isAllocated = 0; TPM2_CTX tpm2Ctx; union { @@ -863,22 +864,34 @@ int TPM2_Native_TestArgs(void* userCtx, int argc, char *argv[]) TPM2_PrintBin(cmdOut.policyGetDigest.policyDigest.buffer, cmdOut.policyGetDigest.policyDigest.size); - /* Read PCR[0] SHA1 */ + /* Many current TPMs allocate no SHA-1 bank; ask before selecting it. A + * query failure is reported, not silently treated as "no bank". */ pcrIndex = 0; - XMEMSET(&cmdIn.pcrRead, 0, sizeof(cmdIn.pcrRead)); - TPM2_SetupPCRSel(&cmdIn.pcrRead.pcrSelectionIn, TPM_ALG_SHA1, pcrIndex); - rc = TPM2_PCR_Read(&cmdIn.pcrRead, &cmdOut.pcrRead); + rc = TPM2_IsPcrBankAllocated(TPM_ALG_SHA1, pcrIndex, &isAllocated); if (rc != TPM_RC_SUCCESS) { - printf("TPM2_PCR_Read failed 0x%x: %s\n", rc, + printf("TPM2_IsPcrBankAllocated failed 0x%x: %s\n", rc, TPM2_GetRCString(rc)); goto exit; } - printf("TPM2_PCR_Read: Index %d, Digest Sz %d, Update Counter %d\n", - pcrIndex, - (int)cmdOut.pcrRead.pcrValues.digests[0].size, - (int)cmdOut.pcrRead.pcrUpdateCounter); - TPM2_PrintBin(cmdOut.pcrRead.pcrValues.digests[0].buffer, - cmdOut.pcrRead.pcrValues.digests[0].size); + if (!isAllocated) { + printf("TPM2_PCR_Read: SHA-1 skipped (no SHA-1 PCR bank allocated)\n"); + } + else { + XMEMSET(&cmdIn.pcrRead, 0, sizeof(cmdIn.pcrRead)); + TPM2_SetupPCRSel(&cmdIn.pcrRead.pcrSelectionIn, TPM_ALG_SHA1, pcrIndex); + rc = TPM2_PCR_Read(&cmdIn.pcrRead, &cmdOut.pcrRead); + if (rc != TPM_RC_SUCCESS) { + printf("TPM2_PCR_Read failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + goto exit; + } + printf("TPM2_PCR_Read: Index %d, Digest Sz %d, Update Counter %d\n", + pcrIndex, + (int)cmdOut.pcrRead.pcrValues.digests[0].size, + (int)cmdOut.pcrRead.pcrUpdateCounter); + TPM2_PrintBin(cmdOut.pcrRead.pcrValues.digests[0].buffer, + cmdOut.pcrRead.pcrValues.digests[0].size); + } #ifndef WOLFTPM2_NO_WOLFCRYPT /* Set Auth Session index 0 */ @@ -892,20 +905,31 @@ int TPM2_Native_TestArgs(void* userCtx, int argc, char *argv[]) session[0].nonceCaller.size = TPM2_GetHashDigestSize(WOLFTPM2_WRAP_DIGEST); session[0].auth = sessionAuth; - /* Policy PCR (Get) */ + /* Policy PCR (Get). Selects the SHA-1 bank, so skip when unallocated. */ pcrIndex = 0; - XMEMSET(&cmdIn.policyPCR, 0, sizeof(cmdIn.policyPCR)); - cmdIn.policyPCR.policySession = sessionHandle; - cmdIn.policyPCR.pcrDigest.size = 0; - TPM2_SetupPCRSel(&cmdIn.policyPCR.pcrs, TPM_ALG_SHA1, pcrIndex); - rc = TPM2_PolicyPCR(&cmdIn.policyPCR); + rc = TPM2_IsPcrBankAllocated(TPM_ALG_SHA1, pcrIndex, &isAllocated); if (rc != TPM_RC_SUCCESS) { - printf("TPM2_PolicyPCR failed 0x%x: %s\n", rc, + printf("TPM2_IsPcrBankAllocated failed 0x%x: %s\n", rc, TPM2_GetRCString(rc)); goto exit; } + if (!isAllocated) { + printf("TPM2_PolicyPCR: SHA-1 skipped (no SHA-1 PCR bank allocated)\n"); + } else { - printf("TPM2_PolicyPCR: Updated\n"); + XMEMSET(&cmdIn.policyPCR, 0, sizeof(cmdIn.policyPCR)); + cmdIn.policyPCR.policySession = sessionHandle; + cmdIn.policyPCR.pcrDigest.size = 0; + TPM2_SetupPCRSel(&cmdIn.policyPCR.pcrs, TPM_ALG_SHA1, pcrIndex); + rc = TPM2_PolicyPCR(&cmdIn.policyPCR); + if (rc != TPM_RC_SUCCESS) { + printf("TPM2_PolicyPCR failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + goto exit; + } + else { + printf("TPM2_PolicyPCR: Updated\n"); + } } XMEMSET(&session[0], 0, sizeof(TPM2_AUTH_SESSION)); session[0].sessionHandle = TPM_RS_PW; @@ -1635,7 +1659,7 @@ int TPM2_Native_TestArgs(void* userCtx, int argc, char *argv[]) cmdIn.encDec.decrypt = NO; cmdIn.encDec.mode = TEST_AES_MODE; rc = TPM2_EncryptDecrypt2(&cmdIn.encDec, &cmdOut.encDec); - if (WOLFTPM_IS_COMMAND_UNAVAILABLE(rc)) { /* some TPM's may not support command */ + if (WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED(rc)) { printf("TPM2_EncryptDecrypt2: Is not a supported feature without enabling due to export controls\n"); perform_EncryptDecrypt2 = 0; rc = 0; @@ -1657,7 +1681,9 @@ int TPM2_Native_TestArgs(void* userCtx, int argc, char *argv[]) cmdIn.encDec.decrypt = YES; cmdIn.encDec.mode = TEST_AES_MODE; rc = TPM2_EncryptDecrypt2(&cmdIn.encDec, &cmdOut.encDec); - if (rc == TPM_RC_COMMAND_CODE) { /* some TPM's may not support command */ + if (WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED(rc)) { + /* Leave rc set: the check below inspects it to tell a real + * result from a skip, and cmdOut holds stale output. */ printf("TPM2_EncryptDecrypt2: Is not a supported feature without enabling due to export controls\n"); } else if (rc != TPM_RC_SUCCESS) { @@ -1673,7 +1699,7 @@ int TPM2_Native_TestArgs(void* userCtx, int argc, char *argv[]) cmdOut.encDec.outData.size) == 0) { printf("Encrypt/Decrypt test success\n"); } - else if (WOLFTPM_IS_COMMAND_UNAVAILABLE(rc)) { + else if (WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED(rc)) { printf("Encrypt/Decrypt test result allowed as pass since hardware doesn't support.\n"); rc = TPM_RC_SUCCESS; } diff --git a/examples/wrap/wrap_test.c b/examples/wrap/wrap_test.c index e4b03e1f..cb082d7e 100644 --- a/examples/wrap/wrap_test.c +++ b/examples/wrap/wrap_test.c @@ -70,6 +70,7 @@ int TPM2_Wrapper_Test(void* userCtx) int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) { int rc, i; + int isSupported = 0; WOLFTPM2_DEV dev; WOLFTPM2_CAPS caps; WOLFTPM2_KEY ekKey; @@ -683,19 +684,27 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) printf("ECC DH Test %s\n", rc == 0 ? "Passed" : "Failed"); /* ECC Public Key Signature Verify Test/Example */ - rc = wolfTPM2_LoadEccPublicKey(&dev, &publicKey, TPM_ECC_NIST_P256, - kEccTestPubQX, sizeof(kEccTestPubQX), - kEccTestPubQY, sizeof(kEccTestPubQY)); + /* Vector below uses a SHA-1 digest, which many current TPMs lack. */ + rc = wolfTPM2_IsAlgSupported(&dev, TPM_ALG_SHA1, &isSupported); if (rc != 0) goto exit; + if (!isSupported) { + printf("ECC Verify Test Skipped (TPM does not implement SHA-1)\n"); + } + else { + rc = wolfTPM2_LoadEccPublicKey(&dev, &publicKey, TPM_ECC_NIST_P256, + kEccTestPubQX, sizeof(kEccTestPubQX), + kEccTestPubQY, sizeof(kEccTestPubQY)); + if (rc != 0) goto exit; - rc = wolfTPM2_VerifyHashScheme(&dev, &publicKey, - kEccTestSigRS, sizeof(kEccTestSigRS), - kEccTestMsg, sizeof(kEccTestMsg), TPM_ALG_ECDSA, TPM_ALG_SHA1); - if (rc != 0) goto exit; + rc = wolfTPM2_VerifyHashScheme(&dev, &publicKey, + kEccTestSigRS, sizeof(kEccTestSigRS), + kEccTestMsg, sizeof(kEccTestMsg), TPM_ALG_ECDSA, TPM_ALG_SHA1); + if (rc != 0) goto exit; - rc = wolfTPM2_UnloadHandle(&dev, &publicKey.handle); - if (rc != 0) goto exit; - printf("ECC Verify Test Passed\n"); + rc = wolfTPM2_UnloadHandle(&dev, &publicKey.handle); + if (rc != 0) goto exit; + printf("ECC Verify Test Passed\n"); + } /*------------------------------------------------------------------------*/ /* ECC KEY LOADING TESTS */ @@ -973,7 +982,7 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) XMEMCPY(aesIv, TEST_AES_IV, (word32)sizeof(TEST_AES_IV)); rc = wolfTPM2_EncryptDecrypt(&dev, &aesKey, message.buffer, cipher.buffer, message.size, aesIv, (word32)sizeof(aesIv), WOLFTPM2_ENCRYPT); - if (rc != 0 && !WOLFTPM_IS_COMMAND_UNAVAILABLE(rc)) goto exit; + if (rc != 0 && !WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED(rc)) goto exit; XMEMSET(plain.buffer, 0, sizeof(plain.buffer)); plain.size = message.size; @@ -990,7 +999,7 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) XMEMCMP(cipher.buffer, TEST_AES_VERIFY, cipher.size) == 0) { printf("Encrypt/Decrypt (known key) test success\n"); } - else if (WOLFTPM_IS_COMMAND_UNAVAILABLE(rc)) { + else if (WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED(rc)) { printf("Encrypt/Decrypt: Is not a supported feature due to export controls\n"); } else { @@ -1020,7 +1029,7 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) XMEMSET(aesIv, 0, sizeof(aesIv)); rc = wolfTPM2_EncryptDecrypt(&dev, &aesKey, message.buffer, cipher.buffer, message.size, aesIv, (word32)sizeof(aesIv), WOLFTPM2_ENCRYPT); - if (rc != 0 && !WOLFTPM_IS_COMMAND_UNAVAILABLE(rc)) goto exit; + if (rc != 0 && !WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED(rc)) goto exit; XMEMSET(plain.buffer, 0, sizeof(plain.buffer)); plain.size = message.size; @@ -1035,7 +1044,7 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) XMEMCMP(message.buffer, plain.buffer, message.size) == 0) { printf("Encrypt/Decrypt test success\n"); } - else if (WOLFTPM_IS_COMMAND_UNAVAILABLE(rc)) { + else if (WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED(rc)) { printf("Encrypt/Decrypt: Is not a supported feature due to export controls\n"); } else { diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 9bbcdef0..110ffb05 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2729,6 +2729,31 @@ static void test_WOLFTPM_IS_COMMAND_UNAVAILABLE(void) AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(0x00000343)); /* layer bits */ AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(0x000b0142)); /* vendor, non-cc */ + /* A command that exists but is switched off answers TPM_RC_DISABLED, which + * must not be confused with an absent one. Same masking and >= 0 gate. */ + AssertIntNE(0, WOLFTPM_IS_COMMAND_DISABLED((int)TPM_RC_DISABLED)); + AssertIntNE(0, WOLFTPM_IS_COMMAND_DISABLED(0x000b0120)); /* vendor bits */ + AssertIntEQ(0, WOLFTPM_IS_COMMAND_DISABLED((int)TPM_RC_COMMAND_CODE)); + AssertIntEQ(0, WOLFTPM_IS_COMMAND_DISABLED((int)TPM_RC_SUCCESS)); + AssertIntEQ(0, WOLFTPM_IS_COMMAND_DISABLED(-189)); + AssertIntEQ(0, WOLFTPM_IS_COMMAND_DISABLED(-1)); + + /* The combined form accepts either, and nothing else. */ + AssertIntNE(0, + WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED((int)TPM_RC_COMMAND_CODE)); + AssertIntNE(0, + WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED((int)TPM_RC_DISABLED)); + AssertIntEQ(0, + WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED((int)TPM_RC_SUCCESS)); + AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED(-189)); + + /* The shared base macro underlying all three. Note TPM_RC_VALUE is not + * usable here: tpm2_asn.h defines it as an ASN error (-203), shadowing the + * response code of the same name from tpm2.h in this translation unit. */ + AssertIntNE(0, WOLFTPM_RC_IS((int)TPM_RC_DISABLED, TPM_RC_DISABLED)); + AssertIntEQ(0, WOLFTPM_RC_IS((int)TPM_RC_DISABLED, TPM_RC_COMMAND_CODE)); + AssertIntEQ(0, WOLFTPM_RC_IS(-189, TPM_RC_DISABLED)); + printf("Test TPM Wrapper: %-40s Passed\n", "IsCommandUnavailable:"); } diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 3a7cdc17..5e00cf0b 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -2173,21 +2173,35 @@ struct wolfTPM_winContext { #define TPM_E_COMMAND_BLOCKED (0x80280400) #endif -/* Mask off vendor/layer high bits so a vendor-decorated TPM_RC_COMMAND_CODE - * (e.g. NS350 returns 0x000b0143 for 0x143) still matches. Gate on >= 0 so a - * propagated negative wolfCrypt error (e.g. -189) is never misread as an - * unavailable command. TPM_E_COMMAND_BLOCKED is a Windows HRESULT (negative), - * matched exactly. */ -#define WOLFTPM_IS_COMMAND_UNAVAILABLE(code) \ - (((code) >= 0 && \ - (((UINT32)(code)) & 0xFFFFu) == (UINT32)TPM_RC_COMMAND_CODE) || \ - (code) == (int)TPM_E_COMMAND_BLOCKED) -#else -#define WOLFTPM_IS_COMMAND_UNAVAILABLE(code) \ - ((code) >= 0 && \ - (((UINT32)(code)) & 0xFFFFu) == (UINT32)TPM_RC_COMMAND_CODE) #endif /* WOLFTPM_WINAPI */ +/* Compare a return code against a TPM_RC, masking off vendor/layer high bits + * so a vendor-decorated code (NS350 returns 0x000b0143 for 0x143) still + * matches. Gate on >= 0 so a propagated negative wolfCrypt error (e.g. -189) + * is never misread as a TPM response code. */ +#define WOLFTPM_RC_IS(code, rc) \ + ((code) >= 0 && (((UINT32)(code)) & 0xFFFFu) == (UINT32)(rc)) + +/* The TPM does not implement this command. TPM_E_COMMAND_BLOCKED is a Windows + * HRESULT (negative), so it is matched exactly rather than masked. */ +#ifdef WOLFTPM_WINAPI + #define WOLFTPM_IS_COMMAND_UNAVAILABLE(code) \ + (WOLFTPM_RC_IS(code, TPM_RC_COMMAND_CODE) || \ + (code) == (int)TPM_E_COMMAND_BLOCKED) +#else + #define WOLFTPM_IS_COMMAND_UNAVAILABLE(code) \ + WOLFTPM_RC_IS(code, TPM_RC_COMMAND_CODE) +#endif + +/* Implemented but switched off, commonly TPM2_EncryptDecrypt for export + * controls; answers TPM_RC_DISABLED not TPM_RC_COMMAND_CODE. */ +#define WOLFTPM_IS_COMMAND_DISABLED(code) \ + WOLFTPM_RC_IS(code, TPM_RC_DISABLED) + +/* Either form of "the TPM will not run this command". */ +#define WOLFTPM_IS_COMMAND_UNAVAILABLE_OR_DISABLED(code) \ + (WOLFTPM_IS_COMMAND_UNAVAILABLE(code) || WOLFTPM_IS_COMMAND_DISABLED(code)) + /* make sure advanced IO is enabled for I2C */ #ifdef WOLFTPM_I2C #undef WOLFTPM_ADV_IO From 192c59ee5da125dca3dda038d54311e7faab2a01 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 15 Sep 2026 17:07:29 -0700 Subject: [PATCH 4/5] Bound TIS wait loops by real time where a monotonic clock exists --- src/tpm2_tis.c | 115 ++++++++++++++++++++++++++++++++++++++++--- wolftpm/tpm2_types.h | 50 +++++++++++++++++++ 2 files changed, 157 insertions(+), 8 deletions(-) diff --git a/src/tpm2_tis.c b/src/tpm2_tis.c index 0a02df57..17ff23de 100644 --- a/src/tpm2_tis.c +++ b/src/tpm2_tis.c @@ -417,22 +417,115 @@ int TPM2_TIS_Status(TPM2_CTX* ctx, byte* status) sizeof(*status)); } +/* Budget for the wait loops below. An iteration count makes the real timeout + * depend on host speed, so a >20 s RSA key generation times out intermittently. + * Use TPM_TIMEOUT_MS of real time where a monotonic clock exists; elsewhere + * keep counting exactly as before so no port gains a requirement. */ +typedef struct TPM2_TIS_TIMEOUT { +#ifdef WOLFTPM_HAVE_MONOTONIC_MS + word32 start; + int haveStart; +#endif +#if defined(DEBUG_WOLFTPM) && !defined(WOLFTPM_NO_STD_HEADERS) && \ + defined(WOLFTPM_HAVE_MONOTONIC_MS) + #define WOLFTPM_TIS_PROGRESS + word32 secs; /* whole seconds already marked with a dot */ +#endif + int tries; +} TPM2_TIS_TIMEOUT; + +static void TPM2_TIS_TimeoutStart(TPM2_TIS_TIMEOUT* to) +{ + XMEMSET(to, 0, sizeof(*to)); + to->tries = TPM_TIMEOUT_TRIES; +#ifdef WOLFTPM_HAVE_MONOTONIC_MS + to->start = XTPM_GET_TIMEMS(); + /* zero tick means the clock could not be read; count instead */ + to->haveStart = (to->start != 0) ? 1 : 0; +#endif +} + +/* Returns 1 once the budget is spent, 0 while there is still time. */ +static int TPM2_TIS_TimeoutExpired(TPM2_TIS_TIMEOUT* to) +{ +#ifdef WOLFTPM_HAVE_MONOTONIC_MS + word32 elapsed; +#endif + + if (to->tries > 0) { + to->tries--; + } +#ifdef WOLFTPM_HAVE_MONOTONIC_MS + if (to->haveStart) { + /* unsigned subtraction stays correct across the word32 wrap */ + elapsed = (word32)(XTPM_GET_TIMEMS() - to->start); + if (elapsed >= TPM_TIMEOUT_MS) { + return 1; + } + #ifdef WOLFTPM_TIS_PROGRESS + /* Waits of a minute or more are normal for key generation, so show a + * dot per second rather than let a slow command look like a hang. */ + if (elapsed / 1000u > to->secs) { + to->secs = elapsed / 1000u; + printf("."); + fflush(stdout); + } + #endif + /* A clock stuck at one value would never expire, so keep the + * iteration cap as a backstop for that case only. */ + return (to->tries <= 0 && elapsed == 0) ? 1 : 0; + } +#endif + return (to->tries <= 0) ? 1 : 0; +} + +#ifdef WOLFTPM_TIS_PROGRESS +/* End the dot line, if any were printed. */ +static void TPM2_TIS_TimeoutDone(TPM2_TIS_TIMEOUT* to) +{ + if (to->secs > 0) { + printf("\n"); + fflush(stdout); + } +} +#else + #define TPM2_TIS_TimeoutDone(to) (void)(to) +#endif + +#ifdef WOLFTPM_DEBUG_TIMEOUT +/* Elapsed ms where a clock exists, else polls taken. */ +static word32 TPM2_TIS_TimeoutSpent(TPM2_TIS_TIMEOUT* to) +{ +#ifdef WOLFTPM_HAVE_MONOTONIC_MS + if (to->haveStart) { + return (word32)(XTPM_GET_TIMEMS() - to->start); + } +#endif + return (word32)(TPM_TIMEOUT_TRIES - to->tries); +} +#endif + int TPM2_TIS_WaitForStatus(TPM2_CTX* ctx, byte status, byte status_mask) { int rc; - int timeout = TPM_TIMEOUT_TRIES; + int expired = 0; + TPM2_TIS_TIMEOUT to; byte reg = 0; + TPM2_TIS_TimeoutStart(&to); do { rc = TPM2_TIS_Status(ctx, ®); if (rc == TPM_RC_SUCCESS && (reg & status) == status_mask) break; XTPM_WAIT(); - } while (rc == TPM_RC_SUCCESS && --timeout > 0); + expired = TPM2_TIS_TimeoutExpired(&to); + } while (rc == TPM_RC_SUCCESS && !expired); + TPM2_TIS_TimeoutDone(&to); #ifdef WOLFTPM_DEBUG_TIMEOUT - printf("TIS_WaitForStatus: Timeout %d\n", TPM_TIMEOUT_TRIES - timeout); + printf("TIS_WaitForStatus: spent %u\n", + (unsigned int)TPM2_TIS_TimeoutSpent(&to)); #endif - if (timeout <= 0) + if (expired) return TPM_RC_TIMEOUT; return rc; } @@ -458,7 +551,10 @@ int TPM2_TIS_GetBurstCount(TPM2_CTX* ctx, word16* burstCount) #endif { - int timeout = TPM_TIMEOUT_TRIES; + int expired = 0; + TPM2_TIS_TIMEOUT to; + + TPM2_TIS_TimeoutStart(&to); *burstCount = 0; do { rc = TPM2_TIS_Read(ctx, TPM_BURST_COUNT(ctx->locality), @@ -469,16 +565,19 @@ int TPM2_TIS_GetBurstCount(TPM2_CTX* ctx, word16* burstCount) if (rc == TPM_RC_SUCCESS && *burstCount > 0) break; XTPM_WAIT(); - } while (rc == TPM_RC_SUCCESS && --timeout > 0); + expired = TPM2_TIS_TimeoutExpired(&to); + } while (rc == TPM_RC_SUCCESS && !expired); + TPM2_TIS_TimeoutDone(&to); #ifdef WOLFTPM_DEBUG_TIMEOUT - printf("TIS_GetBurstCount: Timeout %d\n", TPM_TIMEOUT_TRIES - timeout); + printf("TIS_GetBurstCount: spent %u\n", + (unsigned int)TPM2_TIS_TimeoutSpent(&to)); #endif if (*burstCount > MAX_SPI_FRAMESIZE) *burstCount = MAX_SPI_FRAMESIZE; - if (timeout <= 0) + if (expired) return TPM_RC_TIMEOUT; } diff --git a/wolftpm/tpm2_types.h b/wolftpm/tpm2_types.h index 68463eb1..e2f98466 100644 --- a/wolftpm/tpm2_types.h +++ b/wolftpm/tpm2_types.h @@ -716,6 +716,56 @@ typedef int64_t INT64; #endif #endif +/* Monotonic ms tick for the TIS wait loops. Wraps ~49 days, so compare with + * unsigned subtraction. WOLFTPM_HAVE_MONOTONIC_MS is set only where a clock + * exists, port-supplied included; otherwise the iteration counter is kept. + * WOLFTPM_NO_MONOTONIC_MS forces the counter. */ +#ifndef WOLFTPM_NO_MONOTONIC_MS + +/* Without this a port-supplied hook would be silently ignored. */ +#ifdef XTPM_GET_TIMEMS + #define WOLFTPM_HAVE_MONOTONIC_MS +#elif !defined(WOLFTPM_NO_STD_HEADERS) + #if defined(WOLFTPM_ZEPHYR) + #include + #define XTPM_GET_TIMEMS() ((word32)k_uptime_get()) + #define WOLFTPM_HAVE_MONOTONIC_MS + #elif defined(WOLFSSL_ESPIDF) || defined(FREERTOS) + #define XTPM_GET_TIMEMS() \ + ((word32)xTaskGetTickCount() * (word32)portTICK_PERIOD_MS) + #define WOLFTPM_HAVE_MONOTONIC_MS + #elif defined(_WIN32) + #include + #define XTPM_GET_TIMEMS() ((word32)GetTickCount64()) + #define WOLFTPM_HAVE_MONOTONIC_MS + #else + /* Include first, then feature-test: strict C99 may not expose + * CLOCK_MONOTONIC, and __linux__ alone would fail to compile. */ + #include + #if defined(CLOCK_MONOTONIC) && \ + (!defined(_POSIX_TIMERS) || _POSIX_TIMERS > 0) + static inline word32 XTPM_GET_TIMEMS(void) + { + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + return 0; + } + return (word32)((word32)ts.tv_sec * 1000u + + (word32)(ts.tv_nsec / 1000000L)); + } + #define WOLFTPM_HAVE_MONOTONIC_MS + #endif + #endif +#endif /* XTPM_GET_TIMEMS */ + +#endif /* !WOLFTPM_NO_MONOTONIC_MS */ + +/* Must cover the slowest single command. RSA-2048 key generation has been + * measured at 89 s on a current part, so this leaves roughly 2x headroom. */ +#ifndef TPM_TIMEOUT_MS +#define TPM_TIMEOUT_MS 180000 +#endif + #ifndef BUFFER_ALIGNMENT #define BUFFER_ALIGNMENT 4 #endif From 9afde207aa4edabf92438315a972fcbd4762f64b Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 15 Sep 2026 17:07:29 -0700 Subject: [PATCH 5/5] Disambiguate ASN error codes from TPM response codes --- src/tpm2_asn.c | 28 ++++++++++++++-------------- wolftpm/tpm2_asn.h | 43 +++++++++++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/tpm2_asn.c b/src/tpm2_asn.c index c0943327..f3c8be76 100644 --- a/src/tpm2_asn.c +++ b/src/tpm2_asn.c @@ -50,7 +50,7 @@ int TPM2_ASN_GetLength_ex(const uint8_t* input, word32* inOutIdx, int* len, *len = 0; /* default length */ if ((idx + 1) > maxIdx) { - return TPM_RC_INSUFFICIENT; + return TPM_RC_ASN_INSUFFICIENT; } b = input[idx++]; @@ -58,7 +58,7 @@ int TPM2_ASN_GetLength_ex(const uint8_t* input, word32* inOutIdx, int* len, word32 bytes = b & 0x7F; /* DER does not allow BER indefinite-length (0x80 => bytes == 0) */ if (bytes == 0 || bytes > 3 || (idx + bytes) > maxIdx) { - return TPM_RC_INSUFFICIENT; + return TPM_RC_ASN_INSUFFICIENT; } while (bytes--) { b = input[idx++]; @@ -69,7 +69,7 @@ int TPM2_ASN_GetLength_ex(const uint8_t* input, word32* inOutIdx, int* len, length = b; if (check && (idx + length) > maxIdx) { - return TPM_RC_INSUFFICIENT; + return TPM_RC_ASN_INSUFFICIENT; } *inOutIdx = idx; @@ -93,7 +93,7 @@ int TPM2_ASN_GetLength(const uint8_t* input, word32* inOutIdx, int* len, \param inOutIdx Current position in buffer, updated to new position \param len Decoded length value \param maxIdx Maximum allowed index in buffer - \return Length on success, TPM_RC_VALUE on tag mismatch, TPM_RC_INSUFFICIENT on buffer error + \return Length on success, TPM_RC_ASN_VALUE on tag mismatch, TPM_RC_ASN_INSUFFICIENT on buffer error */ static int TPM2_ASN_GetHeader(const uint8_t* input, byte tag, word32* inOutIdx, int* len, word32 maxIdx) @@ -103,14 +103,14 @@ static int TPM2_ASN_GetHeader(const uint8_t* input, byte tag, word32* inOutIdx, int length; if ((idx + 1) > maxIdx) - return TPM_RC_INSUFFICIENT; + return TPM_RC_ASN_INSUFFICIENT; b = input[idx++]; if (b != tag) - return TPM_RC_VALUE; + return TPM_RC_ASN_VALUE; if (TPM2_ASN_GetLength(input, &idx, &length, maxIdx) < 0) - return TPM_RC_VALUE; + return TPM_RC_ASN_VALUE; *len = length; *inOutIdx = idx; @@ -164,7 +164,7 @@ int TPM2_ASN_DecodeX509Cert(uint8_t* input, int inputSz, byte sigParamTag = 0; if (input == NULL || x509 == NULL) { - rc = TPM_RC_VALUE; + rc = TPM_RC_ASN_VALUE; } /* Decode outer SEQUENCE */ @@ -193,14 +193,14 @@ int TPM2_ASN_DecodeX509Cert(uint8_t* input, int inputSz, if (rc >= 0) { if (len <= 0 || idx >= (word32)inputSz) { - rc = TPM_RC_VALUE; + rc = TPM_RC_ASN_VALUE; } } if (rc >= 0) { /* check version tag is INTEGER */ if (input[idx] != TPM2_ASN_INTEGER) { - rc = TPM_RC_VALUE; + rc = TPM_RC_ASN_VALUE; } } @@ -282,7 +282,7 @@ int TPM2_ASN_DecodeX509Cert(uint8_t* input, int inputSz, if (outerSigAlgSz != tbsSigAlgSz || XMEMCMP(input + outerSigAlgBegin, input + tbsSigAlgBegin, outerSigAlgSz) != 0) { - rc = TPM_RC_VALUE; + rc = TPM_RC_ASN_VALUE; } } } @@ -299,13 +299,13 @@ int TPM2_ASN_DecodeX509Cert(uint8_t* input, int inputSz, if (sigParamTag != TPM2_ASN_TAG_NULL && sigParamTag != (TPM2_ASN_SEQUENCE | TPM2_ASN_CONSTRUCTED)) { - rc = TPM_RC_VALUE; + rc = TPM_RC_ASN_VALUE; } else { rc = TPM2_ASN_GetHeader(input, sigParamTag, &idx, &len, sigAlgEnd); if (rc >= 0 && sigParamTag == TPM2_ASN_TAG_NULL && len != 0) { - rc = TPM_RC_VALUE; + rc = TPM_RC_ASN_VALUE; } if (rc >= 0) { idx += len; @@ -313,7 +313,7 @@ int TPM2_ASN_DecodeX509Cert(uint8_t* input, int inputSz, } } if (rc >= 0 && idx != sigAlgEnd) { - rc = TPM_RC_VALUE; + rc = TPM_RC_ASN_VALUE; } } diff --git a/wolftpm/tpm2_asn.h b/wolftpm/tpm2_asn.h index 0ad05a3d..1ff69870 100644 --- a/wolftpm/tpm2_asn.h +++ b/wolftpm/tpm2_asn.h @@ -35,11 +35,26 @@ #define MAX_CERT_SZ 2048 #endif -/* ASN Error Codes */ -#define TPM_RC_ASN_PARSE (-201) /* ASN parsing error */ -#define TPM_RC_INSUFFICIENT (-202) /* ASN insufficient data */ -#define TPM_RC_VALUE (-203) /* ASN value error (invalid tag) */ -#define TPM_RC_BUFFER (-204) /* ASN buffer error */ +/* ASN Error Codes. + * Spelled TPM_RC_ASN_* so they cannot shadow the TPM response codes of the + * same short name in tpm2.h: TPM_RC_VALUE there is an enum equal to 0x084 and + * TPM_RC_INSUFFICIENT is 0x09A. Because those are enum constants rather than + * macros, a #ifndef guard cannot see them, so any file including both headers + * silently got the ASN meaning. */ +#define TPM_RC_ASN_PARSE (-201) /* ASN parsing error */ +#define TPM_RC_ASN_INSUFFICIENT (-202) /* ASN insufficient data */ +#define TPM_RC_ASN_VALUE (-203) /* ASN value error (invalid tag) */ +#define TPM_RC_ASN_BUFFER (-204) /* ASN buffer error */ + +/* Deprecated short spellings, kept so existing callers still build. They + * shadow the tpm2.h response codes of the same name, so prefer the + * TPM_RC_ASN_* forms above. Define WOLFTPM_NO_DEPRECATED_ASN_RC to drop them + * and get the tpm2.h meanings instead. */ +#ifndef WOLFTPM_NO_DEPRECATED_ASN_RC + #define TPM_RC_INSUFFICIENT TPM_RC_ASN_INSUFFICIENT + #define TPM_RC_VALUE TPM_RC_ASN_VALUE + #define TPM_RC_BUFFER TPM_RC_ASN_BUFFER +#endif /* ASN.1 Constants */ enum { @@ -78,7 +93,7 @@ typedef struct DecodedX509 { \param inOutIdx Current position in buffer, updated to new position \param len Decoded length value \param maxIdx Maximum allowed index in buffer - \return Length on success, TPM_RC_INSUFFICIENT on buffer error + \return Length on success, TPM_RC_ASN_INSUFFICIENT on buffer error */ WOLFTPM_API int TPM2_ASN_GetLength(const uint8_t* input, word32* inOutIdx, int* len, word32 maxIdx); @@ -91,7 +106,7 @@ WOLFTPM_API int TPM2_ASN_GetLength(const uint8_t* input, word32* inOutIdx, \param len Decoded length value \param maxIdx Maximum allowed index in buffer \param check Flag to enable length validation - \return Length on success, TPM_RC_INSUFFICIENT on buffer error + \return Length on success, TPM_RC_ASN_INSUFFICIENT on buffer error */ WOLFTPM_API int TPM2_ASN_GetLength_ex(const uint8_t* input, word32* inOutIdx, int* len, word32 maxIdx, int check); @@ -104,7 +119,7 @@ WOLFTPM_API int TPM2_ASN_GetLength_ex(const uint8_t* input, word32* inOutIdx, \param inOutIdx Current position in buffer, updated to new position \param tag_len Decoded length value \param tag Expected ASN.1 tag value - \return 0 on success, TPM_RC_INSUFFICIENT on buffer error, TPM_RC_VALUE on tag mismatch + \return 0 on success, TPM_RC_ASN_INSUFFICIENT on buffer error, TPM_RC_ASN_VALUE on tag mismatch */ WOLFTPM_API int TPM2_ASN_DecodeTag(const uint8_t* input, int inputSz, int* inOutIdx, int* tag_len, uint8_t tag); @@ -114,8 +129,8 @@ WOLFTPM_API int TPM2_ASN_DecodeTag(const uint8_t* input, int inputSz, \brief Decodes RSA signature from ASN.1 format \param pInput Pointer to buffer containing ASN.1 encoded RSA signature \param inputSz Size of input buffer - \return Size of decoded signature on success, TPM_RC_VALUE on invalid input, - TPM_RC_INSUFFICIENT on buffer error + \return Size of decoded signature on success, TPM_RC_ASN_VALUE on invalid input, + TPM_RC_ASN_INSUFFICIENT on buffer error */ WOLFTPM_API int TPM2_ASN_RsaDecodeSignature(uint8_t** pInput, int inputSz); @@ -124,7 +139,7 @@ WOLFTPM_API int TPM2_ASN_RsaDecodeSignature(uint8_t** pInput, int inputSz); \param input Buffer containing ASN.1 encoded X.509 certificate \param inputSz Size of input buffer \param x509 Structure to store decoded certificate data - \return 0 on success, TPM_RC_VALUE on invalid input, TPM_RC_INSUFFICIENT on buffer error + \return 0 on success, TPM_RC_ASN_VALUE on invalid input, TPM_RC_ASN_INSUFFICIENT on buffer error */ WOLFTPM_API int TPM2_ASN_DecodeX509Cert(uint8_t* input, int inputSz, DecodedX509* x509); @@ -135,8 +150,8 @@ WOLFTPM_API int TPM2_ASN_DecodeX509Cert(uint8_t* input, int inputSz, \param input Buffer containing ASN.1 encoded RSA public key \param inputSz Size of input buffer \param pub TPM2B_PUBLIC structure to store decoded key - \return 0 on success, TPM_RC_VALUE on invalid input, - TPM_RC_INSUFFICIENT on buffer error + \return 0 on success, TPM_RC_ASN_VALUE on invalid input, + TPM_RC_ASN_INSUFFICIENT on buffer error */ WOLFTPM_API int TPM2_ASN_DecodeRsaPubKey(uint8_t* input, int inputSz, TPM2B_PUBLIC* pub); @@ -150,7 +165,7 @@ WOLFTPM_API int TPM2_ASN_DecodeRsaPubKey(uint8_t* input, int inputSz, \param pSig Pointer to buffer containing padded signature, updated to point to unpadded data \param sigSz Size of signature buffer, updated with unpadded size - \return 0 on success, TPM_RC_VALUE on invalid padding + \return 0 on success, TPM_RC_ASN_VALUE on invalid padding */ WOLFTPM_API int TPM2_ASN_RsaUnpadPkcsv15(uint8_t** pSig, int* sigSz);