diff --git a/examples/boot/secure_rot.c b/examples/boot/secure_rot.c index 69cc3a0d8..8a6e8eff1 100644 --- a/examples/boot/secure_rot.c +++ b/examples/boot/secure_rot.c @@ -83,6 +83,7 @@ int TPM2_Boot_SecureROT_Example(void* userCtx, int argc, char *argv[]) enum wc_HashType hashType = WC_HASH_TYPE_SHA256; byte digest[WC_MAX_DIGEST_SIZE]; int digestSz = 0; + int i; byte authBuf[WC_SHA256_DIGEST_SIZE]; int authBufSz = 0; @@ -230,7 +231,18 @@ int TPM2_Boot_SecureROT_Example(void* userCtx, int argc, char *argv[]) nvAttributes, digestSz, authBuf, authBufSz); if (rc == TPM_RC_NV_DEFINED) { printf("Warning: NV Index 0x%x already exists!\n", nvIndex); - rc = 0; + /* Only reuse an index this tool would have created, so a + * planted index cannot hold the root of trust */ + rc = wolfTPM2_NVReadPublic(&dev, nvIndex, &nvPublic); + if (rc == 0 && ((nvPublic.attributes & ~(TPMA_NV_WRITTEN | + TPMA_NV_WRITELOCKED | TPMA_NV_READLOCKED)) != + nvAttributes || nvPublic.dataSize != digestSz || + nvPublic.nameAlg != WOLFTPM2_WRAP_DIGEST || + nvPublic.authPolicy.size != 0)) { + printf("NV Index 0x%x attributes, size, name algorithm " + "or policy do not match\n", nvIndex); + rc = TPM_RC_NV_DEFINED; + } } wolfTPM2_SetAuthHandle(&dev, 0, &nv.handle); } @@ -262,7 +274,10 @@ int TPM2_Boot_SecureROT_Example(void* userCtx, int argc, char *argv[]) rc = wolfTPM2_NVReadAuth(&dev, &nv, nvIndex, digest, (word32*)&digestSz, 0); if (rc == 0) { printf("Read Public Key Hash (%d)\n", digestSz); - TPM2_PrintBin(digest, digestSz); + for (i = 0; i < digestSz; i++) { + printf("%02x", digest[i]); + } + printf("\n"); } else if ((rc & RC_MAX_FMT1) == TPM_RC_HANDLE) { printf("NV index does not exist\n"); diff --git a/examples/keygen/create_primary.c b/examples/keygen/create_primary.c index fcbfa0c98..fafd90b12 100644 --- a/examples/keygen/create_primary.c +++ b/examples/keygen/create_primary.c @@ -335,10 +335,12 @@ int TPM2_CreatePrimaryKey_Example(void* userCtx, int argc, char *argv[]) if (persistHandle > 0) { #ifndef WOLFTPM_WINAPI - /* Move storage key into persistent NV */ + /* Move storage key into persistent NV. EvictControl takes the + * owner or platform hierarchy, never endorsement. */ printf("Storing Primary key to handle 0x%08x\n", persistHandle); - rc = wolfTPM2_NVStoreKey(&dev, hierarchy, primary, - persistHandle); + rc = wolfTPM2_NVStoreKey(&dev, + hierarchy == TPM_RH_PLATFORM ? TPM_RH_PLATFORM : TPM_RH_OWNER, + primary, persistHandle); if (rc != TPM_RC_SUCCESS) goto exit; #else printf("Windows TBS does not allow persisting handles to " diff --git a/examples/management/flush.c b/examples/management/flush.c index 751b599da..d7450429f 100644 --- a/examples/management/flush.c +++ b/examples/management/flush.c @@ -32,6 +32,7 @@ #include #include +#include #ifndef WOLFTPM2_NO_WRAPPER /******************************************************************************/ @@ -59,7 +60,25 @@ int TPM2_Flush_Tool(void* userCtx, int argc, char *argv[]) FlushContext_In flushCtx; if (argc == 2) { - handle = (word32)XSTRTOUL(argv[1], NULL, 0); + char* end = NULL; + unsigned long val; + if (XSTRCMP(argv[1], "-?") == 0 || + XSTRCMP(argv[1], "-h") == 0 || + XSTRCMP(argv[1], "--help") == 0) { + usage(); + return 0; + } + /* strtoul accepts a sign and wraps it, so require a leading digit */ + errno = 0; + val = XSTRTOUL(argv[1], &end, 0); + if (argv[1][0] < '0' || argv[1][0] > '9' || *end != '\0' || + errno == ERANGE || val == 0 || + (unsigned long)(word32)val != val) { + printf("Invalid handle %s\n", argv[1]); + usage(); + return -1; + } + handle = (TPM_HANDLE)val; } else if (argc == 1) { allTransientObjects = 1; @@ -78,7 +97,7 @@ int TPM2_Flush_Tool(void* userCtx, int argc, char *argv[]) } printf("wolfTPM2_Init: success\n"); - if (allTransientObjects || handle == 0) { + if (allTransientObjects) { /* Flush key objects */ for (handle=0x80000000; handle < 0x8000000A; handle++) { flushCtx.flushHandle = handle; diff --git a/examples/run_examples.sh b/examples/run_examples.sh index 8c88ad5a5..694f37a9d 100755 --- a/examples/run_examples.sh +++ b/examples/run_examples.sh @@ -121,6 +121,9 @@ RESULT=$? ./examples/keygen/create_primary -rsa -oh -auth=ThisIsMyStorageKeyAuth -store=0x81000200 >> $TPMPWD/run.out 2>&1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "create primary owner rsa key stored failed! $RESULT" && exit 1 +./examples/keygen/create_primary -rsa -eh -store=0x81000203 >> $TPMPWD/run.out 2>&1 +RESULT=$? +[ $RESULT -ne 0 ] && echo -e "create primary endorsement rsa key stored failed! $RESULT" && exit 1 # Dictionary Attack (DA / noDA) check. Signs with a DA-protected key (rides # through any TPM_RC_RETRY) and shows a noDA key never trips lockout. The @@ -147,7 +150,27 @@ if [ $WOLFCRYPT_ENABLE -eq 1 ]; then RESULT=$? [ $RESULT -ne 0 ] && echo -e "certify RSA IDevID with IAK failed! $RESULT" && exit 1 + # Help and a bad handle must not flush the keys loaded above + ./examples/management/flush -h >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "flush help failed! $RESULT" && exit 1 + # A bad handle is rejected before the tool contacts the TPM. The last value + # overflows unsigned long on 32 and 64 bit targets. + for BADHANDLE in notahandle -2147483648 0x180000000 0x10000000000000000; do + FLUSH_OUT=$(./examples/management/flush $BADHANDLE 2>&1) + RESULT=$? + echo "$FLUSH_OUT" >> $TPMPWD/run.out + [ $RESULT -eq 0 ] && echo -e "flush accepted handle $BADHANDLE!" && exit 1 + case "$FLUSH_OUT" in + *"Preparing to free"*) + echo -e "flush contacted the TPM for handle $BADHANDLE!" && exit 1 ;; + *"Invalid handle"*) ;; + *) echo -e "flush did not reject handle $BADHANDLE!" && exit 1 ;; + esac + done ./examples/management/flush 0x80000000 >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "flush 0x80000000 failed! $RESULT" && exit 1 ./examples/management/flush 0x80000001 >> $TPMPWD/run.out 2>&1 ./examples/keygen/create_primary -ecc -eh -iak -keep >> $TPMPWD/run.out 2>&1 @@ -1051,6 +1074,39 @@ if [ $WOLFCRYPT_ENABLE -eq 1 ] && [ $WOLFCRYPT_DEFAULT -eq 0 ] && [ $NO_FILESYST ./examples/boot/secure_rot -nvindex=0x1400201 >> $TPMPWD/run.out 2>&1 RESULT=$? [ $RESULT -eq 0 ] && echo -e "secure rot write ecc384 read no auth! $RESULT" && exit 1 + + # A pre-existing index that differs is rejected and left unchanged. The hash + # is shorter than the index, so without the check the write would succeed. + ./examples/boot/secure_rot -nvindex=0x1400203 -authstr=test -hash=e77dd3112a27948a3f2d87f32dc69ebeed0b3344c5d7726f5742f4f0c0f451aa >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "secure rot create 0x1400203 failed! $RESULT" && exit 1 + ROT_OUT=$(./examples/boot/secure_rot -nvindex=0x1400203 -authstr=test -hash=be4213f8b3b986639e69ed0ea8b49d94aabbccdd 2>&1) + RESULT=$? + echo "$ROT_OUT" >> $TPMPWD/run.out + [ $RESULT -eq 0 ] && echo -e "secure rot accepted an index of another size!" && exit 1 + case "$ROT_OUT" in + *"do not match"*) ;; + *) echo -e "secure rot did not report the index mismatch!" && exit 1 ;; + esac + ROT_OUT=$(./examples/boot/secure_rot -nvindex=0x1400203 -authstr=test 2>&1) + echo "$ROT_OUT" >> $TPMPWD/run.out + case "$ROT_OUT" in + *e77dd3112a27948a3f2d87f32dc69ebeed0b3344c5d7726f5742f4f0c0f451aa*) ;; + *) echo -e "secure rot changed a rejected index!" && exit 1 ;; + esac + + # Same size, but other attributes and a policy + ./examples/nvram/extend -nvindex=0x1400204 -aes >> $TPMPWD/run.out 2>&1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "nv extend create 0x1400204 failed! $RESULT" && exit 1 + ROT_OUT=$(./examples/boot/secure_rot -nvindex=0x1400204 -authstr=test -hash=e77dd3112a27948a3f2d87f32dc69ebeed0b3344c5d7726f5742f4f0c0f451aa 2>&1) + RESULT=$? + echo "$ROT_OUT" >> $TPMPWD/run.out + [ $RESULT -eq 0 ] && echo -e "secure rot accepted an index with other attributes!" && exit 1 + case "$ROT_OUT" in + *"do not match"*) ;; + *) echo -e "secure rot did not report the attribute mismatch!" && exit 1 ;; + esac fi # Seal/Unseal (PCR Policy) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index b90f365bb..bf995410c 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -21239,12 +21239,13 @@ static TPM_RC FwCheckPolicyAssertions(FWTPM_CTX* ctx, } } } - /* PolicyTemplate binds only the creation template (Part 3 Sec.23.19); - * it does not restrict the command, that is PolicyCommandCode's role. */ - if (rc == 0 && sess->templateHash.size > 0 && - (cmdCode == TPM_CC_Create || cmdCode == TPM_CC_CreatePrimary || - cmdCode == TPM_CC_CreateLoaded)) { - if (FwComputeTemplateHash(sess->authHash, cmdBuf, cmdSize, cpStart, + /* A template hash can only match Create, CreatePrimary or CreateLoaded, + * so any other command fails the policy, as in the reference + * CompareTemplateHash. */ + if (rc == 0 && sess->templateHash.size > 0) { + if ((cmdCode != TPM_CC_Create && cmdCode != TPM_CC_CreatePrimary && + cmdCode != TPM_CC_CreateLoaded) || + FwComputeTemplateHash(sess->authHash, cmdBuf, cmdSize, cpStart, digest, &digestSz) != 0) { rc = TPM_RC_POLICY_FAIL; } @@ -22461,6 +22462,13 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, rspBuf[rspHandleEnd + 3]); } rspParamStart = rspHandleEnd + 4; + /* Never trust paramSize past what the handler wrote */ + if (rspParamStart > rspPkt.pos) { + rspParamSzVal = 0; + } + else if (rspParamSzVal > (UINT32)(rspPkt.pos - rspParamStart)) { + rspParamSzVal = (UINT32)(rspPkt.pos - rspParamStart); + } #ifndef FWTPM_NO_PARAM_ENC rspParamEnd = rspParamStart + (int)rspParamSzVal; #endif diff --git a/src/fwtpm/fwtpm_tis.c b/src/fwtpm/fwtpm_tis.c index e19af4c15..00f607f31 100644 --- a/src/fwtpm/fwtpm_tis.c +++ b/src/fwtpm/fwtpm_tis.c @@ -84,6 +84,7 @@ static void TisHandleRegAccess(FWTPM_CTX* ctx, FWTPM_TIS_REGS* regs) * (STS/GO/FIFO). ACCESS and read-only ID/INT registers are not gated. */ int isOwner; FWTPM_DECLARE_BUF(localCmd, FWTPM_TIS_FIFO_SIZE); + FWTPM_DECLARE_BUF(localRsp, FWTPM_TIS_FIFO_SIZE); isOwner = (ctx->tisLocality >= 0 && loc == ctx->tisLocality); @@ -151,14 +152,18 @@ static void TisHandleRegAccess(FWTPM_CTX* ctx, FWTPM_TIS_REGS* regs) FWTPM_TIS_BURST_COUNT); } if (val & FWTPM_STS_GO) { - /* Execute command - copy to local buffer first to - * prevent TOCTOU if cmd_buf is in shared memory */ + /* Execute command - use local command and response + * buffers to prevent TOCTOU if the registers are in + * shared memory */ UINT32 localCmdLen; int rspSize = 0; int procRc = TPM_RC_FAILURE; TPM_RC rc = TPM_RC_SUCCESS; /* FWTPM_ALLOC_BUF sets on OOM */ FWTPM_ALLOC_BUF(localCmd, FWTPM_TIS_FIFO_SIZE); + if (rc == TPM_RC_SUCCESS) { + FWTPM_ALLOC_BUF(localRsp, FWTPM_TIS_FIFO_SIZE); + } if (rc == TPM_RC_SUCCESS) { localCmdLen = regs->cmd_len; if (localCmdLen > FWTPM_TIS_FIFO_SIZE) { @@ -172,12 +177,24 @@ 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); + rspSize = FWTPM_TIS_FIFO_SIZE; procRc = FWTPM_ProcessCommand(ctx, localCmd, (int)localCmdLen, - regs->rsp_buf, &rspSize, loc); - FWTPM_FREE_BUF(localCmd); + localRsp, &rspSize, loc); + if (procRc == TPM_RC_SUCCESS && rspSize > 0 && + rspSize <= FWTPM_TIS_FIFO_SIZE) { + XMEMCPY(regs->rsp_buf, localRsp, rspSize); + } + else { + rspSize = 0; + } } + TPM2_ForceZero(localRsp, + FWTPM_SIZEOF_BUF(localRsp, FWTPM_TIS_FIFO_SIZE)); + TPM2_ForceZero(localCmd, + FWTPM_SIZEOF_BUF(localCmd, FWTPM_TIS_FIFO_SIZE)); + FWTPM_FREE_BUF(localRsp); + FWTPM_FREE_BUF(localCmd); if (procRc != TPM_RC_SUCCESS || rspSize == 0) { /* Build minimal error response * TPM_ST_NO_SESSIONS = 0x8001 (big-endian) */ diff --git a/tests/fwtpm_hal_unit_tests.c b/tests/fwtpm_hal_unit_tests.c index c797a89a7..43c21a78e 100644 --- a/tests/fwtpm_hal_unit_tests.c +++ b/tests/fwtpm_hal_unit_tests.c @@ -111,16 +111,36 @@ int TestClockUpdate(FWTPM_CTX* ctx); #undef FWTPM_ProcessCommand #undef TPM2_ForceZero +/* Behavior of TestProcessCommand for the GO tests. The response pattern is + * written to the handler's buffer even on failure, like a partial response. */ +static FWTPM_TIS_REGS* gGoRegs; +static int gGoRc = TPM_RC_FAILURE; +static int gGoRspSize; +static int gGoPrivateBufs; + +static byte TestRspByte(int i) +{ + return (byte)(0x40 + (i & 0x3F)); +} + int TestProcessCommand(FWTPM_CTX* ctx, const byte* cmdBuf, int cmdSize, byte* rspBuf, int* rspSize, int locality) { + int i; + (void)ctx; - (void)cmdBuf; (void)cmdSize; - (void)rspBuf; - (void)rspSize; (void)locality; - return TPM_RC_FAILURE; + if (gGoRegs == NULL) { + return TPM_RC_FAILURE; + } + gGoPrivateBufs = (cmdBuf != gGoRegs->cmd_buf && + rspBuf != gGoRegs->rsp_buf && *rspSize == FWTPM_TIS_FIFO_SIZE); + for (i = 0; i < gGoRspSize && i < *rspSize; i++) { + rspBuf[i] = TestRspByte(i); + } + *rspSize = gGoRspSize; + return gGoRc; } int TestStopRequested(void) @@ -517,6 +537,115 @@ static int TestBurstCountWriteIgnored(void) return failed; } +static void TisGoSetup(FWTPM_CTX* ctx, FWTPM_TIS_REGS* regs) +{ + XMEMSET(ctx, 0, sizeof(*ctx)); + XMEMSET(regs, 0, sizeof(*regs)); + XMEMSET(regs->rsp_buf, 0xEE, sizeof(regs->rsp_buf)); + ctx->tisLocality = 0; + regs->cmd_len = 10; + regs->reg_addr = FWTPM_TIS_STS; + regs->reg_len = 1; + regs->reg_is_write = 1; + regs->reg_data[0] = FWTPM_STS_GO; + gGoRegs = regs; + gGoPrivateBufs = 0; +} + +static UINT32 TisDrainFifo(FWTPM_CTX* ctx, FWTPM_TIS_REGS* regs, byte* out, + UINT32 outSz) +{ + UINT32 got = 0; + UINT32 chunk; + + regs->reg_addr = FWTPM_TIS_DATA_FIFO; + regs->reg_is_write = 0; + while (got < regs->rsp_len && got < outSz) { + chunk = regs->rsp_len - got; + if (chunk > (UINT32)sizeof(regs->reg_data)) { + chunk = (UINT32)sizeof(regs->reg_data); + } + regs->reg_len = chunk; + TisHandleRegAccess(ctx, regs); + XMEMCPY(out + got, regs->reg_data, chunk); + got += chunk; + } + return got; +} + +/* The response is staged privately and reaches the FIFO only when complete */ +static int TestTisGoSuccess(void) +{ + static const int rspSize = 100; /* more than one reg_data transfer */ + FWTPM_CTX ctx; + FWTPM_TIS_REGS regs; + byte out[100]; + int failed = 0; + int i; + + TisGoSetup(&ctx, ®s); + gGoRc = TPM_RC_SUCCESS; + gGoRspSize = rspSize; + TisHandleRegAccess(&ctx, ®s); + + if (!gGoPrivateBufs || regs.rsp_len != (UINT32)rspSize || + regs.fifo_read_pos != 0 || + (regs.sts & FWTPM_STS_DATA_AVAIL) == 0 || + regs.rsp_buf[rspSize] != 0xEE) { + failed = 1; + } + if (TisDrainFifo(&ctx, ®s, out, sizeof(out)) != (UINT32)rspSize) { + failed = 1; + } + for (i = 0; i < rspSize; i++) { + if (out[i] != TestRspByte(i)) { + failed = 1; + } + } + gGoRegs = NULL; + printf("%s: TIS GO reads a staged response through the FIFO\n", + failed ? "FAIL" : "PASS"); + return failed; +} + +/* A failed or oversized command yields only the minimal error response, with + * none of what the handler wrote reaching the shared registers. */ +static int TestTisGoFailure(int rc, int rspSize, const char* name) +{ + static const byte expected[10] = { + 0x80, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x01, 0x01 + }; + FWTPM_CTX ctx; + FWTPM_TIS_REGS regs; + byte out[sizeof(expected)]; + int failed = 0; + int i; + + TisGoSetup(&ctx, ®s); + gGoRc = rc; + gGoRspSize = rspSize; + TisHandleRegAccess(&ctx, ®s); + + if (regs.rsp_len != (UINT32)sizeof(expected) || + (regs.sts & FWTPM_STS_DATA_AVAIL) == 0) { + failed = 1; + } + for (i = (int)sizeof(expected); i < (int)sizeof(regs.rsp_buf); i++) { + if (regs.rsp_buf[i] != 0xEE) { + failed = 1; + break; + } + } + if (TisDrainFifo(&ctx, ®s, out, sizeof(out)) != (UINT32)sizeof(out) || + XMEMCMP(out, expected, sizeof(expected)) != 0) { + failed = 1; + } + gGoRegs = NULL; + printf("%s: TIS GO returns the error response for %s\n", + failed ? "FAIL" : "PASS", name); + return failed; +} + int main(void) { int failures = 0; @@ -552,6 +681,11 @@ int main(void) failures += ExpectDisconnectZeroized(); failures += TestMetadataValidation(); failures += TestBurstCountWriteIgnored(); + failures += TestTisGoSuccess(); + failures += TestTisGoFailure(TPM_RC_FAILURE, 100, "a failed command"); + failures += TestTisGoFailure(TPM_RC_SUCCESS, 0, "an empty response"); + failures += TestTisGoFailure(TPM_RC_SUCCESS, FWTPM_TIS_FIFO_SIZE + 1, + "an oversized response"); CleanupEndpoint(); printf("fwTPM HAL endpoint tests: %s\n", diff --git a/tests/fwtpm_unit_tests.c b/tests/fwtpm_unit_tests.c index 5c7b723f6..cbea9d9d2 100644 --- a/tests/fwtpm_unit_tests.c +++ b/tests/fwtpm_unit_tests.c @@ -11887,6 +11887,104 @@ static void test_fwtpm_policy_locality(void) fwtpm_pass("PolicyLocality:", 0); } +/* A PolicyTemplate session authorizes only Create, CreatePrimary and + * CreateLoaded with the bound template, never another command. */ +static TPM_RC SendPolicyTemplate(FWTPM_CTX* ctx, UINT32 sessH, + const byte* templateHash) +{ + int pos = 0, rspSize = 0; + pos = BuildCmdHeader(gCmd, TPM_ST_SESSIONS, 0, TPM_CC_PolicyTemplate); + PutU32BE(gCmd + pos, sessH); pos += 4; + pos = AppendPwAuth(gCmd, pos, NULL, 0); + PutU16BE(gCmd + pos, WC_SHA256_DIGEST_SIZE); pos += 2; + memcpy(gCmd + pos, templateHash, WC_SHA256_DIGEST_SIZE); + pos += WC_SHA256_DIGEST_SIZE; + PutU32BE(gCmd + 2, (UINT32)pos); + FWTPM_ProcessCommand(ctx, gCmd, pos, gRsp, &rspSize, 0); + return GetRspRC(gRsp); +} + +static void test_fwtpm_policy_template_create_only(void) +{ + FWTPM_CTX ctx; + int pos, rspSize = 0; + UINT32 sessH; + UINT16 dSz; + byte templateHash[WC_SHA256_DIGEST_SIZE]; + byte digest[64]; +#ifdef HAVE_ECC + TPM_ALG_ID alg = TPM_ALG_ECC; +#else + TPM_ALG_ID alg = TPM_ALG_RSA; +#endif + + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + + /* The template is inPublic of the CreatePrimary command below */ + AssertIntGT(BuildCreatePrimaryCmd(gCmd, alg), 35); + AssertIntEQ(wc_Sha256Hash(gCmd + 35, GetU16BE(gCmd + 33), templateHash), + 0); + + sessH = StartSessionHelper(&ctx, TPM_SE_POLICY); + AssertIntNE(sessH, 0); + AssertIntEQ(SendPolicyTemplate(&ctx, sessH, templateHash), + TPM_RC_SUCCESS); + AssertIntEQ(SendPolicyCmd(&ctx, TPM_CC_PolicyGetDigest, sessH), + TPM_RC_SUCCESS); + dSz = GetU16BE(gRsp + TPM2_HEADER_SIZE + 4); + AssertIntEQ(dSz, 32); + memcpy(digest, gRsp + TPM2_HEADER_SIZE + 6, dSz); + FlushHandle(&ctx, sessH); + + /* ownerPolicy = PolicyTemplate(templateHash) */ + pos = BuildCmdHeader(gCmd, TPM_ST_SESSIONS, 0, TPM_CC_SetPrimaryPolicy); + PutU32BE(gCmd + pos, TPM_RH_OWNER); pos += 4; + pos = AppendPwAuth(gCmd, pos, NULL, 0); + PutU16BE(gCmd + pos, dSz); pos += 2; + memcpy(gCmd + pos, digest, dSz); pos += dSz; + PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; + PutU32BE(gCmd + 2, (UINT32)pos); + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + sessH = StartSessionHelper(&ctx, TPM_SE_POLICY); + AssertIntNE(sessH, 0); + AssertIntEQ(SendPolicyTemplate(&ctx, sessH, templateHash), + TPM_RC_SUCCESS); + + /* The session must not authorize HierarchyChangeAuth(OWNER) */ + pos = BuildCmdHeader(gCmd, TPM_ST_SESSIONS, 0, + TPM_CC_HierarchyChangeAuth); + PutU32BE(gCmd + pos, TPM_RH_OWNER); pos += 4; + pos = AppendPwAuth(gCmd, pos, NULL, 0); + PutU32BE(gCmd + 18, sessH); + PutU16BE(gCmd + pos, 0); pos += 2; /* newAuth */ + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_POLICY_FAIL); + + /* Nor CreatePrimary with a different template */ + pos = BuildCreatePrimaryCmd(gCmd, alg); + PutU32BE(gCmd + 18, sessH); + PutU32BE(gCmd + 39, GetU32BE(gCmd + 39) ^ TPMA_OBJECT_noDA); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_POLICY_FAIL); + + /* It does authorize CreatePrimary with the bound template */ + pos = BuildCreatePrimaryCmd(gCmd, alg); + PutU32BE(gCmd + 18, sessH); + rspSize = 0; + FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + FlushHandle(&ctx, GetU32BE(gRsp + TPM2_HEADER_SIZE)); + + FWTPM_Cleanup(&ctx); + fwtpm_pass("PolicyTemplate authorizes creation only:", 0); +} + static void test_fwtpm_policy_pcr(void) { FWTPM_CTX ctx; @@ -22522,6 +22620,7 @@ int fwtpm_unit_tests(int argc, char *argv[]) test_fwtpm_policy_restart(); test_fwtpm_policy_command_code(); test_fwtpm_policy_locality(); + test_fwtpm_policy_template_create_only(); test_fwtpm_policy_pcr(); test_fwtpm_policy_pcr_digest_verified(); #ifndef FWTPM_NO_NV diff --git a/wolftpm/tpm2_wrap.h b/wolftpm/tpm2_wrap.h index 8b19603ec..f4bc6f2a8 100644 --- a/wolftpm/tpm2_wrap.h +++ b/wolftpm/tpm2_wrap.h @@ -3647,13 +3647,17 @@ WOLFTPM_API int wolfTPM2_GetRandom(WOLFTPM2_DEV* dev, byte* buf, word32 len); \param dev pointer to a TPM2_DEV struct \param handle pointer to a structure of WOLFTPM2_HANDLE type, with a valid TPM 2.0 handle value - \sa wolfTPM2_Clear + \sa wolfTPM2_UnloadHandles */ WOLFTPM_API int wolfTPM2_UnloadHandle(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle); /*! \ingroup wolfTPM2_Wrappers - \brief Deinitializes wolfTPM and wolfcrypt(if enabled) + \brief Clears the TPM with TPM2_Clear, authorized by the lockout + hierarchy. This removes the owner and endorsement hierarchy objects and + the NV indices created by the owner, and resets their authorizations. + NV indices created by the platform (TPMA_NV_PLATFORMCREATE) are preserved. + It does not deinitialize wolfTPM; use wolfTPM2_Cleanup for that. \return TPM_RC_SUCCESS: successful \return TPM_RC_FAILURE: generic failure (check TPM IO and TPM return code) @@ -3661,7 +3665,7 @@ WOLFTPM_API int wolfTPM2_UnloadHandle(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle \param dev pointer to a TPM2_DEV struct - \sa wolfTPM2_Clear + \sa wolfTPM2_Cleanup */ WOLFTPM_API int wolfTPM2_Clear(WOLFTPM2_DEV* dev);