Created
September 9, 2022 14:35
-
-
Save williamcroberts/a041d8d8aa903f3f7a5ea44b130a8857 to your computer and use it in GitHub Desktop.
Function for computing PCR Policy without a TPM with tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* SPDX-License-Identifier: BSD-3-Clause or LGPL-2.1-or-later or MIT */ | |
#include <assert.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <stdbool.h> | |
#include <tss2/tss2_tpm2_types.h> | |
#include <tss2/tss2_mu.h> | |
#include <openssl/evp.h> | |
const EVP_MD* tpm2_alg_to_ossl(TPMI_ALG_HASH halg) { | |
switch (halg) { | |
case TPM2_ALG_SHA1: | |
return EVP_sha1(); | |
case TPM2_ALG_SHA256: | |
return EVP_sha256(); | |
case TPM2_ALG_SHA384: | |
return EVP_sha384(); | |
case TPM2_ALG_SHA512: | |
return EVP_sha512(); | |
default: | |
return NULL; | |
} | |
} | |
static inline bool is_pcr_selected(TPMS_PCR_SELECTION *s, UINT16 pcr_index) { | |
/* | |
* PCR selections are in a | |
* 3 byte bit mask with the bit index representing the PCR | |
* index, thus index 0 representing pcr 0. | |
*/ | |
return !!((s->pcrSelect[pcr_index / 8]) & (1 << (pcr_index % 8))); | |
} | |
bool calculate_pcr_policy_digest(TPM2B_DIGEST *current_policy_digest, | |
TPML_PCR_SELECTION *selection, TPML_DIGEST *digests, | |
TPMI_ALG_HASH hash_alg, TPM2B_DIGEST *calculated_digest) { | |
bool result = false; | |
const EVP_MD *md = tpm2_alg_to_ossl(hash_alg); | |
if (!md) { | |
fprintf(stderr, | |
"Could not convert tpm2-alg to ossl EVP_MD, got: 0x%x\n", | |
hash_alg); | |
return false; | |
} | |
EVP_MD_CTX *ctx = EVP_MD_CTX_create(); | |
if (!ctx) { | |
fprintf(stderr, "oom\n"); | |
return false; | |
} | |
int rc = EVP_DigestInit(ctx, md); | |
if (rc != 1) { | |
goto out; | |
} | |
UINT16 i; | |
UINT16 dgst_offset = 0; /*offset into the digests array */ | |
/* Step 1, calculate the aggregate expected PCR digests */ | |
/* for each selected BANK, a bank is SHA1, SHA256, etc */ | |
for (i = 0; i < selection->count; i++) { | |
/* get the selection of PCRs for that bank */ | |
TPMS_PCR_SELECTION *s = &selection->pcrSelections[i]; | |
/* for each PCR that could be selected in the selection | |
* | |
* Go through each bit that could be set in the selection, ie sizeOfSelect 2 | |
* means that 2 bytes in the selection contain bits to evaluate | |
* Lore: while a select could be 4 bytes, it never is and tpm's reject them | |
*/ | |
UINT16 pcr; | |
for (pcr = 0; pcr < s->sizeofSelect * 8; pcr++) { | |
/*if the PCR is not selected within the bank skip it */ | |
if (!is_pcr_selected(s, pcr)) { | |
continue; | |
} | |
/* | |
* the digest list is a one to one mapping of selected PCRs | |
* and the current selected bank should match the recorded bank | |
* in the digest list | |
*/ | |
if (dgst_offset >= digests->count) { | |
fprintf(stderr, | |
"Selection is larger than provided digests, got: %u expected %u\n", | |
dgst_offset, digests->count); | |
goto out; | |
} | |
TPM2B_DIGEST *d = &digests->digests[dgst_offset]; | |
rc = EVP_DigestUpdate(ctx, d->buffer, d->size); | |
if (rc != 1) { | |
fprintf(stderr, "EVP_DigestUpdate failed for pcr: %u\n", pcr); | |
goto out; | |
} | |
dgst_offset++; | |
} | |
} | |
TPM2B_DIGEST aggregate_pcr_digest = { 0 }; | |
unsigned int size = sizeof(aggregate_pcr_digest.buffer); | |
rc = EVP_DigestFinal(ctx, aggregate_pcr_digest.buffer, &size); | |
if (rc != 1) { | |
fprintf(stderr, "EVP_DigestFinal failed\n"); | |
goto out; | |
} | |
aggregate_pcr_digest.size = size; | |
/* should be 0x6f ... 0x29 */ | |
/* | |
* Step 2 calculate the Policy Digest | |
* hPolicyAlg(policyDigestold || TPM_CC_PolicyPCR || pcrs || digestTPM) | |
*/ | |
rc = EVP_MD_CTX_reset(ctx); | |
if (rc != 1) { | |
fprintf(stderr, "EVP_MD_CTX_reset failed\n"); | |
goto out; | |
} | |
rc = EVP_DigestInit(ctx, md); | |
if (rc != 1) { | |
goto out; | |
} | |
TPM2B_DIGEST tmp = { 0 }; | |
if (!current_policy_digest) { | |
/* assume starting with 00...00 policy session state */ | |
tmp.size = EVP_MD_CTX_size(ctx); | |
current_policy_digest = &tmp; | |
} | |
/* old/current policy buffer */ | |
rc = EVP_DigestUpdate(ctx, current_policy_digest->buffer, | |
current_policy_digest->size); | |
if (rc != 1) { | |
fprintf(stderr, "EVP_DigestUpdate failed for oldPolicyBuffer\n"); | |
goto out; | |
} | |
/* command code */ | |
size_t offset = 0; | |
uint8_t buffer[4096] = { 0 }; | |
TSS2_RC trc = Tss2_MU_TPM2_CC_Marshal(TPM2_CC_PolicyPCR, buffer, | |
sizeof(buffer), &offset); | |
if (trc != TSS2_RC_SUCCESS) { | |
fprintf(stderr, "Tss2_MU_TPML_PCR_SELECTION_Marshal failed\n"); | |
goto out; | |
} | |
rc = EVP_DigestUpdate(ctx, buffer, offset); | |
if (rc != 1) { | |
fprintf(stderr, "EVP_DigestUpdate failed for Command Code\n"); | |
goto out; | |
} | |
/* Add PCR Selection, reuse buffer so reset offset */ | |
offset = 0; | |
trc = Tss2_MU_TPML_PCR_SELECTION_Marshal(selection, buffer, sizeof(buffer), | |
&offset); | |
if (trc != TSS2_RC_SUCCESS) { | |
fprintf(stderr, "Tss2_MU_TPML_PCR_SELECTION_Marshal failed\n"); | |
goto out; | |
} | |
rc = EVP_DigestUpdate(ctx, buffer, offset); | |
if (rc != 1) { | |
fprintf(stderr, "EVP_DigestUpdate failed for Command Code\n"); | |
goto out; | |
} | |
/* Add the expected aggregate PCR digest */ | |
rc = EVP_DigestUpdate(ctx, aggregate_pcr_digest.buffer, | |
aggregate_pcr_digest.size); | |
if (rc != 1) { | |
fprintf(stderr, "EVP_DigestUpdate failed for Command Code\n"); | |
goto out; | |
} | |
/* get the computed hash */ | |
size = sizeof(calculated_digest->buffer); | |
rc = EVP_DigestFinal(ctx, calculated_digest->buffer, &size); | |
if (rc != 1) { | |
fprintf(stderr, "EVP_DigestFinal failed\n"); | |
goto out; | |
} | |
calculated_digest->size = size; | |
result = true; | |
out: | |
EVP_MD_CTX_destroy(ctx); | |
return result; | |
} | |
void dump_digest(TPM2B_DIGEST *digest) { | |
UINT16 i = 0; | |
printf("(%u)", digest->size); | |
for (i = 0; i < digest->size; i++) { | |
printf("%x", digest->buffer[i]); | |
} | |
} | |
static int test_1(void) { | |
/* Select SHA256:0,1,2 */ | |
TPML_PCR_SELECTION selections = { .count = 1, .pcrSelections = { { .hash = | |
TPM2_ALG_SHA256, .sizeofSelect = 3, .pcrSelect = | |
{ 0x07, 0x00, 0x00 } }, }, }; | |
/* Expected PCR state */ | |
TPML_DIGEST digests = { .count = 3, .digests = { | |
/* PCR 0 */{ .size = 32, .buffer = { 0xdb, 0x94, 0x8a, 0x93, 0x0a, 0x93, | |
0x3f, 0xf6, 0xc7, 0x17, 0x5d, 0x3a, 0x7c, 0x17, 0x27, 0xbd, 0xc9, | |
0xc5, 0x7f, 0xbb, 0x6e, 0x69, 0x94, 0x14, 0xa9, 0x9a, 0xcb, 0xb5, | |
0xf1, 0x7a, 0xcd, 0x5e } }, | |
/* PCR 1 */{ .size = 32, .buffer = { 0x6a, 0xad, 0x2a, 0xb4, 0x7b, 0xc5, | |
0xbc, 0x13, 0x17, 0x8a, 0xb1, 0x49, 0x26, 0xcb, 0xde, 0x4f, 0xf9, | |
0xf9, 0x08, 0x20, 0x24, 0x52, 0x3d, 0x31, 0xdc, 0x53, 0xc6, 0x09, | |
0x09, 0xeb, 0x45, 0x9f } }, | |
/* PCR 2 */{ .size = 32, .buffer = { 0x04, 0x99, 0xc8, 0x1a, 0xeb, 0xf5, | |
0x05, 0x7f, 0x1f, 0x14, 0x4d, 0xd9, 0x9c, 0x5d, 0x6a, 0x05, 0x23, | |
0x47, 0xf9, 0x02, 0xa6, 0xf7, 0xe3, 0x50, 0x32, 0xe8, 0x24, 0xfd, | |
0xf2, 0x4c, 0xeb, 0x04 } } }, }; | |
TPM2B_DIGEST calculated_digest = { 0 }; | |
bool res = calculate_pcr_policy_digest(NULL, &selections, &digests, | |
TPM2_ALG_SHA256, &calculated_digest); | |
if (!res) { | |
fprintf(stderr, "Calculation failed\n"); | |
return 1; | |
} | |
TPM2B_DIGEST expected_digest = { .size = 32, .buffer = { 0x0a, 0x75, 0x38, | |
0x3f, 0xb4, 0xea, 0x83, 0xe7, 0x3e, 0x30, 0x64, 0xd1, 0xc6, 0xe8, | |
0x88, 0x75, 0x22, 0xb9, 0xdc, 0x2a, 0x66, 0x50, 0x9e, 0xb6, 0x0f, | |
0x48, 0x87, 0x20, 0x57, 0x9d, 0x5f, 0x07 } }; | |
printf("Expected Digest: "); | |
dump_digest(&expected_digest); | |
printf("\n"); | |
printf("Calculated Digest: "); | |
dump_digest(&calculated_digest); | |
printf("\n"); | |
if (memcmp(&calculated_digest, &expected_digest, sizeof(expected_digest))) { | |
fprintf(stderr, "digest mismatch\n"); | |
return 1; | |
} | |
return 0; | |
} | |
static int test_2(void) { | |
/* Select SHA256:0,1,2+SHA1:0,1,2 */ | |
// 0000 0002 000b 0307 0000 0004 0307 0000 | |
TPML_PCR_SELECTION selections = { .count = 2, .pcrSelections = { { .hash = | |
TPM2_ALG_SHA256, .sizeofSelect = 3, .pcrSelect = | |
{ 0x07, 0x00, 0x00 } }, { .hash = TPM2_ALG_SHA1, .sizeofSelect = 3, | |
.pcrSelect = { 0x07, 0x00, 0x00 } }, }, }; | |
/* Expected PCR state */ | |
TPML_DIGEST digests = { .count = 6, .digests = { | |
/* SHA256 PCR 0 */{ .size = 32, .buffer = { 0xdb, 0x94, 0x8a, 0x93, 0x0a, | |
0x93, 0x3f, 0xf6, 0xc7, 0x17, 0x5d, 0x3a, 0x7c, 0x17, 0x27, 0xbd, | |
0xc9, 0xc5, 0x7f, 0xbb, 0x6e, 0x69, 0x94, 0x14, 0xa9, 0x9a, 0xcb, | |
0xb5, 0xf1, 0x7a, 0xcd, 0x5e } }, | |
/* SHA256 PCR 1 */{ .size = 32, .buffer = { 0x6a, 0xad, 0x2a, 0xb4, 0x7b, | |
0xc5, 0xbc, 0x13, 0x17, 0x8a, 0xb1, 0x49, 0x26, 0xcb, 0xde, 0x4f, | |
0xf9, 0xf9, 0x08, 0x20, 0x24, 0x52, 0x3d, 0x31, 0xdc, 0x53, 0xc6, | |
0x09, 0x09, 0xeb, 0x45, 0x9f } }, | |
/* SHA256 PCR 2 */{ .size = 32, .buffer = { 0x04, 0x99, 0xc8, 0x1a, 0xeb, | |
0xf5, 0x05, 0x7f, 0x1f, 0x14, 0x4d, 0xd9, 0x9c, 0x5d, 0x6a, 0x05, | |
0x23, 0x47, 0xf9, 0x02, 0xa6, 0xf7, 0xe3, 0x50, 0x32, 0xe8, 0x24, | |
0xfd, 0xf2, 0x4c, 0xeb, 0x04 } }, | |
/* SHA1 PCR 0 */{ .size = 20, .buffer = { 0x6b, 0x75, 0xb1, 0x2b, 0x5d, | |
0xc3, 0x3f, 0x61, 0x8d, 0xe9, 0xb1, 0xc1, 0xb1, 0x30, 0x9a, 0x6a, | |
0xe4, 0xef, 0x3a, 0x8a } }, | |
/* SHA1 PCR 1 */{ .size = 20, .buffer = { 0x28, 0xac, 0x40, 0xd2, 0x48, | |
0x66, 0x4a, 0x8f, 0xd1, 0x5d, 0xcd, 0x4c, 0x5f, 0x38, 0xea, 0x11, | |
0x49, 0x5e, 0x2d, 0x79 } }, | |
/* SHA1 PCR 2 */{ .size = 20, .buffer = { 0x59, 0x6b, 0x01, 0x14, 0xe3, | |
0xf7, 0x70, 0x53, 0xa7, 0x0c, 0xb0, 0xdb, 0xe3, 0xe3, 0xb2, 0x56, | |
0x72, 0xdb, 0x1c, 0xcf } } }, }; | |
TPM2B_DIGEST calculated_digest = { 0 }; | |
bool res = calculate_pcr_policy_digest(NULL, &selections, &digests, | |
TPM2_ALG_SHA256, &calculated_digest); | |
if (!res) { | |
fprintf(stderr, "Calculation failed\n"); | |
return 1; | |
} | |
TPM2B_DIGEST expected_digest = { .size = 32, .buffer = { 0x8c, 0xa5, 0x28, | |
0x4d, 0xe0, 0xd6, 0x43, 0x26, 0xb7, 0xcd, 0x10, 0x30, 0x7a, 0xf6, | |
0x3d, 0x9e, 0xc5, 0x0a, 0xe7, 0x13, 0xe2, 0xdc, 0x2f, 0xe5, 0x60, | |
0x85, 0x1b, 0x7d, 0x3f, 0x90, 0x9b, 0xcd } }; | |
printf("Expected Digest: "); | |
dump_digest(&expected_digest); | |
printf("\n"); | |
printf("Calculated Digest: "); | |
dump_digest(&calculated_digest); | |
printf("\n"); | |
if (memcmp(&calculated_digest, &expected_digest, sizeof(expected_digest))) { | |
fprintf(stderr, "digest mismatch\n"); | |
return 1; | |
} | |
return 0; | |
} | |
int main(int argc, char *argv[]) { | |
int r = test_1(); | |
if (r) { | |
return r; | |
} | |
return test_2(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment