[SeaBIOS] [PATCH 2/2] tcgbios: Implement TPM 2.0 menu item to activate and deactivate PCR banks

Stefan Berger posted 2 patches 7 years, 5 months ago
There is a newer version of this series
[SeaBIOS] [PATCH 2/2] tcgbios: Implement TPM 2.0 menu item to activate and deactivate PCR banks
Posted by Stefan Berger 7 years, 5 months ago
Implement a TPM 2.0 menu item that allows a user to toggle the activation
of PCR banks of the TPM 2.0. After successful activation we shut down the
TPM 2.0 and reset the machine.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 src/std/tcg.h |  18 ++++
 src/tcgbios.c | 229 +++++++++++++++++++++++++++++++++++++++++++++++++-
 src/util.h    |   2 +
 3 files changed, 247 insertions(+), 2 deletions(-)

diff --git a/src/std/tcg.h b/src/std/tcg.h
index 09a92d8..eb9e67d 100644
--- a/src/std/tcg.h
+++ b/src/std/tcg.h
@@ -336,6 +336,12 @@ struct tpm_res_sha1complete {
 #define TPM2_ALG_SHA512             0x000d
 #define TPM2_ALG_SM3_256            0x0012
 
+#define TPM2_ALG_SHA1_FLAG          (1 << 0)
+#define TPM2_ALG_SHA256_FLAG        (1 << 1)
+#define TPM2_ALG_SHA384_FLAG        (1 << 2)
+#define TPM2_ALG_SHA512_FLAG        (1 << 3)
+#define TPM2_ALG_SM3_256_FLAG       (1 << 4)
+
 /* TPM 2 command tags */
 #define TPM2_ST_NO_SESSIONS         0x8001
 #define TPM2_ST_SESSIONS            0x8002
@@ -345,8 +351,10 @@ struct tpm_res_sha1complete {
 #define TPM2_CC_Clear               0x126
 #define TPM2_CC_ClearControl        0x127
 #define TPM2_CC_HierarchyChangeAuth 0x129
+#define TPM2_CC_PCR_Allocate        0x12b
 #define TPM2_CC_SelfTest            0x143
 #define TPM2_CC_Startup             0x144
+#define TPM2_CC_Shutdown            0x145
 #define TPM2_CC_StirRandom          0x146
 #define TPM2_CC_GetCapability       0x17a
 #define TPM2_CC_GetRandom           0x17b
@@ -442,6 +450,15 @@ struct tpm2_res_getcapability {
     u8 data[0]; /* capability dependent data */
 } PACKED;
 
+struct tpm2_req_pcr_allocate {
+    struct tpm_req_header hdr;
+    u32 authhandle;
+    u32 authblocksize;
+    struct tpm2_authblock authblock;
+    u32 count;
+    u8 tpms_pcr_selections[4];
+} PACKED;
+
 struct tpms_pcr_selection {
     u16 hashAlg;
     u8 sizeOfSelect;
@@ -550,5 +567,6 @@ struct pcctes_romex
 #define TPM_PPI_OP_CLEAR 5
 #define TPM_PPI_OP_SET_OWNERINSTALL_TRUE 8
 #define TPM_PPI_OP_SET_OWNERINSTALL_FALSE 9
+#define TPM_PPI_OP_SET_PCR_BANKS 23
 
 #endif // tcg.h
diff --git a/src/tcgbios.c b/src/tcgbios.c
index 0eabc60..a8afec3 100644
--- a/src/tcgbios.c
+++ b/src/tcgbios.c
@@ -163,23 +163,35 @@ struct tpm_log_entry {
 
 static const struct hash_parameters {
     u16 hashalg;
+    u8  hashalg_flag;
     u8  hash_buffersize;
+    const char *name;
 } hash_parameters[] = {
     {
         .hashalg = TPM2_ALG_SHA1,
+        .hashalg_flag = TPM2_ALG_SHA1_FLAG,
         .hash_buffersize = SHA1_BUFSIZE,
+        .name = "SHA1",
     }, {
         .hashalg = TPM2_ALG_SHA256,
+        .hashalg_flag = TPM2_ALG_SHA256_FLAG,
         .hash_buffersize = SHA256_BUFSIZE,
+        .name = "SHA256",
     }, {
         .hashalg = TPM2_ALG_SHA384,
+        .hashalg_flag = TPM2_ALG_SHA384_FLAG,
         .hash_buffersize = SHA384_BUFSIZE,
+        .name = "SHA384",
     }, {
         .hashalg = TPM2_ALG_SHA512,
+        .hashalg_flag = TPM2_ALG_SHA512_FLAG,
         .hash_buffersize = SHA512_BUFSIZE,
+        .name = "SHA512",
     }, {
         .hashalg = TPM2_ALG_SM3_256,
+        .hashalg_flag = TPM2_ALG_SM3_256_FLAG,
         .hash_buffersize = SM3_256_BUFSIZE,
+        .name = "SM3-256",
     }
 };
 
@@ -195,6 +207,42 @@ tpm20_get_hash_buffersize(u16 hashAlg)
     return -1;
 }
 
+static u8
+tpm20_hashalg_to_flag(u16 hashAlg)
+{
+    unsigned i;
+
+    for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+        if (hash_parameters[i].hashalg == hashAlg)
+            return hash_parameters[i].hashalg_flag;
+    }
+    return 0;
+}
+
+static u16
+tpm20_hashalg_flag_to_hashalg(u8 hashalg_flag)
+{
+    unsigned i;
+
+    for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+        if (hash_parameters[i].hashalg_flag == hashalg_flag)
+            return hash_parameters[i].hashalg;
+    }
+    return 0;
+}
+
+static const char *
+tpm20_hashalg_flag_to_name(u8 hashalg_flag)
+{
+    unsigned i;
+
+    for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+        if (hash_parameters[i].hashalg_flag == hashalg_flag)
+            return hash_parameters[i].name;
+    }
+    return NULL;
+}
+
 // Add an entry at the start of the log describing digest formats
 static int
 tpm20_write_EfiSpecIdEventStruct(void)
@@ -447,6 +495,104 @@ tpm20_get_pcrbanks(void)
     return ret;
 }
 
+static int
+tpm20_get_suppt_pcrbanks(u8 *suppt_pcrbanks, u8 *active_pcrbanks)
+{
+    *suppt_pcrbanks = 0;
+    *active_pcrbanks = 0;
+
+    if (!tpm20_pcr_selection)
+        return -1;
+
+    struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections;
+    void *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
+
+    while (1) {
+        u8 sizeOfSelect = sel->sizeOfSelect;
+        void *nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+        if (nsel > end)
+            return 0;
+
+        u16 hashalg = be16_to_cpu(sel->hashAlg);
+        u8 hashalg_flag = tpm20_hashalg_to_flag(hashalg);
+
+        *suppt_pcrbanks |= hashalg_flag;
+
+        unsigned i;
+        for (i = 0; i < sizeOfSelect; i++) {
+            if (sel->pcrSelect[i]) {
+                *active_pcrbanks |= hashalg_flag;
+                break;
+            }
+        }
+
+        sel = nsel;
+    }
+}
+
+static int
+tpm20_set_pcrbanks(u32 active_banks)
+{
+    struct tpm2_req_pcr_allocate trpa = {
+        .hdr.tag     = cpu_to_be16(TPM2_ST_SESSIONS),
+        .hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Allocate),
+        .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+        .authblocksize = cpu_to_be32(sizeof(trpa.authblock)),
+        .authblock = {
+            .handle = cpu_to_be32(TPM2_RS_PW),
+            .noncesize = cpu_to_be16(0),
+            .contsession = TPM2_YES,
+            .pwdsize = cpu_to_be16(0),
+        },
+    };
+    struct tpms_pcr_selection3 {
+        u16 hashAlg;
+        u8 sizeOfSelect;
+        u8 pcrSelect[3];
+    } tps[ARRAY_SIZE(trpa.tpms_pcr_selections)];
+    int i = 0;
+    u8 hashalg_flag = TPM2_ALG_SHA1_FLAG;
+    u8 dontcare, suppt_banks;
+
+    tpm20_get_suppt_pcrbanks(&suppt_banks, &dontcare);
+
+    while (hashalg_flag) {
+        if ((hashalg_flag & suppt_banks)) {
+            u16 hashalg = tpm20_hashalg_flag_to_hashalg(hashalg_flag);
+
+            if (hashalg) {
+                u8 mask = 0;
+                tps[i].hashAlg = cpu_to_be16(hashalg);
+                tps[i].sizeOfSelect = 3;
+
+                if (active_banks & hashalg_flag)
+                    mask = 0xff;
+
+                tps[i].pcrSelect[0] = mask;
+                tps[i].pcrSelect[1] = mask;
+                tps[i].pcrSelect[2] = mask;
+                i++;
+            }
+        }
+        hashalg_flag <<= 1;
+    }
+
+    trpa.count = cpu_to_be32(i);
+    memcpy(trpa.tpms_pcr_selections, tps, i * sizeof(tps[0]));
+    trpa.hdr.totlen = cpu_to_be32(offsetof(struct tpm2_req_pcr_allocate,
+                                           tpms_pcr_selections) +
+                                  i * sizeof(tps[0]));
+
+    struct tpm_rsp_header rsp;
+    u32 resp_length = sizeof(rsp);
+
+    int ret = tpmhw_transmit(0, &trpa.hdr, &rsp, &resp_length,
+                             TPM_DURATION_TYPE_SHORT);
+    ret = ret ? -1 : be32_to_cpu(rsp.errcode);
+
+    return ret;
+}
+
 static int
 tpm12_get_capability(u32 cap, u32 subcap, struct tpm_rsp_header *rsp, u32 rsize)
 {
@@ -1767,7 +1913,7 @@ tpm20_clear(void)
 }
 
 static int
-tpm20_process_cfg(tpm_ppi_code msgCode, int verbose)
+tpm20_process_cfg(tpm_ppi_code msgCode, u32 pprm, int verbose)
 {
     int ret = 0;
 
@@ -1780,6 +1926,13 @@ tpm20_process_cfg(tpm_ppi_code msgCode, int verbose)
             if (!ret)
                  ret = tpm20_clear();
             break;
+
+        case TPM_PPI_OP_SET_PCR_BANKS:
+            ret = tpm20_set_pcrbanks(pprm);
+            if (!ret)
+                ret = tpm_simple_cmd(0, TPM2_CC_Shutdown,
+                                     2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
+            break;
     }
 
     if (ret)
@@ -1962,14 +2115,77 @@ tpm12_menu(void)
     }
 }
 
+static int
+tpm20_menu_change_active_pcrbanks(u8 *activate_banks)
+{
+    u8 active_banks, suppt_banks;
+
+    tpm20_get_suppt_pcrbanks(&suppt_banks, &active_banks);
+
+    *activate_banks = active_banks;
+
+    while (1) {
+        u8 hashalg_flag = TPM2_ALG_SHA1_FLAG;
+        u8 i = 0;
+
+        printf("\nToggle active PCR banks by pressing number key\n\n");
+
+        while (hashalg_flag) {
+            u8 flag = hashalg_flag & suppt_banks;
+            const char *hashname = tpm20_hashalg_flag_to_name(flag);
+
+            i++;
+            if (hashname) {
+                printf("  %d: %s", i, hashname);
+                if (*activate_banks & hashalg_flag)
+                    printf(" (enabled)");
+                printf("\n");
+            }
+
+            hashalg_flag <<= 1;
+        }
+        printf("\n"
+               "ESC: return to previous menu without changing active PCR banks\n"
+               "A  : activate selection\n");
+        u8 flagnum;
+        int show = 0;
+        while (!show) {
+            int scancode = get_keystroke(1000);
+
+            switch (scancode) {
+            case ~0:
+                continue;
+            case 1: /* ESC */
+                printf("\n");
+                return -1;
+            case 2 ... 6: /* keys 1 .. 5 */
+                flagnum = scancode - 1;
+                if (flagnum > i)
+                    continue;
+                if (suppt_banks & (1 << (flagnum - 1))) {
+                    *activate_banks ^= 1 << (flagnum - 1);
+                    show = 1;
+                }
+                break;
+            case SCANCODE_A:
+                return 0;
+            }
+        }
+    }
+}
+
 static void
 tpm20_menu(void)
 {
     int scan_code;
     tpm_ppi_code msgCode;
+    u32 pprm;
+    u8 active_banks;
+    int do_reset = 0;
 
     for (;;) {
         printf("1. Clear TPM\n");
+        printf("2. Change active PCR banks\n");
 
         printf("\nIf no change is desired or if this menu was reached by "
                "mistake, press ESC to\n"
@@ -1988,11 +2204,20 @@ tpm20_menu(void)
         case 2:
             msgCode = TPM_PPI_OP_CLEAR;
             break;
+        case 3:
+            if (tpm20_menu_change_active_pcrbanks(&active_banks) < 0)
+                continue;
+            msgCode = TPM_PPI_OP_SET_PCR_BANKS;
+            pprm = active_banks;
+            do_reset = 1;
+            break;
         default:
             continue;
         }
 
-        tpm20_process_cfg(msgCode, 0);
+        tpm20_process_cfg(msgCode, pprm,  0);
+        if (do_reset)
+            reset();
     }
 }
 
diff --git a/src/util.h b/src/util.h
index 6dd080f..b7169dd 100644
--- a/src/util.h
+++ b/src/util.h
@@ -38,6 +38,8 @@ struct usbdevice_s;
 int bootprio_find_usb(struct usbdevice_s *usbdev, int lun);
 int get_keystroke(int msec);
 
+#define SCANCODE_A 0x1e
+
 // bootsplash.c
 void enable_vga_console(void);
 void enable_bootsplash(void);
-- 
2.17.2
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
[SeaBIOS] Re: [PATCH 2/2] tcgbios: Implement TPM 2.0 menu item to activate and deactivate PCR banks
Posted by Kevin O'Connor 7 years, 5 months ago
On Tue, Jan 22, 2019 at 10:46:24AM -0500, Stefan Berger wrote:
> Implement a TPM 2.0 menu item that allows a user to toggle the activation
> of PCR banks of the TPM 2.0. After successful activation we shut down the
> TPM 2.0 and reset the machine.

Thanks.  In general, it looks fine to me.  Just for my understanding,
could you give a high level overview of when/why a user would use this
menu and what functionality they would miss if this menu were not
available?

I also have a few comments - see below.

[...]
> --- a/src/tcgbios.c
> +++ b/src/tcgbios.c
[...]
> @@ -1780,6 +1926,13 @@ tpm20_process_cfg(tpm_ppi_code msgCode, int verbose)
>              if (!ret)
>                   ret = tpm20_clear();
>              break;
> +
> +        case TPM_PPI_OP_SET_PCR_BANKS:
> +            ret = tpm20_set_pcrbanks(pprm);
> +            if (!ret)
> +                ret = tpm_simple_cmd(0, TPM2_CC_Shutdown,
> +                                     2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
> +            break;
>      }
>  
>      if (ret)
[...]
>  static void
>  tpm20_menu(void)
>  {
>      int scan_code;
>      tpm_ppi_code msgCode;
> +    u32 pprm;
> +    u8 active_banks;
> +    int do_reset = 0;
>  
>      for (;;) {
>          printf("1. Clear TPM\n");
> +        printf("2. Change active PCR banks\n");
>  
>          printf("\nIf no change is desired or if this menu was reached by "
>                 "mistake, press ESC to\n"
> @@ -1988,11 +2204,20 @@ tpm20_menu(void)
>          case 2:
>              msgCode = TPM_PPI_OP_CLEAR;
>              break;
> +        case 3:
> +            if (tpm20_menu_change_active_pcrbanks(&active_banks) < 0)
> +                continue;
> +            msgCode = TPM_PPI_OP_SET_PCR_BANKS;
> +            pprm = active_banks;
> +            do_reset = 1;
> +            break;
>          default:
>              continue;
>          }
>  
> -        tpm20_process_cfg(msgCode, 0);
> +        tpm20_process_cfg(msgCode, pprm,  0);
> +        if (do_reset)
> +            reset();
>      }

FWIW, I find the above confusing.  Could
tpm20_menu_change_active_pcrbanks() just directly invoke
tpm20_set_pcrbanks() et al when needed?

> --- a/src/util.h
> +++ b/src/util.h
> @@ -38,6 +38,8 @@ struct usbdevice_s;
>  int bootprio_find_usb(struct usbdevice_s *usbdev, int lun);
>  int get_keystroke(int msec);
>  
> +#define SCANCODE_A 0x1e

The convention for util.h is to only have forward declarations for
variables and functions - no macros, typedefs, or struct definitions.
Also, you might want take a look at possibly using ascii_to_keycode().

-Kevin
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
[SeaBIOS] Re: [PATCH 2/2] tcgbios: Implement TPM 2.0 menu item to activate and deactivate PCR banks
Posted by Stefan Berger 7 years, 5 months ago
On 1/29/19 4:24 PM, Kevin O'Connor wrote:
> On Tue, Jan 22, 2019 at 10:46:24AM -0500, Stefan Berger wrote:
>> Implement a TPM 2.0 menu item that allows a user to toggle the activation
>> of PCR banks of the TPM 2.0. After successful activation we shut down the
>> TPM 2.0 and reset the machine.
> Thanks.  In general, it looks fine to me.  Just for my understanding,
> could you give a high level overview of when/why a user would use this
> menu and what functionality they would miss if this menu were not
> available?


I'll add this to a v3: A TPM 2.0 may have multiple PCR banks, such as 
for SHA1, SHA256, SHA384, SHA512, and SM3-256. One or multiple of those 
banks may be active (by factory for example) and modifying the set of 
active PCR banks is only possible while in the firmware since it 
requires platform authorization. However, Per spec we generate a random 
password for the platform authorization before we activate the boot 
loader and throw that password away so that one cannot make any 
modifications that require this type of authorization while running an OS.

The only other method of doing this is via the Physical Presence 
Interface by posting a code (and parameter in this particular case) from 
the OS. But this requires the firmware to act on it upon reboot.


>
> I also have a few comments - see below.
>
> [...]
>> --- a/src/tcgbios.c
>> +++ b/src/tcgbios.c
> [...]
>> @@ -1780,6 +1926,13 @@ tpm20_process_cfg(tpm_ppi_code msgCode, int verbose)
>>               if (!ret)
>>                    ret = tpm20_clear();
>>               break;
>> +
>> +        case TPM_PPI_OP_SET_PCR_BANKS:
>> +            ret = tpm20_set_pcrbanks(pprm);
>> +            if (!ret)
>> +                ret = tpm_simple_cmd(0, TPM2_CC_Shutdown,
>> +                                     2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
>> +            break;
>>       }
>>   
>>       if (ret)
> [...]
>>   static void
>>   tpm20_menu(void)
>>   {
>>       int scan_code;
>>       tpm_ppi_code msgCode;
>> +    u32 pprm;
>> +    u8 active_banks;
>> +    int do_reset = 0;
>>   
>>       for (;;) {
>>           printf("1. Clear TPM\n");
>> +        printf("2. Change active PCR banks\n");
>>   
>>           printf("\nIf no change is desired or if this menu was reached by "
>>                  "mistake, press ESC to\n"
>> @@ -1988,11 +2204,20 @@ tpm20_menu(void)
>>           case 2:
>>               msgCode = TPM_PPI_OP_CLEAR;
>>               break;
>> +        case 3:
>> +            if (tpm20_menu_change_active_pcrbanks(&active_banks) < 0)
>> +                continue;
>> +            msgCode = TPM_PPI_OP_SET_PCR_BANKS;
>> +            pprm = active_banks;
>> +            do_reset = 1;
>> +            break;
>>           default:
>>               continue;
>>           }
>>   
>> -        tpm20_process_cfg(msgCode, 0);
>> +        tpm20_process_cfg(msgCode, pprm,  0);
>> +        if (do_reset)
>> +            reset();
>>       }
> FWIW, I find the above confusing.  Could
> tpm20_menu_change_active_pcrbanks() just directly invoke
> tpm20_set_pcrbanks() et al when needed?


It certainly could. I can change this.


>
>> --- a/src/util.h
>> +++ b/src/util.h
>> @@ -38,6 +38,8 @@ struct usbdevice_s;
>>   int bootprio_find_usb(struct usbdevice_s *usbdev, int lun);
>>   int get_keystroke(int msec);
>>   
>> +#define SCANCODE_A 0x1e
> The convention for util.h is to only have forward declarations for
> variables and functions - no macros, typedefs, or struct definitions.
> Also, you might want take a look at possibly using ascii_to_keycode().


Ok.

   Stefan


>
> -Kevin
>
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org