diff --git a/examples/bench/bench.c b/examples/bench/bench.c index 0bd56013..e705548d 100644 --- a/examples/bench/bench.c +++ b/examples/bench/bench.c @@ -191,7 +191,8 @@ static int bench_sym_aes(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* storageKey, if (rc != 0) goto exit; rc = wolfTPM2_CreateAndLoadKey(dev, &aesKey, &storageKey->handle, &publicTemplate, (byte*)gUsageAuth, sizeof(gUsageAuth)-1); - if ((rc & TPM_RC_MODE) == TPM_RC_MODE || (rc & TPM_RC_VALUE) == TPM_RC_VALUE) { + if ((rc & RC_MAX_FMT1) == TPM_RC_MODE || + (rc & RC_MAX_FMT1) == TPM_RC_VALUE) { printf("Benchmark symmetric %s not supported!\n", desc); rc = 0; goto exit; } @@ -234,6 +235,7 @@ static int bench_pqc_mldsa(WOLFTPM2_DEV* dev, double maxDuration, XMEMSET(&mldsaKey, 0, sizeof(mldsaKey)); XMEMSET(&publicTemplate, 0, sizeof(publicTemplate)); XMEMSET(message, 0x11, sizeof(message)); + XMEMSET(sig, 0, sizeof(sig)); rc = wolfTPM2_GetKeyTemplate_MLDSA(&publicTemplate, TPMA_OBJECT_sign | TPMA_OBJECT_fixedTPM | TPMA_OBJECT_fixedParent | @@ -275,6 +277,8 @@ static int bench_pqc_mldsa(WOLFTPM2_DEV* dev, double maxDuration, } while (bench_stats_check(start, &count, maxDuration)); rc = bench_asym_done("ML-DSA", 65, "sign", count, start, rc); if (rc != 0) goto exit; + if (count == 0) + goto exit; /* no signature produced; nothing to verify */ bench_stats_start(&count, &start); do { diff --git a/examples/nvram/read.c b/examples/nvram/read.c index 67286fe5..fe0f9866 100644 --- a/examples/nvram/read.c +++ b/examples/nvram/read.c @@ -187,7 +187,7 @@ int TPM2_NVRAM_Read_Example(void* userCtx, int argc, char *argv[]) printf("NV Read: Attributes 0x%08x\n", nv.attributes); - if (((nv.attributes & TPMA_NV_TPM_NT) >> 4) & TPM_NT_EXTEND) { + if (((nv.attributes & TPMA_NV_TPM_NT) >> 4) == TPM_NT_EXTEND) { byte digest[TPM_SHA256_DIGEST_SIZE]; word32 digestLen = (word32)sizeof(digest); printf("NV Read Extend\n"); diff --git a/examples/pcr/policy.c b/examples/pcr/policy.c index d79d0cb1..b7c0730e 100644 --- a/examples/pcr/policy.c +++ b/examples/pcr/policy.c @@ -145,15 +145,15 @@ int TPM2_PCR_Policy_Test(void* userCtx, int argc, char *argv[]) } printf("wolfTPM2_Init: success\n"); - if (paramEncAlg != TPM_ALG_NULL) { - /* Start an authenticated policy session (salted / unbound) */ - rc = wolfTPM2_StartSession(&dev, &tpmSession, NULL, NULL, - TPM_SE_POLICY, paramEncAlg); - if (rc != 0) goto exit; - printf("TPM2_StartAuthSession: sessionHandle 0x%x\n", - (word32)tpmSession.handle.hndl); + /* Start an authenticated policy session (salted / unbound) */ + rc = wolfTPM2_StartSession(&dev, &tpmSession, NULL, NULL, + TPM_SE_POLICY, paramEncAlg); + if (rc != 0) goto exit; + printf("TPM2_StartAuthSession: sessionHandle 0x%x\n", + (word32)tpmSession.handle.hndl); - /* set session for authorization of the storage key */ + if (paramEncAlg != TPM_ALG_NULL) { + /* set session for parameter encryption */ rc = wolfTPM2_SetAuthSession(&dev, 0, &tpmSession, (TPMA_SESSION_decrypt | TPMA_SESSION_encrypt | TPMA_SESSION_continueSession)); if (rc != 0) goto exit; diff --git a/examples/pcr/quote.c b/examples/pcr/quote.c index bdf7e3bb..5aeb8ecd 100644 --- a/examples/pcr/quote.c +++ b/examples/pcr/quote.c @@ -210,6 +210,7 @@ int TPM2_PCR_Quote_Test(void* userCtx, int argc, char *argv[]) pubKey = (byte*)XMALLOC(pubKeySz, NULL, DYNAMIC_TYPE_PUBLIC_KEY); if (pubKey == NULL) { printf("Failed to malloc buffer for public key\n"); + rc = MEMORY_E; goto exit; } diff --git a/examples/run_examples.sh b/examples/run_examples.sh index 840063c7..5ae68cf5 100755 --- a/examples/run_examples.sh +++ b/examples/run_examples.sh @@ -835,6 +835,23 @@ if [ $WOLFCRYPT_ENABLE -eq 1 ]; then fi rm -f zip.quote +# PCR Policy tests +echo -e "PCR Policy tests" +./examples/pcr/policy 16 >> $TPMPWD/run.out 2>&1 +RESULT=$? +[ $RESULT -ne 0 ] && echo -e "pcr policy failed! $RESULT" && exit 1 +if [ $WOLFCRYPT_ENABLE -eq 1 ]; then + ./examples/pcr/policy 16 -xor >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "pcr policy param enc xor failed! $RESULT" && exit 1 + + if [ $WOLFCRYPT_DEFAULT -eq 0 ]; then + ./examples/pcr/policy 16 -aes >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "pcr policy param enc aes failed! $RESULT" && exit 1 + fi +fi + # Benchmark tests echo -e "Benchmark tests" diff --git a/hal/tpm_io.c b/hal/tpm_io.c index d5757c4d..4590395e 100644 --- a/hal/tpm_io.c +++ b/hal/tpm_io.c @@ -208,6 +208,12 @@ int TPM2_IoCb(TPM2_CTX* ctx, INT32 isRead, UINT32 addr, } #endif +#if !defined(WOLFTPM_I2C) && !defined(WOLFTPM_MMIO) && !defined(WOLFTPM_FWTPM_HAL) + /* the FIFO register transfers plaintext command/response payload */ + TPM2_ForceZero(txBuf, sizeof(txBuf)); + TPM2_ForceZero(rxBuf, sizeof(rxBuf)); +#endif + (void)ctx; return ret; diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index ce17cda3..049b060c 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -93,6 +93,7 @@ static void FwRspInit(TPM2_Packet* pkt, byte* buf, int bufSize) pkt->buf = buf; pkt->pos = TPM2_HEADER_SIZE; /* skip header, filled by Finalize */ pkt->size = bufSize; + pkt->overflow = 0; /* Zero header area so stale data doesn't confuse session detection */ XMEMSET(buf, 0, TPM2_HEADER_SIZE); } @@ -110,10 +111,10 @@ static int FwRspFinalize(TPM2_Packet* pkt, UINT16 tag, TPM_RC rc) } /* Build a minimal error-only response */ -static int FwBuildErrorResponse(byte* rsp, UINT16 tag, TPM_RC rc) +static int FwBuildErrorResponse(byte* rsp, int rspBufSz, UINT16 tag, TPM_RC rc) { TPM2_Packet pkt; - FwRspInit(&pkt, rsp, FWTPM_MAX_COMMAND_SIZE); + FwRspInit(&pkt, rsp, rspBufSz); return FwRspFinalize(&pkt, tag, rc); } @@ -1016,10 +1017,17 @@ static TPM_RC FwCmd_GetRandom(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, /* TPM2B_DIGEST: size + data */ TPM2_Packet_AppendU16(rsp, bytesRequested); - rc = wc_RNG_GenerateBlock(&ctx->rng, - rsp->buf + rsp->pos, bytesRequested); - if (rc != 0) { - rc = TPM_RC_FAILURE; + /* This writes past the packet API, so bound it explicitly. */ + if (rsp->pos + (int)bytesRequested > rsp->size) { + rsp->overflow = 1; + bytesRequested = 0; + } + else { + rc = wc_RNG_GenerateBlock(&ctx->rng, + rsp->buf + rsp->pos, bytesRequested); + if (rc != 0) { + rc = TPM_RC_FAILURE; + } } } @@ -1091,12 +1099,28 @@ static int FwPcrLocalityAllowed(int pcrIndex, int locality, int isReset); /* Overwrite a big-endian UINT32 already appended at buf[pos]. Used to * back-patch a TPML count with the number of entries actually emitted, so a * count/payload mismatch is impossible even if two entries collapse to one. */ -static void FwPatchU32BE(byte* buf, int pos, UINT32 v) +/* Back-patch a reserved field, but only where it actually fits. The + * reservation is dropped silently when the buffer is already full. */ +static void FwPatchU32BE(TPM2_Packet* rsp, int pos, UINT32 v) +{ + if (pos < 0 || pos + 4 > rsp->size) { + rsp->overflow = 1; + return; + } + rsp->buf[pos + 0] = (byte)(v >> 24); + rsp->buf[pos + 1] = (byte)(v >> 16); + rsp->buf[pos + 2] = (byte)(v >> 8); + rsp->buf[pos + 3] = (byte)(v); +} + +/* Back-patch the one-byte moreData flag with the same guard. */ +static void FwPatchMoreData(TPM2_Packet* rsp, int pos, byte v) { - buf[pos + 0] = (byte)(v >> 24); - buf[pos + 1] = (byte)(v >> 16); - buf[pos + 2] = (byte)(v >> 8); - buf[pos + 3] = (byte)(v); + if (pos < 0 || pos + 1 > rsp->size) { + rsp->overflow = 1; + return; + } + rsp->buf[pos] = v; } /* --- TPM2_GetCapability (CC 0x017A) --- */ @@ -1210,7 +1234,7 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, if ((UINT32)numOut > propertyCount) numOut = (int)propertyCount; if (avail > numOut) - rsp->buf[moreDataPos] = 1; /* YES - more entries remain */ + FwPatchMoreData(rsp, moreDataPos, 1); /* more entries remain */ /* Reserve the count and back-patch it to entries actually emitted. */ countPos = rsp->pos; @@ -1235,7 +1259,7 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, haveLast = 1; emitted++; } - FwPatchU32BE(rsp->buf, countPos, (UINT32)emitted); + FwPatchU32BE(rsp, countPos, (UINT32)emitted); break; } @@ -1261,7 +1285,7 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, if ((UINT32)numOut > propertyCount) numOut = (int)propertyCount; if (avail > numOut) - rsp->buf[moreDataPos] = 1; /* YES - more entries remain */ + FwPatchMoreData(rsp, moreDataPos, 1); /* more entries remain */ /* Reserve the count and back-patch it to entries actually emitted. */ countPos = rsp->pos; @@ -1287,7 +1311,7 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, haveLast = 1; emitted++; } - FwPatchU32BE(rsp->buf, countPos, (UINT32)emitted); + FwPatchU32BE(rsp, countPos, (UINT32)emitted); break; } @@ -1418,7 +1442,7 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, /* Truncated (start offset or propertyCount cap): flag moreData=YES. */ if (startIdx + numOut < totalProps) - rsp->buf[moreDataPos] = 1; /* YES */ + FwPatchMoreData(rsp, moreDataPos, 1); /* YES */ TPM2_Packet_AppendU32(rsp, (UINT32)numOut); for (i = 0; i < (UINT32)numOut; i++) { @@ -1496,7 +1520,7 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, /* Truncated (start offset or propertyCount cap): flag moreData=YES. */ if (startIdx + numOut < totalProps) - rsp->buf[moreDataPos] = 1; /* YES */ + FwPatchMoreData(rsp, moreDataPos, 1); /* YES */ TPM2_Packet_AppendU32(rsp, (UINT32)numOut); for (ii = 0; ii < (UINT32)numOut; ii++) { @@ -1589,7 +1613,7 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, if ((UINT32)count > propertyCount) { count = (int)propertyCount; - rsp->buf[moreDataPos] = 1; /* YES - more handles available */ + FwPatchMoreData(rsp, moreDataPos, 1); /* more handles available */ } TPM2_Packet_AppendU32(rsp, (UINT32)count); if (count > 0) { @@ -1662,6 +1686,181 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, /* --- TPM2_TestParms (CC 0x018A) --- */ /* Validates that the given algorithm parameters are supported. * No auth, no output params. */ +/* Validate a hash selector carried inside a scheme's details union. */ +static TPM_RC FwTestHashAlg(UINT16 hashAlg) +{ + TPM_RC rc = TPM_RC_SUCCESS; + + if (TPM2_GetHashDigestSize(hashAlg) <= 0) { + rc = TPM_RC_HASH; + } + return rc; +} + +/* Validate a TPMT_SYM_DEF(_OBJECT). TPM_ALG_NULL means no symmetric alg. */ +static TPM_RC FwTestSymDef(const TPMT_SYM_DEF* sym) +{ + TPM_RC rc = TPM_RC_SUCCESS; + + if (sym->algorithm == TPM_ALG_NULL) { + rc = TPM_RC_SUCCESS; + } +#ifndef NO_AES + else if (sym->algorithm == TPM_ALG_AES) { + UINT16 mode = sym->mode.sym; + if (sym->keyBits.aes != 128 && sym->keyBits.aes != 192 && + sym->keyBits.aes != 256) { + rc = TPM_RC_KEY_SIZE; + } + else if (mode != TPM_ALG_CFB && mode != TPM_ALG_CBC && + mode != TPM_ALG_CTR && mode != TPM_ALG_OFB && + mode != TPM_ALG_ECB) { + rc = TPM_RC_MODE; + } + } +#endif + else { + rc = TPM_RC_SYMMETRIC; + } + return rc; +} + +#ifndef NO_RSA +/* Validate a TPMT_RSA_SCHEME selector and its hash, when it carries one. */ +static TPM_RC FwTestRsaScheme(const TPMT_RSA_SCHEME* scheme) +{ + TPM_RC rc = TPM_RC_SUCCESS; + UINT16 alg = scheme->scheme; + + if (alg == TPM_ALG_NULL || alg == TPM_ALG_RSAES) { + rc = TPM_RC_SUCCESS; + } + else if (alg == TPM_ALG_RSASSA || alg == TPM_ALG_RSAPSS || + alg == TPM_ALG_OAEP) { + rc = FwTestHashAlg(scheme->details.anySig.hashAlg); + } + else { + rc = TPM_RC_SCHEME; + } + return rc; +} +#endif /* !NO_RSA */ + +#ifdef HAVE_ECC +/* Validate a TPMT_ECC_SCHEME selector and its hash. */ +static TPM_RC FwTestEccScheme(const TPMT_ECC_SCHEME* scheme) +{ + TPM_RC rc = TPM_RC_SUCCESS; + UINT16 alg = scheme->scheme; + + if (alg == TPM_ALG_NULL) { + rc = TPM_RC_SUCCESS; + } + else if (alg == TPM_ALG_ECDSA || alg == TPM_ALG_ECDH || + alg == TPM_ALG_ECDAA || alg == TPM_ALG_ECSCHNORR) { + rc = FwTestHashAlg(scheme->details.any.hashAlg); + } + else { + rc = TPM_RC_SCHEME; + } + return rc; +} + +/* Validate a TPMT_KDF_SCHEME selector and its hash. */ +static TPM_RC FwTestKdfScheme(const TPMT_KDF_SCHEME* kdf) +{ + TPM_RC rc = TPM_RC_SUCCESS; + UINT16 alg = kdf->scheme; + + if (alg == TPM_ALG_NULL) { + rc = TPM_RC_SUCCESS; + } + /* HKDF is the KDF an ECC DHKEM object carries, per RFC 9180 Sec.4.1 */ + else if (alg == TPM_ALG_KDF1_SP800_56A || alg == TPM_ALG_KDF2 || + alg == TPM_ALG_KDF1_SP800_108 || alg == TPM_ALG_HKDF) { + rc = FwTestHashAlg(kdf->details.any.hashAlg); + } + else { + rc = TPM_RC_KDF; + } + return rc; +} +#endif /* HAVE_ECC */ + +/* Validate the TPMU_PUBLIC_PARMS body of a TPMT_PUBLIC_PARMS. The type + * selector has already been consumed from the command packet. */ +static TPM_RC FwTestPublicParms(TPM2_Packet* cmd, UINT16 algType) +{ + TPM_RC rc = TPM_RC_SUCCESS; + TPMU_PUBLIC_PARMS params; + + XMEMSET(¶ms, 0, sizeof(params)); + TPM2_Packet_ParsePublicParms(cmd, algType, ¶ms); + + if (algType == TPM_ALG_SYMCIPHER) { + /* A SYMCIPHER key must name a real algorithm, not TPM_ALG_NULL. */ + if (params.symDetail.sym.algorithm == TPM_ALG_NULL) { + rc = TPM_RC_SYMMETRIC; + } + else { + rc = FwTestSymDef((const TPMT_SYM_DEF*)¶ms.symDetail.sym); + } + } + else if (algType == TPM_ALG_KEYEDHASH) { + UINT16 alg = params.keyedHashDetail.scheme.scheme; + if (alg == TPM_ALG_NULL) { + rc = TPM_RC_SUCCESS; + } + else if (alg == TPM_ALG_HMAC) { + rc = FwTestHashAlg(params.keyedHashDetail.scheme.details.hmac.hashAlg); + } + else if (alg == TPM_ALG_XOR) { + rc = FwTestHashAlg(params.keyedHashDetail.scheme.details.xorr.hashAlg); + } + else { + rc = TPM_RC_SCHEME; + } + } +#ifndef NO_RSA + else if (algType == TPM_ALG_RSA) { + UINT16 keyBits = params.rsaDetail.keyBits; + rc = FwTestSymDef(¶ms.rsaDetail.symmetric); + if (rc == 0) { + rc = FwTestRsaScheme(¶ms.rsaDetail.scheme); + } + /* keyBits 0 selects the 2048-bit default in FwDeriveRsaPrimaryKey. */ + if (rc == 0 && keyBits != 0 && keyBits != 1024 && keyBits != 2048 && + keyBits != 3072 && keyBits != 4096) { + rc = TPM_RC_KEY_SIZE; + } + /* exponent 0 selects the default; anything else must be odd > 2. */ + if (rc == 0 && params.rsaDetail.exponent != 0 && + (params.rsaDetail.exponent < 3 || + (params.rsaDetail.exponent & 1) == 0)) { + rc = TPM_RC_VALUE; + } + } +#endif +#ifdef HAVE_ECC + else if (algType == TPM_ALG_ECC) { + rc = FwTestSymDef(¶ms.eccDetail.symmetric); + if (rc == 0) { + rc = FwTestEccScheme(¶ms.eccDetail.scheme); + } + if (rc == 0 && FwGetWcCurveId(params.eccDetail.curveID) < 0) { + rc = TPM_RC_CURVE; + } + if (rc == 0) { + rc = FwTestKdfScheme(¶ms.eccDetail.kdf); + } + } +#endif + else { + rc = TPM_RC_TYPE; + } + return rc; +} + static TPM_RC FwCmd_TestParms(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, TPM2_Packet* rsp, UINT16 cmdTag) { @@ -1692,14 +1891,9 @@ static TPM_RC FwCmd_TestParms(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, #endif case TPM_ALG_KEYEDHASH: case TPM_ALG_SYMCIPHER: - case TPM_ALG_AES: - case TPM_ALG_SHA256: - #ifdef WOLFSSL_SHA384 - case TPM_ALG_SHA384: - #endif - case TPM_ALG_HMAC: - case TPM_ALG_NULL: - /* Supported - skip remaining type-specific params */ + /* Part 3 Sec.30.3.1: unmarshal the parameters and return the + * matching error if any of them is not supported. */ + rc = FwTestPublicParms(cmd, algType); break; #ifdef WOLFTPM_MLDSA /* Part 2 Sec.12.2.3.6: TestParms for ML-DSA / Hash-ML-DSA / ML-KEM @@ -1791,13 +1985,8 @@ static TPM_RC FwCmd_TestParms(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, } #endif /* WOLFTPM_MLKEM */ default: - /* Unrecognized algorithm type. TPM_RC_PARMS only exists - * under WOLFTPM_V185; fall back to TPM_RC_TYPE otherwise. */ - #ifdef WOLFTPM_V185 - rc = TPM_RC_PARMS; - #else + /* Part 2 Table 224: bad TPMI_ALG_PUBLIC is TPM_RC_TYPE */ rc = TPM_RC_TYPE; - #endif break; } } @@ -16221,13 +16410,23 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, #endif int pj, hj; /* Loop indices for auth validation */ int authFail; /* Password comparison result */ + int rspCap; /* Caller's response buffer capacity */ + int rspTruncated = 0; /* Response did not fit the buffer */ if (ctx == NULL || cmdBuf == NULL || rspBuf == NULL || rspSize == NULL) { return BAD_FUNC_ARG; } + /* rspSize is in/out: capacity in, bytes written out. Callers that leave + * it unset get the historic FWTPM_MAX_COMMAND_SIZE assumption. */ + /* Handlers commit state before marshalling and some write outside the + * packet API, so they are only ever run with a full-size buffer. A + * caller with a smaller transport buffer must stage through one. + * rspSize is output-only, so its incoming value is never read. */ + rspCap = FWTPM_MAX_COMMAND_SIZE; + if (cmdSize < TPM2_HEADER_SIZE) { - *rspSize = FwBuildErrorResponse(rspBuf, TPM_ST_NO_SESSIONS, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_COMMAND_SIZE); return TPM_RC_SUCCESS; } @@ -16243,13 +16442,13 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, TPM2_Packet_ParseU32(&cmdPkt, &cmdCode); if (cmdTag != TPM_ST_NO_SESSIONS && cmdTag != TPM_ST_SESSIONS) { - *rspSize = FwBuildErrorResponse(rspBuf, TPM_ST_NO_SESSIONS, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_BAD_TAG); return TPM_RC_SUCCESS; } if ((int)cmdSizeHdr != cmdSize) { - *rspSize = FwBuildErrorResponse(rspBuf, TPM_ST_NO_SESSIONS, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_COMMAND_SIZE); return TPM_RC_SUCCESS; } @@ -16257,14 +16456,14 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, /* A valid command code has only the 16-bit index plus the vendor V bit * (CC_VEND); reject any other reserved bit so it cannot alias a command. */ if ((cmdCode & ~((UINT32)CC_VEND | 0xFFFFu)) != 0) { - *rspSize = FwBuildErrorResponse(rspBuf, TPM_ST_NO_SESSIONS, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_COMMAND_CODE); return TPM_RC_SUCCESS; } if (!ctx->wasStarted && cmdCode != TPM_CC_Startup && cmdCode != TPM_CC_GetCapability) { - *rspSize = FwBuildErrorResponse(rspBuf, TPM_ST_NO_SESSIONS, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_INITIALIZE); return TPM_RC_SUCCESS; } @@ -16278,14 +16477,14 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, entry = FwFindCmdEntry(cmdCode); if (entry == NULL) { - *rspSize = FwBuildErrorResponse(rspBuf, TPM_ST_NO_SESSIONS, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_COMMAND_CODE); return TPM_RC_SUCCESS; } /* Validate minimum command size: header + 4 bytes per input handle */ if (cmdSize < TPM2_HEADER_SIZE + (entry->inHandleCnt * 4)) { - *rspSize = FwBuildErrorResponse(rspBuf, TPM_ST_NO_SESSIONS, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_COMMAND_SIZE); return TPM_RC_SUCCESS; } @@ -16295,7 +16494,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, * and bypasses every downstream auth/HMAC/policy enforcement loop, so * reject up front for any handler that declares authHandleCnt > 0. */ if (cmdTag != TPM_ST_SESSIONS && entry->authHandleCnt > 0) { - *rspSize = FwBuildErrorResponse(rspBuf, TPM_ST_NO_SESSIONS, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_AUTH_MISSING); return TPM_RC_SUCCESS; } @@ -16339,7 +16538,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, int authEnd; /* Reject if authAreaSz exceeds remaining command bytes */ if (authAreaSz > (UINT32)(cmdSize - cmdPkt.pos)) { - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_AUTHSIZE); return TPM_RC_SUCCESS; } @@ -16476,7 +16675,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, /* Check if auth area parsing encountered an error */ if (rc != TPM_RC_SUCCESS) { - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, rc); return TPM_RC_SUCCESS; } @@ -16548,7 +16747,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, printf("fwTPM: Policy digest mismatch for handle " "0x%x (CC=0x%x)\n", entityH, cmdCode); #endif - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_POLICY_FAIL); return TPM_RC_SUCCESS; } @@ -16556,7 +16755,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, if (pSess->hasRequiredLocality && (ctx->activeLocality > WOLFTPM_LOCALITY_MAX || !((1u << ctx->activeLocality) & pSess->requiredLocality))) { - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_LOCALITY); return TPM_RC_SUCCESS; } @@ -16571,7 +16770,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, (int)pSess->cpHashA.size != ccpHashSz || TPM2_ConstantCompare(pSess->cpHashA.buffer, ccpHash, (word32)ccpHashSz) != 0) { - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_POLICY_FAIL); return TPM_RC_SUCCESS; } @@ -16589,7 +16788,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, "handle 0x%x without authPolicy (CC=0x%x)\n", entityH, cmdCode); #endif - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_POLICY_FAIL); return TPM_RC_SUCCESS; } @@ -16610,7 +16809,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, * etc.) — but not Clear via platformAuth, which is the recovery path. */ if (ctx->lockoutAuthFailed && entry->authHandleCnt > 0 && cmdHandleCnt > 0 && cmdHandles[0] == TPM_RH_LOCKOUT) { - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_LOCKOUT); return TPM_RC_SUCCESS; } @@ -16633,7 +16832,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, cmdCode != TPM_CC_StartAuthSession && cmdCode != TPM_CC_FlushContext && !(cmdHandleCnt > 0 && FwHandleIsNoDA(ctx, cmdHandles[0]))) { - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_LOCKOUT); return TPM_RC_SUCCESS; } @@ -16665,7 +16864,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, ctx->orderly = 0; (void)FWTPM_NV_SaveFlags(ctx); #ifdef FWTPM_DA_USED_RETRY - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_RETRY); return TPM_RC_SUCCESS; #endif @@ -16693,7 +16892,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, printf("fwTPM: Password/HMAC auth rejected for handle " "0x%x — policy required (userWithAuth clear)\n", entityH); #endif - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_AUTH_UNAVAILABLE); return TPM_RC_SUCCESS; } @@ -16722,12 +16921,12 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, #endif #ifndef FWTPM_NO_DA if (FwDaRegisterFailure(ctx, entityH)) { - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_LOCKOUT); return TPM_RC_SUCCESS; } #endif - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_AUTH_FAIL); return TPM_RC_SUCCESS; } @@ -16754,7 +16953,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, if (FwComputeCpHash(hSess->authHash, cmdCode, cmdBuf, cmdSize, cmdHandles, cmdHandleCnt, ctx, cpStart, cpHash, &cpHashSz) != 0) { - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_FAILURE); return TPM_RC_SUCCESS; } @@ -16776,7 +16975,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, printf("fwTPM: PolicyPassword auth failed for handle " "0x%x (CC=0x%x)\n", entityH, cmdCode); #endif - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_AUTH_FAIL); return TPM_RC_SUCCESS; } @@ -16811,12 +17010,12 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, #endif #ifndef FWTPM_NO_DA if (FwDaRegisterFailure(ctx, entityH)) { - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_LOCKOUT); return TPM_RC_SUCCESS; } #endif - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_AUTH_FAIL); return TPM_RC_SUCCESS; } @@ -16840,7 +17039,7 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, #ifdef DEBUG_WOLFTPM printf("fwTPM: ParamDecrypt failed %d\n", (int)rc); #endif - *rspSize = FwBuildErrorResponse(rspBuf, + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, TPM_RC_FAILURE); return TPM_RC_SUCCESS; } @@ -16849,11 +17048,22 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, #endif /* !FWTPM_NO_PARAM_ENC */ /* Set up response packet */ - FwRspInit(&rspPkt, rspBuf, FWTPM_MAX_COMMAND_SIZE); + FwRspInit(&rspPkt, rspBuf, rspCap); rc = entry->handler(ctx, &cmdPkt, cmdSize, &rspPkt, cmdTag); - if (rc != TPM_RC_SUCCESS) { - *rspSize = FwBuildErrorResponse(rspBuf, TPM_ST_NO_SESSIONS, rc); + /* The packet layer drops appends that would overrun the buffer, so + * report the truncation instead of returning a malformed packet. The + * session flush and deferred clear below must still run. */ + if (rc == TPM_RC_SUCCESS && rspPkt.overflow) { + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, + TPM_RC_SIZE); + rspTruncated = 1; + } + if (rspTruncated) { + /* response already built above */ + } + else if (rc != TPM_RC_SUCCESS) { + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, TPM_ST_NO_SESSIONS, rc); } else if (cmdTag != TPM_ST_SESSIONS) { /* Non-session: handler already finalized the response */ @@ -16880,6 +17090,9 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, int rpHashSz = 0; const byte* rpBytes = NULL; int rpBytesSz = 0; + /* previous nonceTPM per session, restored if the response overflows + * (66 bytes each, 3 sessions max) */ + TPM2B_NONCE savedNonce[FWTPM_MAX_CMD_AUTHS]; /* Read parameterSize from response buffer */ if (rspHandleEnd + 4 <= rspPkt.pos) { @@ -16894,12 +17107,13 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, rspParamEnd = rspParamStart + (int)rspParamSzVal; #endif - /* Generate fresh nonceTPM BEFORE response encryption (encryption - * uses the new nonceTPM, matching what client receives in auth) */ + /* New nonceTPM before response encryption, saving the old one */ for (j = 0; j < cmdAuthCnt; j++) { if (cmdAuths[j].sess != NULL) { FWTPM_Session* sess = cmdAuths[j].sess; int digestSz = TPM2_GetHashDigestSize(sess->authHash); + XMEMCPY(&savedNonce[j], &sess->nonceTPM, + sizeof(savedNonce[j])); if (digestSz > 0) { rngRc = wc_RNG_GenerateBlock(&ctx->rng, sess->nonceTPM.buffer, digestSz); @@ -17017,9 +17231,24 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, } } - /* Finalize response header */ - FwRspFinalize(&rspPkt, TPM_ST_SESSIONS, TPM_RC_SUCCESS); - *rspSize = rspPkt.pos; + /* Auth area is appended after the handler ran, so it can overrun a + * buffer the parameters alone fit in. Restore the nonces: the client + * never received the new ones. */ + if (rspPkt.overflow) { + for (j = 0; j < cmdAuthCnt; j++) { + if (cmdAuths[j].sess != NULL) { + XMEMCPY(&cmdAuths[j].sess->nonceTPM, &savedNonce[j], + sizeof(savedNonce[j])); + } + } + *rspSize = FwBuildErrorResponse(rspBuf, rspCap, + TPM_ST_NO_SESSIONS, TPM_RC_SIZE); + } + else { + /* Finalize response header */ + FwRspFinalize(&rspPkt, TPM_ST_SESSIONS, TPM_RC_SUCCESS); + *rspSize = rspPkt.pos; + } } /* Per TPM 2.0 spec Part 1 Section 19.6.4: flush sessions where the caller diff --git a/src/fwtpm/fwtpm_io.c b/src/fwtpm/fwtpm_io.c index b593e590..e2d4fdbe 100644 --- a/src/fwtpm/fwtpm_io.c +++ b/src/fwtpm/fwtpm_io.c @@ -353,6 +353,7 @@ static int DispatchAndRespond(FWTPM_CTX* ctx, UINT32 cmdSize, int locality, * SPDM. Allowlist that one command; reject the rest. */ cc = (cmdSize >= 10) ? FwLoadU32BE(ctx->cmdBuf + 6) : 0; if (cc == TPM_CC_GetCapability) { + rspSize = (int)sizeof(ctx->rspBuf); procRc = FWTPM_ProcessCommand(ctx, ctx->cmdBuf, (int)cmdSize, ctx->rspBuf, &rspSize, locality); if (procRc != TPM_RC_SUCCESS || rspSize == 0) { @@ -369,6 +370,7 @@ static int DispatchAndRespond(FWTPM_CTX* ctx, UINT32 cmdSize, int locality, #endif if (!dispatched) { + rspSize = (int)sizeof(ctx->rspBuf); procRc = FWTPM_ProcessCommand(ctx, ctx->cmdBuf, (int)cmdSize, ctx->rspBuf, &rspSize, locality); if (procRc != TPM_RC_SUCCESS || rspSize == 0) { diff --git a/src/fwtpm/fwtpm_main.c b/src/fwtpm/fwtpm_main.c index ae260df5..6beedde7 100644 --- a/src/fwtpm/fwtpm_main.c +++ b/src/fwtpm/fwtpm_main.c @@ -81,10 +81,28 @@ static void usage(const char* progname) } #ifdef WOLFTPM_SPDM_RESPONDER +/* Build a 10-byte TPM response header carrying an error code. */ +static int FwtpmBuildErrorFrame(byte* resp, TPM_RC rc) +{ + resp[0] = 0x80; resp[1] = 0x01; /* tag: TPM_ST_NO_SESSIONS */ + resp[2] = 0x00; resp[3] = 0x00; /* responseSize, upper half */ + resp[4] = 0x00; resp[5] = 0x0A; /* responseSize = 10 */ + resp[6] = (byte)((rc >> 24) & 0xFF); /* responseCode, MSB */ + resp[7] = (byte)((rc >> 16) & 0xFF); + resp[8] = (byte)((rc >> 8) & 0xFF); + resp[9] = (byte)(rc & 0xFF); /* responseCode, LSB */ + return TPM2_HEADER_SIZE; +} + static int fwtpmSpdmTpmDispatch(void* userCtx, const byte* cmd, word32 cmdSz, byte* resp, word32 respBufSz, word32* respSz) { + /* The dispatcher needs a full-size buffer but the SPDM envelope is + * smaller, and ctx->rspBuf is already holding the SPDM frame this call + * is nested inside (fwtpm_io.c), so stage here. Static, not stack: the + * server serves one command at a time and this is 8KB. */ + static byte stageBuf[FWTPM_MAX_COMMAND_SIZE]; FWTPM_CTX* ctx = (FWTPM_CTX*)userCtx; int rc; int rspSize; @@ -92,22 +110,31 @@ static int fwtpmSpdmTpmDispatch(void* userCtx, if (ctx == NULL || cmd == NULL || resp == NULL || respSz == NULL) { return BAD_FUNC_ARG; } - rspSize = (int)respBufSz; - rc = FWTPM_ProcessCommand(ctx, cmd, (int)cmdSz, resp, &rspSize, 0); + rspSize = (int)sizeof(stageBuf); + rc = FWTPM_ProcessCommand(ctx, cmd, (int)cmdSz, stageBuf, &rspSize, 0); + if (rc == TPM_RC_SUCCESS && rspSize >= TPM2_HEADER_SIZE) { + if ((word32)rspSize <= respBufSz) { + XMEMCPY(resp, stageBuf, (size_t)rspSize); + } + else if (respBufSz >= TPM2_HEADER_SIZE) { + /* Does not fit the envelope; report it as a TPM error rather + * than truncating the response. */ + rspSize = FwtpmBuildErrorFrame(resp, TPM_RC_SIZE); + } + else { + rspSize = 0; + } + } /* A non-zero TPM_RC is a valid TPM response - the requester must see * the actual error code. If the dispatcher didn't write one (rspSize * left at zero or partial), synthesize a 10-byte TPM_ST_NO_SESSIONS * error frame here so the SPDM layer encrypts and sends it back. */ if ((rc != TPM_RC_SUCCESS || rspSize < TPM2_HEADER_SIZE) && respBufSz >= TPM2_HEADER_SIZE) { - resp[0] = 0x80; resp[1] = 0x01; /* TPM_ST_NO_SESSIONS */ - resp[2] = 0x00; resp[3] = 0x00; resp[4] = 0x00; resp[5] = 0x0A; - resp[6] = (byte)((rc >> 24) & 0xFF); - resp[7] = (byte)((rc >> 16) & 0xFF); - resp[8] = (byte)((rc >> 8) & 0xFF); - resp[9] = (byte)(rc & 0xFF); - rspSize = TPM2_HEADER_SIZE; + rspSize = FwtpmBuildErrorFrame(resp, (TPM_RC)rc); } + /* The staging copy holds the same plaintext as the wire response */ + TPM2_ForceZero(stageBuf, sizeof(stageBuf)); *respSz = (word32)rspSize; return 0; /* I/O layer succeeded; TPM error code is in the response. */ } diff --git a/src/fwtpm/fwtpm_tis.c b/src/fwtpm/fwtpm_tis.c index c7ec2310..8d26fe2c 100644 --- a/src/fwtpm/fwtpm_tis.c +++ b/src/fwtpm/fwtpm_tis.c @@ -164,6 +164,7 @@ static void TisHandleRegAccess(FWTPM_CTX* ctx, FWTPM_TIS_REGS* regs) /* Gated to the owner above, so loc == ctx->tisLocality: * execute under the addressed (owning) locality. */ + rspSize = (int)sizeof(regs->rsp_buf); procRc = FWTPM_ProcessCommand(ctx, localCmd, (int)localCmdLen, regs->rsp_buf, &rspSize, loc); diff --git a/src/spdm/spdm_responder.c b/src/spdm/spdm_responder.c index bfcf7f41..514f20fc 100644 --- a/src/spdm/spdm_responder.c +++ b/src/spdm/spdm_responder.c @@ -43,6 +43,7 @@ struct WOLFSPDM_RESP_CTX { unsigned int spdmOnlyLock : 1; /* SPDMONLY lock: plaintext TPM * rejected with TPM_RC_DISABLED */ unsigned int pskProvisioned : 1; /* PSK_SET / PSK_CLR vendor state */ + unsigned int clearAuthSet : 1; /* a ClearAuth digest is stored */ } flags; /* SHA-384(ClearAuth) stored on PSK_SET, verified on PSK_CLR. */ @@ -66,10 +67,10 @@ struct WOLFSPDM_RESP_CTX { /* Per-context working buffers. Previously file-scope `static` - * moved here so each ctx is independently reentrant. */ - byte secureInPlain[WOLFSPDM_MAX_MSG_SIZE]; - byte secureOutPlain[WOLFSPDM_MAX_MSG_SIZE]; - byte vdInPayload[WOLFSPDM_MAX_MSG_SIZE]; - byte vdOutPayload[WOLFSPDM_MAX_MSG_SIZE]; + byte secureInPlain[WOLFSPDM_MAX_TPM_MSG_SIZE]; + byte secureOutPlain[WOLFSPDM_MAX_TPM_MSG_SIZE]; + byte vdInPayload[WOLFSPDM_MAX_TPM_MSG_SIZE]; + byte vdOutPayload[WOLFSPDM_MAX_TPM_MSG_SIZE]; }; /* Compile-time guarantee that the public static-size macro is large @@ -250,7 +251,8 @@ void wolfSPDM_RespReset(WOLFSPDM_RESP_CTX* ctx) #define WOLFSPDM_ALGORITHMS 0x63 static int RespHandleVendorDefined(WOLFSPDM_RESP_CTX* rctx, - const byte* in, word32 inSz, byte* out, word32* outSz); + const byte* in, word32 inSz, byte* out, word32* outSz, int fromSecured, + char* vdCodeOut); static int RespBuildKeyExchangeRsp(WOLFSPDM_RESP_CTX* rctx, const byte* in, word32 inSz, byte* out, word32* outSz); static int RespHandleFinish(WOLFSPDM_RESP_CTX* rctx, @@ -502,11 +504,13 @@ static int RespDispatchClear(WOLFSPDM_RESP_CTX* rctx, byte code; int rc; int handlerManagesTranscript = 0; + char vdCode[WOLFSPDM_VDCODE_LEN + 1]; if (inSz < 2) { return WOLFSPDM_E_FRAMING; } code = in[1]; + XMEMSET(vdCode, 0, sizeof(vdCode)); if (code == SPDM_GET_VERSION) { wolfSPDM_TranscriptReset(ctx); @@ -546,13 +550,14 @@ static int RespDispatchClear(WOLFSPDM_RESP_CTX* rctx, handlerManagesTranscript = 1; break; case SPDM_VENDOR_DEFINED_REQUEST: - rc = RespHandleVendorDefined(rctx, in, inSz, out, outSz); + rc = RespHandleVendorDefined(rctx, in, inSz, out, outSz, 0, + vdCode); handlerManagesTranscript = 1; /* For GET_PUBK specifically, mirror what the requester does: - * add Ct = SHA-384(rspPubKey) to the transcript. Detected by - * checking the VdCode in the inbound bytes at offset 9. */ - if (rc == WOLFSPDM_SUCCESS && inSz >= 17 && - XMEMCMP(in + 9, WOLFSPDM_VDCODE_GET_PUBK, + * add Ct = SHA-384(rspPubKey) to the transcript. Keyed on the + * parsed VdCode, whose wire offset varies with vendorIdLen. */ + if (rc == WOLFSPDM_SUCCESS && + XMEMCMP(vdCode, WOLFSPDM_VDCODE_GET_PUBK, WOLFSPDM_VDCODE_LEN) == 0) { byte ct[WOLFSPDM_HASH_SIZE]; int hrc = wolfSPDM_Sha384Hash(ct, @@ -844,7 +849,8 @@ static int RespBuildEndSessionAck(WOLFSPDM_CTX* ctx, } static int RespHandleVendorDefined(WOLFSPDM_RESP_CTX* rctx, - const byte* in, word32 inSz, byte* out, word32* outSz) + const byte* in, word32 inSz, byte* out, word32* outSz, int fromSecured, + char* vdCodeOut) { WOLFSPDM_CTX* ctx = &rctx->ctx; char vdCode[WOLFSPDM_VDCODE_LEN + 1]; @@ -859,11 +865,25 @@ static int RespHandleVendorDefined(WOLFSPDM_RESP_CTX* rctx, word32 off; int rc; - payloadSz = WOLFSPDM_MAX_MSG_SIZE; + payloadSz = WOLFSPDM_MAX_TPM_MSG_SIZE; rc = wolfSPDM_ParseVendorDefined(in, inSz, vdCode, payload, &payloadSz); if (rc < 0) { return rc; } + if (vdCodeOut != NULL) { + XMEMCPY(vdCodeOut, vdCode, WOLFSPDM_VDCODE_LEN + 1); + } + + /* TPM2_CMD, GIVE_PUB and SPDMONLY are only ever sent inside a secured + * message; honouring them from a clear frame would defeat the + * bus-snooping defence. GET_PUBK / GET_STS_ / PSK_* are pre-session by + * design and stay reachable in the clear. */ + if (!fromSecured && + (XSTRCMP(vdCode, WOLFSPDM_VDCODE_TPM2_CMD) == 0 || + XSTRCMP(vdCode, WOLFSPDM_VDCODE_GIVE_PUB) == 0 || + XSTRCMP(vdCode, WOLFSPDM_VDCODE_SPDMONLY) == 0)) { + return WOLFSPDM_E_BAD_STATE; + } if (XSTRCMP(vdCode, WOLFSPDM_VDCODE_TPM2_CMD) == 0) { /* Reserve the VENDOR_DEFINED_RSP wrapper overhead @@ -871,7 +891,7 @@ static int RespHandleVendorDefined(WOLFSPDM_RESP_CTX* rctx, * cannot return more data than will fit inside the response * envelope. Otherwise the wrapper below silently returns * E_BUFFER_SMALL on the largest TPM responses. */ - word32 tpmRespCap = WOLFSPDM_MAX_MSG_SIZE + word32 tpmRespCap = WOLFSPDM_MAX_TPM_MSG_SIZE - (9 + WOLFSPDM_VDCODE_LEN); if (rctx->tpmCb == NULL) { return WOLFSPDM_E_BAD_STATE; @@ -933,10 +953,17 @@ static int RespHandleVendorDefined(WOLFSPDM_RESP_CTX* rctx, if (payloadSz != pskLen + WOLFSPDM_HASH_SIZE) { return WOLFSPDM_E_INVALID_ARG; } + /* Once a ClearAuth is registered, replacing the PSK requires + * PSK_CLR_ first, or that check is trivially skipped. A PSK set by + * configuration has no ClearAuth, so it may still be provisioned. */ + if (rctx->flags.clearAuthSet) { + return WOLFSPDM_E_BAD_STATE; + } XMEMCPY(rctx->pskStore, payload, pskLen); rctx->pskStoreSz = pskLen; XMEMCPY(rctx->clearAuthDigest, payload + pskLen, WOLFSPDM_HASH_SIZE); rctx->flags.pskProvisioned = 1; + rctx->flags.clearAuthSet = 1; /* Mirror into ctx->psk so the next PSK_EXCHANGE can use it. */ XMEMCPY(ctx->psk, rctx->pskStore, rctx->pskStoreSz); ctx->pskSz = rctx->pskStoreSz; @@ -967,6 +994,7 @@ static int RespHandleVendorDefined(WOLFSPDM_RESP_CTX* rctx, wc_ForceZero(rctx->clearAuthDigest, sizeof(rctx->clearAuthDigest)); rctx->pskStoreSz = 0; rctx->flags.pskProvisioned = 0; + rctx->flags.clearAuthSet = 0; wc_ForceZero(ctx->psk, sizeof(ctx->psk)); ctx->pskSz = 0; respPayloadSz = 0; @@ -1046,7 +1074,7 @@ static int RespDispatchSecured(WOLFSPDM_RESP_CTX* rctx, break; case SPDM_VENDOR_DEFINED_REQUEST: rc = RespHandleVendorDefined(rctx, plain, plainSz, - respPlain, &respPlainSz); + respPlain, &respPlainSz, 1, NULL); break; default: rc = RespBuildErrorClear(ctx, diff --git a/src/spdm/spdm_tcg.c b/src/spdm/spdm_tcg.c index dd5e02e1..c940568c 100644 --- a/src/spdm/spdm_tcg.c +++ b/src/spdm/spdm_tcg.c @@ -335,12 +335,14 @@ int wolfSPDM_TCG_GetPubKey( XMEMCPY(pubKey, rsp.payload, rsp.payloadSz); *pubKeySz = rsp.payloadSz; - /* Store for cert_chain_buffer_hash computation */ - if (rsp.payloadSz <= sizeof(ctx->rspPubKey)) { - XMEMCPY(ctx->rspPubKey, rsp.payload, rsp.payloadSz); - ctx->rspPubKeyLen = rsp.payloadSz; - ctx->flags.hasRspPubKey = 1; + /* Store for cert_chain_buffer_hash computation. Skipping it silently + * surfaces later as a misleading handshake state error. */ + if (rsp.payloadSz > sizeof(ctx->rspPubKey)) { + return WOLFSPDM_E_BUFFER_SMALL; } + XMEMCPY(ctx->rspPubKey, rsp.payload, rsp.payloadSz); + ctx->rspPubKeyLen = rsp.payloadSz; + ctx->flags.hasRspPubKey = 1; return WOLFSPDM_SUCCESS; } diff --git a/src/spdm/unit_test.c b/src/spdm/unit_test.c index 0c98801a..19275c4a 100644 --- a/src/spdm/unit_test.c +++ b/src/spdm/unit_test.c @@ -2230,6 +2230,153 @@ static int test_responder_no_plaintext_bypass(void) TEST_PASS(); } +#ifdef WOLFTPM_SPDM_TCG +/* A TCG *clear* frame (tag 0x8101) carrying VENDOR_DEFINED TPM2_CMD passes + * the tag check, so it must be refused by the vendor handler instead. */ +static int test_responder_no_clear_tpm2_cmd(void) +{ + byte rctxBuf[WOLFSPDM_RESP_CTX_STATIC_SIZE]; + WOLFSPDM_RESP_CTX* rctx = (WOLFSPDM_RESP_CTX*)rctxBuf; + byte tpmCmd[12]; + byte spdmMsg[64]; + byte frame[128]; + byte outBuf[256]; + word32 outSz = sizeof(outBuf); + int spdmMsgSz; + int rc; + int v; + + printf("test_responder_no_clear_tpm2_cmd...\n"); + ASSERT_SUCCESS(wolfSPDM_RespInit(rctx)); + ASSERT_SUCCESS(wolfSPDM_RespSetMode(rctx, 1, 0)); + g_tpmCbInvocations = 0; + ASSERT_SUCCESS(wolfSPDM_RespSetTpmCallback(rctx, responder_tpm_stub, NULL)); + + /* TPM2_Startup(SU_CLEAR) */ + XMEMSET(tpmCmd, 0, sizeof(tpmCmd)); + tpmCmd[0] = 0x80; tpmCmd[1] = 0x01; + tpmCmd[5] = 0x0C; + tpmCmd[8] = 0x01; tpmCmd[9] = 0x44; + + /* Every vendor code gated to secured frames must be refused here. */ + for (v = 0; v < 3; v++) { + const char* vd = (v == 0) ? WOLFSPDM_VDCODE_TPM2_CMD : + (v == 1) ? WOLFSPDM_VDCODE_GIVE_PUB : + WOLFSPDM_VDCODE_SPDMONLY; + spdmMsgSz = wolfSPDM_BuildVendorDefined(SPDM_VERSION_13, + vd, tpmCmd, (word32)sizeof(tpmCmd), spdmMsg, sizeof(spdmMsg)); + TEST_ASSERT(spdmMsgSz > 0, "BuildVendorDefined failed"); + + XMEMSET(frame, 0, sizeof(frame)); + frame[0] = 0x81; frame[1] = 0x01; /* WOLFSPDM_TCG_TAG_CLEAR */ + frame[2] = 0; frame[3] = 0; + frame[4] = (byte)(((word32)spdmMsgSz + WOLFSPDM_TCG_HEADER_SIZE) >> 8); + frame[5] = (byte)((word32)spdmMsgSz + WOLFSPDM_TCG_HEADER_SIZE); + XMEMCPY(frame + WOLFSPDM_TCG_HEADER_SIZE, spdmMsg, + (size_t)spdmMsgSz); + + outSz = sizeof(outBuf); + rc = wolfSPDM_RespHandleMessage(rctx, frame, + (word32)spdmMsgSz + WOLFSPDM_TCG_HEADER_SIZE, outBuf, &outSz); + TEST_ASSERT(rc != WOLFSPDM_SUCCESS, + "Clear-frame secured-only vendor code must not succeed"); + ASSERT_EQ(g_tpmCbInvocations, 0, + "TPM callback must NOT run for a clear frame"); + /* SPDMONLY must not have been able to change the lock state. */ + ASSERT_EQ(wolfSPDM_RespIsLocked(rctx), 0, + "clear frame must not alter SPDM-only lock"); + } + wolfSPDM_RespFree(rctx); + TEST_PASS(); +} + +#ifdef WOLFSPDM_NATIONS +/* Send one vendor-defined command in a TCG clear frame. */ +static int resp_send_clear_vd(WOLFSPDM_RESP_CTX* rctx, const char* vdCode, + const byte* payload, word32 payloadSz, byte* out, word32* outSz) +{ + byte spdmMsg[256]; + byte frame[320]; + int spdmMsgSz; + + spdmMsgSz = wolfSPDM_BuildVendorDefined(SPDM_VERSION_13, vdCode, + payload, payloadSz, spdmMsg, sizeof(spdmMsg)); + if (spdmMsgSz <= 0) { + return spdmMsgSz; + } + XMEMSET(frame, 0, sizeof(frame)); + frame[0] = 0x81; frame[1] = 0x01; + frame[4] = (byte)(((word32)spdmMsgSz + WOLFSPDM_TCG_HEADER_SIZE) >> 8); + frame[5] = (byte)((word32)spdmMsgSz + WOLFSPDM_TCG_HEADER_SIZE); + XMEMCPY(frame + WOLFSPDM_TCG_HEADER_SIZE, spdmMsg, (size_t)spdmMsgSz); + return wolfSPDM_RespHandleMessage(rctx, frame, + (word32)spdmMsgSz + WOLFSPDM_TCG_HEADER_SIZE, out, outSz); +} + +/* A provisioned PSK may only be replaced after an authenticated PSK_CLR_. */ +static int test_responder_psk_replace_guard(void) +{ + byte rctxBuf[WOLFSPDM_RESP_CTX_STATIC_SIZE]; + WOLFSPDM_RESP_CTX* rctx = (WOLFSPDM_RESP_CTX*)rctxBuf; + byte setPayload[WOLFSPDM_PSK_MAX_SIZE + WOLFSPDM_HASH_SIZE]; + byte clearAuth[32]; + byte outBuf[256]; + word32 outSz; + int rc; + + printf("test_responder_psk_replace_guard...\n"); + ASSERT_SUCCESS(wolfSPDM_RespInit(rctx)); + ASSERT_SUCCESS(wolfSPDM_RespSetMode(rctx, 1, 1)); + + XMEMSET(clearAuth, 0xC1, sizeof(clearAuth)); + XMEMSET(setPayload, 0xA5, WOLFSPDM_PSK_MAX_SIZE); + ASSERT_SUCCESS(wolfSPDM_Sha384Hash(setPayload + WOLFSPDM_PSK_MAX_SIZE, + clearAuth, sizeof(clearAuth), NULL, 0, NULL, 0)); + + /* First provisioning succeeds. */ + outSz = sizeof(outBuf); + rc = resp_send_clear_vd(rctx, WOLFSPDM_NATIONS_VDCODE_PSK_SET, + setPayload, (word32)sizeof(setPayload), outBuf, &outSz); + ASSERT_SUCCESS(rc); + + /* A second PSK_SET_ must be refused while one is provisioned. */ + outSz = sizeof(outBuf); + rc = resp_send_clear_vd(rctx, WOLFSPDM_NATIONS_VDCODE_PSK_SET, + setPayload, (word32)sizeof(setPayload), outBuf, &outSz); + TEST_ASSERT(rc != WOLFSPDM_SUCCESS, + "replacing a provisioned PSK must be refused"); + + /* A clear with the wrong ClearAuth leaves it provisioned. */ + XMEMSET(clearAuth, 0x00, sizeof(clearAuth)); + outSz = sizeof(outBuf); + rc = resp_send_clear_vd(rctx, WOLFSPDM_NATIONS_VDCODE_PSK_CLEAR, + clearAuth, (word32)sizeof(clearAuth), outBuf, &outSz); + TEST_ASSERT(rc != WOLFSPDM_SUCCESS, "wrong ClearAuth must fail"); + + outSz = sizeof(outBuf); + rc = resp_send_clear_vd(rctx, WOLFSPDM_NATIONS_VDCODE_PSK_SET, + setPayload, (word32)sizeof(setPayload), outBuf, &outSz); + TEST_ASSERT(rc != WOLFSPDM_SUCCESS, + "PSK must remain provisioned after a failed clear"); + + /* The correct ClearAuth releases it and re-provisioning works. */ + XMEMSET(clearAuth, 0xC1, sizeof(clearAuth)); + outSz = sizeof(outBuf); + rc = resp_send_clear_vd(rctx, WOLFSPDM_NATIONS_VDCODE_PSK_CLEAR, + clearAuth, (word32)sizeof(clearAuth), outBuf, &outSz); + ASSERT_SUCCESS(rc); + + outSz = sizeof(outBuf); + rc = resp_send_clear_vd(rctx, WOLFSPDM_NATIONS_VDCODE_PSK_SET, + setPayload, (word32)sizeof(setPayload), outBuf, &outSz); + ASSERT_SUCCESS(rc); + + wolfSPDM_RespFree(rctx); + TEST_PASS(); +} +#endif /* WOLFSPDM_NATIONS */ +#endif /* WOLFTPM_SPDM_TCG */ + #if defined(WOLFTPM_SPDM_PSK) && defined(WOLFTPM_SPDM_TCG) /* In-process I/O glue: route the requester's outbound TCG frame to the @@ -2443,6 +2590,12 @@ int main(void) test_responder_init_free(); test_responder_setmode_rejects_both_off(); test_responder_no_plaintext_bypass(); +#ifdef WOLFTPM_SPDM_TCG + test_responder_no_clear_tpm2_cmd(); +#ifdef WOLFSPDM_NATIONS + test_responder_psk_replace_guard(); +#endif +#endif #if defined(WOLFTPM_SPDM_PSK) && defined(WOLFTPM_SPDM_TCG) test_responder_psk_roundtrip(); #endif diff --git a/src/tpm2.c b/src/tpm2.c index 52845bc0..6c4c8cee 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -923,6 +923,10 @@ TPM_RC TPM2_Cleanup(TPM2_CTX* ctx) TPM2_SetActiveCtx(NULL); } + /* Last command/response still holds plaintext (unsealed data, + * auth values, decrypted parameters) */ + TPM2_ForceZero(ctx->cmdBuf, sizeof(ctx->cmdBuf)); + TPM2_ReleaseLock(ctx); } diff --git a/src/tpm2_cryptocb.c b/src/tpm2_cryptocb.c index 27b26212..a2558197 100644 --- a/src/tpm2_cryptocb.c +++ b/src/tpm2_cryptocb.c @@ -301,7 +301,7 @@ int wolfTPM2_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) wolfTPM2_UnloadHandle(tlsCtx->dev, &key->handle); } } - else if (rc & TPM_RC_CURVE) { + else if ((rc & RC_MAX_FMT1) == TPM_RC_CURVE) { /* if the curve is not supported on TPM, then fall-back to software */ rc = exit_rc; /* Make sure key indicates nothing loaded */ @@ -393,7 +393,7 @@ int wolfTPM2_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) } wolfTPM2_UnloadHandle(tlsCtx->dev, &eccPub.handle); } - else if (rc & TPM_RC_CURVE) { + else if ((rc & RC_MAX_FMT1) == TPM_RC_CURVE) { /* if the curve is not supported on TPM, then fall-back to software */ rc = exit_rc; } @@ -749,6 +749,10 @@ static int wolfTPM2_HashUpdateCache(WOLFTPM2_HASHCTX* hashCtx, /* allocate new cache buffer */ if (hashCtx->cacheBuf == NULL) { hashCtx->cacheSz = 0; + /* the block round-up below must not wrap to zero */ + if (inSz > 0xFFFFFFFFU - (WOLFTPM2_HASH_BLOCK_SZ - 1)) { + return BUFFER_E; + } hashCtx->cacheBufSz = (inSz + WOLFTPM2_HASH_BLOCK_SZ - 1) & ~(WOLFTPM2_HASH_BLOCK_SZ - 1); if (hashCtx->cacheBufSz == 0) @@ -763,8 +767,10 @@ static int wolfTPM2_HashUpdateCache(WOLFTPM2_HASHCTX* hashCtx, else if ((hashCtx->cacheSz + inSz) > hashCtx->cacheBufSz) { byte* oldIn = hashCtx->cacheBuf; word32 oldBufSz = hashCtx->cacheBufSz; - /* check for overflow */ - if (hashCtx->cacheSz + inSz < hashCtx->cacheSz) { + /* check for overflow, including the block round-up below */ + if (hashCtx->cacheSz + inSz < hashCtx->cacheSz || + hashCtx->cacheSz + inSz > + 0xFFFFFFFFU - (WOLFTPM2_HASH_BLOCK_SZ - 1)) { return BUFFER_E; } hashCtx->cacheBufSz = (hashCtx->cacheSz + inSz + @@ -993,6 +999,10 @@ static int RsaPadPss(const byte* input, word32 inputLen, byte* pkcsBlock, enum wc_HashType hType; wc_HashAlg hashCtx; /* big stack consumer */ + if (pkcsBlockLen > RSA_MAX_SIZE/8) { + return RSA_BUFFER_E; + } + switch (hash) { #ifndef NO_SHA256 case SHA256h: @@ -1090,6 +1100,7 @@ static int RsaPadPss(const byte* input, word32 inputLen, byte* pkcsBlock, xorbuf(m, salt + o, (word32)saltLen); } wc_HashFree(&hashCtx, hType); + TPM2_ForceZero(&hashCtx, sizeof(hashCtx)); TPM2_ForceZero(salt, sizeof(salt)); return ret; } diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 737ba81a..b28ffa86 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -134,6 +134,7 @@ void TPM2_Packet_InitBuf(TPM2_Packet* packet, byte* buf, int size) packet->buf = buf; packet->pos = TPM2_HEADER_SIZE; /* skip header (fill during finalize) */ packet->size = size; + packet->overflow = 0; } } @@ -150,6 +151,9 @@ void TPM2_Packet_AppendU8(TPM2_Packet* packet, UINT8 data) packet->buf[packet->pos] = data; packet->pos += sizeof(UINT8); } + else if (packet != NULL) { + packet->overflow = 1; + } } void TPM2_Packet_ParseU8(TPM2_Packet* packet, UINT8* data) { @@ -170,6 +174,9 @@ void TPM2_Packet_AppendU16(TPM2_Packet* packet, UINT16 data) XMEMCPY(&packet->buf[packet->pos], &data, sizeof(UINT16)); packet->pos += sizeof(UINT16); } + else if (packet != NULL) { + packet->overflow = 1; + } } void TPM2_Packet_ParseU16(TPM2_Packet* packet, UINT16* data) { @@ -190,6 +197,9 @@ void TPM2_Packet_AppendU32(TPM2_Packet* packet, UINT32 data) XMEMCPY(&packet->buf[packet->pos], &data, sizeof(UINT32)); packet->pos += sizeof(UINT32); } + else if (packet != NULL) { + packet->overflow = 1; + } } void TPM2_Packet_ParseU32(TPM2_Packet* packet, UINT32* data) { @@ -212,6 +222,9 @@ void TPM2_Packet_AppendU64(TPM2_Packet* packet, UINT64 data) XMEMCPY(&packet->buf[packet->pos], &data, sizeof(UINT64)); packet->pos += sizeof(UINT64); } + else if (packet != NULL) { + packet->overflow = 1; + } } void TPM2_Packet_ParseU64(TPM2_Packet* packet, UINT64* data) { @@ -234,6 +247,9 @@ void TPM2_Packet_AppendS32(TPM2_Packet* packet, INT32 data) XMEMCPY(&packet->buf[packet->pos], &data, sizeof(INT32)); packet->pos += sizeof(INT32); } + else if (packet != NULL) { + packet->overflow = 1; + } } void TPM2_Packet_AppendBytes(TPM2_Packet* packet, byte* buf, int size) @@ -243,6 +259,9 @@ void TPM2_Packet_AppendBytes(TPM2_Packet* packet, byte* buf, int size) XMEMCPY(&packet->buf[packet->pos], buf, size); packet->pos += size; } + else if (packet != NULL) { + packet->overflow = 1; + } } void TPM2_Packet_ParseBytes(TPM2_Packet* packet, byte* buf, int size) { @@ -440,8 +459,9 @@ TPM_ST TPM2_Packet_AppendAuth(TPM2_Packet* packet, TPM2_CTX* ctx, CmdInfo_t* inf { TPM_ST st = TPM_ST_NO_SESSIONS; + /* the return type is a wire tag, so a negative error cannot be encoded */ if (ctx == NULL || info == NULL) - return BAD_FUNC_ARG; + return st; if (ctx->session == NULL) return st; diff --git a/src/tpm2_param_enc.c b/src/tpm2_param_enc.c index be593b9e..c1ba6d97 100644 --- a/src/tpm2_param_enc.c +++ b/src/tpm2_param_enc.c @@ -184,15 +184,27 @@ int TPM2_ParamEnc_AESCFB( static TPM2B_AUTH* TPM2_ParamEncBindKey(TPM2_AUTH_SESSION* session) { int digestSz = TPM2_GetHashDigestSize(session->authHash); - if (session->bind != NULL && digestSz > 0 && - session->name.size > 0 && - session->name.size == session->bindName.size && - XMEMCMP(session->name.name, session->bindName.name, - session->name.size) == 0 && - session->auth.size <= (UINT16)digestSz) { - return session->bind; + int sizeMismatch; + int diff; + UINT16 cmpLen; + + if (session->bind == NULL || digestSz <= 0 || + session->name.size == 0 || + session->auth.size > (UINT16)digestSz) { + return NULL; } - return NULL; + + cmpLen = session->name.size; + if (cmpLen > (UINT16)sizeof(session->bindName.name)) { + cmpLen = (UINT16)sizeof(session->bindName.name); + } + sizeMismatch = (session->name.size != session->bindName.size); + diff = TPM2_ConstantCompare(session->name.name, session->bindName.name, + cmpLen); + if (sizeMismatch | diff) { + return NULL; + } + return session->bind; } /* Build combined param-enc key from session key + optional bind authValue. */ diff --git a/src/tpm2_spdm.c b/src/tpm2_spdm.c index 63f912df..f3c7a44c 100644 --- a/src/tpm2_spdm.c +++ b/src/tpm2_spdm.c @@ -170,11 +170,8 @@ static int wolfTPM2_SPDM_SwtpmIoCb( return rc; } - /* Lower-bound the read so we don't index uninitialized bytes if the - * peer mis-frames a too-short response. Header is 6 bytes minimum. */ - if (packet.pos < TPM2_HEADER_SIZE) { - return -1; - } + /* TPM2_SWTPM_SendCommand validated the received length against + * TPM2_HEADER_SIZE, so the size field below is present. */ /* TCG SPDM Binding header and TPM2 header both carry total size at * bytes [2..5] big-endian. */ diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index d6e17961..041931b0 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -350,6 +350,8 @@ int wolfTPM2_Free(WOLFTPM2_DEV *dev) { if (dev != NULL) { wolfTPM2_Cleanup(dev); + /* Holds session auth values and the command buffer */ + TPM2_ForceZero(dev, sizeof(WOLFTPM2_DEV)); XFREE(dev, NULL, DYNAMIC_TYPE_TMP_BUFFER); } return TPM_RC_SUCCESS; @@ -3411,6 +3413,10 @@ static int SensitiveToPrivate(TPM2B_SENSITIVE* sens, TPM2B_PRIVATE* priv, (void)useIv; rc = NOT_COMPILED_IN; #endif + /* a failed wrap leaves the marshalled sensitive area in the clear */ + if (rc != 0 && priv != NULL) { + TPM2_ForceZero(priv, sizeof(*priv)); + } return rc; } @@ -7063,14 +7069,18 @@ int wolfTPM2_NVCreate(WOLFTPM2_DEV* dev, TPM_HANDLE authHandle, word32 nvIndex, word32 nvAttributes, word32 maxSize, const byte* auth, int authSz) { + int rc; WOLFTPM2_NV nv; WOLFTPM2_HANDLE parent; XMEMSET(&nv, 0, sizeof(nv)); XMEMSET(&parent, 0, sizeof(parent)); parent.hndl = authHandle; - return wolfTPM2_NVCreateAuth(dev, &parent, &nv, nvIndex, nvAttributes, + rc = wolfTPM2_NVCreateAuth(dev, &parent, &nv, nvIndex, nvAttributes, maxSize, auth, authSz); + /* NVOpen copied the index password into the local copy */ + TPM2_ForceZero(&nv, sizeof(nv)); + return rc; } static int wolfTPM2_NVWriteData(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* tpmSession, @@ -7783,7 +7793,7 @@ int wolfTPM2_HashUpdate(WOLFTPM2_DEV* dev, WOLFTPM2_HASH* hash, printf("TPM2_SequenceUpdate failed 0x%x: %s\n", rc, TPM2_GetRCString(rc)); #endif - return rc; + break; } pos += hashSz; } @@ -7793,6 +7803,9 @@ int wolfTPM2_HashUpdate(WOLFTPM2_DEV* dev, WOLFTPM2_HASH* hash, (word32)in.sequenceHandle, dataSz); #endif + /* holds a copy of the hashed input */ + TPM2_ForceZero(&in, sizeof(in)); + return rc; } diff --git a/tests/fwtpm_unit_tests.c b/tests/fwtpm_unit_tests.c index 0f67bf82..33d62aa5 100644 --- a/tests/fwtpm_unit_tests.c +++ b/tests/fwtpm_unit_tests.c @@ -6218,6 +6218,87 @@ static void test_fwtpm_mldsa87_maxbuf(void) fwtpm_pass("MLDSA-87 max-buffer roundtrip:", 1); } +#define FWTPM_TEST_CANARY_SZ 1024 +#define FWTPM_TEST_CANARY_BYTE 0x5A + +static byte gCapRsp[FWTPM_MAX_COMMAND_SIZE + FWTPM_TEST_CANARY_SZ]; + +static void test_fwtpm_response_buffer_capacity(void) +{ + FWTPM_CTX ctx; + int rc, rspSize, cmdSz, pos, j; + UINT32 handle; + UINT32 seqHandle; + byte msg[16]; + + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + + cmdSz = BuildCreatePrimaryCmdParam(gCmd, TPM_ALG_MLDSA, TPM_MLDSA_87); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + handle = GetU32BE(gRsp + TPM2_HEADER_SIZE); + + pos = 0; + PutU16BE(gCmd + pos, TPM_ST_NO_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_SignSequenceStart); pos += 4; + PutU32BE(gCmd + pos, handle); pos += 4; + PutU16BE(gCmd + pos, 0); pos += 2; + PutU16BE(gCmd + pos, 0); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + seqHandle = GetU32BE(gRsp + TPM2_HEADER_SIZE); + memset(msg, 0xAB, sizeof(msg)); + + pos = 0; + PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_SignSequenceComplete); pos += 4; + PutU32BE(gCmd + pos, seqHandle); pos += 4; + PutU32BE(gCmd + pos, handle); pos += 4; + PutU32BE(gCmd + pos, 18); pos += 4; + PutU32BE(gCmd + pos, TPM_RS_PW); pos += 4; + PutU16BE(gCmd + pos, 0); pos += 2; + gCmd[pos++] = 0; PutU16BE(gCmd + pos, 0); pos += 2; + PutU32BE(gCmd + pos, TPM_RS_PW); pos += 4; + PutU16BE(gCmd + pos, 0); pos += 2; + gCmd[pos++] = 0; PutU16BE(gCmd + pos, 0); pos += 2; + PutU16BE(gCmd + pos, sizeof(msg)); pos += 2; + memcpy(gCmd + pos, msg, sizeof(msg)); pos += sizeof(msg); + PutU32BE(gCmd + 2, (UINT32)pos); + + /* A full-size buffer is the documented contract: the response fits and + * nothing is written past it. */ + memset(gCapRsp, 0, sizeof(gCapRsp)); + memset(gCapRsp + FWTPM_MAX_COMMAND_SIZE, FWTPM_TEST_CANARY_BYTE, + FWTPM_TEST_CANARY_SZ); + rspSize = FWTPM_MAX_COMMAND_SIZE; + rc = FWTPM_ProcessCommand(&ctx, gCmd, pos, gCapRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gCapRsp), TPM_RC_SUCCESS); + AssertTrue(rspSize <= FWTPM_MAX_COMMAND_SIZE); + for (j = 0; j < FWTPM_TEST_CANARY_SZ; j++) { + AssertIntEQ(gCapRsp[FWTPM_MAX_COMMAND_SIZE + j], + FWTPM_TEST_CANARY_BYTE); + } + + /* An MLDSA-87 signature is the largest response and must still fit. */ + AssertTrue(rspSize > 4096); + + BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 14, TPM_CC_FlushContext); + PutU32BE(gCmd + 10, handle); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, 14, gRsp, &rspSize, 0); + + FWTPM_Cleanup(&ctx); + fwtpm_pass("Response buffer capacity respected:", 1); +} + /* ---- Hash-ML-DSA sequence round-trip across 44/65/87 ----------------- * SignSequenceStart -> SequenceUpdate(chunked) -> SignSequenceComplete * exercises the hash accumulator path (wc_HashUpdate) through all three @@ -8437,17 +8518,138 @@ static void test_fwtpm_test_parms(void) memset(&ctx, 0, sizeof(ctx)); AssertIntEQ(fwtpm_test_startup(&ctx), 0); - /* TestParms: RSA-2048 */ + /* TestParms: RSA-2048. TPMS_RSA_PARMS is symmetric, scheme, keyBits, + * exponent - in that order. */ pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); PutU16BE(gCmd + pos, TPM_ALG_RSA); pos += 2; - PutU16BE(gCmd + pos, 2048); pos += 2; /* keyBits */ - PutU32BE(gCmd + pos, 0); pos += 4; /* exponent */ + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* symmetric */ PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* scheme */ + PutU16BE(gCmd + pos, 2048); pos += 2; /* keyBits */ + PutU32BE(gCmd + pos, 0); pos += 4; /* exponent */ PutU32BE(gCmd + 2, (UINT32)pos); rspSize = 0; FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + /* An unsupported RSA key size must be rejected, not accepted. */ + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_RSA); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; + PutU16BE(gCmd + pos, 777); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_KEY_SIZE); + + /* A bogus RSA signing scheme must be rejected. */ + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_RSA); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; + PutU16BE(gCmd + pos, 0x7F7F); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SCHEME); + +#ifdef HAVE_ECC + /* An unsupported ECC curve must be rejected. */ + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_ECC); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* symmetric */ + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* scheme */ + PutU16BE(gCmd + pos, 0x7F7F); pos += 2; /* curveID */ + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* kdf */ + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_CURVE); + + /* The DHKEM shape used by Encapsulate/Decapsulate must be accepted. */ + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_ECC); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* symmetric */ + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; /* scheme */ + PutU16BE(gCmd + pos, TPM_ECC_NIST_P256); pos += 2; /* curveID */ + PutU16BE(gCmd + pos, TPM_ALG_HKDF); pos += 2; /* kdf scheme */ + PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; /* kdf hash */ + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + /* A KDF hash the TPM cannot do is still rejected. */ + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_ECC); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; + PutU16BE(gCmd + pos, TPM_ECC_NIST_P256); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_HKDF); pos += 2; + PutU16BE(gCmd + pos, 0x7F7F); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_HASH); +#endif + + /* A hash algorithm is not a TPMI_ALG_PUBLIC selector. */ + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_TYPE); + + /* Neither is a bare symmetric algorithm or TPM_ALG_NULL. */ + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_AES); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_TYPE); + + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_NULL); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_TYPE); + +#ifndef NO_AES + /* SYMCIPHER: AES-128-CFB is supported, a bad key size is not. */ + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_SYMCIPHER); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_AES); pos += 2; + PutU16BE(gCmd + pos, 128); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_CFB); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_SYMCIPHER); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_AES); pos += 2; + PutU16BE(gCmd + pos, 64); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_CFB); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_KEY_SIZE); +#else + /* Without AES the only symmetric algorithm is unsupported. */ + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_SYMCIPHER); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_AES); pos += 2; + PutU16BE(gCmd + pos, 128); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_CFB); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SYMMETRIC); +#endif /* !NO_AES */ + FWTPM_Cleanup(&ctx); fwtpm_pass("TestParms(RSA-2048):", 0); } @@ -11046,6 +11248,7 @@ int fwtpm_unit_tests(int argc, char *argv[]) test_fwtpm_signseq_slot_exhaustion(); test_fwtpm_signseq_longmsg_boundary(); test_fwtpm_mldsa87_maxbuf(); + test_fwtpm_response_buffer_capacity(); test_fwtpm_mlkem1024_maxbuf(); test_fwtpm_hash_mldsa_seq_all_params(); #endif diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 560972fe..8e229973 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -4947,6 +4947,79 @@ static void test_TPM2_ASN_DecodeX509Cert_Errors(void) #endif } +static void test_TPM2_ASN_RsaUnpadPkcsv15(void) +{ +#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN) + byte blk[64]; + byte* p; + int sz; + int i; + + /* Well formed: 00 01 FF*8 00 then 2 data bytes */ + XMEMSET(blk, 0xFF, sizeof(blk)); + blk[0] = 0x00; blk[1] = 0x01; blk[10] = 0x00; + blk[11] = 0xAA; blk[12] = 0xBB; + p = blk; sz = 13; + AssertIntEQ(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0); + AssertIntEQ(sz, 2); + AssertIntEQ(p[0], 0xAA); + AssertIntEQ(p[1], 0xBB); + + /* Exactly 8 pad bytes is the minimum accepted */ + XMEMSET(blk, 0xFF, sizeof(blk)); + blk[0] = 0x00; blk[1] = 0x01; blk[10] = 0x00; blk[11] = 0x5A; + p = blk; sz = 12; + AssertIntEQ(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0); + AssertIntEQ(sz, 1); + + /* Seven pad bytes must be rejected */ + XMEMSET(blk, 0xFF, sizeof(blk)); + blk[0] = 0x00; blk[1] = 0x01; blk[9] = 0x00; blk[10] = 0x5A; + p = blk; sz = 11; + AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0); + + /* Wrong leading byte */ + XMEMSET(blk, 0xFF, sizeof(blk)); + blk[0] = 0x01; blk[1] = 0x01; blk[10] = 0x00; + p = blk; sz = 12; + AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0); + + /* Block type 2 must be rejected (this routine is type 1 only) */ + XMEMSET(blk, 0xFF, sizeof(blk)); + blk[0] = 0x00; blk[1] = 0x02; blk[10] = 0x00; + p = blk; sz = 12; + AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0); + + /* No separator at all (all 0xFF tail) */ + XMEMSET(blk, 0xFF, sizeof(blk)); + blk[0] = 0x00; blk[1] = 0x01; + p = blk; sz = 16; + AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0); + + /* Non-zero, non-FF byte where the separator belongs */ + XMEMSET(blk, 0xFF, sizeof(blk)); + blk[0] = 0x00; blk[1] = 0x01; blk[10] = 0x7E; + p = blk; sz = 16; + AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0); + + /* Too short to hold a block */ + for (i = 0; i < 3; i++) { + XMEMSET(blk, 0x00, sizeof(blk)); + p = blk; sz = i; + AssertIntNE(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0); + } + + /* Separator as the final byte yields an empty payload */ + XMEMSET(blk, 0xFF, sizeof(blk)); + blk[0] = 0x00; blk[1] = 0x01; blk[11] = 0x00; + p = blk; sz = 12; + AssertIntEQ(TPM2_ASN_RsaUnpadPkcsv15(&p, &sz), 0); + AssertIntEQ(sz, 0); + + printf("Test TPM Wrapper: %-40s Passed\n", "ASN RsaUnpadPkcsv15:"); +#endif +} + #if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN) #include #endif @@ -7237,6 +7310,7 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_CSR(); test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS(); test_TPM2_ASN_DecodeX509Cert_Errors(); + test_TPM2_ASN_RsaUnpadPkcsv15(); test_TPM2_ASN_DecodeX509Cert_Valid(); test_TPM2_ASN_DecodeTag_Errors(); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM2_PEM_DECODE) && \ diff --git a/wolftpm/fwtpm/fwtpm_command.h b/wolftpm/fwtpm/fwtpm_command.h index 705b309a..f9e38d95 100644 --- a/wolftpm/fwtpm/fwtpm_command.h +++ b/wolftpm/fwtpm/fwtpm_command.h @@ -46,7 +46,9 @@ \param cmdBuf input command buffer (big-endian TPM packet) \param cmdSize size of cmdBuf in bytes \param rspBuf output response buffer (caller-allocated) - \param rspSize in: capacity of rspBuf; out: bytes written + \param rspSize out: bytes written. rspBuf must be at least + FWTPM_MAX_COMMAND_SIZE bytes; a caller whose transport buffer is + smaller must stage the response through one that is not. \param locality TPM locality (0-4) reported by the transport \sa FWTPM_IO_ServerLoop diff --git a/wolftpm/spdm/spdm_responder.h b/wolftpm/spdm/spdm_responder.h index a471c18f..e85ff074 100644 --- a/wolftpm/spdm/spdm_responder.h +++ b/wolftpm/spdm/spdm_responder.h @@ -49,6 +49,11 @@ typedef int (*WOLFSPDM_RESP_TPM_CB)(void* userCtx, WOLFTPM_API int wolfSPDM_RespInit(WOLFSPDM_RESP_CTX* ctx); WOLFTPM_API void wolfSPDM_RespFree(WOLFSPDM_RESP_CTX* ctx); +/* The tunnel buffers stay at WOLFSPDM_MAX_MSG_SIZE: the secured path + * (encrypt, transport and requester buffers) is capped there too, so a + * larger TPM response could not be delivered even if staged here. */ +#define WOLFSPDM_MAX_TPM_MSG_SIZE WOLFSPDM_MAX_MSG_SIZE + /* RESP_CTX embeds the requester CTX + four MAX_MSG_SIZE working buffers * (4 * 4096) + identity/PSK material + tpm callback + flags. Static buffer * is sized for that worst case; spdm_responder.c has a compile-time assert diff --git a/wolftpm/tpm2_asn.h b/wolftpm/tpm2_asn.h index c72291ac..0ad05a3d 100644 --- a/wolftpm/tpm2_asn.h +++ b/wolftpm/tpm2_asn.h @@ -143,7 +143,10 @@ WOLFTPM_API int TPM2_ASN_DecodeRsaPubKey(uint8_t* input, int inputSz, /*! \ingroup ASN - \brief Removes PKCS#1 v1.5 padding from RSA signature + \brief Removes PKCS#1 v1.5 padding from RSA signature. The padding checks + are not constant time, so this must only be used on public signature + data such as the output of an RSA public key operation. Do not apply it + to a value recovered with a private key. \param pSig Pointer to buffer containing padded signature, updated to point to unpadded data \param sigSz Size of signature buffer, updated with unpadded size diff --git a/wolftpm/tpm2_packet.h b/wolftpm/tpm2_packet.h index 1699d6f9..52d7c747 100644 --- a/wolftpm/tpm2_packet.h +++ b/wolftpm/tpm2_packet.h @@ -98,6 +98,7 @@ typedef struct TPM2_Packet { byte* buf; int pos; int size; + unsigned int overflow:1; } TPM2_Packet;