diff --git a/docs/FWTPM.md b/docs/FWTPM.md index b7554647..46696371 100644 --- a/docs/FWTPM.md +++ b/docs/FWTPM.md @@ -131,6 +131,13 @@ make | `WOLFTPM_FWTPM_TIS` | `--enable-fwtpm` without `--enable-swtpm` | | `WOLFTPM_ADV_IO` | Set with `WOLFTPM_FWTPM_HAL` | | `WOLFTPM_FWTPM_NV_APPEND_ONLY` | `--enable-fwtpm-nv-appendonly` (CMake `WOLFTPM_FWTPM_NV_APPEND_ONLY=yes`) | +| `WOLFTPM_FWTPM_TCG_TEST` | Manually (`CFLAGS=-DWOLFTPM_FWTPM_TCG_TEST`); off by default | + +No vendor commands are registered by default. Define `WOLFTPM_FWTPM_TCG_TEST` to compile in the optional `TPM2_Vendor_TCG_Test` (`0x20000000`) echo command. + +### Command-Code Enforcement + +A valid command code carries only the 16-bit index plus, for vendor commands, the V bit (`CC_VEND`, bit 29); codes with any other reserved bit set, or codes not in the dispatch table, are rejected with `TPM_RC_COMMAND_CODE`. `TPM2_GetCapability(TPM_CAP_COMMANDS)` returns proper `TPMA_CC` values (index + handle attributes + V bit, ordered by command code). ## Usage diff --git a/src/fwtpm/README.md b/src/fwtpm/README.md index fbc05c19..75840250 100644 --- a/src/fwtpm/README.md +++ b/src/fwtpm/README.md @@ -140,7 +140,10 @@ control), spec version targeting, and HAL abstraction details. Key options: `--enable-fwtpm`, `--enable-fwtpm-only`, `--enable-swtpm`, `--enable-fwtpm-small-ctx`, `--enable-fuzz`. Feature disable macros: `FWTPM_NO_ATTESTATION`, `FWTPM_NO_NV`, `FWTPM_NO_POLICY`, `FWTPM_NO_CREDENTIAL`, -`FWTPM_NO_DA`, `FWTPM_NO_PARAM_ENC`. +`FWTPM_NO_DA`, `FWTPM_NO_PARAM_ENC`. Feature enable macros: +`WOLFTPM_FWTPM_TCG_TEST` (optional `Vendor_TCG_Test` echo command, off by default). + +Command codes with reserved bits set (only the 16-bit index plus the `CC_VEND` V bit are valid), or codes not in the dispatch table, are rejected with `TPM_RC_COMMAND_CODE`. ## SPDM Responder Mode @@ -192,7 +195,9 @@ ContextSave, ContextLoad, ReadPublic, Clear, ClearControl, ChangeEPS, ChangePPS, HierarchyChangeAuth, SetPrimaryPolicy, EvictControl, Create, ObjectChangeAuth, Load, Sign, VerifySignature, Hash, HMAC, HMAC\_Start, HashSequenceStart, SequenceUpdate, SequenceComplete, EventSequenceComplete, StartAuthSession, -Unseal, LoadExternal, Import, Duplicate, Rewrap, CreateLoaded, +Unseal, LoadExternal, Import, Duplicate, Rewrap, CreateLoaded + +**Optional vendor command (off by default, `WOLFTPM_FWTPM_TCG_TEST`):** Vendor\_TCG\_Test **Conditional on algorithm (`NO_RSA` / `HAVE_ECC` / `NO_AES`):** diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index dfb07ede..c16cd05e 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -84,6 +84,7 @@ static FWTPM_HashSeq* FwFindHashSeq(FWTPM_CTX* ctx, TPM_HANDLE handle); /* Command table accessors (fwCmdTable is defined near end of file) */ static int FwGetCmdCount(void); static TPM_CC FwGetCmdCcAt(int idx); +static UINT32 FwGetCmdAttrsAt(int idx); /* --- Response helpers using TPM2_Packet --- */ /* Initialize a response packet on the given buffer */ @@ -1079,8 +1080,6 @@ static TPM_RC FwCmd_StirRandom(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, /* Per-PCR reset/extend locality helper (defined with its tables below). */ static int FwPcrLocalityAllowed(int pcrIndex, int locality, int isReset); -/* Mask a TPM command code to its 16-bit value (strip vendor/reserved bits). */ -#define FW_CC_MASK 0x0000FFFFu /* PCRs 0-15 are the SRTM set that TPM_PT_PCR_SAVE reports as saved. */ #define FWTPM_PCR_SAVE_COUNT 16 /* pcrProps[].kind: how a TPM_CAP_PCR_PROPERTIES row maps to the locality table. */ @@ -1251,11 +1250,10 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, UINT32 cc, bestCc = 0, lastCc = 0; int haveLast = 0; - /* Honor the property cursor and page in ascending command-code - * order (the dispatch table is not sorted); see TPM_CAP_ALGS - * above for the rationale. */ + /* Page in ascending command-code order; sort by full TPM_CC (so + * vendor commands sort last) and emit a TPMA_CC (FwGetCmdAttrsAt). */ for (k = 0; k < numCmds; k++) { - cc = (UINT32)FwGetCmdCcAt(k) & FW_CC_MASK; + cc = (UINT32)FwGetCmdCcAt(k); if (cc >= property) avail++; } @@ -1271,7 +1269,7 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, for (i = 0; i < (UINT32)numOut; i++) { best = -1; for (k = 0; k < numCmds; k++) { - cc = (UINT32)FwGetCmdCcAt(k) & FW_CC_MASK; + cc = (UINT32)FwGetCmdCcAt(k); if (cc < property) continue; if (haveLast && cc <= lastCc) @@ -1283,7 +1281,8 @@ static TPM_RC FwCmd_GetCapability(FWTPM_CTX* ctx, TPM2_Packet* cmd, } if (best < 0) break; /* defensive: no further candidates */ - TPM2_Packet_AppendU32(rsp, bestCc); + /* Emit a TPMA_CC, not the bare command code. */ + TPM2_Packet_AppendU32(rsp, FwGetCmdAttrsAt(best)); lastCc = bestCc; haveLast = 1; emitted++; @@ -13995,8 +13994,9 @@ static TPM_RC FwCmd_ZGen_2Phase(FWTPM_CTX* ctx, TPM2_Packet* cmd, } #endif /* HAVE_ECC */ +#ifdef WOLFTPM_FWTPM_TCG_TEST /* --- TPM2_Vendor_TCG_Test (CC 0x20000000) --- */ -/* Vendor-specific test command. Echoes input data as output. */ +/* Optional vendor test command (echoes input); off by default. */ static TPM_RC FwCmd_Vendor_TCG_Test(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, TPM2_Packet* rsp, UINT16 cmdTag) { @@ -14040,6 +14040,7 @@ static TPM_RC FwCmd_Vendor_TCG_Test(FWTPM_CTX* ctx, TPM2_Packet* cmd, return rc; } +#endif /* WOLFTPM_FWTPM_TCG_TEST */ /* ================================================================== */ /* v1.85 PQC Commands */ @@ -15910,7 +15911,7 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = { { TPM_CC_Hash, FwCmd_Hash, 0, 0, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC }, { TPM_CC_HMAC, FwCmd_HMAC, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC }, { TPM_CC_HMAC_Start, FwCmd_HMAC_Start, 1, 1, 1, FW_CMD_FLAG_ENC }, - { TPM_CC_HashSequenceStart, FwCmd_HashSequenceStart, 0, 0, 0, FW_CMD_FLAG_ENC }, + { TPM_CC_HashSequenceStart, FwCmd_HashSequenceStart, 0, 0, 1, FW_CMD_FLAG_ENC }, { TPM_CC_SequenceUpdate, FwCmd_SequenceUpdate, 1, 1, 0, FW_CMD_FLAG_ENC }, { 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 }, @@ -15922,7 +15923,7 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = { { TPM_CC_ZGen_2Phase, FwCmd_ZGen_2Phase, 1, 1, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC }, #endif /* --- Sessions --- */ - { TPM_CC_StartAuthSession, FwCmd_StartAuthSession, 2, 0, 0, 0 }, + { TPM_CC_StartAuthSession, FwCmd_StartAuthSession, 2, 0, 1, 0 }, { TPM_CC_Unseal, FwCmd_Unseal, 1, 1, 0, FW_CMD_FLAG_DEC }, /* --- Policy --- */ #ifndef FWTPM_NO_POLICY @@ -16003,8 +16004,10 @@ static const FWTPM_CMD_ENTRY fwCmdTable[] = { { TPM_CC_DictionaryAttackLockReset, FwCmd_DictionaryAttackLockReset, 1, 1, 0, 0 }, { TPM_CC_DictionaryAttackParameters, FwCmd_DictionaryAttackParameters, 1, 1, 0, 0 }, #endif - /* --- Vendor --- */ + /* --- Vendor (optional; off by default, see WOLFTPM_FWTPM_TCG_TEST) --- */ +#ifdef WOLFTPM_FWTPM_TCG_TEST { TPM_CC_Vendor_TCG_Test, FwCmd_Vendor_TCG_Test, 0, 0, 0, FW_CMD_FLAG_ENC | FW_CMD_FLAG_DEC }, +#endif /* --- v1.85 PQC handlers --- */ #ifdef WOLFTPM_MLKEM_ENCAP { TPM_CC_Encapsulate, FwCmd_Encapsulate, 1, 0, 0, FW_CMD_FLAG_DEC }, @@ -16039,6 +16042,25 @@ static TPM_CC FwGetCmdCcAt(int idx) return fwCmdTable[idx].cc; } +/* TPMA_CC for the command at table index idx: commandIndex (15:0), cHandles + * (27:25), rHandle (28), and vendor V bit (29). Used for TPM_CAP_COMMANDS. */ +static UINT32 FwGetCmdAttrsAt(int idx) +{ + const FWTPM_CMD_ENTRY* e; + UINT32 attrs; + + if (idx < 0 || idx >= FWTPM_CMD_TABLE_SIZE) + return 0; + e = &fwCmdTable[idx]; + attrs = (UINT32)(e->cc & 0xFFFFu); /* commandIndex */ + attrs |= ((UINT32)(e->inHandleCnt) & 0x7u) << 25; /* cHandles */ + if (e->outHandleCnt > 0) + attrs |= ((UINT32)1 << 28); /* rHandle */ + if ((UINT32)e->cc & (UINT32)CC_VEND) + attrs |= (UINT32)CC_VEND; /* V */ + return attrs; +} + static const FWTPM_CMD_ENTRY* FwFindCmdEntry(TPM_CC cc) { int i; @@ -16232,6 +16254,14 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, return TPM_RC_SUCCESS; } + /* 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, + 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, diff --git a/tests/fwtpm_unit_tests.c b/tests/fwtpm_unit_tests.c index 873b014d..c3694210 100644 --- a/tests/fwtpm_unit_tests.c +++ b/tests/fwtpm_unit_tests.c @@ -118,6 +118,25 @@ static UINT32 GetU32BE(const byte* buf) ((UINT32)buf[2] << 8) | buf[3]; } +/* Command code (commandIndex + vendor V bit) from a TPMA_CC; used to page + * TPM_CAP_COMMANDS by command code rather than the raw attribute word. */ +static UINT32 TpmaCcToCmdCode(UINT32 tpma) +{ + return (tpma & 0xFFFFu) | (tpma & (UINT32)CC_VEND); +} + +/* cHandles field (number of command handles) from a TPMA_CC, bits 27:25. */ +static UINT32 TpmaCcHandles(UINT32 tpma) +{ + return (tpma >> 25) & 0x7u; +} + +/* rHandle bit (response returns a handle) from a TPMA_CC, bit 28. */ +static UINT32 TpmaCcRHandle(UINT32 tpma) +{ + return (tpma >> 28) & 0x1u; +} + /* Build a TPM command header. Returns TPM2_HEADER_SIZE (10). */ static int BuildCmdHeader(byte* buf, UINT16 tag, UINT32 totalSize, UINT32 cc) { @@ -671,7 +690,8 @@ static void test_fwtpm_getcap_commands(void) moreData = gRsp[TPM2_HEADER_SIZE]; cap = GetU32BE(gRsp + TPM2_HEADER_SIZE + 1); count = GetU32BE(gRsp + TPM2_HEADER_SIZE + 5); - firstCc = GetU32BE(gRsp + TPM2_HEADER_SIZE + 9); + /* TPM_CAP_COMMANDS returns TPMA_CC values; page by the command code. */ + firstCc = TpmaCcToCmdCode(GetU32BE(gRsp + TPM2_HEADER_SIZE + 9)); AssertIntEQ(moreData, 1); AssertIntEQ(cap, TPM_CAP_COMMANDS); AssertIntEQ(count, 1); @@ -687,12 +707,143 @@ static void test_fwtpm_getcap_commands(void) rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); AssertIntEQ(rc, TPM_RC_SUCCESS); AssertIntEQ(GetU32BE(gRsp + TPM2_HEADER_SIZE + 5), 1); - AssertIntGT(GetU32BE(gRsp + TPM2_HEADER_SIZE + 9), firstCc); + AssertIntGT(TpmaCcToCmdCode(GetU32BE(gRsp + TPM2_HEADER_SIZE + 9)), firstCc); FWTPM_Cleanup(&ctx); fwtpm_pass("GetCapability(COMMANDS):", 0); } +/* TPM_CAP_COMMANDS must report a TPMA_CC with attributes, not a bare index. */ +static void test_fwtpm_getcap_commands_tpma(void) +{ + FWTPM_CTX ctx; + int rc, rspSize, cmdSz; + UINT32 tpma; + + memset(&ctx, 0, sizeof(ctx)); + rc = fwtpm_test_startup(&ctx); + AssertIntEQ(rc, 0); + + /* PCR_Extend (0x182) takes one handle: expect commandIndex 0x182, + * cHandles == 1, no V bit. */ + cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_GetCapability); + PutU32BE(gCmd + cmdSz, TPM_CAP_COMMANDS); cmdSz += 4; + PutU32BE(gCmd + cmdSz, TPM_CC_PCR_Extend); 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_PCR_Extend); + AssertIntEQ(TpmaCcHandles(tpma), 1); + AssertIntEQ(TpmaCcRHandle(tpma), 0); /* no response handle */ + AssertIntEQ(tpma & (UINT32)CC_VEND, 0); + + /* HashSequenceStart returns a sequence handle: expect rHandle == 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_HashSequenceStart); 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_HashSequenceStart); + AssertIntEQ(TpmaCcRHandle(tpma), 1); /* returns a handle */ + + /* StartAuthSession returns a session handle: expect rHandle == 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_StartAuthSession); 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_StartAuthSession); + AssertIntEQ(TpmaCcRHandle(tpma), 1); /* returns a handle */ + + FWTPM_Cleanup(&ctx); + fwtpm_pass("GetCapability(COMMANDS) TPMA_CC:", 0); +} + +/* 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) +{ + FWTPM_CTX ctx; + int rc, rspSize, cmdSz; + + memset(&ctx, 0, sizeof(ctx)); + rc = fwtpm_test_startup(&ctx); + AssertIntEQ(rc, 0); + + /* Reserved bit 16 set: must NOT alias TPM_CC_GetCapability. */ + cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, + (UINT32)TPM_CC_GetCapability | 0x00010000u); + PutU32BE(gCmd + cmdSz, TPM_CAP_TPM_PROPERTIES); cmdSz += 4; + PutU32BE(gCmd + cmdSz, TPM_PT_MANUFACTURER); 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_COMMAND_CODE); + + /* Reserved bit 30 set on an otherwise-valid index. */ + cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, + (UINT32)TPM_CC_GetCapability | 0x40000000u); + PutU32BE(gCmd + cmdSz, TPM_CAP_TPM_PROPERTIES); cmdSz += 4; + PutU32BE(gCmd + cmdSz, TPM_PT_MANUFACTURER); 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_COMMAND_CODE); + + /* Well-formed but unregistered vendor code: clears the reserved-bit gate, + * rejected by dispatch as unimplemented. */ + cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 10, + (UINT32)CC_VEND | 0x0FFFu); + 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_COMMAND_CODE); + + /* TCG_Test (CC_VEND) is structurally valid: command-specific result + * (dispatched only under WOLFTPM_FWTPM_TCG_TEST), never a reserved-bit + * reject. */ + cmdSz = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, + (UINT32)TPM_CC_Vendor_TCG_Test); + PutU16BE(gCmd + cmdSz, 0); cmdSz += 2; /* dataSize = 0 */ + PutU32BE(gCmd + 2, (UINT32)cmdSz); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); +#ifdef WOLFTPM_FWTPM_TCG_TEST + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); +#else + AssertIntEQ(GetRspRC(gRsp), TPM_RC_COMMAND_CODE); +#endif + + FWTPM_Cleanup(&ctx); + fwtpm_pass("Command-code reserved-bit enforcement:", 0); +} + static void test_fwtpm_getcap_properties(void) { FWTPM_CTX ctx; @@ -781,6 +932,9 @@ static void getcap_paging_check(FWTPM_CTX* ctx, UINT32 cap, int idIs16) if (count == 1) { id = idIs16 ? (UINT32)GetU16BE(gRsp + TPM2_HEADER_SIZE + 9) : GetU32BE(gRsp + TPM2_HEADER_SIZE + 9); + /* TPM_CAP_COMMANDS entries are TPMA_CC; page by command code. */ + if (!idIs16) + id = TpmaCcToCmdCode(id); if (total > 0) AssertIntGT((int)id, (int)prev); /* strictly ascending */ prev = id; @@ -10759,6 +10913,7 @@ int fwtpm_unit_tests(int argc, char *argv[]) test_fwtpm_bad_tag(); test_fwtpm_size_mismatch(); test_fwtpm_unknown_command(); + test_fwtpm_cc_reserved_bits(); test_fwtpm_no_startup(); test_fwtpm_null_args(); @@ -10774,6 +10929,7 @@ int fwtpm_unit_tests(int argc, char *argv[]) /* GetCapability */ test_fwtpm_getcap_algorithms(); test_fwtpm_getcap_commands(); + test_fwtpm_getcap_commands_tpma(); test_fwtpm_getcap_properties(); test_fwtpm_getcap_pcrs(); test_fwtpm_getcap_paging(); diff --git a/tests/unit_tests.c b/tests/unit_tests.c index ca40237d..cdaf33d2 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1518,6 +1518,24 @@ static void test_TPM2_ConstantCompare(void) printf("Test TPM Wrapper: %-40s Passed\n", "ConstantCompare:"); } +/* WOLFTPM_IS_COMMAND_UNAVAILABLE must match TPM_RC_COMMAND_CODE even with + * vendor bits set (NS350 returns 0x000b0143), without aliasing other codes. */ +static void test_WOLFTPM_IS_COMMAND_UNAVAILABLE(void) +{ + AssertIntNE(0, WOLFTPM_IS_COMMAND_UNAVAILABLE((int)TPM_RC_COMMAND_CODE)); + AssertIntNE(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(0x000b0143)); /* vendor bits */ + AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE((int)TPM_RC_COMMAND_SIZE)); + AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE((int)TPM_RC_SUCCESS)); + /* Negative wolfCrypt errors must not alias onto the command code. */ + AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(-189)); /* ASN_CRL_CONFIRM_E */ + AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(-1)); /* generic negative */ + /* Positive layer/vendor bits must not alias either. */ + AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(0x00000343)); /* layer bits */ + AssertIntEQ(0, WOLFTPM_IS_COMMAND_UNAVAILABLE(0x000b0142)); /* vendor, non-cc */ + + printf("Test TPM Wrapper: %-40s Passed\n", "IsCommandUnavailable:"); +} + static void test_TPM2_AesCfbRoundtrip(void) { #if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_AES) && \ @@ -6840,6 +6858,7 @@ int unit_tests(int argc, char *argv[]) test_TPM2_HmacCompute(); test_TPM2_HashCompute(); test_TPM2_ConstantCompare(); + test_WOLFTPM_IS_COMMAND_UNAVAILABLE(); test_TPM2_AesCfbRoundtrip(); test_TPM2_KDFa_MultiHash(); test_TPM2_KDFe_MultiHash(); diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 70bebcf8..dbba0b8d 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -2151,9 +2151,19 @@ struct wolfTPM_winContext { #define TPM_E_COMMAND_BLOCKED (0x80280400) #endif -#define WOLFTPM_IS_COMMAND_UNAVAILABLE(code) ((code) == (int)TPM_RC_COMMAND_CODE || (code) == (int)TPM_E_COMMAND_BLOCKED) +/* Mask off vendor/layer high bits so a vendor-decorated TPM_RC_COMMAND_CODE + * (e.g. NS350 returns 0x000b0143 for 0x143) still matches. Gate on >= 0 so a + * propagated negative wolfCrypt error (e.g. -189) is never misread as an + * unavailable command. TPM_E_COMMAND_BLOCKED is a Windows HRESULT (negative), + * matched exactly. */ +#define WOLFTPM_IS_COMMAND_UNAVAILABLE(code) \ + (((code) >= 0 && \ + (((UINT32)(code)) & 0xFFFFu) == (UINT32)TPM_RC_COMMAND_CODE) || \ + (code) == (int)TPM_E_COMMAND_BLOCKED) #else -#define WOLFTPM_IS_COMMAND_UNAVAILABLE(code) (code == (int)TPM_RC_COMMAND_CODE) +#define WOLFTPM_IS_COMMAND_UNAVAILABLE(code) \ + ((code) >= 0 && \ + (((UINT32)(code)) & 0xFFFFu) == (UINT32)TPM_RC_COMMAND_CODE) #endif /* WOLFTPM_WINAPI */ /* make sure advanced IO is enabled for I2C */