]> xenbits.xensource.com Git - people/andrewcoop/seabios.git/commitdiff
tpm: Extend tpm20_extend to support extending to multiple PCR banks
authorStefan Berger <stefanb@linux.vnet.ibm.com>
Fri, 5 Aug 2016 15:07:11 +0000 (11:07 -0400)
committerKevin O'Connor <kevin@koconnor.net>
Wed, 10 Aug 2016 19:01:04 +0000 (15:01 -0400)
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>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
src/std/tcg.h
src/tcgbios.c

index 730b39c0c771d4fb2769596b47a0dad057d4a102..401b20b58b0bcb1e11d3fda6d1aa18b3febe0071 100644 (file)
@@ -100,6 +100,10 @@ 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
 
 /* Input and Output blocks for the TCG BIOS commands */
 
@@ -381,6 +385,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
@@ -441,18 +449,12 @@ struct tpm2_req_hierarchychangeauth {
     struct tpm2b_20 newAuth;
 } PACKED;
 
-struct tpm2_digest_value {
-    u16 hashalg; /* TPM2_ALG_SHA1 */
-    u8 sha1[SHA1_BUFSIZE];
-} PACKED;
-
 struct tpm2_req_extend {
     struct tpm_req_header hdr;
     u32 pcrindex;
     u32 authblocksize;
     struct tpm2_authblock authblock;
-    u32 count;
-    struct tpm2_digest_value digest;
+    u8 digest[0];
 } PACKED;
 
 struct tpm2_req_clearcontrol {
@@ -506,6 +508,22 @@ struct tpml_pcr_selection {
 
 /* TPM 2 log entry */
 
+struct tpm2_digest_value {
+    u16 hashAlg;
+    u8 hash[0]; /* size depends on hashAlg */
+} PACKED;
+
+struct tpm2_digest_values {
+    u32 count;
+    struct tpm2_digest_value digest[0];
+} PACKED;
+
+struct tpm_log_header {
+    u32 pcrindex;
+    u32 eventtype;
+    u8 digest[0];
+} PACKED;
+
 struct tpml_digest_values_sha1 {
     u32 count; /* number of digests */
     u16 hashtype;
index 9dddc4b537a22ebe677f830146bd69d9462bf561..57131d759f07d8b5b59f7b47b9310045f34530bf 100644 (file)
@@ -245,6 +245,121 @@ tpm_can_show_menu(void)
     return 0;
 }
 
+// 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)
+           + 5 * sizeof(struct tpm2_digest_value)
+           + SHA1_BUFSIZE + SHA256_BUFSIZE + SHA384_BUFSIZE
+           + SHA512_BUFSIZE + SM3_256_BUFSIZE];
+} PACKED;
+
+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;
+    }
+}
+
+/*
+ * Build the TPM2 tpm2_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.
+ *
+ * le: the log entry to build the digest in
+ * sha1: the sha1 hash value to use
+ *
+ * Returns the digest size; -1 on fatal error
+ */
+static int
+tpm20_build_digest(struct tpm_log_entry *le, const u8 *sha1)
+{
+    if (!tpm20_pcr_selection)
+        return -1;
+
+    struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections;
+    void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
+    void *dest = le->hdr.digest + sizeof(struct tpm2_digest_values);
+
+    u32 count;
+    for (count = 0; count < be32_to_cpu(tpm20_pcr_selection->count); count++) {
+        u8 sizeOfSelect = sel->sizeOfSelect;
+
+        nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+        if (nsel > end)
+            break;
+
+        int 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;
+        }
+
+        /* buffer size sanity check before writing */
+        struct tpm2_digest_value *v = dest;
+        if (dest + sizeof(*v) + hsize > (void*)le + sizeof(*le)) {
+            dprintf(DEBUG_tcg, "tpm_log_entry is too small\n");
+            return -1;
+        }
+
+        v->hashAlg = sel->hashAlg;
+        memset(v->hash, 0, hsize);
+        memcpy(v->hash, sha1, hsize > SHA1_BUFSIZE ? SHA1_BUFSIZE : hsize);
+
+        dest += sizeof(*v) + hsize;
+        sel = nsel;
+    }
+
+    if (sel != end) {
+        dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n");
+        return -1;
+    }
+
+    struct tpm2_digest_values *v = (void*)le->hdr.digest;
+    v->count = cpu_to_be32(count);
+
+    return dest - (void*)le->hdr.digest;
+}
+
+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;
+}
+
+static int
+tpm_build_digest(struct tpm_log_entry *le, const u8 *sha1)
+{
+    switch (TPM_version) {
+    case TPM_VERSION_1_2:
+        return tpm12_build_digest(le, sha1);
+    case TPM_VERSION_2:
+        return tpm20_build_digest(le, sha1);
+    }
+    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
@@ -477,15 +592,15 @@ tpm20_set_timeouts(void)
 }
 
 static int
-tpm12_extend(u32 pcrindex, const u8 *digest)
+tpm12_extend(struct tpm_log_entry *le, int digest_len)
 {
     struct tpm_req_extend tre = {
         .hdr.tag     = cpu_to_be16(TPM_TAG_RQU_CMD),
         .hdr.totlen  = cpu_to_be32(sizeof(tre)),
         .hdr.ordinal = cpu_to_be32(TPM_ORD_Extend),
-        .pcrindex    = cpu_to_be32(pcrindex),
+        .pcrindex    = cpu_to_be32(le->hdr.pcrindex),
     };
-    memcpy(tre.digest, digest, sizeof(tre.digest));
+    memcpy(tre.digest, le->hdr.digest, sizeof(tre.digest));
 
     struct tpm_rsp_extend rsp;
     u32 resp_length = sizeof(rsp);
@@ -497,13 +612,13 @@ tpm12_extend(u32 pcrindex, const u8 *digest)
     return 0;
 }
 
-static int tpm20_extend(u32 pcrindex, const u8 *digest)
+static int tpm20_extend(struct tpm_log_entry *le, int digest_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),
+        .pcrindex    = cpu_to_be32(le->hdr.pcrindex),
         .authblocksize = cpu_to_be32(sizeof(tmp_tre.authblock)),
         .authblock = {
             .handle = cpu_to_be32(TPM2_RS_PW),
@@ -512,14 +627,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) + sizeof(le->pad)];
     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->digest[0], le->hdr.digest, digest_len);
+
+    tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + digest_len);
 
     struct tpm_rsp_header rsp;
     u32 resp_length = sizeof(rsp);
@@ -532,13 +646,13 @@ static int tpm20_extend(u32 pcrindex, const u8 *digest)
 }
 
 static int
-tpm_extend(u32 pcrindex, const u8 *digest)
+tpm_extend(struct tpm_log_entry *le, int digest_len)
 {
     switch (TPM_version) {
     case TPM_VERSION_1_2:
-        return tpm12_extend(pcrindex, digest);
+        return tpm12_extend(le, digest_len);
     case TPM_VERSION_2:
-        return tpm20_extend(pcrindex, digest);
+        return tpm20_extend(le, digest_len);
     }
     return -1;
 }
@@ -573,7 +687,15 @@ 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);
+
+    struct tpm_log_entry le = {
+        .hdr.pcrindex = pcrindex,
+        .hdr.eventtype = event_type,
+    };
+    int digest_len = tpm_build_digest(&le, entry.digest.sha1);
+    if (digest_len < 0)
+        return;
+    int ret = tpm_extend(&le, digest_len);
     if (ret) {
         tpm_set_failure();
         return;
@@ -1103,8 +1225,16 @@ 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);
+
+    struct tpm_log_entry le = {
+        .hdr.pcrindex = pcpes->pcrindex,
+        .hdr.eventtype = pcpes->eventtype,
+    };
+    int digest_len = tpm_build_digest(&le, pcpes->digest);
+    if (digest_len < 0)
+        return TCG_GENERAL_ERROR;
     if (extend) {
-        int ret = tpm_extend(pcpes->pcrindex, pcpes->digest);
+        int ret = tpm_extend(&le, digest_len);
         if (ret)
             return TCG_TCG_COMMAND_ERROR;
     }