security/tpm: add TPM log format as per 2.0 spec

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 <michal.zygowski@3mdeb.com>
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/68748
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
This commit is contained in:
Sergii Dmytruk 2022-10-23 00:47:55 +03:00 committed by Felix Held
parent 4191dbf0c9
commit 53db677586
8 changed files with 410 additions and 1 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,70 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef __TPM2_LOG_SERIALIZED_H__
#define __TPM2_LOG_SERIALIZED_H__
#include <commonlib/bsd/tpm_log_defs.h>
#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

View File

@ -4,6 +4,7 @@
#define TSPI_H_
#include <security/tpm/tpm1_log_serialized.h>
#include <security/tpm/tpm2_log_serialized.h>
#include <security/tpm/tspi/logs.h>
#include <security/tpm/tss.h>
#include <commonlib/tpm_log_serialized.h>
@ -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();
}
/**

View File

@ -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)

View File

@ -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 <endian.h>
#include <console/console.h>
#include <security/tpm/tspi.h>
#include <security/tpm/tspi/crtm.h>
#include <security/tpm/tspi/logs.h>
#include <region_file.h>
#include <string.h>
#include <symbols.h>
#include <cbmem.h>
#include <vb2_sha.h>
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));
}
}

View File

@ -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_ */