From 53db677586e3e7c4a874f1ef16f34a6d01c269a0 Mon Sep 17 00:00:00 2001 From: Sergii Dmytruk Date: Sun, 23 Oct 2022 00:47:55 +0300 Subject: security/tpm: add TPM log format as per 2.0 spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Used by default for all boards with TPM2 which don't specify log format explicitly. Change-Id: I0fac386bebab1b7104378ae3424957c6497e84e1 Ticket: https://ticket.coreboot.org/issues/422 Ticket: https://ticket.coreboot.org/issues/423 Signed-off-by: Michał Żygowski Signed-off-by: Sergii Dmytruk Reviewed-on: https://review.coreboot.org/c/coreboot/+/68748 Tested-by: build bot (Jenkins) Reviewed-by: Julius Werner --- .../bsd/include/commonlib/bsd/tpm_log_defs.h | 37 ++++ src/security/tpm/Kconfig | 25 ++- src/security/tpm/Makefile.inc | 6 + src/security/tpm/tpm2_log_serialized.h | 70 +++++++ src/security/tpm/tspi.h | 15 ++ src/security/tpm/tspi/crtm.h | 13 ++ src/security/tpm/tspi/log-tpm2.c | 230 +++++++++++++++++++++ src/security/tpm/tspi/logs.h | 15 ++ 8 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 src/security/tpm/tpm2_log_serialized.h create mode 100644 src/security/tpm/tspi/log-tpm2.c diff --git a/src/commonlib/bsd/include/commonlib/bsd/tpm_log_defs.h b/src/commonlib/bsd/include/commonlib/bsd/tpm_log_defs.h index a45a21274689..144d55a33196 100644 --- a/src/commonlib/bsd/include/commonlib/bsd/tpm_log_defs.h +++ b/src/commonlib/bsd/include/commonlib/bsd/tpm_log_defs.h @@ -9,6 +9,21 @@ #define TCPA_SPEC_ID_EVENT_SIGNATURE "Spec ID Event00" #define TCG_EFI_SPEC_ID_EVENT_SIGNATURE "Spec ID Event03" +#define TPM2_ALG_ERROR 0x0000 +#define TPM2_ALG_HMAC 0x0005 +#define TPM2_ALG_NULL 0x0010 +#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 + +#define SHA1_DIGEST_SIZE 20 +#define SHA256_DIGEST_SIZE 32 +#define SHA384_DIGEST_SIZE 48 +#define SHA512_DIGEST_SIZE 64 +#define SM3_256_DIGEST_SIZE 32 + #define EV_PREBOOT_CERT 0x00000000 #define EV_POST_CODE 0x00000001 #define EV_UNUSED 0x00000002 @@ -39,4 +54,26 @@ struct spec_id_event_data { uint8_t vendor_info_size; } __packed; +struct tpm_digest_sizes { + uint16_t alg_id; + uint16_t digest_size; +} __packed; + +struct tcg_efi_spec_id_event { + uint32_t pcr_index; + uint32_t event_type; + uint8_t digest[20]; + uint32_t event_size; + uint8_t signature[16]; + uint32_t platform_class; + uint8_t spec_version_minor; + uint8_t spec_version_major; + uint8_t spec_errata; + uint8_t uintn_size; + uint32_t num_of_algorithms; + struct tpm_digest_sizes digest_sizes[0]; /* variable number of members */ + /* uint8_t vendor_info_size; */ + /* uint8_t vendor_info[vendor_info_size]; */ +} __packed; + #endif diff --git a/src/security/tpm/Kconfig b/src/security/tpm/Kconfig index 467773dee469..8466d80dbe66 100644 --- a/src/security/tpm/Kconfig +++ b/src/security/tpm/Kconfig @@ -98,7 +98,7 @@ choice prompt "TPM event log format" depends on TPM_MEASURED_BOOT default TPM_LOG_TPM1 if TPM1 - default TPM_LOG_CB + default TPM_LOG_TPM2 if TPM2 config TPM_LOG_CB bool "coreboot's custom format" @@ -110,6 +110,29 @@ config TPM_LOG_TPM1 help Log per TPM 1.2 specification. See "TCG PC Client Specific Implementation Specification for Conventional BIOS". +config TPM_LOG_TPM2 + bool "TPM 2.0 format" + depends on TPM2 + help + Log per TPM 2.0 specification. + See "TCG PC Client Platform Firmware Profile Specification". + +endchoice + +choice + prompt "TPM2 hashing algorithm" + depends on TPM_MEASURED_BOOT && TPM_LOG_TPM2 + default TPM_HASH_SHA1 if TPM1 + default TPM_HASH_SHA256 if TPM2 + +config TPM_HASH_SHA1 + bool "SHA1" +config TPM_HASH_SHA256 + bool "SHA256" +config TPM_HASH_SHA384 + bool "SHA384" +config TPM_HASH_SHA512 + bool "SHA512" endchoice diff --git a/src/security/tpm/Makefile.inc b/src/security/tpm/Makefile.inc index a8c25d6fd804..ae06cb0ea6ce 100644 --- a/src/security/tpm/Makefile.inc +++ b/src/security/tpm/Makefile.inc @@ -67,4 +67,10 @@ verstage-$(CONFIG_TPM_LOG_TPM1) += tspi/log-tpm1.c postcar-$(CONFIG_TPM_LOG_TPM1) += tspi/log-tpm1.c bootblock-$(CONFIG_TPM_LOG_TPM1) += tspi/log-tpm1.c +ramstage-$(CONFIG_TPM_LOG_TPM2) += tspi/log-tpm2.c +romstage-$(CONFIG_TPM_LOG_TPM2) += tspi/log-tpm2.c +verstage-$(CONFIG_TPM_LOG_TPM2) += tspi/log-tpm2.c +postcar-$(CONFIG_TPM_LOG_TPM2) += tspi/log-tpm2.c +bootblock-$(CONFIG_TPM_LOG_TPM2) += tspi/log-tpm2.c + endif # CONFIG_TPM_MEASURED_BOOT diff --git a/src/security/tpm/tpm2_log_serialized.h b/src/security/tpm/tpm2_log_serialized.h new file mode 100644 index 000000000000..2b4e43c63548 --- /dev/null +++ b/src/security/tpm/tpm2_log_serialized.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef __TPM2_LOG_SERIALIZED_H__ +#define __TPM2_LOG_SERIALIZED_H__ + +#include + +#define TPM_20_SPEC_ID_EVENT_SIGNATURE "Spec ID Event03" +#define TPM_20_LOG_DATA_MAX_LENGTH 50 + +#define TPM_20_LOG_VI_MAGIC 0x32544243 /* "CBT2" in LE */ +#define TPM_20_LOG_VI_MAJOR 1 +#define TPM_20_LOG_VI_MINOR 0 + +/* + * TPM2.0 log entries can't be generally represented as C structures due to + * varying number of digests and their sizes. However, it works as long as + * we're only using single kind of digests. + */ +#if CONFIG(TPM_LOG_TPM2) +# if CONFIG(TPM_HASH_SHA1) +# define TPM_20_LOG_DIGEST_MAX_LENGTH SHA1_DIGEST_SIZE +# endif +# if CONFIG(TPM_HASH_SHA256) +# define TPM_20_LOG_DIGEST_MAX_LENGTH SHA256_DIGEST_SIZE +# endif +# if CONFIG(TPM_HASH_SHA384) +# define TPM_20_LOG_DIGEST_MAX_LENGTH SHA384_DIGEST_SIZE +# endif +# if CONFIG(TPM_HASH_SHA512) +# define TPM_20_LOG_DIGEST_MAX_LENGTH SHA512_DIGEST_SIZE +# endif + +# ifndef TPM_20_LOG_DIGEST_MAX_LENGTH +# error "Misconfiguration: failed to determine TPM hashing algorithm" +# endif +#else +# define TPM_20_LOG_DIGEST_MAX_LENGTH 1 /* To avoid compilation error */ +#endif + +/* TCG_PCR_EVENT2 */ +struct tpm_2_log_entry { + uint32_t pcr; + uint32_t event_type; + uint32_t digest_count; /* Always 1 in current implementation */ + uint16_t digest_type; + uint8_t digest[TPM_20_LOG_DIGEST_MAX_LENGTH]; + uint32_t data_length; + uint8_t data[TPM_20_LOG_DATA_MAX_LENGTH]; +} __packed; + +struct tpm_2_vendor { + uint8_t reserved; + uint8_t version_major; + uint8_t version_minor; + uint32_t magic; + uint16_t max_entries; + uint16_t num_entries; + uint32_t entry_size; +} __packed; + +struct tpm_2_log_table { + struct tcg_efi_spec_id_event header; /* TCG_PCR_EVENT actually */ + struct tpm_digest_sizes digest_sizes[1]; + uint8_t vendor_info_size; + struct tpm_2_vendor vendor; + struct tpm_2_log_entry entries[0]; /* Variable number of entries */ +} __packed; + +#endif diff --git a/src/security/tpm/tspi.h b/src/security/tpm/tspi.h index 5de0aa2dd3f8..33f363cab6c7 100644 --- a/src/security/tpm/tspi.h +++ b/src/security/tpm/tspi.h @@ -4,6 +4,7 @@ #define TSPI_H_ #include +#include #include #include #include @@ -32,6 +33,8 @@ static inline void *tpm_log_cbmem_init(void) return tpm_cb_log_cbmem_init(); if (CONFIG(TPM_LOG_TPM1)) return tpm1_log_cbmem_init(); + if (CONFIG(TPM_LOG_TPM2)) + return tpm2_log_cbmem_init(); return NULL; } @@ -45,6 +48,8 @@ static inline void tpm_preram_log_clear(void) tpm_cb_preram_log_clear(); else if (CONFIG(TPM_LOG_TPM1)) tpm1_preram_log_clear(); + else if (CONFIG(TPM_LOG_TPM2)) + tpm2_preram_log_clear(); } /** @@ -56,6 +61,8 @@ static inline uint16_t tpm_log_get_size(const void *log_table) return tpm_cb_log_get_size(log_table); if (CONFIG(TPM_LOG_TPM1)) return tpm1_log_get_size(log_table); + if (CONFIG(TPM_LOG_TPM2)) + return tpm2_log_get_size(log_table); return 0; } @@ -68,6 +75,8 @@ static inline void tpm_log_copy_entries(const void *from, void *to) tpm_cb_log_copy_entries(from, to); else if (CONFIG(TPM_LOG_TPM1)) tpm1_log_copy_entries(from, to); + else if (CONFIG(TPM_LOG_TPM2)) + tpm2_log_copy_entries(from, to); } /** @@ -80,6 +89,8 @@ static inline int tpm_log_get(int entry_idx, int *pcr, const uint8_t **digest_da return tpm_cb_log_get(entry_idx, pcr, digest_data, digest_algo, event_name); if (CONFIG(TPM_LOG_TPM1)) return tpm1_log_get(entry_idx, pcr, digest_data, digest_algo, event_name); + if (CONFIG(TPM_LOG_TPM2)) + return tpm2_log_get(entry_idx, pcr, digest_data, digest_algo, event_name); return 1; } @@ -100,6 +111,8 @@ static inline void tpm_log_add_table_entry(const char *name, const uint32_t pcr, tpm_cb_log_add_table_entry(name, pcr, digest_algo, digest, digest_len); else if (CONFIG(TPM_LOG_TPM1)) tpm1_log_add_table_entry(name, pcr, digest_algo, digest, digest_len); + else if (CONFIG(TPM_LOG_TPM2)) + tpm2_log_add_table_entry(name, pcr, digest_algo, digest, digest_len); } /** @@ -111,6 +124,8 @@ static inline void tpm_log_dump(void *unused) tpm_cb_log_dump(); else if (CONFIG(TPM_LOG_TPM1)) tpm1_log_dump(); + else if (CONFIG(TPM_LOG_TPM2)) + tpm2_log_dump(); } /** diff --git a/src/security/tpm/tspi/crtm.h b/src/security/tpm/tspi/crtm.h index 241607757b76..ffa4867594ca 100644 --- a/src/security/tpm/tspi/crtm.h +++ b/src/security/tpm/tspi/crtm.h @@ -22,6 +22,19 @@ # define TPM_MEASURE_ALGO VB2_HASH_SHA256 #elif CONFIG(TPM_LOG_TPM1) # define TPM_MEASURE_ALGO VB2_HASH_SHA1 +#elif CONFIG(TPM_LOG_TPM2) +# if CONFIG(TPM_HASH_SHA1) +# define TPM_MEASURE_ALGO VB2_HASH_SHA1 +# endif +# if CONFIG(TPM_HASH_SHA256) +# define TPM_MEASURE_ALGO VB2_HASH_SHA256 +# endif +# if CONFIG(TPM_HASH_SHA384) +# define TPM_MEASURE_ALGO VB2_HASH_SHA384 +# endif +# if CONFIG(TPM_HASH_SHA512) +# define TPM_MEASURE_ALGO VB2_HASH_SHA512 +# endif #endif #if !defined(TPM_MEASURE_ALGO) diff --git a/src/security/tpm/tspi/log-tpm2.c b/src/security/tpm/tspi/log-tpm2.c new file mode 100644 index 000000000000..897ccedbff37 --- /dev/null +++ b/src/security/tpm/tspi/log-tpm2.c @@ -0,0 +1,230 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Unlike log.c this implements TPM log according to TPM2.0 specification + * rather then using coreboot-specific log format. + * + * First entry is in TPM1.2 format and serves as a header, the rest are in + * a newer (agile) format which supports SHA256 and multiple hashes, but we + * store only one hash. + * + * This is defined in "TCG EFI Protocol Specification". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static uint16_t tpmalg_from_vb2_hash(enum vb2_hash_algorithm hash_type) +{ + switch (hash_type) { + case VB2_HASH_SHA1: + return TPM2_ALG_SHA1; + case VB2_HASH_SHA256: + return TPM2_ALG_SHA256; + case VB2_HASH_SHA384: + return TPM2_ALG_SHA384; + case VB2_HASH_SHA512: + return TPM2_ALG_SHA512; + + default: + return 0xFF; + } +} + +void *tpm2_log_cbmem_init(void) +{ + static struct tpm_2_log_table *tclt; + if (tclt) + return tclt; + + if (cbmem_possibly_online()) { + size_t tpm_log_len; + struct tcg_efi_spec_id_event *hdr; + + tclt = cbmem_find(CBMEM_ID_TPM2_TCG_LOG); + if (tclt) + return tclt; + + tpm_log_len = sizeof(struct tpm_2_log_table) + + MAX_TPM_LOG_ENTRIES * sizeof(struct tpm_2_log_entry); + tclt = cbmem_add(CBMEM_ID_TPM2_TCG_LOG, tpm_log_len); + if (!tclt) + return NULL; + + memset(tclt, 0, tpm_log_len); + hdr = &tclt->header; + + hdr->event_type = htole32(EV_NO_ACTION); + hdr->event_size = htole32(33 + sizeof(tclt->vendor)); + strcpy((char *)hdr->signature, TPM_20_SPEC_ID_EVENT_SIGNATURE); + hdr->platform_class = htole32(0x00); // client platform + hdr->spec_version_minor = 0x00; + hdr->spec_version_major = 0x02; + hdr->spec_errata = 0x00; + hdr->uintn_size = 0x02; // 64-bit UINT + hdr->num_of_algorithms = htole32(1); + hdr->digest_sizes[0].alg_id = htole16(tpmalg_from_vb2_hash(TPM_MEASURE_ALGO)); + hdr->digest_sizes[0].digest_size = htole16(vb2_digest_size(TPM_MEASURE_ALGO)); + + tclt->vendor_info_size = sizeof(tclt->vendor); + tclt->vendor.reserved = 0; + tclt->vendor.version_major = TPM_20_LOG_VI_MAJOR; + tclt->vendor.version_minor = TPM_20_LOG_VI_MINOR; + tclt->vendor.magic = htole32(TPM_20_LOG_VI_MAGIC); + tclt->vendor.max_entries = htole16(MAX_TPM_LOG_ENTRIES); + tclt->vendor.num_entries = htole16(0); + tclt->vendor.entry_size = htole32(sizeof(struct tpm_2_log_entry)); + } + + return tclt; +} + +void tpm2_log_dump(void) +{ + int i, j; + struct tpm_2_log_table *tclt; + int hash_size; + const char *alg_name; + + tclt = tpm_log_init(); + if (!tclt) + return; + + hash_size = vb2_digest_size(TPM_MEASURE_ALGO); + alg_name = vb2_get_hash_algorithm_name(TPM_MEASURE_ALGO); + + printk(BIOS_INFO, "coreboot TPM 2.0 measurements:\n\n"); + for (i = 0; i < le16toh(tclt->vendor.num_entries); i++) { + struct tpm_2_log_entry *tce = &tclt->entries[i]; + + printk(BIOS_INFO, " PCR-%u ", le32toh(tce->pcr)); + + for (j = 0; j < hash_size; j++) + printk(BIOS_INFO, "%02x", tce->digest[j]); + + printk(BIOS_INFO, " %s [%s]\n", alg_name, tce->data); + } + printk(BIOS_INFO, "\n"); +} + +void tpm2_log_add_table_entry(const char *name, const uint32_t pcr, + enum vb2_hash_algorithm digest_algo, + const uint8_t *digest, + const size_t digest_len) +{ + struct tpm_2_log_table *tclt; + struct tpm_2_log_entry *tce; + + tclt = tpm_log_init(); + if (!tclt) { + printk(BIOS_WARNING, "TPM LOG: non-existent!\n"); + return; + } + + if (!name) { + printk(BIOS_WARNING, "TPM LOG: entry name not set\n"); + return; + } + + if (digest_algo != TPM_MEASURE_ALGO) { + printk(BIOS_WARNING, "TPM LOG: digest is of unsupported type: %s\n", + vb2_get_hash_algorithm_name(digest_algo)); + return; + } + + if (digest_len != vb2_digest_size(TPM_MEASURE_ALGO)) { + printk(BIOS_WARNING, "TPM LOG: digest has invalid length: %d\n", + (int)digest_len); + return; + } + + if (le16toh(tclt->vendor.num_entries) >= le16toh(tclt->vendor.max_entries)) { + printk(BIOS_WARNING, "TPM LOG: log table is full\n"); + return; + } + + tce = &tclt->entries[le16toh(tclt->vendor.num_entries)]; + tclt->vendor.num_entries = htole16(le16toh(tclt->vendor.num_entries) + 1); + + tce->pcr = htole32(pcr); + tce->event_type = htole32(EV_ACTION); + + tce->digest_count = htole32(1); + tce->digest_type = htole16(tpmalg_from_vb2_hash(TPM_MEASURE_ALGO)); + memcpy(tce->digest, digest, vb2_digest_size(TPM_MEASURE_ALGO)); + + tce->data_length = htole32(sizeof(tce->data)); + strncpy((char *)tce->data, name, sizeof(tce->data) - 1); + tce->data[sizeof(tce->data) - 1] = '\0'; +} + +int tpm2_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, + enum vb2_hash_algorithm *digest_algo, const char **event_name) +{ + struct tpm_2_log_table *tclt; + struct tpm_2_log_entry *tce; + + tclt = tpm_log_init(); + if (!tclt) + return 1; + + if (entry_idx < 0 || entry_idx >= le16toh(tclt->vendor.num_entries)) + return 1; + + tce = &tclt->entries[entry_idx]; + + *pcr = le32toh(tce->pcr); + *digest_data = tce->digest; + *digest_algo = TPM_MEASURE_ALGO; /* We validate algorithm on addition */ + *event_name = (char *)tce->data; + return 0; +} + +uint16_t tpm2_log_get_size(const void *log_table) +{ + const struct tpm_2_log_table *tclt = log_table; + return le16toh(tclt->vendor.num_entries); +} + +void tpm2_preram_log_clear(void) +{ + printk(BIOS_INFO, "TPM LOG: clearing the log\n"); + /* + * Pre-RAM log is only for internal use and isn't exported anywhere, hence it's header + * is not initialized. + */ + struct tpm_2_log_table *tclt = (struct tpm_2_log_table *)_tpm_log; + tclt->vendor.max_entries = htole16(MAX_TPM_LOG_ENTRIES); + tclt->vendor.num_entries = htole16(0); +} + +void tpm2_log_copy_entries(const void *from, void *to) +{ + const struct tpm_2_log_table *from_log = from; + struct tpm_2_log_table *to_log = to; + int i; + + for (i = 0; i < le16toh(from_log->vendor.num_entries); i++) { + struct tpm_2_log_entry *tce = + &to_log->entries[le16toh(to_log->vendor.num_entries)]; + to_log->vendor.num_entries = htole16(le16toh(to_log->vendor.num_entries) + 1); + + tce->pcr = from_log->entries[i].pcr; + tce->event_type = from_log->entries[i].event_type; + + tce->digest_count = from_log->entries[i].digest_count; + tce->digest_type = from_log->entries[i].digest_type; + memcpy(tce->digest, from_log->entries[i].digest, sizeof(tce->digest)); + + tce->data_length = from_log->entries[i].data_length; + memcpy(tce->data, from_log->entries[i].data, sizeof(tce->data)); + } +} diff --git a/src/security/tpm/tspi/logs.h b/src/security/tpm/tspi/logs.h index 417017628eea..2d802f0bc5fb 100644 --- a/src/security/tpm/tspi/logs.h +++ b/src/security/tpm/tspi/logs.h @@ -36,4 +36,19 @@ void tpm1_log_add_table_entry(const char *name, const uint32_t pcr, const size_t digest_len); void tpm1_log_dump(void); +/* TPM 2.0 log format */ + +void *tpm2_log_init(void); +void *tpm2_log_cbmem_init(void); +void tpm2_preram_log_clear(void); +uint16_t tpm2_log_get_size(const void *log_table); +void tpm2_log_copy_entries(const void *from, void *to); +int tpm2_log_get(int entry_idx, int *pcr, const uint8_t **digest_data, + enum vb2_hash_algorithm *digest_algo, const char **event_name); +void tpm2_log_add_table_entry(const char *name, const uint32_t pcr, + enum vb2_hash_algorithm digest_algo, + const uint8_t *digest, + const size_t digest_len); +void tpm2_log_dump(void); + #endif /* LOGS_H_ */ -- cgit v1.2.3