F-7272 - Honor caller response buffer capacity in FWTPM_ProcessCommand

pull/561/head
Aidan Garske 2026-07-27 12:12:48 -07:00
parent 6e688cb9f1
commit 247853b4b0
8 changed files with 259 additions and 61 deletions

View File

@ -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)
{
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 + 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)
{
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) {
@ -16221,13 +16245,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 +16277,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 +16291,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 +16312,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 +16329,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 +16373,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 +16510,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 +16582,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 +16590,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 +16605,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 +16623,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 +16644,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 +16667,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 +16699,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 +16727,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 +16756,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 +16788,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 +16810,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 +16845,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 +16874,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 +16883,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 +16925,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 +16942,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 +17066,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

View File

@ -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) {

View File

@ -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. */
}

View File

@ -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);

View File

@ -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)
{

View File

@ -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
@ -11046,6 +11127,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

View File

@ -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

View File

@ -98,6 +98,7 @@ typedef struct TPM2_Packet {
byte* buf;
int pos;
int size;
unsigned int overflow:1;
} TPM2_Packet;