F-10829 - Set TPMA_CC flushed bit for SequenceComplete commands

pull/579/head
Aidan Garske 2026-08-19 09:50:13 -07:00
parent 38b08ea8fd
commit 373a22f517
2 changed files with 123 additions and 4 deletions

View File

@ -16676,6 +16676,7 @@ typedef struct {
UINT8 encDecFlags; /* Bit 0: first cmd param is TPM2B (can decrypt) */
/* Bit 1: first rsp param is TPM2B (can encrypt) */
/* Bit 2: first auth handle has DUP role */
/* Bit 3: command flushes its handle (TPMA_CC.F) */
} FWTPM_CMD_ENTRY;
#ifndef FWTPM_NO_PARAM_ENC
@ -16689,6 +16690,10 @@ typedef struct {
* a policy session, never a password or HMAC session. */
#define FW_CMD_FLAG_AUTH_DUP 0x04
/* Part 3 command-description modifier reported through TPMA_CC. {F}: the
* object or sequence named by the command's handle is flushed on success. */
#define FW_CMD_MOD_FLUSHED 0x08
/* inH aH oH flags */
static const FWTPM_CMD_ENTRY fwCmdTable[] = {
/* --- Basic (always enabled) --- */
@ -16754,8 +16759,8 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = {
* SignSequenceComplete / VerifySequenceComplete, so it is never advertised
* once the hash commands (and their sequence producers) are gated out. */
#ifndef FWTPM_NO_HASH_CMDS
{ TPM_CC_SequenceComplete, FwCmd_SequenceComplete, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC },
{ TPM_CC_EventSequenceComplete, FwCmd_EventSequenceComplete, 2, 2, 0, FW_CMD_FLAG_ENC },
{ TPM_CC_SequenceComplete, FwCmd_SequenceComplete, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC | FW_CMD_MOD_FLUSHED },
{ TPM_CC_EventSequenceComplete, FwCmd_EventSequenceComplete, 2, 2, 0, FW_CMD_FLAG_ENC | FW_CMD_MOD_FLUSHED },
#endif /* !FWTPM_NO_HASH_CMDS */
/* --- ECC --- */
#ifdef HAVE_ECC
@ -16869,12 +16874,12 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = {
#endif
#ifdef WOLFTPM_MLDSA_SIGN
{ TPM_CC_SignSequenceStart, FwCmd_SignSequenceStart, 1, 0, 1, FW_CMD_FLAG_ENC },
{ TPM_CC_SignSequenceComplete, FwCmd_SignSequenceComplete, 2, 2, 0, FW_CMD_FLAG_ENC },
{ TPM_CC_SignSequenceComplete, FwCmd_SignSequenceComplete, 2, 2, 0, FW_CMD_FLAG_ENC | FW_CMD_MOD_FLUSHED },
{ TPM_CC_SignDigest, FwCmd_SignDigest, 1, 1, 0, FW_CMD_FLAG_ENC },
#endif
#ifdef WOLFTPM_MLDSA_VERIFY
{ TPM_CC_VerifySequenceStart, FwCmd_VerifySequenceStart, 1, 0, 1, FW_CMD_FLAG_ENC },
{ TPM_CC_VerifySequenceComplete, FwCmd_VerifySequenceComplete, 2, 1, 0, 0 },
{ TPM_CC_VerifySequenceComplete, FwCmd_VerifySequenceComplete, 2, 1, 0, FW_CMD_MOD_FLUSHED },
{ TPM_CC_VerifyDigestSignature, FwCmd_VerifyDigestSignature, 1, 0, 0, FW_CMD_FLAG_ENC },
#endif
};
@ -16908,6 +16913,8 @@ static UINT32 FwGetCmdAttrsAt(int idx)
attrs |= ((UINT32)(e->inHandleCnt) & 0x7u) << 25; /* cHandles */
if (e->outHandleCnt > 0)
attrs |= ((UINT32)1 << 28); /* rHandle */
if (e->encDecFlags & FW_CMD_MOD_FLUSHED)
attrs |= ((UINT32)1 << 24); /* flushed */
if ((UINT32)e->cc & (UINT32)CC_VEND)
attrs |= (UINT32)CC_VEND; /* V */
return attrs;

View File

@ -137,6 +137,14 @@ static UINT32 TpmaCcRHandle(UINT32 tpma)
return (tpma >> 28) & 0x1u;
}
#ifndef FWTPM_NO_HASH_CMDS
/* flushed bit (handle's object/sequence is flushed on success), bit 24. */
static UINT32 TpmaCcFlushed(UINT32 tpma)
{
return (tpma >> 24) & 0x1u;
}
#endif /* !FWTPM_NO_HASH_CMDS */
/* Build a TPM command header. Returns TPM2_HEADER_SIZE (10). */
static int BuildCmdHeader(byte* buf, UINT16 tag, UINT32 totalSize, UINT32 cc)
{
@ -781,6 +789,107 @@ static void test_fwtpm_getcap_commands_tpma(void)
fwtpm_pass("GetCapability(COMMANDS) TPMA_CC:", 0);
}
/* TPM_CAP_COMMANDS must set TPMA_CC.flushed for every {F} command (the
* handle's object/sequence is flushed on success): SequenceComplete and
* EventSequenceComplete per TPM 2.0 Part 3. Both live under the hash-command
* group, so this check follows that gate. */
#ifndef FWTPM_NO_HASH_CMDS
static void test_fwtpm_getcap_commands_flushed(void)
{
FWTPM_CTX ctx;
int rc, rspSize, cmdSz;
UINT32 tpma;
memset(&ctx, 0, sizeof(ctx));
rc = fwtpm_test_startup(&ctx);
AssertIntEQ(rc, 0);
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
PutU32BE(gCmd + cmdSz, TPM_CAP_COMMANDS); cmdSz += 4;
PutU32BE(gCmd + cmdSz, TPM_CC_SequenceComplete); cmdSz += 4;
PutU32BE(gCmd + cmdSz, 1); cmdSz += 4;
PutU32BE(gCmd + 2, (UINT32)cmdSz);
rspSize = 0;
rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
AssertIntEQ(rc, TPM_RC_SUCCESS);
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 5), 1); /* count */
tpma = GetU32BE(gRsp + TPM2_HEADER_SIZE + 9);
AssertIntEQ(TpmaCcToCmdCode(tpma), (UINT32)TPM_CC_SequenceComplete);
AssertIntEQ(TpmaCcFlushed(tpma), 1);
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
PutU32BE(gCmd + cmdSz, TPM_CAP_COMMANDS); cmdSz += 4;
PutU32BE(gCmd + cmdSz, TPM_CC_EventSequenceComplete); cmdSz += 4;
PutU32BE(gCmd + cmdSz, 1); cmdSz += 4;
PutU32BE(gCmd + 2, (UINT32)cmdSz);
rspSize = 0;
rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
AssertIntEQ(rc, TPM_RC_SUCCESS);
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 5), 1); /* count */
tpma = GetU32BE(gRsp + TPM2_HEADER_SIZE + 9);
AssertIntEQ(TpmaCcToCmdCode(tpma), (UINT32)TPM_CC_EventSequenceComplete);
AssertIntEQ(TpmaCcFlushed(tpma), 1);
/* SequenceUpdate updates a sequence but does not complete or flush it, so
* its flushed bit must be clear. Guards against an unconditional set. */
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
PutU32BE(gCmd + cmdSz, TPM_CAP_COMMANDS); cmdSz += 4;
PutU32BE(gCmd + cmdSz, TPM_CC_SequenceUpdate); cmdSz += 4;
PutU32BE(gCmd + cmdSz, 1); cmdSz += 4;
PutU32BE(gCmd + 2, (UINT32)cmdSz);
rspSize = 0;
rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
AssertIntEQ(rc, TPM_RC_SUCCESS);
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 5), 1); /* count */
tpma = GetU32BE(gRsp + TPM2_HEADER_SIZE + 9);
AssertIntEQ(TpmaCcToCmdCode(tpma), (UINT32)TPM_CC_SequenceUpdate);
AssertIntEQ(TpmaCcFlushed(tpma), 0);
#ifdef WOLFTPM_MLDSA_SIGN
/* SignSequenceComplete flushes its sign sequence on success. */
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
PutU32BE(gCmd + cmdSz, TPM_CAP_COMMANDS); cmdSz += 4;
PutU32BE(gCmd + cmdSz, TPM_CC_SignSequenceComplete); cmdSz += 4;
PutU32BE(gCmd + cmdSz, 1); cmdSz += 4;
PutU32BE(gCmd + 2, (UINT32)cmdSz);
rspSize = 0;
rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
AssertIntEQ(rc, TPM_RC_SUCCESS);
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
tpma = GetU32BE(gRsp + TPM2_HEADER_SIZE + 9);
AssertIntEQ(TpmaCcToCmdCode(tpma), (UINT32)TPM_CC_SignSequenceComplete);
AssertIntEQ(TpmaCcFlushed(tpma), 1);
#endif /* WOLFTPM_MLDSA_SIGN */
#ifdef WOLFTPM_MLDSA_VERIFY
/* VerifySequenceComplete flushes its verify sequence on success. */
cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability);
PutU32BE(gCmd + cmdSz, TPM_CAP_COMMANDS); cmdSz += 4;
PutU32BE(gCmd + cmdSz, TPM_CC_VerifySequenceComplete); cmdSz += 4;
PutU32BE(gCmd + cmdSz, 1); cmdSz += 4;
PutU32BE(gCmd + 2, (UINT32)cmdSz);
rspSize = 0;
rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0);
AssertIntEQ(rc, TPM_RC_SUCCESS);
AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS);
tpma = GetU32BE(gRsp + TPM2_HEADER_SIZE + 9);
AssertIntEQ(TpmaCcToCmdCode(tpma), (UINT32)TPM_CC_VerifySequenceComplete);
AssertIntEQ(TpmaCcFlushed(tpma), 1);
#endif /* WOLFTPM_MLDSA_VERIFY */
FWTPM_Cleanup(&ctx);
fwtpm_pass("GetCapability(COMMANDS) flushed bit:", 0);
}
#endif /* !FWTPM_NO_HASH_CMDS */
/* Command codes with reserved bits set must be rejected with
* TPM_RC_COMMAND_CODE, never aliased onto a permitted command. */
static void test_fwtpm_cc_reserved_bits(void)
@ -12021,6 +12130,9 @@ int fwtpm_unit_tests(int argc, char *argv[])
test_fwtpm_getcap_algorithms();
test_fwtpm_getcap_commands();
test_fwtpm_getcap_commands_tpma();
#ifndef FWTPM_NO_HASH_CMDS
test_fwtpm_getcap_commands_flushed();
#endif
test_fwtpm_getcap_properties();
test_fwtpm_getcap_pcrs();
test_fwtpm_getcap_paging();