Skip to content

Instantly share code, notes, and snippets.

@williamcroberts
Created September 9, 2022 14:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save williamcroberts/a041d8d8aa903f3f7a5ea44b130a8857 to your computer and use it in GitHub Desktop.
Save williamcroberts/a041d8d8aa903f3f7a5ea44b130a8857 to your computer and use it in GitHub Desktop.
Function for computing PCR Policy without a TPM with tests
/* 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