[SeaBIOS] [PATCH v2 3/6] tpm: Extend tpm20_extend to support extending to multiple PCR banks

Stefan Berger stefanb at linux.vnet.ibm.com
Tue Jul 26 17:19:50 CEST 2016


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 at linux.vnet.ibm.com>

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 | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 138 insertions(+), 10 deletions(-)

diff --git a/src/std/tcg.h b/src/std/tcg.h
index 1644684..3500867 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 204f5ad..206c296 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
  ****************************************************************/
@@ -80,6 +82,107 @@ static TPMVersion TPM_version;
 static u32 tpm20_pcr_selection_size;
 static struct tpml_pcr_selection *tpm20_pcr_selection;
 
+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
+ * pcrindex: the PCR index
+ * hash: the hash value to write
+ * hashAlg: the hash alogorithm determining the size of the hash
+ * count: pointer to a counter for how many entries were writte
+ *
+ * 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, u32 pcrindex,
+                            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;
+
+        u8 idx = pcrindex >> 3;
+        u8 mask = (1 << (pcrindex & 7));
+        /* skip if PCR not used in this bank */
+        if (idx > sizeOfSelect || !(sel->pcrSelect[idx] & mask))
+            continue;
+
+        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;
+        }
+
+        if (dest) {
+            struct tpm2_digest_value *v =
+              (struct tpm2_digest_value *)&dest[offset];
+            v->hashAlg = sel->hashAlg;
+            memset(v->hash, 0, hsize);
+            memcpy(v->hash, hash, min(hash_size, hsize));
+        }
+        offset += sizeof(sel->hashAlg) + hsize;
+        sel = nsel;
+    }
+
+    if (sel != end) {
+        dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n");
+        return -1;
+    }
+
+    if (dest && offset > destlen)
+        panic("buffer for tpml_digest_values is too small\n");
+
+    if (dest) {
+        struct tpml_digest_values *v = (struct tpml_digest_values *)dest;
+        v->count = cpu_to_be32(count);
+    }
+
+    return offset;
+}
+
 static struct tcpa_descriptor_rev2 *
 find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp)
 {
@@ -501,7 +604,7 @@ static int tpm20_extend(u32 pcrindex, const u8 *digest, u16 hashAlg)
 {
     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 +615,18 @@ static int tpm20_extend(u32 pcrindex, const u8 *digest, u16 hashAlg)
             .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(hashAlg);
-    memcpy(tre->digest.sha1, digest, sizeof(tmp_tre.digest.sha1));
+
+    int size = tpm20_write_tpml_dig_values(&tre->data[0],
+        sizeof(buffer) - offsetof(struct tpm2_req_extend, data),
+        pcrindex, digest, hashAlg);
+    if (size < 0)
+        return size;
+
+    tre->hdr.totlen = cpu_to_be32(sizeof(tmp_tre) + size);
 
     struct tpm_rsp_header rsp;
     u32 resp_length = sizeof(rsp);
-- 
2.5.5




More information about the SeaBIOS mailing list