diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index d76639f1..0dba7fd8 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -10667,6 +10667,7 @@ static TPM_RC FwCmd_PolicyPCR(FWTPM_CTX* ctx, TPM2_Packet* cmd, UINT32 sessHandle; UINT16 pcrDigestSize; byte pcrDigest[TPM_MAX_DIGEST_SIZE]; + byte liveDigest[TPM_MAX_DIGEST_SIZE]; TPML_PCR_SELECTION pcrs; FWTPM_Session* sess = NULL; int digestSz = 0; @@ -10733,8 +10734,10 @@ static TPM_RC FwCmd_PolicyPCR(FWTPM_CTX* ctx, TPM2_Packet* cmd, } } - /* If pcrDigest.size == 0, compute it from current PCR values */ - if (rc == 0 && pcrDigestSize == 0) { + /* Compute the digest of the selected PCRs when none was supplied, and + * always for a real policy session so a supplied digest is verified + * against the live PCR values rather than trusted (Part 3 Sec.23.7). */ + if (rc == 0 && (pcrDigestSize == 0 || sess->sessionType == TPM_SE_POLICY)) { /* Hash together all selected PCR values */ wcHash = FwGetWcHashType(sess->authHash); if (wc_HashInit_ex(hashCtx, wcHash, NULL, INVALID_DEVID) != 0) { @@ -10750,17 +10753,29 @@ static TPM_RC FwCmd_PolicyPCR(FWTPM_CTX* ctx, TPM2_Packet* cmd, pcrs.pcrSelections[i].hash); if (bankIdx < 0 || pcrDSz == 0) continue; - for (j = 0; j < IMPLEMENTATION_PCR; j++) { + for (j = 0; j < IMPLEMENTATION_PCR && rc == 0; j++) { if (j / 8 < pcrs.pcrSelections[i].sizeofSelect && (pcrs.pcrSelections[i].pcrSelect[j / 8] & (1 << (j % 8)))) { - wc_HashUpdate(hashCtx, wcHash, - ctx->pcrDigest[j][bankIdx], pcrDSz); + if (wc_HashUpdate(hashCtx, wcHash, + ctx->pcrDigest[j][bankIdx], pcrDSz) != 0) { + rc = TPM_RC_FAILURE; + } } } } - pcrDigestSize = digestSz; - wc_HashFinal(hashCtx, wcHash, pcrDigest); + if (rc == 0 && wc_HashFinal(hashCtx, wcHash, liveDigest) != 0) { + rc = TPM_RC_FAILURE; + } + if (rc == 0 && pcrDigestSize != 0 && + (pcrDigestSize != digestSz || + TPM2_ConstantCompare(pcrDigest, liveDigest, digestSz) != 0)) { + rc = TPM_RC_VALUE; + } + if (rc == 0) { + pcrDigestSize = (UINT16)digestSz; + XMEMCPY(pcrDigest, liveDigest, digestSz); + } } if (hashInit) { wc_HashFree(hashCtx, wcHash); diff --git a/tests/fwtpm_unit_tests.c b/tests/fwtpm_unit_tests.c index a15e5059..49a7e189 100644 --- a/tests/fwtpm_unit_tests.c +++ b/tests/fwtpm_unit_tests.c @@ -9587,6 +9587,65 @@ static void test_fwtpm_policy_cphash_enforced(void) } #endif /* !FWTPM_NO_NV */ +/* PolicyPCR selecting PCR 0 in the SHA-256 bank with an optional caller digest */ +static TPM_RC SendPolicyPcrCmd(FWTPM_CTX* ctx, UINT32 sessH, + const byte* digest, UINT16 digestSz) +{ + int pos = 0, rspSize = 0; + PutU16BE(gCmd + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_PolicyPCR); pos += 4; + PutU32BE(gCmd + pos, sessH); pos += 4; + pos = AppendPwAuth(gCmd, pos, NULL, 0); + PutU16BE(gCmd + pos, digestSz); pos += 2; + if (digestSz > 0) { + memcpy(gCmd + pos, digest, digestSz); pos += digestSz; + } + PutU32BE(gCmd + pos, 1); pos += 4; /* count */ + PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; /* hash */ + gCmd[pos++] = 3; /* sizeofSelect */ + gCmd[pos++] = 0x01; gCmd[pos++] = 0x00; gCmd[pos++] = 0x00; /* PCR 0 */ + PutU32BE(gCmd + 2, (UINT32)pos); + FWTPM_ProcessCommand(ctx, gCmd, pos, gRsp, &rspSize, 0); + return GetRspRC(gRsp); +} + +/* A real policy session must verify a caller-supplied pcrDigest against the + * live PCR values; only a trial session may take it on faith. */ +static void test_fwtpm_policy_pcr_digest_verified(void) +{ + FWTPM_CTX ctx; + UINT32 sessH; + byte pcr0[32]; + byte expect[32]; + byte wrong[32]; + + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + memset(pcr0, 0, sizeof(pcr0)); /* PCR 0 reset by Startup */ + AssertIntEQ(wc_Hash(WC_HASH_TYPE_SHA256, pcr0, sizeof(pcr0), + expect, sizeof(expect)), 0); + memset(wrong, 0xAB, sizeof(wrong)); + + sessH = StartSessionHelper(&ctx, TPM_SE_POLICY); + AssertIntNE(sessH, 0); + AssertIntEQ(SendPolicyPcrCmd(&ctx, sessH, wrong, sizeof(wrong)), + TPM_RC_VALUE); + AssertIntEQ(SendPolicyPcrCmd(&ctx, sessH, expect, 16), TPM_RC_VALUE); + AssertIntEQ(SendPolicyPcrCmd(&ctx, sessH, expect, sizeof(expect)), + TPM_RC_SUCCESS); + FlushHandle(&ctx, sessH); + + sessH = StartSessionHelper(&ctx, TPM_SE_TRIAL); + AssertIntNE(sessH, 0); + AssertIntEQ(SendPolicyPcrCmd(&ctx, sessH, wrong, sizeof(wrong)), + TPM_RC_SUCCESS); + FlushHandle(&ctx, sessH); + + FWTPM_Cleanup(&ctx); + fwtpm_pass("PolicyPCR digest verified:", 0); +} + #if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN) static void test_fwtpm_admin_authorization_requires_policy(void) { @@ -14543,6 +14602,7 @@ int fwtpm_unit_tests(int argc, char *argv[]) test_fwtpm_policy_command_code(); test_fwtpm_policy_locality(); test_fwtpm_policy_pcr(); + test_fwtpm_policy_pcr_digest_verified(); test_fwtpm_policy_ticket_zero_digest_rejected(); test_fwtpm_policyauthorize_null_ticket_rejected(); #ifndef FWTPM_NO_NV