From b8da5d8c1cbd7c6465e54f2d408a81aa2776750b Mon Sep 17 00:00:00 2001 From: Ruchika Gupta Date: Mon, 4 Apr 2022 13:32:02 +0530 Subject: [PATCH] core: Add support to parse TPM eventlog and extend PCRs Support for OP-TEE to parse the TPM eventlog. The eventlog format is based on TCG specification [1], so we call this TCG framework. To parse the eventlog and extend PCR's device is needed which supports PCR's. This device can be TPM or any other HSM which supports PCR like registers. Such a device can register itself as a TCG provider for PCR information and ability to extend the PCR's. [1] TCG PC Client Platform Firmware Profile Specification link: https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/ Signed-off-by: Ruchika Gupta Acked-by: Jens Wiklander Acked-by: Etienne Carriere --- core/include/kernel/tcg.h | 164 +++++++++++++++ core/kernel/sub.mk | 4 + core/kernel/tcg.c | 420 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 588 insertions(+) create mode 100644 core/include/kernel/tcg.h create mode 100644 core/kernel/tcg.c diff --git a/core/include/kernel/tcg.h b/core/include/kernel/tcg.h new file mode 100644 index 00000000000..f4f92d6ac54 --- /dev/null +++ b/core/include/kernel/tcg.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2022, Linaro Limited + * + * This file refers the following TCG specification. + * TCG PC Client Platform Firmware Profile Specification + */ + +#ifndef __KERNEL_TCG_H__ +#define __KERNEL_TCG_H__ + +#include +#include + +#define TPM2_EVENT_LOG_SIZE 4096 + +/* + * SHA1 Event Log Entry Format + * + * @pcr_index: PCRIndex event extended to + * @event_type: Type of event (see EFI specs) + * @digest: Value extended into PCR index + * @event_size: Size of event + * @event: Event data + */ +struct tcg_pcr_event { + uint32_t pcr_index; + uint32_t event_type; + uint8_t digest[TPM2_SHA1_DIGEST_SIZE]; + uint32_t event_size; + uint8_t event[]; +}; + +/* + * Crypto Agile Log Entry Format + * + * @pcr_index: PCRIndex event extended to + * @event_type: Type of event + * @digests: List of digests extended to PCR index + * @event_size: Size of the event data + * @event: Event data + */ +struct tcg_pcr_event2 { + uint32_t pcr_index; + uint32_t event_type; + struct tpml_digest_values digests; + uint32_t event_size; + uint8_t event[]; +} __packed; + +#define TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03 "Spec ID Event03" +#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2 2 +#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2 0 +#define TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_ERRATA_TPM2 2 + +/* + * struct TCG_EfiSpecIdEventAlgorithmSize - hashing algorithm information + * + * @algorithm_id: algorithm defined in enum tpm2_algorithms + * @digest_size: size of the algorithm + */ +struct tcg_efi_spec_id_event_algorithm_size { + uint16_t algorithm_id; + uint16_t digest_size; +}; + +/** + * struct TCG_EfiSpecIDEventStruct - content of the event log header + * + * @signature: signature, set to Spec ID Event03 + * @platform_class: class defined in TCG ACPI Specification + * Client Common Header. + * @spec_version_minor: minor version + * @spec_version_major: major version + * @spec_errata: major version + * @uintn_size: size of the efi_uintn_t fields used in various + * data structures used in this specification. + * 0x01 indicates uint32_t and 0x02 indicates + * uint64_t + * @number_of_algorithms: hashing algorithms used in this event log + * @digest_sizes: array of number_of_algorithms pairs + * 1st member defines the algorithm id + * 2nd member defines the algorithm size + */ +struct tcg_efi_spec_id_event { + 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 number_of_algorithms; + struct tcg_efi_spec_id_event_algorithm_size digest_sizes[]; +} __packed; + +/* + * event types, cf. + * "TCG Server Management Domain Firmware Profile Specification", + * rev 1.00, 2020-05-01 + */ +#define EV_NO_ACTION U(0x00000003) + +struct tcg_pcr_ops { + /* + * pcr_info() - get the supported, active PCRs and number of banks + * + * @selection_mask: bitmask with the algorithms supported + * @active_mask: bitmask with the active algorithms + * @num_pcr: number of PCR banks + * + */ + TEE_Result (*pcr_info)(uint32_t *selection_mask, uint32_t *active_mask, + uint32_t *num_pcr); + /* + * pcr_extend() - Extend a PCR for a given tpml_digest_values + * + * @pcr_idx: PCR Index + * @alg: algorithm of digest + * @digest: buffer containing the digest + * @digest_len: length of the buffer + * + * @Return: status code + */ + TEE_Result (*pcr_extend)(uint8_t pcr_idx, uint16_t alg, void *digest, + uint32_t digest_len); +}; + +#if defined(CFG_CORE_TCG_PROVIDER) + +/* + * Eventlog is the informational record of measurements. These measurements + * need to be extended to PCR's if the firmware passing the evenlog has + * not done so. The function parses the TPM evenlog information received + * from earlier firmware and extends the PCRs. The device supporting the + * PCRs needs to be registered with the TCG framework. + */ +TEE_Result tcg_process_fw_eventlog(void); + +/* + * TCG PC Client Platform Firmware profile Specification talks about + * eventlogging. These eventlogs need to be extended into PCR's. The PCRs + * are available with TPM's. There may be other HSM's which may support PCRs. + * The HSM's or TPM needs to provide interface to get PCR info and extend the + * digests into PCR's. The platform needs to register the PCR providers + * with the TCG framework. + */ +TEE_Result register_tcg_pcr_provider(struct tcg_pcr_ops *ops); + +#else + +static inline TEE_Result tcg_process_fw_eventlog(void) +{ + return TEE_ERROR_NOT_SUPPORTED; +} + +static inline TEE_Result +register_tcg_pcr_provider(struct tcg_pcr_ops *ops __unused) +{ + return TEE_ERROR_NOT_SUPPORTED; +} + +#endif + +#endif /* __KERNEL_TCG_H__ */ diff --git a/core/kernel/sub.mk b/core/kernel/sub.mk index 60961231152..59896137454 100644 --- a/core/kernel/sub.mk +++ b/core/kernel/sub.mk @@ -33,6 +33,10 @@ srcs-y += wait_queue.c srcs-y += notif.c srcs-y += thread.c +ifeq ($(CFG_CORE_TPM_EVENT_LOG),y) +srcs-$(CFG_CORE_TCG_PROVIDER) += tcg.c +endif + ifeq ($(CFG_WITH_USER_TA),y) srcs-y += user_ta.c srcs-$(CFG_REE_FS_TA) += ree_fs_ta.c diff --git a/core/kernel/tcg.c b/core/kernel/tcg.c new file mode 100644 index 00000000000..100432d5265 --- /dev/null +++ b/core/kernel/tcg.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2022, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct tcg_pcr_ops *pcr_provider; + +static TEE_Result tcg_get_pcr_info(uint32_t *selection_mask, + uint32_t *active_mask, uint32_t *num_pcr) +{ + if (!pcr_provider || !pcr_provider->pcr_info) + return TEE_ERROR_GENERIC; + + return pcr_provider->pcr_info(selection_mask, active_mask, num_pcr); +} + +static TEE_Result tcg_pcr_extend(uint32_t pcr_index, + struct tpml_digest_values *digest_list) +{ + uint32_t i = 0; + + if (!pcr_provider || !pcr_provider->pcr_extend) + return TEE_ERROR_GENERIC; + + for (i = 0; i < digest_list->count; i++) { + uint32_t alg = digest_list->digests[i].hash_alg; + uint8_t *digest = (uint8_t *)&digest_list->digests[i].digest; + + if (pcr_provider->pcr_extend(pcr_index, alg, digest, + tpm2_get_alg_len(alg))) { + EMSG("Failed to extend PCR"); + return TEE_ERROR_COMMUNICATION; + } + } + + return TEE_SUCCESS; +} + +static uint32_t tcg_event_final_size(struct tpml_digest_values *digest_list) +{ + uint32_t len = 0; + size_t i = 0; + + len = offsetof(struct tcg_pcr_event2, digests); + len += offsetof(struct tpml_digest_values, digests); + for (i = 0; i < digest_list->count; i++) { + uint16_t hash_alg = digest_list->digests[i].hash_alg; + + len += offsetof(struct tpmt_ha, digest); + len += tpm2_get_alg_len(hash_alg); + } + len += sizeof(uint32_t); /* tcg_pcr_event2 event_size*/ + + return len; +} + +/* + * tcg_parse_event_log_header() - Parse and verify the event log header fields + * + * @buffer: Pointer to the start of the eventlog + * @size: Size of the eventlog + * @pos: Return offset of the next event in buffer right + * after the event header i.e specID + * + * Return: status code + */ +static TEE_Result tcg_parse_event_log_header(void *buffer, uint32_t size, + uint32_t *pos) +{ + struct tcg_pcr_event *event_header = (struct tcg_pcr_event *)buffer; + uint32_t i = 0; + + if (size < sizeof(*event_header)) + return TEE_ERROR_BAD_FORMAT; + + if (get_unaligned_le32(&event_header->pcr_index) != 0 || + get_unaligned_le32(&event_header->event_type) != EV_NO_ACTION) + return TEE_ERROR_BAD_FORMAT; + + for (i = 0; i < sizeof(event_header->digest); i++) { + if (event_header->digest[i]) + return TEE_ERROR_BAD_FORMAT; + } + + *pos += sizeof(*event_header); + + return TEE_SUCCESS; +} + +/* + * tcg_parse_specid_event() - Parse and verify the specID Event in the eventlog + * + * @buffer: Pointer to the start of the eventlog + * @log_size: Size of the eventlog + * @pos: [in] Offset of specID event in the eventlog buffer + * [out] Return offset of the next event in the buffer + * after the specID + * @digest_list: list of digests in the event + * + * Return: status code + */ +static TEE_Result tcg_parse_specid_event(void *buffer, uint32_t log_size, + uint32_t *pos, + struct tpml_digest_values *digest_list) +{ + struct tcg_efi_spec_id_event *spec_event = NULL; + struct tcg_pcr_event *event_header = buffer; + uint8_t vendor_sz = 0; + uint16_t hash_alg = 0; + uint32_t active = 0; + uint32_t alg_count = 0; + uint32_t i = 0; + uint32_t pcr_count = 0; + uint32_t spec_active = 0; + uint32_t supported = 0; + size_t spec_event_size = 0; + + if ((*pos + sizeof(*spec_event)) > log_size) + return TEE_ERROR_BAD_FORMAT; + + /* Check specID event data */ + spec_event = (struct tcg_efi_spec_id_event *)((uintptr_t)buffer + *pos); + /* Check for signature */ + if (memcmp(spec_event->signature, TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03, + sizeof(TCG_EFI_SPEC_ID_EVENT_SIGNATURE_03))) { + EMSG("specID Event: Signature mismatch"); + return TEE_ERROR_BAD_FORMAT; + } + + if (spec_event->spec_version_minor != + TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MINOR_TPM2 || + spec_event->spec_version_major != + TCG_EFI_SPEC_ID_EVENT_SPEC_VERSION_MAJOR_TPM2) + return TEE_ERROR_BAD_FORMAT; + + if (!spec_event->number_of_algorithms) { + EMSG("specID Event: Number of algorithms incorrect"); + return TEE_ERROR_BAD_FORMAT; + } + + alg_count = spec_event->number_of_algorithms; + + if (alg_count > TPM2_NUM_PCR_BANKS) + return TEE_ERROR_BAD_FORMAT; + + if (tcg_get_pcr_info(&supported, &active, &pcr_count)) + return TEE_ERROR_COMMUNICATION; + + digest_list->count = 0; + /* + * We have to take care that the sequence of algorithms that we record + * in digest_list matches the sequence in eventlog. + */ + for (i = 0; i < alg_count; i++) { + hash_alg = + get_unaligned_le16(&spec_event->digest_sizes[i].algorithm_id); + + if (!(supported & tpm2_alg_to_tcg_mask(hash_alg))) { + EMSG("specID Event: Unsupported algorithm"); + return TEE_ERROR_BAD_FORMAT; + } + digest_list->digests[digest_list->count++].hash_alg = hash_alg; + + spec_active |= tpm2_alg_to_tcg_mask(hash_alg); + } + + /* + * TCG specification expects the event log to have hashes for all + * active PCR's + */ + if (spec_active != active) { + /* + * Previous stage bootloader should know all the active PCR's + * and use them in the Eventlog. + */ + EMSG("specID Event: All active hash alg not present"); + return TEE_ERROR_BAD_FORMAT; + } + + /* + * the size of the spec event and placement of vendor_info_size + * depends on supported algorithms + */ + spec_event_size = + offsetof(struct tcg_efi_spec_id_event, digest_sizes) + + alg_count * sizeof(spec_event->digest_sizes[0]); + + if (*pos + spec_event_size >= log_size) + return TEE_ERROR_BAD_FORMAT; + + vendor_sz = *(uint8_t *)((uintptr_t)buffer + *pos + spec_event_size); + + spec_event_size += sizeof(vendor_sz) + vendor_sz; + *pos += spec_event_size; + + if (get_unaligned_le32(&event_header->event_size) != spec_event_size) { + EMSG("specID event: header event size mismatch"); + /* Right way to handle this can be to call SetActive PCR's */ + return TEE_ERROR_BAD_FORMAT; + } + + return TEE_SUCCESS; +} + +/* + * tcg_parse_event() - Parse the event in the eventlog + * + * @buffer: Pointer to the start of the eventlog + * @log_size: Size of the eventlog + * @offset: [in] Offset of the event in the eventlog buffer + * [out] Return offset of the next event in the buffer + * @digest_list: list of digests in the event. + * @pcr Index of the PCR in the event + * + * Return: status code + */ +static TEE_Result tcg_parse_event(void *buffer, uint32_t log_size, + uint32_t *offset, + struct tpml_digest_values *digest_list, + uint32_t *pcr) +{ + struct tcg_pcr_event2 *event = NULL; + uint32_t count = 0, size = 0, event_size = 0; + uint32_t i = 0; + size_t pos = 0; + + event_size = tcg_event_final_size(digest_list); + if (*offset >= log_size || *offset + event_size > log_size) { + EMSG("Event exceeds log size"); + return TEE_ERROR_BAD_FORMAT; + } + + event = (struct tcg_pcr_event2 *)((uintptr_t)buffer + *offset); + *pcr = get_unaligned_le32(&event->pcr_index); + + /* get the count */ + count = get_unaligned_le32(&event->digests.count); + if (count != digest_list->count) + return TEE_ERROR_BAD_FORMAT; + + /* + * Element 'digests' of type tpml_digest_values in struct tcg_pcr_event2 + * is a list of digests. The count of digests in this list depends on + * the number of active PCR banks. Further this list contains elements + * of type tpmt_ha whose size depends on the hash algorithm. So, the + * position of each of the element in the list (of type tpmt_ha) needs + * to be calculated. + */ + pos = offsetof(struct tcg_pcr_event2, digests); + + /* Position of first element of type tpmt_ha in the digest list */ + pos += offsetof(struct tpml_digest_values, digests); + + for (i = 0; i < digest_list->count; i++) { + uint16_t alg = 0; + uint16_t hash_alg = digest_list->digests[i].hash_alg; + uint8_t *digest = (uint8_t *)&digest_list->digests[i].digest; + + /* Element hash_alg in struct tpmt_ha */ + alg = get_unaligned_le16((void *)((uintptr_t)event + pos)); + + /* + * The sequence of algorithm must match that from digest list + * in spec ID event. + */ + if (alg != hash_alg) + return TEE_ERROR_BAD_FORMAT; + + pos += offsetof(struct tpmt_ha, digest); + memcpy(digest, (void *)((uintptr_t)event + pos), + tpm2_get_alg_len(hash_alg)); + + /* Calculate position of next tpmt_ha element in the event */ + pos += tpm2_get_alg_len(hash_alg); + } + + /* WARNING - Since size of digest lists can vary, the + * position of event and event_size elements in tcg_pcr_event2 needs to + * be determined dynamically. + */ + size = get_unaligned_le32((void *)((uintptr_t)event + pos)); + event_size += size; + pos += sizeof(uint32_t); /* tcg_pcr_event2 event_size*/ + pos += size; + + /* make sure the calculated buffer is what we checked against */ + if (pos != event_size) + return TEE_ERROR_BAD_FORMAT; + + if (pos > log_size) + return TEE_ERROR_BAD_FORMAT; + + *offset += pos; + + return TEE_SUCCESS; +} + +/** + * tcg_process_fw_eventlog() - Parse the eventlog and extend the PCR's + * + * Return: status code + */ +TEE_Result tcg_process_fw_eventlog(void) +{ + void *buffer = NULL; + void *tmp = NULL; + uint32_t i = 0, pcr = 0, pos = 0; + size_t digest_list_sz = 0; + size_t sz = TPM2_EVENT_LOG_SIZE; + TEE_Result ret = TEE_SUCCESS; + struct tpml_digest_values *digest_list = NULL; + + if (!pcr_provider) { + EMSG("No provider available for PCR's"); + return TEE_ERROR_GENERIC; + } + + buffer = malloc(TPM2_EVENT_LOG_SIZE); + if (!buffer) { + EMSG("Error allocating mem"); + return TEE_ERROR_OUT_OF_MEMORY; + } + + ret = tpm_get_event_log(buffer, &sz); + if (ret == TEE_ERROR_SHORT_BUFFER) { + tmp = realloc(buffer, sz); + if (!tmp) + goto out; + + buffer = tmp; + /* Try to get the eventlog again */ + ret = tpm_get_event_log(buffer, &sz); + } + + if (ret) + goto out; + + pos = 0; + /* Parse the eventlog to check for its validity */ + ret = tcg_parse_event_log_header(buffer, sz, &pos); + if (ret) { + EMSG("Error parsing event log header"); + goto out; + } + + digest_list_sz = sizeof(struct tpml_digest_values); + digest_list = malloc(digest_list_sz); + if (!digest_list) { + EMSG("Error allocating mem"); + ret = TEE_ERROR_OUT_OF_MEMORY; + goto out; + } + + /* Populate the digest_list with digest algs by parsing specid event */ + ret = tcg_parse_specid_event(buffer, sz, &pos, digest_list); + if (ret) { + EMSG("Error parsing SPEC ID Event"); + goto out; + } + + while (pos < sz) { + ret = tcg_parse_event(buffer, sz, &pos, digest_list, + &pcr); + if (ret) { + EMSG("Error parsing event"); + goto out; + } + + ret = tcg_pcr_extend(pcr, digest_list); + if (ret != TEE_SUCCESS) { + EMSG("Error in extending PCR"); + goto out; + } + + /* Clear the digest for next event */ + for (i = 0; i < digest_list->count; i++) { + uint16_t hash_alg = digest_list->digests[i].hash_alg; + uint8_t *digest = + (uint8_t *)&digest_list->digests[i].digest; + + /* Clear the digest in the digest_list */ + memset(digest, 0, tpm2_get_alg_len(hash_alg)); + } + } + +out: + free(digest_list); + free(buffer); + + return ret; +} + +boot_final(tcg_process_fw_eventlog); + +TEE_Result register_tcg_pcr_provider(struct tcg_pcr_ops *ops) +{ + /* Only 1 PCR provider is supported */ + if (pcr_provider) { + EMSG("Provider already registered"); + return TEE_ERROR_GENERIC; + } + + if (!ops) + return TEE_ERROR_GENERIC; + + pcr_provider = ops; + + return TEE_SUCCESS; +}