This series of patches extends the TPM2 code to extend the BIOS related PCRs 0-7 in all available banks. This prevents that these PCRs remain untouched and filled with bogus values by applications. For example, the SHA1 hash is extended into the SHA256 bank. The value that is extended into this bank is essentially a SHA1 with zero bytes used for filling it to the size of a sha256 hash. This is done for all PCR banks of the TPM2 where these PCRs are available.
v2->v3: - Following Kevin's comments; would defer split-up of code in tcgbios.c to a later time
v1->v2: - extended the log functions for logging the additional hashes. This added more patches to this series
Regards, Stefan
Stefan Berger (7): tpm: Retrieve the PCR Bank configuration tpm: Restructure tpm20_extend to use buffer and take hash as parameter tpm: Refactor tpml_digest_values_sha1 structure tpm: Extend tpm20_extend to support extending to multiple PCR banks tpm: Move tpm_log_init to a later point tpm: Adjust the TPM2 log header to show all hashes tpm: Append to TPM2 log the hashes used for PCR extension
src/std/tcg.h | 86 +++++++++++-- src/tcgbios.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 421 insertions(+), 69 deletions(-)
Implement tpm20_get_capability and retrieve the PCR Bank configuration from the TPM using this function.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/std/tcg.h | 29 +++++++++++++++++++++++++++++ src/tcgbios.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+)
diff --git a/src/std/tcg.h b/src/std/tcg.h index c59f671..d60ee09 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -394,12 +394,16 @@ struct tpm_res_sha1complete { #define TPM2_CC_SelfTest 0x143 #define TPM2_CC_Startup 0x144 #define TPM2_CC_StirRandom 0x146 +#define TPM2_CC_GetCapability 0x17a #define TPM2_CC_GetRandom 0x17b #define TPM2_CC_PCR_Extend 0x182
/* TPM 2 error codes */ #define TPM2_RC_INITIALIZE 0x100
+/* TPM 2 Capabilities */ +#define TPM2_CAP_PCRS 0x00000005 + /* TPM 2 data structures */
struct tpm2b_stir { @@ -475,6 +479,31 @@ struct tpm2_req_hierarchycontrol { u8 state; } PACKED;
+struct tpm2_req_getcapability { + struct tpm_req_header hdr; + u32 capability; + u32 property; + u32 propertycount; +} PACKED; + +struct tpm2_res_getcapability { + struct tpm_rsp_header hdr; + u8 moreData; + u32 capability; + u8 data[0]; /* capability dependent data */ +} PACKED; + +struct tpms_pcr_selection { + u16 hashAlg; + u8 sizeOfSelect; + u8 pcrSelect[0]; +} PACKED; + +struct tpml_pcr_selection { + u32 count; + struct tpms_pcr_selection selections[0]; +} PACKED; + /* TPM 2 log entry */
struct tpml_digest_values_sha1 { diff --git a/src/tcgbios.c b/src/tcgbios.c index 334d99b..f9c6f74 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -24,6 +24,7 @@ #include "tcgbios.h"// tpm_*, prototypes #include "util.h" // printf, get_keystroke #include "stacks.h" // wait_threads, reset +#include "malloc.h" // malloc_high
/**************************************************************** * TPM 1.2 commands @@ -76,6 +77,9 @@ static int TPM_has_physical_presence;
static TPMVersion TPM_version;
+static u32 tpm20_pcr_selection_size; +static struct tpml_pcr_selection *tpm20_pcr_selection; + static struct tcpa_descriptor_rev2 * find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp) { @@ -366,6 +370,57 @@ tpm12_get_capability(u32 cap, u32 subcap, struct tpm_rsp_header *rsp, u32 rsize) }
static int +tpm20_getcapability(u32 capability, u32 property, u32 count, + struct tpm_rsp_header *rsp, u32 rsize) +{ + struct tpm2_req_getcapability trg = { + .hdr.tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .hdr.totlen = cpu_to_be32(sizeof(trg)), + .hdr.ordinal = cpu_to_be32(TPM2_CC_GetCapability), + .capability = cpu_to_be32(capability), + .property = cpu_to_be32(property), + .propertycount = cpu_to_be32(count), + }; + + u32 resp_size = rsize; + int ret = tpmhw_transmit(0, &trg.hdr, rsp, &resp_size, + TPM_DURATION_TYPE_SHORT); + ret = (ret || + rsize < be32_to_cpu(rsp->totlen)) ? -1 : be32_to_cpu(rsp->errcode); + + dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_GetCapability = 0x%08x\n", + ret); + + return ret; +} + +static int +tpm20_get_pcrbanks(void) +{ + u8 buffer[128]; + struct tpm2_res_getcapability *trg = + (struct tpm2_res_getcapability *)&buffer; + + int ret = tpm20_getcapability(TPM2_CAP_PCRS, 0, 8, &trg->hdr, + sizeof(buffer)); + if (ret) + return ret; + + u32 size = be32_to_cpu(trg->hdr.totlen) - + offsetof(struct tpm2_res_getcapability, data); + tpm20_pcr_selection = malloc_high(size); + if (tpm20_pcr_selection) { + memcpy(tpm20_pcr_selection, &trg->data, size); + tpm20_pcr_selection_size = size; + } else { + warn_noalloc(); + ret = -1; + } + + return ret; +} + +static int tpm12_determine_timeouts(void) { struct tpm_res_getcap_timeouts timeouts; @@ -701,6 +756,10 @@ tpm20_startup(void) if (ret) goto err_exit;
+ ret = tpm20_get_pcrbanks(); + if (ret) + goto err_exit; + return 0;
err_exit:
Restructure the tpm20_extend function to use a buffer for the command to send to the TPM. The size of the buffer is calculated from the size of tpm2_req_extend structure and the appended SHA1 hash.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/std/tcg.h | 2 +- src/tcgbios.c | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/src/std/tcg.h b/src/std/tcg.h index d60ee09..1644684 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -442,7 +442,6 @@ struct tpm2_req_hierarchychangeauth { } PACKED;
struct tpm2_digest_value { - u32 count; /* 1 entry only */ u16 hashalg; /* TPM2_ALG_SHA1 */ u8 sha1[SHA1_BUFSIZE]; } PACKED; @@ -452,6 +451,7 @@ struct tpm2_req_extend { u32 pcrindex; u32 authblocksize; struct tpm2_authblock authblock; + u32 count; struct tpm2_digest_value digest; } PACKED;
diff --git a/src/tcgbios.c b/src/tcgbios.c index f9c6f74..98bab9d 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -499,28 +499,31 @@ tpm12_extend(u32 pcrindex, const u8 *digest)
static int tpm20_extend(u32 pcrindex, const u8 *digest) { - struct tpm2_req_extend tre = { + struct tpm2_req_extend tmp_tre = { .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), - .hdr.totlen = cpu_to_be32(sizeof(tre)), + .hdr.totlen = cpu_to_be32(sizeof(tmp_tre)), .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend), .pcrindex = cpu_to_be32(pcrindex), - .authblocksize = cpu_to_be32(sizeof(tre.authblock)), + .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)), .authblock = { .handle = cpu_to_be32(TPM2_RS_PW), .noncesize = cpu_to_be16(0), .contsession = TPM2_YES, .pwdsize = cpu_to_be16(0), }, - .digest = { - .count = cpu_to_be32(1), - .hashalg = cpu_to_be16(TPM2_ALG_SHA1), - }, }; - memcpy(tre.digest.sha1, digest, sizeof(tre.digest.sha1)); + u32 count = 1; + u8 buffer[sizeof(tmp_tre) + sizeof(struct tpm2_digest_value)]; + struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer; + + memcpy(tre, &tmp_tre, sizeof(tmp_tre)); + tre->count = cpu_to_be32(count); + tre->digest.hashalg = cpu_to_be16(TPM2_ALG_SHA1); + memcpy(tre->digest.sha1, digest, sizeof(tmp_tre.digest.sha1));
struct tpm_rsp_header rsp; u32 resp_length = sizeof(rsp); - int ret = tpmhw_transmit(0, &tre.hdr, &rsp, &resp_length, + int ret = tpmhw_transmit(0, &tre->hdr, &rsp, &resp_length, TPM_DURATION_TYPE_SHORT); if (ret || resp_length != sizeof(rsp) || rsp.errcode) return -1;
Refactor the tpml_digest_values_sha1 structure so we can later cast it to the more general tpml_digest_values structure. Move the count member into this structure.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/std/tcg.h | 6 +++--- src/tcgbios.c | 20 ++++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/src/std/tcg.h b/src/std/tcg.h index 1644684..730b39c 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -507,15 +507,15 @@ struct tpml_pcr_selection { /* TPM 2 log entry */
struct tpml_digest_values_sha1 { + u32 count; /* number of digests */ u16 hashtype; u8 sha1[SHA1_BUFSIZE]; -}; +} PACKED;
struct tcg_pcr_event2_sha1 { u32 pcrindex; u32 eventtype; - u32 count; /* number of digests */ - struct tpml_digest_values_sha1 digests[1]; + struct tpml_digest_values_sha1 digest; u32 eventdatasize; u8 event[0]; } PACKED; diff --git a/src/tcgbios.c b/src/tcgbios.c index 98bab9d..9dddc4b 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -166,7 +166,7 @@ tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event struct pcpes *pcpes = (void*)tpm_state.log_area_next_entry; pcpes->pcrindex = entry->pcrindex; pcpes->eventtype = entry->eventtype; - memcpy(pcpes->digest, entry->digests[0].sha1, sizeof(pcpes->digest)); + memcpy(pcpes->digest, entry->digest.sha1, sizeof(pcpes->digest)); pcpes->eventdatasize = entry->eventdatasize; memcpy(pcpes->event, event, entry->eventdatasize); size = sizeof(*pcpes) + entry->eventdatasize; @@ -567,11 +567,13 @@ tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, .pcrindex = pcrindex, .eventtype = event_type, .eventdatasize = event_length, - .count = 1, - .digests[0].hashtype = TPM2_ALG_SHA1, + .digest = { + .count = 1, + .hashtype = TPM2_ALG_SHA1, + } }; - sha1(hashdata, hashdata_length, entry.digests[0].sha1); - int ret = tpm_extend(entry.pcrindex, entry.digests[0].sha1); + sha1(hashdata, hashdata_length, entry.digest.sha1); + int ret = tpm_extend(entry.pcrindex, entry.digest.sha1); if (ret) { tpm_set_failure(); return; @@ -1110,10 +1112,12 @@ hash_log_extend(struct pcpes *pcpes, const void *hashdata, u32 hashdata_length .pcrindex = pcpes->pcrindex, .eventtype = pcpes->eventtype, .eventdatasize = pcpes->eventdatasize, - .count = 1, - .digests[0].hashtype = TPM2_ALG_SHA1, + .digest = { + .count = 1, + .hashtype = TPM2_ALG_SHA1, + } }; - memcpy(entry.digests[0].sha1, pcpes->digest, sizeof(entry.digests[0].sha1)); + memcpy(entry.digest.sha1, pcpes->digest, sizeof(entry.digest.sha1)); int ret = tpm_log_event(&entry, pcpes->event, TPM_version); if (ret) return TCG_PC_LOGOVERFLOW;
Extend the tpm20_extend function to support extending a hash to multiple PCR banks. The sha1 hash that's being extended into the sha256 bank for example, will be filled with zero-bytes to the size of a sha256 hash.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
v2->v3: - added function tpm_write_tpml_digest values that also writes the structure for a TPM 1.2 - TPM2_PCR_Extend disregards PCR values for banks where they are not implemented; always include these PCR values now - remove panic() calls and do verifications for writing into buffer inside loop
v1->v2: - renamed function to tpm2_write_tpml_dig_values - write the count value inside the function - pass size of given fixed-sized buffer and check that it is large enough --- src/std/tcg.h | 29 ++++++++-- src/tcgbios.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 184 insertions(+), 17 deletions(-)
diff --git a/src/std/tcg.h b/src/std/tcg.h index 730b39c..8d71226 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -100,6 +100,19 @@ enum irq_ids { #define EV_IPL_PARTITION_DATA 14
#define SHA1_BUFSIZE 20 +#define SHA256_BUFSIZE 32 +#define SHA384_BUFSIZE 48 +#define SHA512_BUFSIZE 64 +#define SM3_256_BUFSIZE 32 + +/* maximum size the tpml_digest_values structure can have with these hashes */ +#define MAX_TPML_DIGEST_VALUES_SIZE \ + (4 + /* u32 count */ \ + 2 + SHA1_BUFSIZE + \ + 2 + SHA256_BUFSIZE + \ + 2 + SHA384_BUFSIZE + \ + 2 + SHA512_BUFSIZE + \ + 2 + SM3_256_BUFSIZE)
/* Input and Output blocks for the TCG BIOS commands */
@@ -381,6 +394,10 @@ struct tpm_res_sha1complete { #define TPM2_RH_PLATFORM 0x4000000c
#define TPM2_ALG_SHA1 0x0004 +#define TPM2_ALG_SHA256 0x000b +#define TPM2_ALG_SHA384 0x000c +#define TPM2_ALG_SHA512 0x000d +#define TPM2_ALG_SM3_256 0x0012
/* TPM 2 command tags */ #define TPM2_ST_NO_SESSIONS 0x8001 @@ -442,8 +459,13 @@ struct tpm2_req_hierarchychangeauth { } PACKED;
struct tpm2_digest_value { - u16 hashalg; /* TPM2_ALG_SHA1 */ - u8 sha1[SHA1_BUFSIZE]; + u16 hashAlg; /* TPM2_ALG_SHA1 */ + u8 hash[0]; /* size depends on hashAlg */ +} PACKED; + +struct tpml_digest_values { + u32 count; + struct tpm2_digest_value digest[0]; } PACKED;
struct tpm2_req_extend { @@ -451,8 +473,7 @@ struct tpm2_req_extend { u32 pcrindex; u32 authblocksize; struct tpm2_authblock authblock; - u32 count; - struct tpm2_digest_value digest; + u8 data[0]; /* struct tpml_digest_values digests; */ } PACKED;
struct tpm2_req_clearcontrol { diff --git a/src/tcgbios.c b/src/tcgbios.c index 9dddc4b..de02864 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -26,6 +26,8 @@ #include "stacks.h" // wait_threads, reset #include "malloc.h" // malloc_high
+#define min(a,b) ((a) < (b)) ? (a) : (b) + /**************************************************************** * TPM 1.2 commands ****************************************************************/ @@ -245,6 +247,131 @@ tpm_can_show_menu(void) return 0; }
+static int +tpm20_get_hash_buffersize(u16 hashAlg) +{ + switch (hashAlg) { + case TPM2_ALG_SHA1: + return SHA1_BUFSIZE; + case TPM2_ALG_SHA256: + return SHA256_BUFSIZE; + case TPM2_ALG_SHA384: + return SHA384_BUFSIZE; + case TPM2_ALG_SHA512: + return SHA512_BUFSIZE; + case TPM2_ALG_SM3_256: + return SM3_256_BUFSIZE; + default: + return -1; + } +} + +/* + * Write the TPM2 tpml_digest_values data structure from the given hash. + * Follow the PCR bank configuration of the TPM and write the same hash + * in either truncated or zero-padded form in the areas of all the other + * hashes. For example, write the sha1 hash in the area of the sha256 + * hash and fill the remaining bytes with zeros. Or truncate the sha256 + * hash when writing it in the area of the sha1 hash. + * + * dest: destination buffer to write into; if NULL, nothing is written + * destlen: length of destination buffer + * hash: the hash value to write + * hashAlg: the hash alogorithm determining the size of the hash + * + * Returns the number of bytes needed in the buffer; -1 on fatal error + */ +static int +tpm20_write_tpml_dig_values(u8 *dest, size_t destlen, + const u8 *hash, u16 hashAlg) +{ + if (!tpm20_pcr_selection) + return -1; + + int hsize; + struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; + void *nsel, *end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size; + int hash_size = tpm20_get_hash_buffersize(hashAlg); + int offset = offsetof(struct tpml_digest_values, digest); + u32 count; + + for (count = 0; + count < be32_to_cpu(tpm20_pcr_selection->count); + count++) { + u8 sizeOfSelect = sel->sizeOfSelect; + + nsel = (void *)sel + + offsetof(struct tpms_pcr_selection, pcrSelect) + + sizeOfSelect; + if (nsel > end) + break; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf(DEBUG_tcg, "TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + struct tpm2_digest_value *v = (struct tpm2_digest_value *)&dest[offset]; + + /* buffer size sanity check before writing */ + offset += sizeof(sel->hashAlg) + hsize; + if (offset > destlen) { + dprintf(DEBUG_tcg, "Buffer for tpml_digest_values is too small\n"); + return -1; + } + + v->hashAlg = sel->hashAlg; + memset(v->hash, 0, hsize); + memcpy(v->hash, hash, min(hash_size, hsize)); + + sel = nsel; + } + + if (sel != end) { + dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n"); + return -1; + } + + struct tpml_digest_values *v = (struct tpml_digest_values *)dest; + v->count = cpu_to_be32(count); + + return offset; +} + +static int +tpm12_write_tpml_dig_values(u8 *dest, size_t destlen, + const u8 *hash, u16 hashAlg) +{ + if (destlen < sizeof(struct tpml_digest_values_sha1)) { + dprintf(DEBUG_tcg, "Buffer for tpml_digest_values_sha1 is too small\n"); + return -1; + } + + struct tpml_digest_values_sha1 *tdvs = + (struct tpml_digest_values_sha1 *)dest; + + tdvs->count = 1; + tdvs->hashtype = hashAlg; + memcpy(tdvs->sha1, hash, sizeof(tdvs->sha1)); + + return sizeof(*tdvs); +} + +static int +tpm_write_tpml_digest_values(u8 *dest, size_t destlen, + const u8 *hash, u16 hashAlg) +{ + switch (TPM_version) { + case TPM_VERSION_1_2: + return tpm12_write_tpml_dig_values(dest, destlen, hash, hashAlg); + case TPM_VERSION_2: + return tpm20_write_tpml_dig_values(dest, destlen, hash, hashAlg); + } + return -1; +} + /* * Send a TPM command with the given ordinal. Append the given buffer * containing all data in network byte order to the command (this is @@ -497,11 +624,12 @@ tpm12_extend(u32 pcrindex, const u8 *digest) return 0; }
-static int tpm20_extend(u32 pcrindex, const u8 *digest) +static int tpm20_extend(u32 pcrindex, + const struct tpml_digest_values *tdv, int tdv_len) { struct tpm2_req_extend tmp_tre = { .hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS), - .hdr.totlen = cpu_to_be32(sizeof(tmp_tre)), + .hdr.totlen = cpu_to_be32(0), .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Extend), .pcrindex = cpu_to_be32(pcrindex), .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)), @@ -512,14 +640,13 @@ static int tpm20_extend(u32 pcrindex, const u8 *digest) .pwdsize = cpu_to_be16(0), }, }; - u32 count = 1; - u8 buffer[sizeof(tmp_tre) + sizeof(struct tpm2_digest_value)]; + u8 buffer[sizeof(tmp_tre) + MAX_TPML_DIGEST_VALUES_SIZE]; struct tpm2_req_extend *tre = (struct tpm2_req_extend *)buffer;
memcpy(tre, &tmp_tre, sizeof(tmp_tre)); - tre->count = cpu_to_be32(count); - tre->digest.hashalg = cpu_to_be16(TPM2_ALG_SHA1); - memcpy(tre->digest.sha1, digest, sizeof(tmp_tre.digest.sha1)); + memcpy(&tre->data[0], tdv, tdv_len); + + tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + tdv_len);
struct tpm_rsp_header rsp; u32 resp_length = sizeof(rsp); @@ -532,13 +659,13 @@ static int tpm20_extend(u32 pcrindex, const u8 *digest) }
static int -tpm_extend(u32 pcrindex, const u8 *digest) +tpm_extend(u32 pcrindex, const struct tpml_digest_values *tdv, int tdv_len) { switch (TPM_version) { - case TPM_VERSION_1_2: - return tpm12_extend(pcrindex, digest); + case TPM_VERSION_1_2: ; + return tpm12_extend(pcrindex, tdv->digest[0].hash); case TPM_VERSION_2: - return tpm20_extend(pcrindex, digest); + return tpm20_extend(pcrindex, tdv, tdv_len); } return -1; } @@ -573,7 +700,16 @@ tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, } }; sha1(hashdata, hashdata_length, entry.digest.sha1); - int ret = tpm_extend(entry.pcrindex, entry.digest.sha1); + + u8 buffer[MAX_TPML_DIGEST_VALUES_SIZE]; + + int tdv_len = tpm_write_tpml_digest_values(buffer, sizeof(buffer), + entry.digest.sha1, TPM2_ALG_SHA1); + if (tdv_len < 0) + return; + + struct tpml_digest_values *tdv = (struct tpml_digest_values *)buffer; + int ret = tpm_extend(pcrindex, tdv, tdv_len); if (ret) { tpm_set_failure(); return; @@ -1103,8 +1239,18 @@ hash_log_extend(struct pcpes *pcpes, const void *hashdata, u32 hashdata_length return TCG_INVALID_INPUT_PARA; if (hashdata) sha1(hashdata, hashdata_length, pcpes->digest); + + u8 buffer[MAX_TPML_DIGEST_VALUES_SIZE]; + + int tdv_len = tpm_write_tpml_digest_values(buffer, sizeof(buffer), + pcpes->digest, TPM2_ALG_SHA1); + if (tdv_len < 0) + return TCG_GENERAL_ERROR; + + struct tpml_digest_values *tdv = (struct tpml_digest_values *)buffer; + if (extend) { - int ret = tpm_extend(pcpes->pcrindex, pcpes->digest); + int ret = tpm_extend(pcpes->pcrindex, tdv, tdv_len); if (ret) return TCG_TCG_COMMAND_ERROR; }
On Fri, Aug 05, 2016 at 11:07:11AM -0400, Stefan Berger wrote:
Extend the tpm20_extend function to support extending a hash to multiple PCR banks. The sha1 hash that's being extended into the sha256 bank for example, will be filled with zero-bytes to the size of a sha256 hash.
[...]
@@ -573,7 +700,16 @@ tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, } }; sha1(hashdata, hashdata_length, entry.digest.sha1);
- int ret = tpm_extend(entry.pcrindex, entry.digest.sha1);
- u8 buffer[MAX_TPML_DIGEST_VALUES_SIZE];
- int tdv_len = tpm_write_tpml_digest_values(buffer, sizeof(buffer),
entry.digest.sha1, TPM2_ALG_SHA1);
- if (tdv_len < 0)
return;
- struct tpml_digest_values *tdv = (struct tpml_digest_values *)buffer;
- int ret = tpm_extend(pcrindex, tdv, tdv_len); if (ret) { tpm_set_failure(); return;
If the code introduced a new struct with the maximum sized log entry instead of declaring buffers of size MAX_TPML_DIGEST_VALUES_SIZE I think the code would be a little simpler. That new struct could then be used for both tpm_extend() and tpm_log_event() making patches 5-7 simpler.
See https://github.com/KevinOConnor/seabios/tree/testing for what I was thinking.
-Kevin
On 08/09/2016 01:36 PM, Kevin O'Connor wrote:
On Fri, Aug 05, 2016 at 11:07:11AM -0400, Stefan Berger wrote:
Extend the tpm20_extend function to support extending a hash to multiple PCR banks. The sha1 hash that's being extended into the sha256 bank for example, will be filled with zero-bytes to the size of a sha256 hash.
[...]
@@ -573,7 +700,16 @@ tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, } }; sha1(hashdata, hashdata_length, entry.digest.sha1);
- int ret = tpm_extend(entry.pcrindex, entry.digest.sha1);
- u8 buffer[MAX_TPML_DIGEST_VALUES_SIZE];
- int tdv_len = tpm_write_tpml_digest_values(buffer, sizeof(buffer),
entry.digest.sha1, TPM2_ALG_SHA1);
- if (tdv_len < 0)
return;
- struct tpml_digest_values *tdv = (struct tpml_digest_values *)buffer;
- int ret = tpm_extend(pcrindex, tdv, tdv_len); if (ret) { tpm_set_failure(); return;
If the code introduced a new struct with the maximum sized log entry instead of declaring buffers of size MAX_TPML_DIGEST_VALUES_SIZE I think the code would be a little simpler. That new struct could then be used for both tpm_extend() and tpm_log_event() making patches 5-7 simpler.
See https://github.com/KevinOConnor/seabios/tree/testing for what I was thinking.
So I tested this and your code works just as well. These data structures are a bit tricky and it takes a while to see how for example the tpm_log_header is used for TPM 1.2 and TPM 2. I left a comment in the patch on github. Maybe you could leave a comment in the tpm_log_header datastructure stating that digest holds the SHA1 hash in TPM 1.2 case and a tpm2_digest_values struct in TPM 2 case. Otherwise I think you can merge this in.
Thanks, Stefan
-Kevin
On Tue, Aug 09, 2016 at 10:23:36PM -0400, Stefan Berger wrote:
On 08/09/2016 01:36 PM, Kevin O'Connor wrote:
On Fri, Aug 05, 2016 at 11:07:11AM -0400, Stefan Berger wrote:
Extend the tpm20_extend function to support extending a hash to multiple PCR banks. The sha1 hash that's being extended into the sha256 bank for example, will be filled with zero-bytes to the size of a sha256 hash.
[...]
@@ -573,7 +700,16 @@ tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, } }; sha1(hashdata, hashdata_length, entry.digest.sha1);
- int ret = tpm_extend(entry.pcrindex, entry.digest.sha1);
- u8 buffer[MAX_TPML_DIGEST_VALUES_SIZE];
- int tdv_len = tpm_write_tpml_digest_values(buffer, sizeof(buffer),
entry.digest.sha1, TPM2_ALG_SHA1);
- if (tdv_len < 0)
return;
- struct tpml_digest_values *tdv = (struct tpml_digest_values *)buffer;
- int ret = tpm_extend(pcrindex, tdv, tdv_len); if (ret) { tpm_set_failure(); return;
If the code introduced a new struct with the maximum sized log entry instead of declaring buffers of size MAX_TPML_DIGEST_VALUES_SIZE I think the code would be a little simpler. That new struct could then be used for both tpm_extend() and tpm_log_event() making patches 5-7 simpler.
See https://github.com/KevinOConnor/seabios/tree/testing for what I was thinking.
So I tested this and your code works just as well. These data structures are a bit tricky and it takes a while to see how for example the tpm_log_header is used for TPM 1.2 and TPM 2. I left a comment in the patch on github. Maybe you could leave a comment in the tpm_log_header datastructure stating that digest holds the SHA1 hash in TPM 1.2 case and a tpm2_digest_values struct in TPM 2 case. Otherwise I think you can merge this in.
How about the update below (which I'll squash into the appropriate patches).
-Kevin
--- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -518,6 +518,11 @@ struct tpm2_digest_values { struct tpm2_digest_value digest[0]; } PACKED;
+// Each entry in the TPM log contains: a tpm_log_header, a variable +// length digest, a tpm_log_trailer, and a variable length event. On +// TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest contains a +// tpm2_digest_values struct followed by a variable number of +// tpm2_digest_value structs. struct tpm_log_header { u32 pcrindex; u32 eventtype; diff --git a/src/tcgbios.c b/src/tcgbios.c index a3074e2..7f045e3 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -136,6 +136,11 @@ tpm_tcpa_probe(void) /* * Extend the ACPI log with the given entry by copying the * entry data into the log. + * Input + * entry : The header data to use (including the variable length digest) + * digest_len : Length of the digest in 'entry' + * event : Pointer to the event body to be copied into the log + * event_len : Length of 'event' * * Output: * Returns an error code in case of faiure, 0 in case of success @@ -197,7 +202,10 @@ tpm_can_show_menu(void) return 0; }
-// Maximum supported log entry header (header + digest) +// A 'struct tpm_log_entry' is a local data structure containing a +// 'tpm_log_header' followed by space for the maximum supported +// digest. (The digest is a sha1 hash on tpm1.2 or a series of +// tpm2_digest_value structs on tpm2.0) struct tpm_log_entry { struct tpm_log_header hdr; u8 pad[sizeof(struct tpm2_digest_values) @@ -360,6 +368,7 @@ tpm20_build_digest(struct tpm_log_entry *le, const u8 *sha1) static int tpm12_build_digest(struct tpm_log_entry *le, const u8 *sha1) { + // On TPM 1.2 the digest contains just the SHA1 hash memcpy(le->hdr.digest, sha1, SHA1_BUFSIZE); return SHA1_BUFSIZE; }
On Wed, Aug 10, 2016 at 11:17:30AM -0400, Kevin O'Connor wrote:
How about the update below (which I'll squash into the appropriate patches).
-Kevin
--- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -518,6 +518,11 @@ struct tpm2_digest_values { struct tpm2_digest_value digest[0]; } PACKED;
+// Each entry in the TPM log contains: a tpm_log_header, a variable +// length digest, a tpm_log_trailer, and a variable length event. On +// TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest contains a +// tpm2_digest_values struct followed by a variable number of +// tpm2_digest_value structs.
Or slightly more detailed here:
// Each entry in the TPM log contains: a tpm_log_header, a variable // length digest, a tpm_log_trailer, and a variable length event. The // 'digest' matches what is sent to the TPM hardware via the Extend // command. On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest // contains a tpm2_digest_values struct followed by a variable number // of tpm2_digest_value structs (as specified by the hardware via the // TPM2_CAP_PCRS request).
-Kevin
On 08/10/2016 01:22 PM, Kevin O'Connor wrote:
On Wed, Aug 10, 2016 at 11:17:30AM -0400, Kevin O'Connor wrote:
How about the update below (which I'll squash into the appropriate patches).
-Kevin
--- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -518,6 +518,11 @@ struct tpm2_digest_values { struct tpm2_digest_value digest[0]; } PACKED;
+// Each entry in the TPM log contains: a tpm_log_header, a variable +// length digest, a tpm_log_trailer, and a variable length event. On +// TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest contains a +// tpm2_digest_values struct followed by a variable number of +// tpm2_digest_value structs.
Or slightly more detailed here:
// Each entry in the TPM log contains: a tpm_log_header, a variable // length digest, a tpm_log_trailer, and a variable length event. The // 'digest' matches what is sent to the TPM hardware via the Extend // command. On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest // contains a tpm2_digest_values struct followed by a variable number // of tpm2_digest_value structs (as specified by the hardware via the // TPM2_CAP_PCRS request).
Great!
Stefan
-Kevin
On Wed, Aug 10, 2016 at 03:10:18PM -0400, Stefan Berger wrote:
On 08/10/2016 01:22 PM, Kevin O'Connor wrote:
On Wed, Aug 10, 2016 at 11:17:30AM -0400, Kevin O'Connor wrote:
How about the update below (which I'll squash into the appropriate patches).
-Kevin
--- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -518,6 +518,11 @@ struct tpm2_digest_values { struct tpm2_digest_value digest[0]; } PACKED; +// Each entry in the TPM log contains: a tpm_log_header, a variable +// length digest, a tpm_log_trailer, and a variable length event. On +// TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest contains a +// tpm2_digest_values struct followed by a variable number of +// tpm2_digest_value structs.
Or slightly more detailed here:
// Each entry in the TPM log contains: a tpm_log_header, a variable // length digest, a tpm_log_trailer, and a variable length event. The // 'digest' matches what is sent to the TPM hardware via the Extend // command. On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest // contains a tpm2_digest_values struct followed by a variable number // of tpm2_digest_value structs (as specified by the hardware via the // TPM2_CAP_PCRS request).
Great!
Thanks - I committed this updated series.
-Kevin
Move tpm_log_init to a point after tpm_startup since in the subsequent steps we will need to be able to call TPM2_GetCapabilties from this function to learn about the TPM's PCR bank configuration. We can move it there, since tpm_startup does not write anything into the log.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/tcgbios.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/tcgbios.c b/src/tcgbios.c index de02864..acf75c7 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -940,17 +940,19 @@ tpm_setup(void) if (ret) return;
- tpm_log_init(); - TPM_working = 1;
- if (runningOnXen()) + if (runningOnXen()) { + tpm_log_init(); return; + }
ret = tpm_startup(); if (ret) return;
+ tpm_log_init(); + tpm_smbios_measure(); tpm_add_action(2, "Start Option ROM Scan"); }
Adjust the TPM2's log header event to show all hashes that are found in the subsequent log entries.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/std/tcg.h | 18 ++++++--- src/tcgbios.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 104 insertions(+), 29 deletions(-)
diff --git a/src/std/tcg.h b/src/std/tcg.h index 8d71226..eff0890 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -541,6 +541,14 @@ struct tcg_pcr_event2_sha1 { u8 event[0]; } PACKED;
+struct TCG_EfiSpecIdEventAlgorithms { + u32 numberOfAlgorithms; + struct TCG_EfiSpecIDEventAlgorithmSize { + u16 algorithmId; + u16 digestSize; + } digestSizes[0]; +} PACKED; + struct TCG_EfiSpecIdEventStruct { u8 signature[16]; u32 platformClass; @@ -548,14 +556,12 @@ struct TCG_EfiSpecIdEventStruct { u8 specVersionMajor; u8 specErrata; u8 uintnSize; - u32 numberOfAlgorithms; - struct TCG_EfiSpecIdEventAlgorithmSize { - u16 algorithmId; - u16 digestSize; - } digestSizes[1]; + struct TCG_EfiSpecIdEventAlgorithms algs; + /* u8 vendorInfoSize; u8 vendorInfo[0]; -}; + */ +} PACKED;
#define TPM_TCPA_ACPI_CLASS_CLIENT 0
diff --git a/src/tcgbios.c b/src/tcgbios.c index acf75c7..b468867 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -55,6 +55,13 @@ static const u8 TPM2_SelfTest_YES[] = { TPM2_YES }; /* full test */ typedef u8 tpm_ppi_code;
/**************************************************************** + * prototypes + ****************************************************************/ + +static int tpm20_get_pcrbanks(); +static int tpm20_get_hash_buffersize(u16 hashAlg); + +/**************************************************************** * ACPI TCPA table interface ****************************************************************/
@@ -187,38 +194,104 @@ tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event return 0; }
-/* - * Initialize the log; a TPM2 log needs a special TPM 1.2 log entry - * as the first entry serving identification purposes - */ -static void -tpm_log_init(void) +int +tpm20_write_EfiSpecIdEventStruct(char *dest, size_t destlen) { - struct TCG_EfiSpecIdEventStruct event = { + if (!tpm20_pcr_selection) + return -1; + + struct TCG_EfiSpecIdEventStruct temp = { .signature = "Spec ID Event03", .platformClass = TPM_TCPA_ACPI_CLASS_CLIENT, .specVersionMinor = 0, .specVersionMajor = 2, .specErrata = 0, .uintnSize = 2, - .numberOfAlgorithms = 1, - .digestSizes[0] = { - .algorithmId = TPM2_ALG_SHA1, - .digestSize = SHA1_BUFSIZE, + .algs = { + .numberOfAlgorithms = 0, /* fill below */ }, - .vendorInfoSize = 0, - }; - struct tcg_pcr_event2_sha1 entry = { - .eventtype = EV_NO_ACTION, - .eventdatasize = sizeof(event), - }; + }, *event = (struct TCG_EfiSpecIdEventStruct *)dest;
+ int hsize; + struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections; + void *nsel, *end = (void *)tpm20_pcr_selection + tpm20_pcr_selection_size; + struct TCG_EfiSpecIdEventAlgorithms *algs = &event->algs; + + int offset = offsetof(struct TCG_EfiSpecIdEventStruct, + algs.digestSizes[0]); + u32 count; + + memcpy(event, &temp, offset); + + for (count = 0; + count < be32_to_cpu(tpm20_pcr_selection->count); + count++) { + u8 sizeOfSelect = sel->sizeOfSelect; + + nsel = (void *)sel + + offsetof(struct tpms_pcr_selection, pcrSelect) + + sizeOfSelect; + if (nsel > end) + break; + + hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg)); + if (hsize < 0) { + dprintf(DEBUG_tcg, "TPM is using an unsupported hash: %d\n", + be16_to_cpu(sel->hashAlg)); + return -1; + } + + algs->digestSizes[count].algorithmId = be16_to_cpu(sel->hashAlg); + algs->digestSizes[count].digestSize = hsize; + + offset += sizeof(algs->digestSizes[0]); + + sel = nsel; + } + + if (sel != end) { + dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n"); + return -1; + } + event->algs.numberOfAlgorithms = count; + + u32 *vendorInfoSize = (u32 *)&dest[offset]; + *vendorInfoSize = 0; + offset += sizeof(*vendorInfoSize); + + if (offset > destlen) + printf("buffer for log event is too small (%d < %d)\n", + destlen, offset); + + return offset; +} + +/* + * Initialize the log; a TPM2 log needs a special TPM 1.2 log entry + * as the first entry serving identification purposes + */ +static void +tpm_log_init(void) +{ switch (TPM_version) { case TPM_VERSION_1_2: break; - case TPM_VERSION_2: + case TPM_VERSION_2: ; + int ret = tpm20_get_pcrbanks(); + /* if this failed, no entry will be written */ + if (ret) + return; + /* write a 1.2 type of entry */ - tpm_log_event(&entry, &event, TPM_VERSION_1_2); + char buf[256]; + ret = tpm20_write_EfiSpecIdEventStruct(buf, sizeof(buf)); + if (ret < 0) + return; + struct tcg_pcr_event2_sha1 entry = { + .eventtype = EV_NO_ACTION, + .eventdatasize = ret, + }; + tpm_log_event(&entry, buf, TPM_VERSION_1_2); } }
@@ -897,10 +970,6 @@ tpm20_startup(void) if (ret) goto err_exit;
- ret = tpm20_get_pcrbanks(); - if (ret) - goto err_exit; - return 0;
err_exit:
Modify the function that writes the TPM logs to take a tpml_digest_values structure and its length as an additional parameter and write this structure's contents into the log.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/std/tcg.h | 6 ++++-- src/tcgbios.c | 59 +++++++++++++++++++++++++++++++++++------------------------ 2 files changed, 39 insertions(+), 26 deletions(-)
diff --git a/src/std/tcg.h b/src/std/tcg.h index eff0890..f502de4 100644 --- a/src/std/tcg.h +++ b/src/std/tcg.h @@ -533,10 +533,12 @@ struct tpml_digest_values_sha1 { u8 sha1[SHA1_BUFSIZE]; } PACKED;
-struct tcg_pcr_event2_sha1 { +struct tcg_pcr_event2_data { u32 pcrindex; u32 eventtype; - struct tpml_digest_values_sha1 digest; + /* missing here is the variable size + struct tpml_digest_values digests[]; + */ u32 eventdatasize; u8 event[0]; } PACKED; diff --git a/src/tcgbios.c b/src/tcgbios.c index b468867..18050d0 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -153,8 +153,9 @@ tpm_tcpa_probe(void) * Returns an error code in case of faiure, 0 in case of success */ static int -tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event - , TPMVersion tpm_version) +tpm_log_event(struct tcg_pcr_event2_data *entry, + struct tpml_digest_values *tdv, int tdv_len, + const void *event, TPMVersion tpm_version) { dprintf(DEBUG_tcg, "TCGBIOS: LASA = %p, next entry = %p\n", tpm_state.log_area_start_address, tpm_state.log_area_next_entry); @@ -162,7 +163,7 @@ tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event if (tpm_state.log_area_next_entry == NULL) return -1;
- u32 size = sizeof(*entry) + entry->eventdatasize; + u32 size = sizeof(*entry) + tdv_len + entry->eventdatasize; u32 logsize = (tpm_state.log_area_next_entry + size - tpm_state.log_area_start_address); if (logsize > tpm_state.log_area_minimum_length) { @@ -175,15 +176,26 @@ tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event struct pcpes *pcpes = (void*)tpm_state.log_area_next_entry; pcpes->pcrindex = entry->pcrindex; pcpes->eventtype = entry->eventtype; - memcpy(pcpes->digest, entry->digest.sha1, sizeof(pcpes->digest)); + memcpy(pcpes->digest, tdv->digest[0].hash, sizeof(pcpes->digest)); pcpes->eventdatasize = entry->eventdatasize; memcpy(pcpes->event, event, entry->eventdatasize); size = sizeof(*pcpes) + entry->eventdatasize; break; case TPM_VERSION_2: ; - struct tcg_pcr_event2_sha1 *e = (void*)tpm_state.log_area_next_entry; - memcpy(e, entry, sizeof(*e)); - memcpy(e->event, event, entry->eventdatasize); + u8 *dest = tpm_state.log_area_next_entry; + + unsigned int offset = offsetof(struct tcg_pcr_event2_data, + eventdatasize); + memcpy(dest, entry, offset); + + memcpy(&dest[offset], tdv, tdv_len); + offset += tdv_len; + + u32 *eventdatasize = (u32 *)&dest[offset]; + *eventdatasize = entry->eventdatasize; + offset += sizeof(entry->eventdatasize); + + memcpy(&dest[offset], event, entry->eventdatasize); break; }
@@ -287,11 +299,18 @@ tpm_log_init(void) ret = tpm20_write_EfiSpecIdEventStruct(buf, sizeof(buf)); if (ret < 0) return; - struct tcg_pcr_event2_sha1 entry = { + struct tcg_pcr_event2_data entry = { .eventtype = EV_NO_ACTION, .eventdatasize = ret, }; - tpm_log_event(&entry, buf, TPM_VERSION_1_2); + struct tpml_digest_values_sha1 tdvs = { + .count = 1, + .hashtype = TPM2_ALG_SHA1, + .sha1 = {0, }, + }; + struct tpml_digest_values *tdv = (struct tpml_digest_values *)&tdvs; + + tpm_log_event(&entry, tdv, sizeof(tdvs), buf, TPM_VERSION_1_2); } }
@@ -763,21 +782,18 @@ tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, if (!tpm_is_working()) return;
- struct tcg_pcr_event2_sha1 entry = { + u8 digest[SHA1_BUFSIZE]; + struct tcg_pcr_event2_data entry = { .pcrindex = pcrindex, .eventtype = event_type, .eventdatasize = event_length, - .digest = { - .count = 1, - .hashtype = TPM2_ALG_SHA1, - } }; - sha1(hashdata, hashdata_length, entry.digest.sha1); + sha1(hashdata, hashdata_length, digest);
u8 buffer[MAX_TPML_DIGEST_VALUES_SIZE];
int tdv_len = tpm_write_tpml_digest_values(buffer, sizeof(buffer), - entry.digest.sha1, TPM2_ALG_SHA1); + digest, TPM2_ALG_SHA1); if (tdv_len < 0) return;
@@ -787,7 +803,7 @@ tpm_add_measurement_to_log(u32 pcrindex, u32 event_type, tpm_set_failure(); return; } - tpm_log_event(&entry, event, TPM_version); + tpm_log_event(&entry, tdv, tdv_len, event, TPM_version); }
@@ -1325,17 +1341,12 @@ hash_log_extend(struct pcpes *pcpes, const void *hashdata, u32 hashdata_length if (ret) return TCG_TCG_COMMAND_ERROR; } - struct tcg_pcr_event2_sha1 entry = { + struct tcg_pcr_event2_data entry = { .pcrindex = pcpes->pcrindex, .eventtype = pcpes->eventtype, .eventdatasize = pcpes->eventdatasize, - .digest = { - .count = 1, - .hashtype = TPM2_ALG_SHA1, - } }; - memcpy(entry.digest.sha1, pcpes->digest, sizeof(entry.digest.sha1)); - int ret = tpm_log_event(&entry, pcpes->event, TPM_version); + int ret = tpm_log_event(&entry, tdv, tdv_len, pcpes->event, TPM_version); if (ret) return TCG_PC_LOGOVERFLOW; return 0;