| /* |
| * Copyright (c) 2021 Arm Limited and Contributors. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * |
| * DRTM measurements into TPM PCRs. |
| * |
| * Authors: |
| * Lucian Paul-Trifu <lucian.paultrifu@gmail.com> |
| * |
| */ |
| #include <assert.h> |
| |
| #include <mbedtls/md.h> |
| |
| #include <common/debug.h> |
| #include <drivers/auth/mbedtls/mbedtls_common.h> |
| #include <lib/xlat_tables/xlat_tables_v2.h> |
| |
| #include "drtm_main.h" |
| #include "drtm_measurements.h" |
| |
| #define XLAT_PAGE_SIZE PAGE_SIZE |
| #if XLAT_PAGE_SIZE != DRTM_PAGE_SIZE |
| #warning "xlat library page size differs from DRTM page size;"\ |
| " mmap_add_dynamic_region() calls to the xlat library might fail" |
| #endif |
| |
| |
| #define DRTM_EVENT_ARM_BASE 0x9000U |
| #define DRTM_EVENT_TYPE(n) (DRTM_EVENT_ARM_BASE + (unsigned int)(n)) |
| |
| #define DRTM_EVENT_ARM_PCR_SCHEMA DRTM_EVENT_TYPE(1) |
| #define DRTM_EVENT_ARM_DCE DRTM_EVENT_TYPE(2) |
| #define DRTM_EVENT_ARM_DCE_PUBKEY DRTM_EVENT_TYPE(3) |
| #define DRTM_EVENT_ARM_DLME DRTM_EVENT_TYPE(4) |
| #define DRTM_EVENT_ARM_DLME_EP DRTM_EVENT_TYPE(5) |
| #define DRTM_EVENT_ARM_DEBUG_CONFIG DRTM_EVENT_TYPE(6) |
| #define DRTM_EVENT_ARM_NONSECURE_CONFIG DRTM_EVENT_TYPE(7) |
| #define DRTM_EVENT_ARM_DCE_SECONDARY DRTM_EVENT_TYPE(8) |
| #define DRTM_EVENT_ARM_TZFW DRTM_EVENT_TYPE(9) |
| #define DRTM_EVENT_ARM_SEPARATOR DRTM_EVENT_TYPE(10) |
| |
| #define DRTM_NULL_DATA ((unsigned char []){ 0 }) |
| #define DRTM_EVENT_ARM_SEP_DATA \ |
| (const unsigned char []){'A', 'R', 'M', '_', 'D', 'R', 'T', 'M' } |
| |
| #if !defined(DRTM_TPM_HASH_ALG) |
| /* |
| * This is an error condition. However, avoid emitting a further error message, |
| * since an explanatory one will have already been emitted by the header file. |
| */ |
| #define DRTM_TPM_HASH_ALG TPM_ALG_NONE |
| #define DRTM_MBEDTLS_HASH_ALG MBEDTLS_MD_NONE |
| #else |
| #define DRTM_MBEDTLS_HASH_ALG \ |
| EXPAND_AND_COMBINE(MBEDTLS_MD_SHA, DRTM_SHA_ALG) |
| #endif |
| |
| |
| #define CHECK_RC(rc, func_call) { \ |
| if ((rc)) { \ |
| ERROR("%s(): " #func_call "failed unexpectedly rc=%d\n", \ |
| __func__, rc); \ |
| panic(); \ |
| } \ |
| } |
| |
| |
| int drtm_measurements_init(void) |
| { |
| mbedtls_init(); |
| |
| return 0; |
| } |
| |
| #define calc_hash(data_ptr, data_len, output) \ |
| mbedtls_md(mbedtls_md_info_from_type((mbedtls_md_type_t)DRTM_MBEDTLS_HASH_ALG),\ |
| data_ptr, data_len, output) |
| |
| enum drtm_retc drtm_take_measurements(const struct_drtm_dl_args *a, |
| struct drtm_event_log *log) |
| { |
| struct tpm_log_1digest_shaX { |
| struct tpm_log_digests digests_1; |
| struct tpm_log_digest d; |
| unsigned char digest[MBEDTLS_MD_MAX_SIZE]; |
| } __packed __aligned(__alignof(struct tpm_log_digests)); |
| struct tpm_log_1digest_shaX digests_buf = { |
| .digests_1 = { |
| .count = 1, |
| }, |
| .d = (struct tpm_log_digest) { |
| .h_alg = DRTM_TPM_HASH_ALG, |
| .buf_bytes = sizeof(((struct tpm_log_1digest_shaX *)0)->digest), |
| }, |
| {0} |
| }; |
| int rc; |
| uint8_t pcr_schema; |
| tpm_log_info_t *const tpm_log_info = &log->tpm_log_info; |
| |
| rc = tpm_log_init(log->tpm_log_mem, sizeof(log->tpm_log_mem), |
| (enum tpm_hash_alg[]){ DRTM_TPM_HASH_ALG }, 1, |
| tpm_log_info); |
| CHECK_RC(rc, tpm_log_init); |
| |
| /** |
| * Measurements extended into PCR-17. |
| * |
| * PCR-17: Measure the DCE image. Extend digest of (char)0 into PCR-17 |
| * since the D-CRTM and the DCE are not separate. |
| */ |
| rc = calc_hash(DRTM_NULL_DATA, sizeof(DRTM_NULL_DATA), digests_buf.digest); |
| CHECK_RC(rc, calc_hash(NULL_DATA_1)); |
| |
| rc = tpm_log_add_event(tpm_log_info, DRTM_EVENT_ARM_DCE, TPM_PCR_17, |
| &digests_buf.digests_1, NULL, 0); |
| CHECK_RC(rc, tpm_log_add_event_arm_dce); |
| |
| /* PCR-17: Measure the PCR schema DRTM launch argument. */ |
| pcr_schema = DL_ARGS_GET_PCR_SCHEMA(a); |
| rc = calc_hash(&pcr_schema, sizeof(pcr_schema), digests_buf.digest); |
| CHECK_RC(rc, calc_hash(pcr_schema)); |
| |
| rc = tpm_log_add_event(tpm_log_info, DRTM_EVENT_ARM_PCR_SCHEMA, TPM_PCR_17, |
| &digests_buf.digests_1, NULL, 0); |
| CHECK_RC(rc, tpm_log_add_event(ARM_PCR_SCHEMA_17)); |
| |
| /* PCR-17: Measure the enable state of external-debug, and trace. */ |
| /* |
| * TODO: Measure the enable state of external-debug and trace. This should |
| * be returned through a platform-specific hook. |
| */ |
| |
| /* PCR-17: Measure the security lifecycle state. */ |
| /* |
| * TODO: Measure the security lifecycle state. This is an implementation- |
| * defined value, retrieved through an implementation-defined mechanisms. |
| */ |
| |
| /* |
| * PCR-17: Optionally measure the NWd DCE. |
| * It is expected that such subsequent DCE stages are signed and verified. |
| * Whether they are measured in addition to signing is implementation |
| * -defined. |
| * Here the choice is to not measure any NWd DCE, in favour of PCR value |
| * resilience to any NWd DCE updates. |
| */ |
| |
| /* PCR-17: End of DCE measurements. */ |
| rc = calc_hash(DRTM_EVENT_ARM_SEP_DATA, sizeof(DRTM_EVENT_ARM_SEP_DATA), |
| digests_buf.digest); |
| CHECK_RC(rc, calc_hash(ARM_SEP_DATA_17)); |
| |
| rc = tpm_log_add_event(tpm_log_info, DRTM_EVENT_ARM_SEPARATOR, TPM_PCR_17, |
| &digests_buf.digests_1, |
| DRTM_EVENT_ARM_SEP_DATA, sizeof(DRTM_EVENT_ARM_SEP_DATA)); |
| CHECK_RC(rc, tpm_log_add_event(ARM_SEPARATOR_17)); |
| |
| /** |
| * Measurements extended into PCR-18. |
| * |
| * PCR-18: Measure the PCR schema DRTM launch argument. |
| */ |
| pcr_schema = DL_ARGS_GET_PCR_SCHEMA(a); |
| rc = calc_hash(&pcr_schema, sizeof(pcr_schema), digests_buf.digest); |
| CHECK_RC(rc, calc_hash(pcr_schema)); |
| |
| rc = tpm_log_add_event(tpm_log_info, DRTM_EVENT_ARM_PCR_SCHEMA, TPM_PCR_18, |
| &digests_buf.digests_1, NULL, 0); |
| CHECK_RC(rc, tpm_log_add_event(ARM_PCR_SCHEMA_17)); |
| |
| /* |
| * PCR-18: Measure the public key used to verify DCE image(s) signatures. |
| * Extend digest of (char)0, since we do not expect the NWd DCE to be |
| * present. |
| */ |
| assert(a->dce_nwd_size == 0); |
| rc = calc_hash(DRTM_NULL_DATA, sizeof(DRTM_NULL_DATA), digests_buf.digest); |
| CHECK_RC(rc, calc_hash(NULL_DATA_2)); |
| |
| rc = tpm_log_add_event(tpm_log_info, DRTM_EVENT_ARM_DCE_PUBKEY, TPM_PCR_18, |
| &digests_buf.digests_1, NULL, 0); |
| CHECK_RC(rc, tpm_log_add_event(ARM_DCE_PUBKEY)); |
| |
| /* PCR-18: Measure the DLME image. */ |
| uintptr_t dlme_img_mapping; |
| size_t dlme_img_mapping_bytes; |
| |
| dlme_img_mapping_bytes = ALIGNED_UP(a->dlme_img_size, DRTM_PAGE_SIZE); |
| rc = mmap_add_dynamic_region_alloc_va(a->dlme_paddr + a->dlme_img_off, |
| &dlme_img_mapping, |
| dlme_img_mapping_bytes, MT_RO_DATA | MT_NS); |
| if (rc) { |
| WARN("DRTM: %s: mmap_add_dynamic_region() failed rc=%d\n", __func__, rc); |
| return INTERNAL_ERROR; |
| } |
| |
| rc = calc_hash((void *)dlme_img_mapping, a->dlme_img_size, |
| digests_buf.digest); |
| CHECK_RC(rc, calc_hash(dlme_img)); |
| |
| rc = tpm_log_add_event(tpm_log_info, DRTM_EVENT_ARM_DLME, TPM_PCR_18, |
| &digests_buf.digests_1, NULL, 0); |
| CHECK_RC(rc, tpm_log_add_event(ARM_DLME)); |
| |
| rc = mmap_remove_dynamic_region(dlme_img_mapping, dlme_img_mapping_bytes); |
| CHECK_RC(rc, mmap_remove_dynamic_region); |
| |
| /* PCR-18: Measure the DLME image entry point. */ |
| uint64_t dlme_img_ep = DL_ARGS_GET_DLME_ENTRY_POINT(a); |
| |
| rc = calc_hash((unsigned char *)&dlme_img_ep, sizeof(dlme_img_ep), |
| digests_buf.digest); |
| CHECK_RC(rc, calc_hash(dlme_img_ep_off)); |
| |
| rc = tpm_log_add_event(tpm_log_info, DRTM_EVENT_ARM_DLME_EP, TPM_PCR_18, |
| &digests_buf.digests_1, NULL, 0); |
| CHECK_RC(rc, tpm_log_add_event(ARM_DLME_EP)); |
| |
| /* PCR-18: End of DCE measurements. */ |
| rc = calc_hash(DRTM_EVENT_ARM_SEP_DATA, sizeof(DRTM_EVENT_ARM_SEP_DATA), |
| digests_buf.digest); |
| CHECK_RC(rc, calc_hash(ARM_SEP_DATA_18)); |
| |
| rc = tpm_log_add_event(tpm_log_info, DRTM_EVENT_ARM_SEPARATOR, TPM_PCR_18, |
| &digests_buf.digests_1, |
| DRTM_EVENT_ARM_SEP_DATA, sizeof(DRTM_EVENT_ARM_SEP_DATA)); |
| CHECK_RC(rc, tpm_log_add_event(ARM_SEPARATOR_18)); |
| |
| /* |
| * If the DCE is unable to log a measurement because there is no available |
| * space in the event log region, the DCE must extend a hash of the value |
| * 0xFF (1 byte in size) into PCR[17] and PCR[18] and enter remediation. |
| */ |
| return SUCCESS; |
| } |
| |
| void drtm_serialise_event_log(char *dst, const struct drtm_event_log *src, |
| size_t *event_log_size_out) |
| { |
| if (src) { |
| tpm_log_serialise(dst, &src->tpm_log_info, event_log_size_out); |
| } else { |
| if (dst != NULL) { |
| ERROR("%s(): cannot serialise the unexpected NULL event log\n", |
| __func__); |
| panic(); |
| } |
| if (event_log_size_out) { |
| /* |
| * DRTM Beta0: Note that the advertised minimum required size ought |
| * to be 64KiB, rather than a more economical size of our choosing. |
| */ |
| *event_log_size_out = DRTM_EVENT_LOG_INIT_SIZE; |
| } |
| } |
| } |