diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index 93e62a8c5f4..92bc60b2058 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -7,10 +7,6 @@ ifneq ($(CFG_INSECURE),y) CFG_QCOM_QFPROM_FUSEPROV ?= y endif -ifeq ($(CFG_QCOM_QFPROM_FUSEPROV),y) -$(call force,CFG_QCOM_QFPROM,y) -endif - CFG_QCOM_PAS_PTA ?= y ifeq ($(CFG_QCOM_PAS_PTA),y) @@ -20,7 +16,23 @@ ifeq ($(CFG_QCOM_PAS_PTA),y) CFG_RESERVED_VASPACE_SIZE ?= (256 * 1024 * 1024) CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe CFG_QCOM_PAS_AUTH ?= y -CFG_PAS_MD_SLOTS = 8 endif CFG_QCOM_HWKM ?= y + +ifeq ($(CFG_QCOM_PAS_AUTH),y) +$(call force,CFG_QCOM_FUSE_PTA,y) +CFG_PAS_MD_SLOTS = 8 +# This chip's OEM_CONFIG2 fuse row has a per-root-cert hash function +# select bit; targets without it always use SHA-384. +$(call force,CFG_QCOM_SEGMENT_HASH_SELECT,y) +# OEM-provisioned multi-root-certificate activation/revocation bitmasks +# (bit N = root cert N), blown to fuses at boot. 0 (default) provisions +# nothing. +CFG_QCOM_MRC_ACTIVATION_LIST ?= 0 +CFG_QCOM_MRC_REVOCATION_LIST ?= 0 +endif + +ifneq ($(filter y,$(CFG_QCOM_QFPROM_FUSEPROV) $(CFG_QCOM_FUSE_PTA)),) +$(call force,CFG_QCOM_QFPROM,y) +endif diff --git a/core/drivers/qcom/qfprom/lemans/qfprom_target.h b/core/drivers/qcom/qfprom/lemans/qfprom_target.h index 8207330b4af..6dd244920cd 100644 --- a/core/drivers/qcom/qfprom/lemans/qfprom_target.h +++ b/core/drivers/qcom/qfprom/lemans/qfprom_target.h @@ -228,4 +228,57 @@ enum qfprom_perm_bit_pos { #define OEM_SPARE_30_PERM_MASK BIT(OEM_SPARE_30) #define OEM_SPARE_31_PERM_MASK BIT(OEM_SPARE_31) +#define PIL_ARB_EN_OFFSET 0x01bc +#define PIL_ARB_EN_BMSK 0x00000004 +#define PIL_ARB_LSB_OFFSET 0x0278 +#define PIL_ARB_LSB_BMSK 0xffffffff +#define PIL_ARB_MSB_OFFSET 0x027c +#define PIL_ARB_MSB_BMSK 0x0fffffff +#define PIL_ARB_MSB_ENABLED 1 +#define PIL_ARB_RAW_ADDR ANTI_ROLLBACK_9_ADDR +#define PIL_ARB_LSB_MAX_VERSION 32 +#define PIL_ARB_MSB_MAX_VERSION 28 + +#define OEM_CONFIG0_OFFSET 0x604c +#define ROOT_CERT_TOTAL_NUM_BMSK 0x00060000 +#define ROOT_CERT_TOTAL_NUM_SHFT 17 + +#define OEM_CONFIG2_OFFSET 0x6054 +#define EKU_ENFORCEMENT_EN_SHFT 30 +#define SEGMENT_HASH_FUNCTION_SELECT0_SHFT 16 + +#define SECURE_BOOT_APPS_OFFSET 0x606c +#define SECURE_BOOT_AUTH_EN_BMSK BIT(5) +#define SECURE_BOOT_USE_SERIAL_NUM_BMSK BIT(6) +#define SECURE_BOOT_PK_HASH_IN_FUSE_BMSK BIT(4) + +#define JTAG_ID_OFFSET 0x6130 +#define JTAG_ID_AUTH_BMSK GENMASK_32(27, 0) + +#define SERIAL_NUM_OFFSET 0x6134 + +#define OEM_ID_OFFSET 0x6138 +#define OEM_ID_BMSK GENMASK_32(31, 16) +#define OEM_ID_SHFT 16 +#define MODEL_ID_BMSK GENMASK_32(15, 0) +#define MODEL_ID_SHFT 0 + +#define PK_HASH0_OFFSET 0x6190 +#define QFPROM_ROOT_OF_TRUST_BYTE_SIZE 48 + +#define MRC_STICKY_BIT_OFFSET 0x61f0 +#define MRC_STICKY_BIT_BMSK BIT(3) + +#define MRC_ACTIVATION_LIST_OFFSET 0x6268 +#define MRC_REVOCATION_LIST_OFFSET 0x6270 +#define MRC_ROOT_CERT_LIST_BMSK 0x0000000f + +/* MRC_2_0 region: activation = row 0, revocation = row 1. */ +#define MRC_ACTIVATION_LIST_RAW_ADDR MRC_2_0_ADDR +#define MRC_REVOCATION_LIST_RAW_ADDR (MRC_2_0_ADDR + 0x8) + +#define TCSR_SOC_HW_VERSION_ADDR 0x01FC8000 +#define SOC_HW_VERSION_FAM_DEV_BMSK 0xffff0000 +#define SOC_HW_VERSION_FAM_DEV_SHFT 16 + #endif /* __QFPROM_TARGET_H__ */ diff --git a/core/drivers/qcom/qfprom/qfprom_secboot.c b/core/drivers/qcom/qfprom/qfprom_secboot.c new file mode 100644 index 00000000000..186bd550f24 --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_secboot.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qfprom_priv.h" +#include "qfprom_target.h" + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, TCSR_SOC_HW_VERSION_ADDR, + CORE_MMU_PGDIR_SIZE); + +static TEE_Result read_sense_reg(uint32_t offset, uint32_t *out) +{ + struct qfprom_context *drv = qfprom_get_context(); + + if (!drv->raw_base_va) + return TEE_ERROR_BAD_STATE; + + *out = io_read32(drv->raw_base_va + offset); + + return TEE_SUCCESS; +} + +TEE_Result qcom_secboot_is_enabled(bool *enabled) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t val = 0; + + if (!enabled) + return TEE_ERROR_BAD_PARAMETERS; + + res = read_sense_reg(SECURE_BOOT_APPS_OFFSET, &val); + if (res) + return res; + + *enabled = (val & SECURE_BOOT_AUTH_EN_BMSK) != 0; + + return TEE_SUCCESS; +} + +TEE_Result qcom_secboot_is_use_serial_num_enabled(bool *enabled) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t val = 0; + + if (!enabled) + return TEE_ERROR_BAD_PARAMETERS; + + res = read_sense_reg(SECURE_BOOT_APPS_OFFSET, &val); + if (res) + return res; + + *enabled = (val & SECURE_BOOT_USE_SERIAL_NUM_BMSK) != 0; + + return TEE_SUCCESS; +} + +static TEE_Result read_corr_reg(uint32_t offset, uint32_t *out) +{ + struct qfprom_context *drv = qfprom_get_context(); + + if (!drv->corr_base_va) + return TEE_ERROR_BAD_STATE; + + *out = io_read32(drv->corr_base_va + offset); + + return TEE_SUCCESS; +} + +static TEE_Result write_sense_reg(uint32_t offset, uint32_t val) +{ + struct qfprom_context *drv = qfprom_get_context(); + + if (!drv->raw_base_va) + return TEE_ERROR_BAD_STATE; + + io_write32(drv->raw_base_va + offset, val); + + return TEE_SUCCESS; +} + +/* + * Count set bits in @bitmask. + * Cannot use __builtin_popcount(): OP-TEE core builds AArch64 with + * -mgeneral-regs-only, so the compiler cannot inline the NEON sequence + * and falls back to a libgcc call core does not link against. + */ +static uint32_t popcount32(uint32_t bitmask) +{ + uint32_t nb = 0; + + while (bitmask) { + if (bitmask & 1) + nb++; + bitmask >>= 1; + } + + return nb; +} + +TEE_Result qcom_secboot_get_pil_rollback_version(uint32_t *version) +{ + TEE_Result res = TEE_ERROR_GENERIC; + bool secboot = false; + uint32_t lsb = 0; + uint32_t msb = 0; + uint32_t en = 0; + + if (!version) + return TEE_ERROR_BAD_PARAMETERS; + + *version = 0; + + res = qcom_secboot_is_enabled(&secboot); + if (res) + return res; + if (!secboot) + return TEE_SUCCESS; + + res = read_corr_reg(PIL_ARB_EN_OFFSET, &en); + if (res) + return res; + if (!(en & PIL_ARB_EN_BMSK)) + return TEE_SUCCESS; + + res = read_corr_reg(PIL_ARB_LSB_OFFSET, &lsb); + if (res) + return res; + + *version = popcount32(lsb & PIL_ARB_LSB_BMSK); + + if (PIL_ARB_MSB_ENABLED) { + res = read_corr_reg(PIL_ARB_MSB_OFFSET, &msb); + if (res) + return res; + *version += popcount32(msb & PIL_ARB_MSB_BMSK); + } + + return TEE_SUCCESS; +} + +static uint32_t unary_mask(uint32_t n) +{ + if (n >= 32) + return 0xffffffffu; + if (!n) + return 0; + return GENMASK_32(n - 1, 0); +} + +TEE_Result qcom_secboot_blow_pil_rollback_version(uint32_t version) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t row[2] = { }; + uint32_t cur_lsb = 0; + uint32_t cur_msb = 0; + bool secboot = false; + uint32_t lsb_n = 0; + uint32_t msb_n = 0; + uint32_t cur = 0; + uint32_t en = 0; + + res = qcom_secboot_is_enabled(&secboot); + if (res) + return res; + if (!secboot) + return TEE_SUCCESS; + + res = read_corr_reg(PIL_ARB_EN_OFFSET, &en); + if (res) + return res; + if (!(en & PIL_ARB_EN_BMSK)) + return TEE_SUCCESS; + + res = read_corr_reg(PIL_ARB_LSB_OFFSET, &cur_lsb); + if (res) + return res; + cur = popcount32(cur_lsb & PIL_ARB_LSB_BMSK); + if (PIL_ARB_MSB_ENABLED) { + res = read_corr_reg(PIL_ARB_MSB_OFFSET, &cur_msb); + if (res) + return res; + cur += popcount32(cur_msb & PIL_ARB_MSB_BMSK); + } + + if (version <= cur) + return TEE_SUCCESS; + + if (version > PIL_ARB_LSB_MAX_VERSION + PIL_ARB_MSB_MAX_VERSION) { + EMSG("PAS ARB: version %#"PRIx32" exceeds fuse capacity", + version); + return TEE_ERROR_BAD_PARAMETERS; + } + + /* qfprom_write_row() only blows 0->1 bits; OR in current contents. */ + lsb_n = MIN(version, (uint32_t)PIL_ARB_LSB_MAX_VERSION); + if (version > PIL_ARB_LSB_MAX_VERSION) + msb_n = version - PIL_ARB_LSB_MAX_VERSION; + + row[0] = (cur_lsb & PIL_ARB_LSB_BMSK) | unary_mask(lsb_n); + row[1] = (cur_msb & PIL_ARB_MSB_BMSK) | unary_mask(msb_n); + + res = qfprom_hw_init(); + if (res) + return res; + + res = qfprom_write_row(PIL_ARB_RAW_ADDR, row); + qfprom_hw_deinit(); + if (res) { + EMSG("PAS ARB: fuse write failed: %#"PRIx32, res); + return res; + } + + DMSG("PAS ARB: advanced device version %#"PRIx32" -> %#"PRIx32, cur, + version); + + return TEE_SUCCESS; +} + +TEE_Result qcom_secboot_get_root_of_trust(uint8_t *hash, size_t len) +{ + size_t off = 0; + + if (!hash) + return TEE_ERROR_BAD_PARAMETERS; + + if (len != QFPROM_ROOT_OF_TRUST_BYTE_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + for (off = 0; off < len; off += sizeof(uint32_t)) { + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t word = 0; + + res = read_sense_reg(PK_HASH0_OFFSET + off, &word); + if (res) + return res; + + memcpy(hash + off, &word, sizeof(word)); + } + + return TEE_SUCCESS; +} + +TEE_Result qcom_secboot_get_device_ids(struct qcom_secboot_device_ids *ids) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t val = 0; + + if (!ids) + return TEE_ERROR_BAD_PARAMETERS; + + res = read_sense_reg(OEM_ID_OFFSET, &val); + if (res) + return res; + ids->oem_id = (val & OEM_ID_BMSK) >> OEM_ID_SHFT; + ids->model_id = (val & MODEL_ID_BMSK) >> MODEL_ID_SHFT; + + res = read_sense_reg(JTAG_ID_OFFSET, &val); + if (res) + return res; + ids->jtag_id = val & JTAG_ID_AUTH_BMSK; + + res = read_sense_reg(SERIAL_NUM_OFFSET, &ids->serial_num); + if (res) + return res; + + return TEE_SUCCESS; +} + +#define SEGMENT_HASH_ROOT_CERT_SEL_MAX 3U + +/* + * Return the hash algorithm's digest size (SHA-256 or SHA-384) selected + * for the root cert at @root_cert_sel, per the OEM_CONFIG2 fuse row. + */ +TEE_Result qcom_secboot_get_segment_hash_len(uint32_t root_cert_sel, + uint32_t *hash_len) +{ + if (!hash_len) + return TEE_ERROR_BAD_PARAMETERS; + + if (root_cert_sel > SEGMENT_HASH_ROOT_CERT_SEL_MAX) + return TEE_ERROR_BAD_PARAMETERS; + + if (IS_ENABLED(CFG_QCOM_SEGMENT_HASH_SELECT)) { + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t val = 0; + + res = read_sense_reg(OEM_CONFIG2_OFFSET, &val); + if (res) + return res; + + if (val & BIT32(SEGMENT_HASH_FUNCTION_SELECT0_SHFT + + root_cert_sel)) + *hash_len = TEE_SHA256_HASH_SIZE; + else + *hash_len = TEE_SHA384_HASH_SIZE; + } else { + *hash_len = TEE_SHA384_HASH_SIZE; + } + + return TEE_SUCCESS; +} + +TEE_Result qcom_secboot_get_eku_enforcement_en(bool *enabled) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t val = 0; + + if (!enabled) + return TEE_ERROR_BAD_PARAMETERS; + + res = read_sense_reg(OEM_CONFIG2_OFFSET, &val); + if (res) + return res; + + *enabled = val & BIT32(EKU_ENFORCEMENT_EN_SHFT); + + return TEE_SUCCESS; +} + +#define SECBOOT_MAX_NUM_ROOT_CERTS 4U + +TEE_Result qcom_secboot_get_mrc_info(bool *root_sel_enabled, + uint32_t *num_roots, + uint32_t *activation_list, + uint32_t *revocation_list) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t total = 0; + uint32_t val = 0; + + if (!root_sel_enabled || !num_roots || !activation_list || + !revocation_list) + return TEE_ERROR_BAD_PARAMETERS; + + *root_sel_enabled = false; + *num_roots = 1; + *activation_list = 0; + *revocation_list = 0; + + res = read_sense_reg(SECURE_BOOT_APPS_OFFSET, &val); + if (res) + return res; + if (!(val & SECURE_BOOT_PK_HASH_IN_FUSE_BMSK)) + return TEE_SUCCESS; + + res = read_sense_reg(OEM_CONFIG0_OFFSET, &val); + if (res) + return res; + + total = ((val & ROOT_CERT_TOTAL_NUM_BMSK) >> ROOT_CERT_TOTAL_NUM_SHFT) + + 1; + if (total > SECBOOT_MAX_NUM_ROOT_CERTS) + return TEE_ERROR_BAD_STATE; + if (total <= 1) + return TEE_SUCCESS; + + res = read_sense_reg(MRC_ACTIVATION_LIST_OFFSET, &val); + if (res) + return res; + *activation_list = val & MRC_ROOT_CERT_LIST_BMSK; + + res = read_sense_reg(MRC_REVOCATION_LIST_OFFSET, &val); + if (res) + return res; + *revocation_list = val & MRC_ROOT_CERT_LIST_BMSK; + + *num_roots = total; + *root_sel_enabled = true; + + return TEE_SUCCESS; +} + +#ifdef CFG_QCOM_PAS_AUTH +/* + * The MRC row is write-protected once the sticky bit is set, so skip + * straight to the sticky-bit check rather than attempting (and expecting + * to fail) a write on every normal boot. + */ +static TEE_Result qcom_secboot_blow_mrc_fuses(uint32_t activation_list, + uint32_t revocation_list) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t row[2] = { }; + uint32_t sticky = 0; + + if (activation_list & ~MRC_ROOT_CERT_LIST_BMSK || + revocation_list & ~MRC_ROOT_CERT_LIST_BMSK) { + EMSG("MRC: activation/revocation list out of range: %#"PRIx32 + "/%#"PRIx32, activation_list, revocation_list); + return TEE_ERROR_BAD_PARAMETERS; + } + + res = read_sense_reg(MRC_STICKY_BIT_OFFSET, &sticky); + if (res) + return res; + + if (sticky & MRC_STICKY_BIT_BMSK) { + DMSG("MRC: fuse row locked for this boot, skipping"); + return TEE_SUCCESS; + } + + res = qfprom_hw_init(); + if (res) + return res; + + /* Antifuse writes only set bits; safe to write the bitmap directly. */ + row[0] = activation_list; + res = qfprom_write_row(MRC_ACTIVATION_LIST_RAW_ADDR, row); + if (res) { + DMSG("MRC: activation list fuse write failed: %#"PRIx32, res); + goto out; + } + + row[0] = revocation_list; + res = qfprom_write_row(MRC_REVOCATION_LIST_RAW_ADDR, row); + if (res) { + DMSG("MRC: revocation list fuse write failed: %#"PRIx32, res); + goto out; + } + + res = write_sense_reg(MRC_STICKY_BIT_OFFSET, MRC_STICKY_BIT_BMSK); + if (res) + EMSG("MRC: failed to set sticky bit: %#"PRIx32, res); + +out: + qfprom_hw_deinit(); + return res; +} + +static TEE_Result qcom_secboot_provision_mrc_fuses(void) +{ + if (!CFG_QCOM_MRC_ACTIVATION_LIST && !CFG_QCOM_MRC_REVOCATION_LIST) + return TEE_SUCCESS; + + return qcom_secboot_blow_mrc_fuses(CFG_QCOM_MRC_ACTIVATION_LIST, + CFG_QCOM_MRC_REVOCATION_LIST); +} +driver_init(qcom_secboot_provision_mrc_fuses); +#endif /* CFG_QCOM_PAS_AUTH */ + +TEE_Result qcom_secboot_get_soc_hw_version(uint32_t *fam_dev) +{ + static vaddr_t soc_hw_version_addr; + uint32_t val = 0; + + if (!fam_dev) + return TEE_ERROR_BAD_PARAMETERS; + + if (!soc_hw_version_addr) { + soc_hw_version_addr = + (vaddr_t)phys_to_virt(TCSR_SOC_HW_VERSION_ADDR, + MEM_AREA_IO_SEC, + sizeof(uint32_t)); + if (!soc_hw_version_addr) + return TEE_ERROR_GENERIC; + } + + val = io_read32(soc_hw_version_addr); + *fam_dev = (val & SOC_HW_VERSION_FAM_DEV_BMSK) >> + SOC_HW_VERSION_FAM_DEV_SHFT; + + return TEE_SUCCESS; +} diff --git a/core/drivers/qcom/qfprom/sub.mk b/core/drivers/qcom/qfprom/sub.mk index f4bde830f83..5c83e7bba69 100644 --- a/core/drivers/qcom/qfprom/sub.mk +++ b/core/drivers/qcom/qfprom/sub.mk @@ -7,6 +7,7 @@ srcs-y += qfprom_core.c srcs-y += qfprom_hal.c srcs-y += qfprom_target.c srcs-y += $(PLATFORM_FLAVOR)/qfprom_fuse_region.c +srcs-$(CFG_QCOM_FUSE_PTA) += qfprom_secboot.c global-incdirs-y += . global-incdirs-y += $(PLATFORM_FLAVOR) diff --git a/core/include/drivers/qcom/qfprom/qfprom.h b/core/include/drivers/qcom/qfprom/qfprom.h index ff6f27ad8bf..db6750366cb 100644 --- a/core/include/drivers/qcom/qfprom/qfprom.h +++ b/core/include/drivers/qcom/qfprom/qfprom.h @@ -31,11 +31,59 @@ enum qfprom_error { QFPROM_ERROR_TIMEOUT = 0x11, }; +struct qcom_secboot_device_ids { + uint32_t oem_id; + uint32_t model_id; + uint32_t jtag_id; + uint32_t serial_num; +}; + /* Read QFPROM row data */ TEE_Result qfprom_read_row(uint32_t addr, enum qfprom_addr_space type, uint32_t *data); +/* Is secure boot (authentication) enabled on this device? */ +TEE_Result qcom_secboot_is_enabled(bool *enabled); + +/* Is the serial-number fuse used as part of device binding? */ +TEE_Result qcom_secboot_is_use_serial_num_enabled(bool *enabled); + +/* Read the OEM root-of-trust anchor hash (PK_HASH0). */ +TEE_Result qcom_secboot_get_root_of_trust(uint8_t *hash, size_t len); + +/* Read the PIL anti-rollback fuse version (set bits in the ARB row). */ +TEE_Result qcom_secboot_get_pil_rollback_version(uint32_t *version); + +/* Advance the PIL anti-rollback fuse to at least @version. */ +TEE_Result qcom_secboot_blow_pil_rollback_version(uint32_t version); + +/* Read the OEM/model/JTAG/serial device-identity fuses. */ +TEE_Result qcom_secboot_get_device_ids(struct qcom_secboot_device_ids *ids); + +/* Read the SoC family/device word from TCSR_SOC_HW_VERSION. */ +TEE_Result qcom_secboot_get_soc_hw_version(uint32_t *fam_dev); + +/* + * Segment/hash-table digest size for @root_cert_sel; unrelated to + * cert-chain or root-of-trust hashing. + */ +TEE_Result qcom_secboot_get_segment_hash_len(uint32_t root_cert_sel, + uint32_t *hash_len); + +/* Is code-signing EKU enforcement required for this device? */ +TEE_Result qcom_secboot_get_eku_enforcement_en(bool *enabled); + +/* + * Report whether the anchor is fuse-resident with more than one + * provisioned root, and if so, the root count and the per-index + * activation/revocation bitmaps. + */ +TEE_Result qcom_secboot_get_mrc_info(bool *root_sel_enabled, + uint32_t *num_roots, + uint32_t *activation_list, + uint32_t *revocation_list); + /* Write QFPROM row data */ TEE_Result qfprom_write_row(uint32_t addr, uint32_t *data); diff --git a/core/pta/qcom/fuse/pta_qcom_fuse.c b/core/pta/qcom/fuse/pta_qcom_fuse.c new file mode 100644 index 00000000000..f0ba9ea9c90 --- /dev/null +++ b/core/pta/qcom/fuse/pta_qcom_fuse.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#define TA_PAS_UUID { 0xcff7d191, 0x7ca0, 0x4784, \ + { 0xaf, 0x13, 0x48, 0x22, 0x3b, 0x9a, 0x4f, 0xbe} } + +#define PTA_NAME "qcom_fuse.pta" + +static TEE_Result get_secboot_state(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + bool en = false; + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + res = qcom_secboot_is_enabled(&en); + if (res) + return res; + + params[0].value.a = en; + + return TEE_SUCCESS; +} + +static TEE_Result get_root_of_trust(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (params[0].memref.size < PTA_QCOM_FUSE_ROOT_OF_TRUST_SIZE) { + params[0].memref.size = PTA_QCOM_FUSE_ROOT_OF_TRUST_SIZE; + return TEE_ERROR_SHORT_BUFFER; + } + + params[0].memref.size = PTA_QCOM_FUSE_ROOT_OF_TRUST_SIZE; + + return qcom_secboot_get_root_of_trust(params[0].memref.buffer, + PTA_QCOM_FUSE_ROOT_OF_TRUST_SIZE); +} + +static TEE_Result get_device_ids(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + struct qcom_secboot_device_ids ids = { }; + TEE_Result res = TEE_ERROR_GENERIC; + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + res = qcom_secboot_get_device_ids(&ids); + if (res) + return res; + + params[0].value.a = ids.oem_id; + params[0].value.b = ids.model_id; + params[1].value.a = ids.jtag_id; + params[1].value.b = ids.serial_num; + + return TEE_SUCCESS; +} + +static TEE_Result get_soc_hw_version(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t fam_dev = 0; + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + res = qcom_secboot_get_soc_hw_version(&fam_dev); + if (res) + return res; + + params[0].value.a = fam_dev; + return TEE_SUCCESS; +} + +static TEE_Result get_segment_hash_len(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INOUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t hash_len = 0; + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + res = qcom_secboot_get_segment_hash_len(params[0].value.a, + &hash_len); + if (res) + return res; + + params[0].value.b = hash_len; + return TEE_SUCCESS; +} + +static TEE_Result get_eku_enforcement_en(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + bool en = false; + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + res = qcom_secboot_get_eku_enforcement_en(&en); + if (res) + return res; + + params[0].value.a = en; + + return TEE_SUCCESS; +} + +static TEE_Result get_use_serial_num(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + bool en = false; + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + res = qcom_secboot_is_use_serial_num_enabled(&en); + if (res) + return res; + + params[0].value.a = en; + + return TEE_SUCCESS; +} + +static TEE_Result get_pil_rollback_version(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t version = 0; + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + res = qcom_secboot_get_pil_rollback_version(&version); + if (res) + return res; + + params[0].value.a = version; + return TEE_SUCCESS; +} + +static TEE_Result blow_pil_rollback_version(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + return qcom_secboot_blow_pil_rollback_version(params[0].value.a); +} + +static TEE_Result get_mrc_info(uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + bool root_sel_enabled = false; + uint32_t activation_list = 0; + uint32_t revocation_list = 0; + uint32_t num_roots = 0; + + if (param_types != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + res = qcom_secboot_get_mrc_info(&root_sel_enabled, &num_roots, + &activation_list, &revocation_list); + if (res) + return res; + + params[0].value.a = root_sel_enabled; + params[0].value.b = num_roots; + params[1].value.a = activation_list; + params[1].value.b = revocation_list; + + return TEE_SUCCESS; +} + +static TEE_Result invoke_command(void *sess_ctx __unused, + uint32_t cmd_id, + uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + switch (cmd_id) { + case PTA_QCOM_FUSE_GET_SECBOOT_STATE: + return get_secboot_state(param_types, params); + case PTA_QCOM_FUSE_GET_ROOT_OF_TRUST: + return get_root_of_trust(param_types, params); + case PTA_QCOM_FUSE_GET_DEVICE_IDS: + return get_device_ids(param_types, params); + case PTA_QCOM_FUSE_GET_SOC_HW_VERSION: + return get_soc_hw_version(param_types, params); + case PTA_QCOM_FUSE_GET_SEGMENT_HASH_LEN: + return get_segment_hash_len(param_types, params); + case PTA_QCOM_FUSE_GET_EKU_ENFORCEMENT_EN: + return get_eku_enforcement_en(param_types, params); + case PTA_QCOM_FUSE_GET_USE_SERIAL_NUM: + return get_use_serial_num(param_types, params); + case PTA_QCOM_FUSE_GET_PIL_ROLLBACK_VERSION: + return get_pil_rollback_version(param_types, params); + case PTA_QCOM_FUSE_BLOW_PIL_ROLLBACK_VERSION: + return blow_pil_rollback_version(param_types, params); + case PTA_QCOM_FUSE_GET_MRC_INFO: + return get_mrc_info(param_types, params); + default: + return TEE_ERROR_NOT_IMPLEMENTED; + } +} + +static TEE_Result open_session(uint32_t pt __unused, + TEE_Param params[TEE_NUM_PARAMS] __unused, + void **sess_ctx __unused) +{ + struct ts_session *s = ts_get_calling_session(); + TEE_UUID ta_uuid = TA_PAS_UUID; + struct ts_ctx *ctx = NULL; + + if (!s) + return TEE_ERROR_ACCESS_DENIED; + + ctx = s->ctx; + if (memcmp(&ctx->uuid, &ta_uuid, sizeof(TEE_UUID))) + return TEE_ERROR_ACCESS_DENIED; + + return TEE_SUCCESS; +} + +pseudo_ta_register(.uuid = PTA_QCOM_FUSE_UUID, + .name = PTA_NAME, + .flags = PTA_DEFAULT_FLAGS, + .open_session_entry_point = open_session, + .invoke_command_entry_point = invoke_command); diff --git a/core/pta/qcom/fuse/sub.mk b/core/pta/qcom/fuse/sub.mk new file mode 100644 index 00000000000..d30ac3d4f58 --- /dev/null +++ b/core/pta/qcom/fuse/sub.mk @@ -0,0 +1 @@ +srcs-y += pta_qcom_fuse.c diff --git a/core/pta/qcom/pas/pas_auth.c b/core/pta/qcom/pas/pas_auth.c index 8968f75f47e..e8186bd10c8 100644 --- a/core/pta/qcom/pas/pas_auth.c +++ b/core/pta/qcom/pas/pas_auth.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -124,8 +125,9 @@ static TEE_Result parse_elf(const uint8_t *fw, size_t fw_size, return TEE_ERROR_BAD_FORMAT; PARSE_EHDR(e_info, ehdr, true); - if (e_info->elf_hdr_len < sizeof(*ehdr) || - e_info->phdr_entry_len < sizeof(Elf64_Phdr)) + if (e_info->elf_hdr_len != sizeof(*ehdr) || + e_info->phdr_offset != e_info->elf_hdr_len || + e_info->phdr_entry_len != sizeof(Elf64_Phdr)) return TEE_ERROR_BAD_FORMAT; break; } @@ -136,8 +138,9 @@ static TEE_Result parse_elf(const uint8_t *fw, size_t fw_size, return TEE_ERROR_BAD_FORMAT; PARSE_EHDR(e_info, ehdr, false); - if (e_info->elf_hdr_len < sizeof(*ehdr) || - e_info->phdr_entry_len < sizeof(Elf32_Phdr)) + if (e_info->elf_hdr_len != sizeof(*ehdr) || + e_info->phdr_offset != e_info->elf_hdr_len || + e_info->phdr_entry_len != sizeof(Elf32_Phdr)) return TEE_ERROR_BAD_FORMAT; break; } @@ -325,6 +328,8 @@ static TEE_Result verify_segments(const struct verify_ctx *ctx) expected = ctx->hash_table + i * ctx->hash_len; + dcache_inv_range(ctx->fw + offset, p_info.file_len); + res = hash_verify(ctx->hash_algo, ctx->fw + offset, p_info.file_len, expected, ctx->hash_len); if (res) { @@ -379,6 +384,16 @@ TEE_Result pas_platform_verify_image(uint32_t pas_id, } fw_size = data->fw_size; + if (!core_pbuf_is(CORE_MEM_NON_SEC, fw->base, fw_size)) { + EMSG("PAS auth: carveout %#"PRIxPA"/%#"PRIx32" not non-secure", + fw->base, fw_size); + return TEE_ERROR_SECURITY; + } + + /* + * TOCTOU protection for the carveout is expected to come from + * the kernel's MMU/SMMU configuration. + */ fw_va = core_mmu_add_mapping(MEM_AREA_RAM_NSEC, fw->base, fw_size); if (!fw_va) { EMSG("PAS auth: can't map carveout %#"PRIxPA"/%#"PRIx32, diff --git a/core/pta/qcom/pas/platform/kodiak/dsp_fw.c b/core/pta/qcom/pas/platform/kodiak/dsp_fw.c index df970e1b382..2ff3d63efe7 100644 --- a/core/pta/qcom/pas/platform/kodiak/dsp_fw.c +++ b/core/pta/qcom/pas/platform/kodiak/dsp_fw.c @@ -4,6 +4,7 @@ */ #include +#include #include #include diff --git a/core/pta/qcom/pas/platform/pas_data.h b/core/pta/qcom/pas/platform/pas_data.h index 1cf99887bee..cbd22921139 100644 --- a/core/pta/qcom/pas/platform/pas_data.h +++ b/core/pta/qcom/pas/platform/pas_data.h @@ -11,15 +11,6 @@ #include #include -#define PAS_ID_QDSP6 1 -#define PAS_ID_WPSS 6 -#define PAS_ID_IRIS 9 -#define PAS_ID_TURING 18 -#define PAS_ID_TURING1 30 -#define PAS_ID_CAMERA 33 -#define PAS_ID_GPDSP0 39 -#define PAS_ID_GPDSP1 40 - struct qcom_pas_data { uint32_t pas_id; struct io_pa_va base; diff --git a/core/pta/qcom/sub.mk b/core/pta/qcom/sub.mk index 2f0ac9af9fa..2e09c26303e 100644 --- a/core/pta/qcom/sub.mk +++ b/core/pta/qcom/sub.mk @@ -1 +1,5 @@ subdirs-$(CFG_QCOM_PAS_PTA) += pas + +# Exposes qfprom-backed fuse reads to user TAs. +CFG_QCOM_FUSE_PTA ?= n +subdirs-$(CFG_QCOM_FUSE_PTA) += fuse diff --git a/lib/libutee/include/pta_qcom_fuse.h b/lib/libutee/include/pta_qcom_fuse.h new file mode 100644 index 00000000000..0e2c7c30bca --- /dev/null +++ b/lib/libutee/include/pta_qcom_fuse.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PTA_QCOM_FUSE_H +#define __PTA_QCOM_FUSE_H + +/* + * Interface to the pseudo TA which exposes Qualcomm fuse state to + * user-mode TAs that cannot access the fuse driver directly. + */ + +#define PTA_QCOM_FUSE_UUID { 0x6b46384c, 0x4a3e, 0x4b9d, \ + { 0xa8, 0x2f, 0x1c, 0x3d, 0xe5, 0x9f, 0xa2, 0x11 } } + +/* + * Query whether secure boot is enabled. + * + * [out] params[0].value.a: 1 if secure boot is enabled, 0 otherwise + */ +#define PTA_QCOM_FUSE_GET_SECBOOT_STATE 1 + +#define PTA_QCOM_FUSE_ROOT_OF_TRUST_SIZE 48 + +/* + * Read the OEM root-of-trust digest. + * + * [out] params[0].memref: buffer receiving the digest; must be at least + * PTA_QCOM_FUSE_ROOT_OF_TRUST_SIZE bytes + */ +#define PTA_QCOM_FUSE_GET_ROOT_OF_TRUST 2 + +/* + * Read device-identity fields. + * + * [out] params[0].value.a: OEM_ID + * [out] params[0].value.b: MODEL_ID + * [out] params[1].value.a: JTAG_ID (masked to authentication bits) + * [out] params[1].value.b: serial number + */ +#define PTA_QCOM_FUSE_GET_DEVICE_IDS 3 + +/* + * Read the SOC hardware version family|device field. + * + * [out] params[0].value.a: family|device number + */ +#define PTA_QCOM_FUSE_GET_SOC_HW_VERSION 4 + +/* + * Read the firmware-segment hash digest size for a root_cert_sel index. + * + * [in] params[0].value.a: root_cert_sel (0-3) + * [out] params[0].value.b: digest size in bytes (32=SHA-256, 48=SHA-384) + */ +#define PTA_QCOM_FUSE_GET_SEGMENT_HASH_LEN 5 + +/* + * Query whether Extended Key Usage enforcement is fused on. + * + * [out] params[0].value.a: 1 if EKU enforcement is enabled, 0 otherwise + */ +#define PTA_QCOM_FUSE_GET_EKU_ENFORCEMENT_EN 6 + +/* + * Query the APPS serial-number binding override fuse. + * + * [out] params[0].value.a: 1 if the override is blown, 0 otherwise + */ +#define PTA_QCOM_FUSE_GET_USE_SERIAL_NUM 7 + +/* + * Read the PIL subsystem anti-rollback device version. + * + * [out] params[0].value.a: device version (0 when ARB enforcement is off) + */ +#define PTA_QCOM_FUSE_GET_PIL_ROLLBACK_VERSION 8 + +/* + * Advance the PIL subsystem anti-rollback fuse to a new device version. + * + * [in] params[0].value.a: image version to record as the new floor + */ +#define PTA_QCOM_FUSE_BLOW_PIL_ROLLBACK_VERSION 9 + +/* + * Read the multiple-root-certificate (MRC) provisioning state. + * + * [out] params[0].value.a: 1 if root selection is enabled, 0 otherwise + * [out] params[0].value.b: number of provisioned roots (1 when disabled) + * [out] params[1].value.a: per-index activation bitmap + * [out] params[1].value.b: per-index revocation bitmap + */ +#define PTA_QCOM_FUSE_GET_MRC_INFO 10 + +#endif /* __PTA_QCOM_FUSE_H */ diff --git a/lib/libutee/include/pta_qcom_pas.h b/lib/libutee/include/pta_qcom_pas.h index 4a1bd6c5fc6..39ec73ab981 100644 --- a/lib/libutee/include/pta_qcom_pas.h +++ b/lib/libutee/include/pta_qcom_pas.h @@ -14,6 +14,15 @@ * of the remote processor management */ +#define PAS_ID_QDSP6 1 +#define PAS_ID_WPSS 6 +#define PAS_ID_IRIS 9 +#define PAS_ID_TURING 18 +#define PAS_ID_TURING1 30 +#define PAS_ID_CAMERA 33 +#define PAS_ID_GPDSP0 39 +#define PAS_ID_GPDSP1 40 + #define PTA_QCOM_PAS_UUID { 0xdaedbae4, 0xcf3e, 0x4b76, \ { 0xa5, 0xc5, 0xdb, 0xf8, 0xb6, 0xfd, 0x5a, 0xf4} } diff --git a/ta/qcom_pas/include/auth/pas_auth.h b/ta/qcom_pas/include/auth/pas_auth.h index 1f5c88db14c..88685df6966 100644 --- a/ta/qcom_pas/include/auth/pas_auth.h +++ b/ta/qcom_pas/include/auth/pas_auth.h @@ -19,7 +19,8 @@ struct qcom_pas_session { TEE_Result pas_auth_save_metadata(struct qcom_pas_session *s, uint32_t pt, TEE_Param params[TEE_NUM_PARAMS]); -TEE_Result pas_auth_prepare(struct qcom_pas_session *s, uint32_t pas_id); +TEE_Result pas_auth_prepare_and_authenticate(struct qcom_pas_session *s, + uint32_t pas_id); TEE_Result pas_auth_verify(struct qcom_pas_session *s, TEE_TASessionHandle pta_session, @@ -38,8 +39,8 @@ pas_auth_save_metadata(struct qcom_pas_session *s __unused, } static inline TEE_Result -pas_auth_prepare(struct qcom_pas_session *s __unused, - uint32_t pas_id __unused) +pas_auth_prepare_and_authenticate(struct qcom_pas_session *s __unused, + uint32_t pas_id __unused) { return TEE_SUCCESS; } diff --git a/ta/qcom_pas/include/auth/pas_fuse.h b/ta/qcom_pas/include/auth/pas_fuse.h new file mode 100644 index 00000000000..ad1b88b8a21 --- /dev/null +++ b/ta/qcom_pas/include/auth/pas_fuse.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __AUTH_PAS_FUSE_H +#define __AUTH_PAS_FUSE_H + +#include +#include +#include + +struct pas_device_ids { + uint32_t oem_id; + uint32_t model_id; + uint32_t jtag_id; + uint32_t serial_num; +}; + +struct pas_fuse_hw_binding_info { + struct pas_device_ids ids; + bool use_serial_num_override; +}; + +enum pas_secboot_state { + PAS_SECBOOT_OFF, + PAS_SECBOOT_ON, + PAS_SECBOOT_UNKNOWN, +}; + +#ifdef CFG_QCOM_PAS_AUTH +TEE_Result pas_fuse_open(void); + +void pas_fuse_close(void); +#else +static inline TEE_Result pas_fuse_open(void) +{ + return TEE_SUCCESS; +} + +static inline void pas_fuse_close(void) +{ +} +#endif /* CFG_QCOM_PAS_AUTH */ + +TEE_Result pas_fuse_get_secboot_state(enum pas_secboot_state *state); + +TEE_Result pas_fuse_get_root_anchor(uint8_t *anchor); + +TEE_Result pas_fuse_get_hw_binding_info(struct pas_fuse_hw_binding_info *info); + +TEE_Result pas_fuse_get_soc_hw_version(uint32_t *fam_dev); + +TEE_Result pas_fuse_get_eku_enforcement_en(bool *eku_enforced); + +/* + * Segment/hash-table digest size for @root_cert_sel; unrelated to + * cert-chain or root-of-trust hashing. + */ +TEE_Result pas_fuse_get_segment_hash_len(uint32_t root_cert_sel, + uint32_t *hash_len); + +struct pas_fuse_mrc_info { + uint32_t num_roots; + uint32_t activation_list; + uint32_t revocation_list; +}; + +TEE_Result pas_fuse_get_mrc_info(struct pas_fuse_mrc_info *info); + +TEE_Result pas_fuse_get_pil_rollback_version(uint32_t *dev_ver); + +TEE_Result pas_fuse_blow_pil_rollback_version(uint32_t version); + +#endif /* __AUTH_PAS_FUSE_H */ diff --git a/ta/qcom_pas/include/auth/pas_mbn.h b/ta/qcom_pas/include/auth/pas_mbn.h index d48306d95b4..c5fe19a5268 100644 --- a/ta/qcom_pas/include/auth/pas_mbn.h +++ b/ta/qcom_pas/include/auth/pas_mbn.h @@ -17,8 +17,8 @@ * [ hash-segment bytes ] verbatim MBN hash-segment phdr content * * MBN hash segment, v6 (48-byte header): - * [header][qti meta][oem meta][hash table] - * [qti sig][qti certs][oem sig][oem certs] + * [header][qc meta][oem meta][hash table] + * [qc sig][qc certs][oem sig][oem certs] * * Hash table: one digest per ELF program header; entry 0 = digest of the ELF * header plus program-header table, entry i = digest of the segment at phdr i. @@ -26,57 +26,52 @@ #define PAS_MBN_VERSION_6 6 -/* MBN header field offsets (bytes from hash-segment start) */ -#define MBN_OFF_VERSION 0x04 -#define MBN_OFF_QC_SIG_SIZE 0x08 -#define MBN_OFF_QC_CERT_SIZE 0x0c -#define MBN_OFF_CODE_SIZE 0x14 -#define MBN_OFF_OEM_SIG_SIZE 0x1c -#define MBN_OFF_OEM_CERT_SIZE 0x24 -#define MBN_OFF_QC_META_SIZE 0x28 -#define MBN_OFF_OEM_META_SIZE 0x2c - -#define MBN_HDR_SIZE_V6 0x30 - -struct pas_mbn { +struct pas_mbn_header_v6 { + uint32_t reserved0; uint32_t version; + uint32_t qc_signature_size; + uint32_t qc_cert_chain_size; + uint32_t image_size; + uint32_t code_size; + uint32_t reserved1; + uint32_t oem_signature_size; + uint32_t reserved2; + uint32_t oem_cert_chain_size; + uint32_t qc_metadata_size; + uint32_t oem_metadata_size; +}; +struct pas_hash_segment_info { + uint32_t version; const uint8_t *hash_table; size_t hash_table_size; uint32_t num_entries; uint32_t hash_len; - const uint8_t *signed_region; size_t signed_region_size; - const uint8_t *oem_meta; size_t oem_meta_size; const uint8_t *oem_sig; size_t oem_sig_size; const uint8_t *oem_certs; size_t oem_certs_size; - - const uint8_t *qti_meta; - size_t qti_meta_size; - const uint8_t *qti_sig; - size_t qti_sig_size; - const uint8_t *qti_certs; - size_t qti_certs_size; - + const uint8_t *qc_meta; + size_t qc_meta_size; + const uint8_t *qc_sig; + size_t qc_sig_size; + const uint8_t *qc_certs; + size_t qc_certs_size; bool uie_encrypted; }; -/* Read a little-endian uint32_t from @p. */ -uint32_t pas_mbn_read_u32(const uint8_t *p); - -TEE_Result pas_mbn_locate(const uint8_t *md, size_t md_size, - const uint8_t **seg, size_t *seg_size); +TEE_Result pas_mbn_get_hash_segment(const uint8_t *md, size_t md_size, + const uint8_t **seg, size_t *seg_size); -TEE_Result pas_mbn_reserve_region(const uint8_t *segment, size_t segment_size, - size_t *offset, size_t len, - const uint8_t **region, size_t *region_len); +TEE_Result pas_mbn_get_region(const uint8_t *segment, size_t segment_size, + size_t *offset, size_t len, + const uint8_t **region, size_t *region_len); TEE_Result pas_mbn_parse(const uint8_t *md, size_t md_size, - uint32_t hash_len, struct pas_mbn *out); + uint32_t hash_len, struct pas_hash_segment_info *out); #endif /* __AUTH_PAS_MBN_H */ diff --git a/ta/qcom_pas/include/auth/pas_meta.h b/ta/qcom_pas/include/auth/pas_meta.h index 6423e9dddd5..96dd55ac95f 100644 --- a/ta/qcom_pas/include/auth/pas_meta.h +++ b/ta/qcom_pas/include/auth/pas_meta.h @@ -12,20 +12,57 @@ #include #include +#define PAS_META_FLAG_IN_USE_SOC_HW_VERSION 1 +#define PAS_META_FLAG_USE_SERIAL_NUMBER 2 +#define PAS_META_FLAG_OEM_ID_INDEPENDENT 3 +#define PAS_META_FLAG_IN_USE_JTAG_ID 10 +#define PAS_META_FLAG_MODEL_ID_INDEPENDENT 11 + +#define PAS_META_FLAG_ROOT_REVOKE_ACTIVATE_SHIFT 4 +#define PAS_META_FLAG_UIE_KEY_SWITCH_SHIFT 6 +#define PAS_META_FLAG_DEBUG_SHIFT 8 +#define PAS_META_OPTION_MASK 3U +#define PAS_META_OPTION_MAX 2U +#define PAS_META_OPTION_ENABLE_SN 2U + struct pas_md_slot { void *meta_data; size_t meta_data_size; uint32_t pas_id; bool in_use; - struct pas_mbn mbn; + struct pas_hash_segment_info mbn; bool ready; }; -TEE_Result pas_meta_get_version(const uint8_t *meta_data, - size_t meta_data_size, uint32_t *version); +struct pas_oem_metadata { + uint32_t major; + uint32_t minor; + uint32_t sw_id; + uint32_t hw_id; + uint32_t oem_id; + uint32_t model_id; + uint32_t secondary_sw_id; + uint32_t flags; + uint32_t soc_vers[12]; + uint32_t serial_num[8]; + uint32_t root_cert_sel; + uint32_t anti_rollback; +}; + +TEE_Result pas_meta_get_root_cert_sel(const uint8_t *meta_data, + size_t meta_data_size, + uint32_t *root_cert_sel); + +TEE_Result pas_meta_verify_elf_headers_hash(const uint8_t *meta_data, + size_t meta_data_size, + const uint8_t *hash_table, + uint32_t hash_len); + +TEE_Result pas_meta_get(const struct pas_hash_segment_info *hs, + struct pas_oem_metadata *meta); -TEE_Result pas_meta_segment_hash_len(const uint8_t *meta_data, - size_t meta_data_size, - uint32_t *hash_len); +TEE_Result +pas_meta_get_signed_region_copy(const struct pas_hash_segment_info *hs, + uint8_t **out, size_t *out_len); #endif /* __AUTH_PAS_META_H */ diff --git a/ta/qcom_pas/include/auth/pas_policy.h b/ta/qcom_pas/include/auth/pas_policy.h new file mode 100644 index 00000000000..0033bc14bf5 --- /dev/null +++ b/ta/qcom_pas/include/auth/pas_policy.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __AUTH_PAS_POLICY_H +#define __AUTH_PAS_POLICY_H + +#include +#include + +#define SECBOOT_ADSP_SW_TYPE 0x04 +#define SECBOOT_IRIS_SW_TYPE 0x0E +#define SECBOOT_TURING_SW_TYPE 0x17 +#define SECBOOT_CAMERA_SW_TYPE 0x34 +#define SECBOOT_TURING1_SW_TYPE 0x44 +#define SECBOOT_GPDSP0_SW_TYPE 0x58 +#define SECBOOT_GPDSP1_SW_TYPE 0x5A + +enum pas_arb_fuse_bank { + PAS_ARB_HLOS_FUSE_BANK = 0, + PAS_ARB_UEFI_FUSE_BANK, + PAS_ARB_SEPARATE_FUSE_BANK, +}; + +struct pas_policy_entry { + uint32_t pas_id; + uint32_t swid; + enum pas_arb_fuse_bank arb_bank; +}; + +TEE_Result pas_policy_expected_swid(uint32_t pas_id, uint32_t *swid); + +TEE_Result pas_policy_expected_arb_bank(uint32_t pas_id, + enum pas_arb_fuse_bank *bank); + +#endif /* __AUTH_PAS_POLICY_H */ diff --git a/ta/qcom_pas/include/auth/pas_sig.h b/ta/qcom_pas/include/auth/pas_sig.h new file mode 100644 index 00000000000..a4355f85961 --- /dev/null +++ b/ta/qcom_pas/include/auth/pas_sig.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __AUTH_PAS_SIG_H +#define __AUTH_PAS_SIG_H + +#include +#include +#include +#include + +#define PAS_SIG_MAX_HASH_SIZE 48U +#define PAS_SIG_MAX_SIG_SIZE 512U + +TEE_Result pas_sig_verify_cert_chain(const uint8_t *chain_der, + size_t chain_der_len, bool eku_enforced, + uint32_t num_roots, + uint32_t root_cert_sel, + const uint8_t **leaf_der, + size_t *leaf_der_len, + const uint8_t **roots_der, + size_t *roots_der_len); + +TEE_Result pas_sig_check_root_cert_index(uint32_t root_cert_sel, + uint32_t num_roots, + uint32_t activation_list, + uint32_t revocation_list); + +TEE_Result pas_sig_check_root_of_trust(uint32_t rot_hash_algo, + size_t rot_hash_len, + const uint8_t *root_der, + size_t root_der_len, + const uint8_t *expected); + +TEE_Result pas_sig_algo_from_leaf(const uint8_t *leaf_der, + size_t leaf_der_len, uint32_t *sig_algo, + uint32_t *sig_hash_algo); + +TEE_Result pas_sig_verify_signature(uint32_t sig_algo, + uint32_t sig_hash_algo, + const uint8_t *leaf_der, + size_t leaf_der_len, + const uint8_t *msg, size_t msg_len, + const uint8_t *sig, size_t sig_len); + +#endif /* __AUTH_PAS_SIG_H */ diff --git a/ta/qcom_pas/include/auth/pas_sig_auth.h b/ta/qcom_pas/include/auth/pas_sig_auth.h new file mode 100644 index 00000000000..61499ed19ec --- /dev/null +++ b/ta/qcom_pas/include/auth/pas_sig_auth.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __AUTH_PAS_SIG_AUTH_H +#define __AUTH_PAS_SIG_AUTH_H + +#include + +#include "pas_mbn.h" +#include "pas_meta.h" + +/* + * Pick the firmware-segment hash-table digest size for @slot's signed + * root cert, so the caller can size the hash table before parsing it. + */ +TEE_Result pas_sig_auth_segment_hash_len(const struct pas_md_slot *slot, + uint32_t *segment_hash_len); + +/* + * Authenticate @hs's signing chain, root-of-trust, and device bindings, + * then check the metadata against its ELF-header hash-table entry. + */ +TEE_Result pas_sig_auth_verify_image(const struct pas_hash_segment_info *hs, + const uint8_t *meta_data, + size_t meta_data_size, + uint32_t pas_id, + uint32_t segment_hash_len, + const uint8_t *anchor); + +/* + * Advance the device's PIL anti-rollback floor to @hs's OEM metadata + * version, once the image it belongs to has been verified. + */ +TEE_Result pas_sig_auth_commit_rollback(const struct pas_hash_segment_info *hs, + uint32_t pas_id); + +#endif /* __AUTH_PAS_SIG_AUTH_H */ diff --git a/ta/qcom_pas/src/auth/lemans/pas_policy_platform.h b/ta/qcom_pas/src/auth/lemans/pas_policy_platform.h new file mode 100644 index 00000000000..fd0c0d36eb3 --- /dev/null +++ b/ta/qcom_pas/src/auth/lemans/pas_policy_platform.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_POLICY_PLATFORM_H +#define __PAS_POLICY_PLATFORM_H + +#include +#include + +static const struct pas_policy_entry pas_policy_map[] = { + { PAS_ID_QDSP6, SECBOOT_ADSP_SW_TYPE }, + { PAS_ID_IRIS, SECBOOT_IRIS_SW_TYPE }, + { PAS_ID_TURING, SECBOOT_TURING_SW_TYPE }, + { PAS_ID_TURING1, SECBOOT_TURING1_SW_TYPE }, + { PAS_ID_CAMERA, SECBOOT_CAMERA_SW_TYPE }, + { PAS_ID_GPDSP0, SECBOOT_GPDSP0_SW_TYPE }, + { PAS_ID_GPDSP1, SECBOOT_GPDSP1_SW_TYPE }, +}; + +#endif /* __PAS_POLICY_PLATFORM_H */ diff --git a/ta/qcom_pas/src/auth/pas_auth.c b/ta/qcom_pas/src/auth/pas_auth.c index 38c23134801..c88392deb48 100644 --- a/ta/qcom_pas/src/auth/pas_auth.c +++ b/ta/qcom_pas/src/auth/pas_auth.c @@ -4,10 +4,14 @@ */ #include +#include #include #include +#include +#include #include #include +#include #include #include @@ -77,11 +81,14 @@ TEE_Result pas_auth_save_metadata(struct qcom_pas_session *s, uint32_t pt, return TEE_SUCCESS; } -TEE_Result pas_auth_prepare(struct qcom_pas_session *s, uint32_t pas_id) +TEE_Result pas_auth_prepare_and_authenticate(struct qcom_pas_session *s, + uint32_t pas_id) { struct pas_md_slot *slot = get_meta_data_slot(s, pas_id); + uint8_t anchor[PTA_QCOM_FUSE_ROOT_OF_TRUST_SIZE] = { }; TEE_Result res = TEE_ERROR_GENERIC; - uint32_t hash_len = 0; + uint32_t segment_hash_len = 0; + enum pas_secboot_state secboot_state = PAS_SECBOOT_UNKNOWN; if (!slot) { EMSG("PAS auth: no metadata for pas_id=%#"PRIx32 @@ -89,23 +96,41 @@ TEE_Result pas_auth_prepare(struct qcom_pas_session *s, uint32_t pas_id) return TEE_ERROR_BAD_STATE; } - res = pas_meta_segment_hash_len(slot->meta_data, slot->meta_data_size, - &hash_len); + res = pas_fuse_get_secboot_state(&secboot_state); + if (res) + secboot_state = PAS_SECBOOT_UNKNOWN; + + res = pas_sig_auth_segment_hash_len(slot, &segment_hash_len); if (res) { EMSG("PAS auth: cannot pick hash size: %#"PRIx32, res); - return res; + goto out; } - res = pas_mbn_parse(slot->meta_data, slot->meta_data_size, hash_len, - &slot->mbn); + res = pas_mbn_parse(slot->meta_data, slot->meta_data_size, + segment_hash_len, &slot->mbn); if (res) { EMSG("PAS auth: MBN parse failed: %#"PRIx32, res); - return res; + goto out; + } + + if (secboot_state != PAS_SECBOOT_OFF) { + res = pas_fuse_get_root_anchor(anchor); + if (res) + goto out; + + res = pas_sig_auth_verify_image(&slot->mbn, slot->meta_data, + slot->meta_data_size, pas_id, + segment_hash_len, anchor); + if (res) + goto out; } slot->ready = true; + res = TEE_SUCCESS; +out: + memzero_explicit(anchor, sizeof(anchor)); - return TEE_SUCCESS; + return res; } TEE_Result pas_auth_verify(struct qcom_pas_session *s, @@ -156,7 +181,10 @@ TEE_Result pas_auth_verify(struct qcom_pas_session *s, PTA_QCOM_PAS_VERIFY_IMAGE, pt, vp, NULL); TEE_Free(buffer); - return res; + if (res) + return res; + + return pas_sig_auth_commit_rollback(&slot->mbn, pas_id); } TEE_Result pas_auth_release_metadata(struct qcom_pas_session *s, diff --git a/ta/qcom_pas/src/auth/pas_fuse.c b/ta/qcom_pas/src/auth/pas_fuse.c new file mode 100644 index 00000000000..f5635de6d24 --- /dev/null +++ b/ta/qcom_pas/src/auth/pas_fuse.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +static TEE_TASessionHandle fuse_session; + +TEE_Result pas_fuse_open(void) +{ + static const TEE_UUID fuse_uuid = PTA_QCOM_FUSE_UUID; + TEE_Result res = TEE_ERROR_GENERIC; + + if (fuse_session != TEE_HANDLE_NULL) + return TEE_SUCCESS; + + res = TEE_OpenTASession(&fuse_uuid, TEE_TIMEOUT_INFINITE, 0, NULL, + &fuse_session, NULL); + if (res) + EMSG("PAS fuse: cannot open fuse PTA: %#"PRIx32, res); + + return res; +} + +void pas_fuse_close(void) +{ + if (fuse_session == TEE_HANDLE_NULL) + return; + + TEE_CloseTASession(fuse_session); + fuse_session = TEE_HANDLE_NULL; +} + +static TEE_Result fuse_pta_invoke(uint32_t cmd, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + if (fuse_session == TEE_HANDLE_NULL) + return TEE_ERROR_BAD_STATE; + + return TEE_InvokeTACommand(fuse_session, TEE_TIMEOUT_INFINITE, cmd, + param_types, params, NULL); +} + +TEE_Result pas_fuse_get_secboot_state(enum pas_secboot_state *state) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + res = fuse_pta_invoke(PTA_QCOM_FUSE_GET_SECBOOT_STATE, pt, params); + if (res) { + EMSG("PAS fuse: cannot read secboot state: %#"PRIx32, res); + return res; + } + *state = params[0].value.a ? PAS_SECBOOT_ON : PAS_SECBOOT_OFF; + + return TEE_SUCCESS; +} + +TEE_Result pas_fuse_get_root_anchor(uint8_t *anchor) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_OUTPUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + params[0].memref.buffer = anchor; + params[0].memref.size = PTA_QCOM_FUSE_ROOT_OF_TRUST_SIZE; + res = fuse_pta_invoke(PTA_QCOM_FUSE_GET_ROOT_OF_TRUST, pt, params); + if (res) + EMSG("PAS fuse: cannot read root of trust: %#"PRIx32, res); + + return res; +} + +TEE_Result pas_fuse_get_hw_binding_info(struct pas_fuse_hw_binding_info *info) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + res = fuse_pta_invoke(PTA_QCOM_FUSE_GET_DEVICE_IDS, pt, params); + if (res) { + EMSG("PAS fuse: cannot read device ids: %#"PRIx32, res); + return res; + } + info->ids.oem_id = params[0].value.a; + info->ids.model_id = params[0].value.b; + info->ids.jtag_id = params[1].value.a; + info->ids.serial_num = params[1].value.b; + + memset(params, 0, sizeof(params)); + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + res = fuse_pta_invoke(PTA_QCOM_FUSE_GET_USE_SERIAL_NUM, pt, params); + if (res) { + EMSG("PAS fuse: cannot read USE_SERIAL_NUM fuse: %#"PRIx32, + res); + return res; + } + info->use_serial_num_override = params[0].value.a; + + return TEE_SUCCESS; +} + +TEE_Result pas_fuse_get_soc_hw_version(uint32_t *fam_dev) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + res = fuse_pta_invoke(PTA_QCOM_FUSE_GET_SOC_HW_VERSION, pt, params); + if (res) { + EMSG("PAS fuse: cannot read SOC_HW_VERSION: %#"PRIx32, res); + return res; + } + + *fam_dev = params[0].value.a; + + return TEE_SUCCESS; +} + +TEE_Result pas_fuse_get_eku_enforcement_en(bool *eku_enforced) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + res = fuse_pta_invoke(PTA_QCOM_FUSE_GET_EKU_ENFORCEMENT_EN, pt, params); + if (res) { + EMSG("PAS fuse: cannot read EKU enforcement fuse: %#"PRIx32, + res); + return res; + } + + *eku_enforced = params[0].value.a; + + return TEE_SUCCESS; +} + +TEE_Result pas_fuse_get_segment_hash_len(uint32_t root_cert_sel, + uint32_t *hash_len) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + params[0].value.a = root_cert_sel; + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INOUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + res = fuse_pta_invoke(PTA_QCOM_FUSE_GET_SEGMENT_HASH_LEN, pt, params); + if (res) { + EMSG("PAS fuse: cannot read segment hash size: %#"PRIx32, res); + return res; + } + + *hash_len = params[0].value.b; + + return TEE_SUCCESS; +} + +TEE_Result pas_fuse_get_mrc_info(struct pas_fuse_mrc_info *info) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + info->num_roots = 1; + info->activation_list = 0; + info->revocation_list = 0; + + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, + TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + res = fuse_pta_invoke(PTA_QCOM_FUSE_GET_MRC_INFO, pt, params); + if (res) { + EMSG("PAS fuse: cannot read MRC info: %#"PRIx32, res); + return res; + } + + if (!params[0].value.a) + return TEE_SUCCESS; + + info->num_roots = params[0].value.b; + info->activation_list = params[1].value.a; + info->revocation_list = params[1].value.b; + + return TEE_SUCCESS; +} + +TEE_Result pas_fuse_get_pil_rollback_version(uint32_t *dev_ver) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + res = fuse_pta_invoke(PTA_QCOM_FUSE_GET_PIL_ROLLBACK_VERSION, pt, + params); + if (res) { + EMSG("PAS fuse: cannot read PIL rollback version: %#"PRIx32, + res); + return res; + } + + *dev_ver = params[0].value.a; + + return TEE_SUCCESS; +} + +TEE_Result pas_fuse_blow_pil_rollback_version(uint32_t version) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + params[0].value.a = version; + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); + res = fuse_pta_invoke(PTA_QCOM_FUSE_BLOW_PIL_ROLLBACK_VERSION, pt, + params); + if (res) + IMSG("PAS ARB: fuse advance failed: %#"PRIx32, res); + + return res; +} diff --git a/ta/qcom_pas/src/auth/pas_mbn.c b/ta/qcom_pas/src/auth/pas_mbn.c index 72cbdf2db71..ecff228daca 100644 --- a/ta/qcom_pas/src/auth/pas_mbn.c +++ b/ta/qcom_pas/src/auth/pas_mbn.c @@ -17,26 +17,18 @@ #define MBN_PT_FLAG_TYPE_MASK 0x07000000U #define MBN_PT_FLAG_HASH_TYPE_MASK 0x02000000U -uint32_t pas_mbn_read_u32(const uint8_t *p) +TEE_Result pas_mbn_get_hash_segment(const uint8_t *metadata, + size_t metadata_size, + const uint8_t **seg, size_t *seg_size) { - uint32_t v = 0; - - memcpy(&v, p, sizeof(v)); - - return v; -} - -TEE_Result pas_mbn_locate(const uint8_t *md, size_t md_size, - const uint8_t **seg, size_t *seg_size) -{ - const unsigned char *ident = md; + const unsigned char *ident = metadata; size_t phdr_entry_len = 0; size_t phdr_entry_min = 0; size_t phdr_table_end = 0; size_t phdr_offset = 0; size_t phdr_count = 0; uint32_t hash_flags = 0; - size_t hash_seg_len = 0; + size_t hash_seg_size = 0; size_t elf_hdr_len = 0; size_t hash_seg_offset = 0; size_t hash_seg_end = 0; @@ -44,7 +36,7 @@ TEE_Result pas_mbn_locate(const uint8_t *md, size_t md_size, bool found = false; size_t i = 0; - if (md_size < EI_NIDENT) + if (metadata_size < EI_NIDENT) return TEE_ERROR_BAD_FORMAT; if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || @@ -66,17 +58,17 @@ TEE_Result pas_mbn_locate(const uint8_t *md, size_t md_size, return TEE_ERROR_BAD_FORMAT; } - if (md_size < elf_hdr_len) + if (metadata_size < elf_hdr_len) return TEE_ERROR_BAD_FORMAT; if (is_64) { - const Elf64_Ehdr *ehdr = (const void *)md; + const Elf64_Ehdr *ehdr = (const void *)metadata; phdr_offset = ehdr->e_phoff; phdr_entry_len = ehdr->e_phentsize; phdr_count = ehdr->e_phnum; } else { - const Elf32_Ehdr *ehdr = (const void *)md; + const Elf32_Ehdr *ehdr = (const void *)metadata; phdr_offset = ehdr->e_phoff; phdr_entry_len = ehdr->e_phentsize; @@ -89,22 +81,22 @@ TEE_Result pas_mbn_locate(const uint8_t *md, size_t md_size, if (MUL_OVERFLOW(phdr_entry_len, phdr_count, &phdr_table_end) || ADD_OVERFLOW(phdr_table_end, phdr_offset, &phdr_table_end) || - phdr_table_end > md_size) + phdr_table_end > metadata_size) return TEE_ERROR_BAD_FORMAT; for (i = 0; i < phdr_count; i++) { - const uint8_t *p = md + phdr_offset + i * phdr_entry_len; + const uint8_t *p = metadata + phdr_offset + i * phdr_entry_len; if (is_64) { const Elf64_Phdr *phdr = (const void *)p; hash_flags = phdr->p_flags; - hash_seg_len = phdr->p_filesz; + hash_seg_size = phdr->p_filesz; } else { const Elf32_Phdr *phdr = (const void *)p; hash_flags = phdr->p_flags; - hash_seg_len = phdr->p_filesz; + hash_seg_size = phdr->p_filesz; } if ((hash_flags & MBN_PT_FLAG_TYPE_MASK) == @@ -126,19 +118,19 @@ TEE_Result pas_mbn_locate(const uint8_t *md, size_t md_size, ADD_OVERFLOW(hash_seg_offset, elf_hdr_len, &hash_seg_offset)) return TEE_ERROR_BAD_FORMAT; - if (ADD_OVERFLOW(hash_seg_offset, hash_seg_len, &hash_seg_end) || - hash_seg_end > md_size || !hash_seg_len) + if (ADD_OVERFLOW(hash_seg_offset, hash_seg_size, &hash_seg_end) || + hash_seg_end > metadata_size || !hash_seg_size) return TEE_ERROR_BAD_FORMAT; - *seg = md + hash_seg_offset; - *seg_size = hash_seg_len; + *seg = metadata + hash_seg_offset; + *seg_size = hash_seg_size; return TEE_SUCCESS; } -TEE_Result pas_mbn_reserve_region(const uint8_t *segment, size_t segment_size, - size_t *offset, size_t len, - const uint8_t **region, size_t *region_len) +TEE_Result pas_mbn_get_region(const uint8_t *segment, size_t segment_size, + size_t *offset, size_t len, + const uint8_t **region, size_t *region_len) { if (!len) { *region = NULL; @@ -156,51 +148,54 @@ TEE_Result pas_mbn_reserve_region(const uint8_t *segment, size_t segment_size, return TEE_SUCCESS; } -TEE_Result pas_mbn_parse(const uint8_t *md, size_t md_size, - uint32_t hash_len, struct pas_mbn *out) +TEE_Result pas_mbn_parse(const uint8_t *metadata, size_t metadata_size, + uint32_t hash_len, struct pas_hash_segment_info *out) { + struct pas_mbn_header_v6 header = { }; TEE_Result res = TEE_ERROR_GENERIC; + const uint8_t *hash_seg = NULL; uint32_t oem_cert_size = 0; uint32_t oem_meta_size = 0; - const uint8_t *seg = NULL; uint32_t oem_sig_size = 0; uint32_t qc_cert_size = 0; uint32_t qc_meta_size = 0; + size_t hash_seg_size = 0; uint32_t qc_sig_size = 0; size_t signed_size = 0; uint32_t code_size = 0; uint32_t version = 0; size_t hdr_size = 0; - size_t seg_size = 0; size_t offset = 0; - if (!md || !md_size || !out || !hash_len) + if (!metadata || !metadata_size || !out || !hash_len) return TEE_ERROR_BAD_PARAMETERS; memset(out, 0, sizeof(*out)); - res = pas_mbn_locate(md, md_size, &seg, &seg_size); + res = pas_mbn_get_hash_segment(metadata, metadata_size, &hash_seg, + &hash_seg_size); if (res) return res; - if (seg_size < MBN_HDR_SIZE_V6) + if (hash_seg_size < sizeof(header)) return TEE_ERROR_BAD_FORMAT; - version = pas_mbn_read_u32(seg + MBN_OFF_VERSION); + memcpy(&header, hash_seg, sizeof(header)); + version = header.version; if (version != PAS_MBN_VERSION_6) { EMSG("PAS auth: unsupported MBN version %#"PRIx32, version); return TEE_ERROR_BAD_FORMAT; } - hdr_size = MBN_HDR_SIZE_V6; + hdr_size = sizeof(header); /* "code_size" is the hash-table length in bytes, not a code length. */ - code_size = pas_mbn_read_u32(seg + MBN_OFF_CODE_SIZE); - qc_sig_size = pas_mbn_read_u32(seg + MBN_OFF_QC_SIG_SIZE); - qc_cert_size = pas_mbn_read_u32(seg + MBN_OFF_QC_CERT_SIZE); - oem_sig_size = pas_mbn_read_u32(seg + MBN_OFF_OEM_SIG_SIZE); - oem_cert_size = pas_mbn_read_u32(seg + MBN_OFF_OEM_CERT_SIZE); - qc_meta_size = pas_mbn_read_u32(seg + MBN_OFF_QC_META_SIZE); - oem_meta_size = pas_mbn_read_u32(seg + MBN_OFF_OEM_META_SIZE); + code_size = header.code_size; + qc_sig_size = header.qc_signature_size; + qc_cert_size = header.qc_cert_chain_size; + oem_sig_size = header.oem_signature_size; + oem_cert_size = header.oem_cert_chain_size; + qc_meta_size = header.qc_metadata_size; + oem_meta_size = header.oem_metadata_size; if (!code_size || code_size % hash_len) return TEE_ERROR_BAD_FORMAT; @@ -212,54 +207,60 @@ TEE_Result pas_mbn_parse(const uint8_t *md, size_t md_size, * [qc_meta || oem_meta || hash table]. */ offset = hdr_size; - out->signed_region = seg; + out->signed_region = hash_seg; if (ADD_OVERFLOW(qc_meta_size, oem_meta_size, &signed_size) || ADD_OVERFLOW(signed_size, code_size, &signed_size) || ADD_OVERFLOW(signed_size, hdr_size, &signed_size)) return TEE_ERROR_BAD_FORMAT; - if (signed_size > seg_size) + if (signed_size > hash_seg_size) return TEE_ERROR_BAD_FORMAT; out->signed_region_size = signed_size; - res = pas_mbn_reserve_region(seg, seg_size, &offset, qc_meta_size, - &out->qti_meta, &out->qti_meta_size); + res = pas_mbn_get_region(hash_seg, hash_seg_size, &offset, qc_meta_size, + &out->qc_meta, &out->qc_meta_size); if (res) return res; - res = pas_mbn_reserve_region(seg, seg_size, &offset, oem_meta_size, - &out->oem_meta, &out->oem_meta_size); + res = pas_mbn_get_region(hash_seg, hash_seg_size, &offset, + oem_meta_size, &out->oem_meta, + &out->oem_meta_size); if (res) return res; - out->hash_table = seg + offset; + out->hash_table = hash_seg + offset; out->hash_table_size = code_size; out->hash_len = hash_len; out->num_entries = code_size / hash_len; offset += code_size; - res = pas_mbn_reserve_region(seg, seg_size, &offset, qc_sig_size, - &out->qti_sig, &out->qti_sig_size); + res = pas_mbn_get_region(hash_seg, hash_seg_size, &offset, qc_sig_size, + &out->qc_sig, &out->qc_sig_size); if (res) return res; - res = pas_mbn_reserve_region(seg, seg_size, &offset, qc_cert_size, - &out->qti_certs, &out->qti_certs_size); + res = pas_mbn_get_region(hash_seg, hash_seg_size, &offset, qc_cert_size, + &out->qc_certs, &out->qc_certs_size); if (res) return res; - res = pas_mbn_reserve_region(seg, seg_size, &offset, oem_sig_size, - &out->oem_sig, &out->oem_sig_size); + res = pas_mbn_get_region(hash_seg, hash_seg_size, &offset, oem_sig_size, + &out->oem_sig, &out->oem_sig_size); if (res) return res; - res = pas_mbn_reserve_region(seg, seg_size, &offset, oem_cert_size, - &out->oem_certs, &out->oem_certs_size); + res = pas_mbn_get_region(hash_seg, hash_seg_size, &offset, + oem_cert_size, &out->oem_certs, + &out->oem_certs_size); if (res) return res; out->version = version; - if (offset + sizeof(uint32_t) <= seg_size && - pas_mbn_read_u32(seg + offset) == UIE_ENC_PARAM_MAGIC) - out->uie_encrypted = true; + if (offset + sizeof(uint32_t) <= hash_seg_size) { + uint32_t uie_magic = 0; + + memcpy(&uie_magic, hash_seg + offset, sizeof(uie_magic)); + if (uie_magic == UIE_ENC_PARAM_MAGIC) + out->uie_encrypted = true; + } return TEE_SUCCESS; } diff --git a/ta/qcom_pas/src/auth/pas_meta.c b/ta/qcom_pas/src/auth/pas_meta.c index d8a3af905c5..c89978ff610 100644 --- a/ta/qcom_pas/src/auth/pas_meta.c +++ b/ta/qcom_pas/src/auth/pas_meta.c @@ -5,50 +5,225 @@ #include #include +#include +#include +#include +#include +#include #include #include +#include -TEE_Result pas_meta_get_version(const uint8_t *meta_data, - size_t meta_data_size, uint32_t *version) +TEE_Result pas_meta_get_root_cert_sel(const uint8_t *meta_data, + size_t meta_data_size, + uint32_t *root_cert_sel) { + struct pas_mbn_header_v6 header = { }; TEE_Result res = TEE_ERROR_GENERIC; - const uint8_t *seg = NULL; - size_t seg_size = 0; + const uint8_t *oem_meta = NULL; + const uint8_t *qc_meta = NULL; + const uint8_t *segment = NULL; + size_t oem_metadata_size = 0; + size_t qc_metadata_size = 0; + size_t segment_size = 0; + uint32_t version = 0; + size_t hdr_size = 0; + size_t offset = 0; - if (!meta_data || !meta_data_size || !version) + if (!meta_data || !meta_data_size || !root_cert_sel) return TEE_ERROR_BAD_PARAMETERS; - res = pas_mbn_locate(meta_data, meta_data_size, &seg, &seg_size); + res = pas_mbn_get_hash_segment(meta_data, meta_data_size, &segment, + &segment_size); if (res) return res; - if (seg_size < MBN_OFF_VERSION + sizeof(uint32_t)) + if (segment_size < sizeof(header)) return TEE_ERROR_BAD_FORMAT; - *version = pas_mbn_read_u32(seg + MBN_OFF_VERSION); + memcpy(&header, segment, sizeof(header)); + version = header.version; + if (version != PAS_MBN_VERSION_6) { + EMSG("PAS auth: unsupported MBN version %#"PRIx32, version); + return TEE_ERROR_BAD_FORMAT; + } + + hdr_size = sizeof(header); + if (segment_size < hdr_size) + return TEE_ERROR_BAD_FORMAT; + + offset = hdr_size; + res = pas_mbn_get_region(segment, segment_size, &offset, + header.qc_metadata_size, &qc_meta, + &qc_metadata_size); + if (res) + return res; + + res = pas_mbn_get_region(segment, segment_size, &offset, + header.oem_metadata_size, &oem_meta, + &oem_metadata_size); + if (res) + return res; + + if (!oem_meta) + return TEE_ERROR_NO_DATA; + + if (oem_metadata_size < sizeof(struct pas_oem_metadata)) + return TEE_ERROR_BAD_FORMAT; + + memcpy(root_cert_sel, oem_meta + + offsetof(struct pas_oem_metadata, root_cert_sel), + sizeof(*root_cert_sel)); return TEE_SUCCESS; } -TEE_Result pas_meta_segment_hash_len(const uint8_t *meta_data, - size_t meta_data_size, uint32_t *hash_len) +TEE_Result pas_meta_verify_elf_headers_hash(const uint8_t *meta_data, + size_t meta_data_size, + const uint8_t *hash_table, + uint32_t hash_len) { + uint8_t digest[TEE_SHA384_HASH_SIZE] = { }; + TEE_OperationHandle op = TEE_HANDLE_NULL; TEE_Result res = TEE_ERROR_GENERIC; - uint32_t version = 0; + size_t digest_len = sizeof(digest); + size_t phdr_entry_len = 0; + size_t elf_hdr_len = 0; + size_t phdr_count = 0; + size_t hdr_len = 0; + uint32_t algo = 0; - if (!hash_len) + if (!meta_data || !meta_data_size || !hash_table) return TEE_ERROR_BAD_PARAMETERS; - res = pas_meta_get_version(meta_data, meta_data_size, &version); - if (res) - return res; - - switch (version) { - case PAS_MBN_VERSION_6: - *hash_len = TEE_SHA384_HASH_SIZE; - return TEE_SUCCESS; + switch (hash_len) { + case TEE_SHA256_HASH_SIZE: + algo = TEE_ALG_SHA256; + break; + case TEE_SHA384_HASH_SIZE: + algo = TEE_ALG_SHA384; + break; default: - EMSG("PAS auth: unsupported MBN version %#"PRIx32, version); return TEE_ERROR_NOT_SUPPORTED; } + + if (meta_data_size < EI_NIDENT) + return TEE_ERROR_BAD_FORMAT; + + if (meta_data[EI_MAG0] != ELFMAG0 || meta_data[EI_MAG1] != ELFMAG1 || + meta_data[EI_MAG2] != ELFMAG2 || meta_data[EI_MAG3] != ELFMAG3) + return TEE_ERROR_BAD_FORMAT; + + if (meta_data[EI_CLASS] == ELFCLASS64) { + const Elf64_Ehdr *ehdr = (const void *)meta_data; + + if (meta_data_size < sizeof(*ehdr)) + return TEE_ERROR_BAD_FORMAT; + elf_hdr_len = ehdr->e_ehsize; + phdr_entry_len = ehdr->e_phentsize; + phdr_count = ehdr->e_phnum; + } else if (meta_data[EI_CLASS] == ELFCLASS32) { + const Elf32_Ehdr *ehdr = (const void *)meta_data; + + if (meta_data_size < sizeof(*ehdr)) + return TEE_ERROR_BAD_FORMAT; + elf_hdr_len = ehdr->e_ehsize; + phdr_entry_len = ehdr->e_phentsize; + phdr_count = ehdr->e_phnum; + } else { + return TEE_ERROR_BAD_FORMAT; + } + + if (MUL_OVERFLOW(phdr_entry_len, phdr_count, &hdr_len) || + ADD_OVERFLOW(hdr_len, elf_hdr_len, &hdr_len) || + hdr_len > meta_data_size) + return TEE_ERROR_BAD_FORMAT; + + res = TEE_AllocateOperation(&op, algo, TEE_MODE_DIGEST, 0); + if (res != TEE_SUCCESS) + return res; + + res = TEE_DigestDoFinal(op, meta_data, hdr_len, digest, &digest_len); + if (res != TEE_SUCCESS) + goto out; + + if (digest_len != hash_len || + consttime_memcmp(digest, hash_table, hash_len) != 0) + res = TEE_ERROR_SECURITY; + else + res = TEE_SUCCESS; +out: + TEE_FreeOperation(op); + memzero_explicit(digest, sizeof(digest)); + + return res; +} + +TEE_Result pas_meta_get(const struct pas_hash_segment_info *hs, + struct pas_oem_metadata *meta) +{ + const uint8_t *m = NULL; + + if (!hs || !meta) + return TEE_ERROR_BAD_PARAMETERS; + + if (!hs->oem_meta || !hs->oem_meta_size) + return TEE_ERROR_NO_DATA; + + if (hs->oem_meta_size < sizeof(struct pas_oem_metadata)) + return TEE_ERROR_BAD_FORMAT; + + m = hs->oem_meta; + memcpy(meta, m, sizeof(*meta)); + + return TEE_SUCCESS; +} + +static void mask_meta_block(uint8_t *copy, size_t copy_len, + const uint8_t *block, size_t block_len, + const uint8_t *base) +{ + size_t off = 0; + + if (!block || !block_len) + return; + + off = (size_t)(block - base); + if (off < copy_len && block_len <= copy_len - off) + memset(copy + off, 0, block_len); +} + +static void zero_field(uint8_t *copy, size_t copy_len, size_t off) +{ + if (off + sizeof(uint32_t) <= copy_len) + memset(copy + off, 0, sizeof(uint32_t)); +} + +TEE_Result +pas_meta_get_signed_region_copy(const struct pas_hash_segment_info *hs, + uint8_t **out, size_t *out_len) +{ + uint8_t *copy = NULL; + + if (!hs || !hs->signed_region || !hs->signed_region_size || !out || + !out_len) + return TEE_ERROR_BAD_PARAMETERS; + + copy = TEE_Malloc(hs->signed_region_size, TEE_MALLOC_FILL_ZERO); + if (!copy) + return TEE_ERROR_OUT_OF_MEMORY; + + memcpy(copy, hs->signed_region, hs->signed_region_size); + + zero_field(copy, hs->signed_region_size, + offsetof(struct pas_mbn_header_v6, qc_signature_size)); + zero_field(copy, hs->signed_region_size, + offsetof(struct pas_mbn_header_v6, qc_cert_chain_size)); + mask_meta_block(copy, hs->signed_region_size, hs->qc_meta, + hs->qc_meta_size, hs->signed_region); + + *out = copy; + *out_len = hs->signed_region_size; + + return TEE_SUCCESS; } diff --git a/ta/qcom_pas/src/auth/pas_policy.c b/ta/qcom_pas/src/auth/pas_policy.c new file mode 100644 index 00000000000..bf7115a9681 --- /dev/null +++ b/ta/qcom_pas/src/auth/pas_policy.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +static const struct pas_policy_entry *find_entry(uint32_t pas_id) +{ + size_t i = 0; + + for (i = 0; i < ARRAY_SIZE(pas_policy_map); i++) + if (pas_policy_map[i].pas_id == pas_id) + return &pas_policy_map[i]; + + return NULL; +} + +TEE_Result pas_policy_expected_swid(uint32_t pas_id, uint32_t *swid) +{ + const struct pas_policy_entry *e = find_entry(pas_id); + + if (!swid) + return TEE_ERROR_BAD_PARAMETERS; + if (!e) + return TEE_ERROR_NOT_SUPPORTED; + + *swid = e->swid; + return TEE_SUCCESS; +} + +TEE_Result pas_policy_expected_arb_bank(uint32_t pas_id, + enum pas_arb_fuse_bank *bank) +{ + const struct pas_policy_entry *e = find_entry(pas_id); + + if (!bank) + return TEE_ERROR_BAD_PARAMETERS; + if (!e) + return TEE_ERROR_NOT_SUPPORTED; + + *bank = e->arb_bank; + return TEE_SUCCESS; +} diff --git a/ta/qcom_pas/src/auth/pas_sig.c b/ta/qcom_pas/src/auth/pas_sig.c new file mode 100644 index 00000000000..35d4fc0b7f0 --- /dev/null +++ b/ta/qcom_pas/src/auth/pas_sig.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DER_SEQUENCE_TAG \ + (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) + +static TEE_Result md_from_tee(uint32_t hash_algo, mbedtls_md_type_t *md) +{ + switch (hash_algo) { + case TEE_ALG_SHA256: + *md = MBEDTLS_MD_SHA256; + return TEE_SUCCESS; + case TEE_ALG_SHA384: + *md = MBEDTLS_MD_SHA384; + return TEE_SUCCESS; + default: + return TEE_ERROR_NOT_SUPPORTED; + } +} + +static TEE_Result digest(uint32_t hash_algo, const uint8_t *msg, size_t msg_len, + uint8_t *out, size_t *out_len) +{ + TEE_OperationHandle op = TEE_HANDLE_NULL; + TEE_Result res = TEE_ERROR_GENERIC; + + res = TEE_AllocateOperation(&op, hash_algo, TEE_MODE_DIGEST, 0); + if (res != TEE_SUCCESS) + return res; + + res = TEE_DigestDoFinal(op, msg, msg_len, out, out_len); + + TEE_FreeOperation(op); + + return res; +} + +static const mbedtls_x509_crt_profile pas_crt_profile = { + .allowed_mds = MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA384), + .allowed_pks = MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECDSA) | + MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECKEY), + .allowed_curves = MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP384R1), +}; + +/* mbedTLS treats an absent EKU extension as unrestricted; require it. */ +static TEE_Result check_eku(const mbedtls_x509_crt *leaf, bool enforced) +{ + size_t oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_CODE_SIGNING); + int ext = MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE; + + if (!enforced) + return TEE_SUCCESS; + + if (!mbedtls_x509_crt_has_ext_type(leaf, ext)) { + EMSG("PAS auth: leaf cert has no EKU extension"); + return TEE_ERROR_SECURITY; + } + + if (mbedtls_x509_crt_check_extended_key_usage(leaf, + MBEDTLS_OID_CODE_SIGNING, + oid_len)) { + EMSG("PAS auth: leaf cert missing code-signing EKU"); + return TEE_ERROR_SECURITY; + } + + return TEE_SUCCESS; +} + +/* mbedTLS treats an absent KeyUsage extension as unrestricted; require it. */ +static TEE_Result check_chain_constraints(const mbedtls_x509_crt *leaf, + bool eku_enforced) +{ + uint32_t ku = MBEDTLS_X509_KU_DIGITAL_SIGNATURE; + const mbedtls_x509_crt *crt = NULL; + TEE_Result res = TEE_ERROR_GENERIC; + size_t depth = 0; + + if (!mbedtls_x509_crt_has_ext_type(leaf, MBEDTLS_X509_EXT_KEY_USAGE)) { + EMSG("PAS auth: leaf cert has no KeyUsage extension"); + return TEE_ERROR_SECURITY; + } + + if (mbedtls_x509_crt_check_key_usage(leaf, ku)) { + EMSG("PAS auth: leaf cert missing digitalSignature KeyUsage"); + return TEE_ERROR_SECURITY; + } + + res = check_eku(leaf, eku_enforced); + if (res) + return res; + + for (crt = leaf; crt; crt = crt->next, depth++) { + int ca = mbedtls_x509_crt_get_ca_istrue(crt); + + if (ca < 0) { + EMSG("PAS auth: cannot read CA flag at depth %zu", + depth); + return TEE_ERROR_SECURITY; + } + + if (crt == leaf) { + if (ca) { + EMSG("PAS auth: leaf cert asserts CA"); + return TEE_ERROR_SECURITY; + } + continue; + } + + if (!ca) { + EMSG("PAS auth: issuer at depth %zu is not a CA", + depth); + return TEE_ERROR_SECURITY; + } + } + + return TEE_SUCCESS; +} + +static bool asn1_buf_eq(const mbedtls_x509_buf *a, const mbedtls_x509_buf *b) +{ + return a->len && a->len == b->len && !memcmp(a->p, b->p, a->len); +} + +static TEE_Result check_issuer_linkage(const mbedtls_x509_crt *issuer, + const mbedtls_x509_crt *subject) +{ + const mbedtls_x509_authority *akid = &subject->authority_key_id; + + if (akid->keyIdentifier.len && + issuer->subject_key_id.len && + !asn1_buf_eq(&akid->keyIdentifier, &issuer->subject_key_id)) { + EMSG("PAS auth: AKID/SKID mismatch in cert chain"); + return TEE_ERROR_SECURITY; + } + + if (akid->authorityCertSerialNumber.len && + !asn1_buf_eq(&akid->authorityCertSerialNumber, &issuer->serial)) { + EMSG("PAS auth: AKID serial mismatch in cert chain"); + return TEE_ERROR_SECURITY; + } + + return TEE_SUCCESS; +} + +/* mbedTLS's cert profile can't express exact algo pairings; gate them here. */ +static TEE_Result check_sig_algo(const mbedtls_x509_crt *crt, + mbedtls_pk_type_t *pk_out, + mbedtls_md_type_t *md_out) +{ + mbedtls_pk_type_t pk = MBEDTLS_PK_NONE; + mbedtls_md_type_t md = MBEDTLS_MD_NONE; + + if (mbedtls_oid_get_sig_alg(&crt->sig_oid, &md, &pk)) + return TEE_ERROR_SECURITY; + + if (pk != MBEDTLS_PK_ECDSA || md != MBEDTLS_MD_SHA384) + return TEE_ERROR_SECURITY; + + if (pk_out) + *pk_out = pk; + if (md_out) + *md_out = md; + + return TEE_SUCCESS; +} + +static TEE_Result check_chain_sig_algos(const mbedtls_x509_crt *leaf, + size_t num_prefix) +{ + const mbedtls_x509_crt *crt = NULL; + size_t i = 0; + + for (crt = leaf->next, i = 1; crt && i < num_prefix; + crt = crt->next, i++) { + if (check_sig_algo(crt, NULL, NULL)) { + EMSG("PAS auth: unsupported sig algo at depth %zu", i); + return TEE_ERROR_SECURITY; + } + } + + return TEE_SUCCESS; +} + +#define PAS_MIN_NUM_CERTS 2U +#define PAS_MAX_CERT_CHAIN_LEVEL 3U +#define PAS_MAX_NUM_ROOT_CERTS 4U +#define PAS_TOTAL_MAX_CERTS (PAS_MAX_NUM_ROOT_CERTS + \ + PAS_MAX_CERT_CHAIN_LEVEL - 1) + +/* The fixed root index accepted when no provisioned root is usable. */ +#define PAS_DEFAULT_SAFE_ROOT_INDEX 3U + +TEE_Result pas_sig_check_root_cert_index(uint32_t root_cert_sel, + uint32_t num_roots, + uint32_t activation_list, + uint32_t revocation_list) +{ + uint32_t non_avail = 0; + uint32_t mask = 0; + + if (!num_roots || num_roots > PAS_MAX_NUM_ROOT_CERTS || + root_cert_sel >= num_roots) + return TEE_ERROR_SECURITY; + + mask = GENMASK_32(PAS_MAX_NUM_ROOT_CERTS - 1, 0); + non_avail = (revocation_list & mask) | (~activation_list & mask); + + if (__builtin_popcount(non_avail) >= (int)PAS_MAX_NUM_ROOT_CERTS) { + if (root_cert_sel == PAS_DEFAULT_SAFE_ROOT_INDEX) + return TEE_SUCCESS; + EMSG("PAS auth: no usable root cert, sel %#"PRIx32, + root_cert_sel); + return TEE_ERROR_SECURITY; + } + + if ((activation_list >> root_cert_sel) & 1U && + !((revocation_list >> root_cert_sel) & 1U)) + return TEE_SUCCESS; + + EMSG("PAS auth: root cert %#"PRIx32" inactive or revoked", + root_cert_sel); + return TEE_ERROR_SECURITY; +} + +TEE_Result pas_sig_verify_cert_chain(const uint8_t *chain_der, + size_t chain_der_len, bool eku_enforced, + uint32_t num_roots, + uint32_t root_cert_sel, + const uint8_t **leaf_der, + size_t *leaf_der_len, + const uint8_t **roots_der, + size_t *roots_der_len) +{ + TEE_Result res = TEE_ERROR_SECURITY; + const mbedtls_x509_crt *crt = NULL; + mbedtls_x509_crt *sel_root = NULL; + mbedtls_x509_crt *leaf = NULL; + mbedtls_x509_crt chain = { }; + mbedtls_x509_crt trust = { }; + size_t num_prefix = 0; + size_t sel_index = 0; + size_t roots_off = 0; + size_t num_certs = 0; + uint32_t flags = 0; + size_t off = 0; + size_t i = 0; + int rc = 0; + + if (!chain_der || !chain_der_len || !leaf_der || !leaf_der_len || + !num_roots || num_roots > PAS_MAX_NUM_ROOT_CERTS || + root_cert_sel >= num_roots) + return TEE_ERROR_BAD_PARAMETERS; + + mbedtls_x509_crt_init(&chain); + mbedtls_x509_crt_init(&trust); + + while (off < chain_der_len && chain_der[off] == DER_SEQUENCE_TAG) { + mbedtls_x509_crt *added = NULL; + + if (num_certs >= PAS_TOTAL_MAX_CERTS) + break; + + if (mbedtls_x509_crt_parse_der(&chain, chain_der + off, + chain_der_len - off)) { + EMSG("PAS auth: cert %zu parse failed", num_certs); + goto out; + } + + added = &chain; + while (added->next) + added = added->next; + + off += added->raw.len; + num_certs++; + } + + for (i = off; i < chain_der_len; i++) { + if (chain_der[i] != 0xFF) { + EMSG("PAS auth: non-0xFF byte at chain offset %zu", i); + goto out; + } + } + + if (num_certs <= num_roots) { + EMSG("PAS auth: chain has %zu certs, need > %#"PRIx32" roots", + num_certs, num_roots); + goto out; + } + num_prefix = num_certs - num_roots; + if (num_prefix < (PAS_MIN_NUM_CERTS - 1) || + num_prefix > (PAS_MAX_CERT_CHAIN_LEVEL - 1)) { + EMSG("PAS auth: chain has %zu non-root certs, want [%u, %u]", + num_prefix, PAS_MIN_NUM_CERTS - 1, + PAS_MAX_CERT_CHAIN_LEVEL - 1); + goto out; + } + + leaf = &chain; + + sel_index = num_prefix + root_cert_sel; + for (crt = &chain, i = 0; crt; crt = crt->next, i++) { + if (i < num_prefix) + roots_off += crt->raw.len; + if (i == sel_index) + sel_root = (mbedtls_x509_crt *)crt; + } + if (!sel_root) { + EMSG("PAS auth: selected root %zu not present", sel_index); + goto out; + } + + if (mbedtls_x509_crt_parse_der(&trust, sel_root->raw.p, + sel_root->raw.len)) { + EMSG("PAS auth: root cert re-parse failed"); + goto out; + } + + rc = mbedtls_x509_crt_verify_with_profile(leaf, &trust, NULL, + &pas_crt_profile, NULL, + &flags, NULL, NULL); + /* + * No trusted time: tolerate a failure whose only cause is an + * expired/future cert. A zero flags with a nonzero rc means + * verification did not run to completion for an unrelated reason + * (e.g. a parse or allocation failure), which must still be fatal. + */ + if (rc && (!flags || + (flags & ~(uint32_t)(MBEDTLS_X509_BADCERT_EXPIRED | + MBEDTLS_X509_BADCERT_FUTURE)))) { + EMSG("PAS auth: cert chain verify failed (%#"PRIx32")", flags); + goto out; + } + + res = check_chain_constraints(leaf, eku_enforced); + if (res) + goto out; + + res = check_chain_sig_algos(leaf, num_prefix); + if (res) + goto out; + + crt = leaf; + for (i = 0; i + 1 < num_prefix; i++) { + res = check_issuer_linkage(crt->next, crt); + if (res) + goto out; + crt = crt->next; + } + res = check_issuer_linkage(sel_root, crt); + if (res) + goto out; + + if (roots_off > off || off > chain_der_len || + leaf->raw.len > chain_der_len) { + EMSG("PAS auth: cert DER length exceeds chain buffer"); + res = TEE_ERROR_SECURITY; + goto out; + } + + *leaf_der = chain_der; + *leaf_der_len = leaf->raw.len; + if (roots_der) + *roots_der = chain_der + roots_off; + if (roots_der_len) + *roots_der_len = off - roots_off; + + res = TEE_SUCCESS; +out: + mbedtls_x509_crt_free(&trust); + mbedtls_x509_crt_free(&chain); + + return res; +} + +static TEE_Result pas_sig_verify_hash(uint32_t hash_algo, + const uint8_t *data, size_t data_len, + const uint8_t *expected, + size_t hash_len) +{ + uint8_t digest_buf[PAS_SIG_MAX_HASH_SIZE] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + size_t digest_buf_len = sizeof(digest_buf); + + if (!data || !expected || !hash_len || hash_len > digest_buf_len) + return TEE_ERROR_BAD_PARAMETERS; + + res = digest(hash_algo, data, data_len, digest_buf, &digest_buf_len); + if (res != TEE_SUCCESS) + goto out; + + if (digest_buf_len != hash_len) { + res = TEE_ERROR_SECURITY; + goto out; + } + + if (consttime_memcmp(digest_buf, expected, hash_len) != 0) + res = TEE_ERROR_SECURITY; + else + res = TEE_SUCCESS; +out: + memzero_explicit(digest_buf, sizeof(digest_buf)); + + return res; +} + +TEE_Result pas_sig_check_root_of_trust(uint32_t rot_hash_algo, + size_t rot_hash_len, + const uint8_t *root_der, + size_t root_der_len, + const uint8_t *expected) +{ + if (!root_der || !root_der_len || !expected) + return TEE_ERROR_BAD_PARAMETERS; + + return pas_sig_verify_hash(rot_hash_algo, root_der, root_der_len, + expected, rot_hash_len); +} + +TEE_Result pas_sig_algo_from_leaf(const uint8_t *leaf_der, + size_t leaf_der_len, uint32_t *sig_algo, + uint32_t *sig_hash_algo) +{ + mbedtls_pk_type_t pk = MBEDTLS_PK_NONE; + mbedtls_md_type_t md = MBEDTLS_MD_NONE; + TEE_Result res = TEE_ERROR_SECURITY; + mbedtls_x509_crt leaf = { }; + + if (!leaf_der || !leaf_der_len || !sig_algo || !sig_hash_algo) + return TEE_ERROR_BAD_PARAMETERS; + + mbedtls_x509_crt_init(&leaf); + if (mbedtls_x509_crt_parse_der(&leaf, leaf_der, leaf_der_len)) { + EMSG("PAS auth: leaf cert parse failed"); + goto out; + } + + res = check_sig_algo(&leaf, &pk, &md); + if (res) { + EMSG("PAS auth: leaf signatureAlgorithm not accepted"); + goto out; + } + + switch (pk) { + case MBEDTLS_PK_ECDSA: + *sig_hash_algo = TEE_ALG_SHA384; + *sig_algo = TEE_ALG_ECDSA_SHA384; + break; + default: + res = TEE_ERROR_SECURITY; + goto out; + } + + res = TEE_SUCCESS; +out: + mbedtls_x509_crt_free(&leaf); + + return res; +} + +/* mbedTLS rejects trailing padding; derive the true DER length here. */ +static size_t ecdsa_der_sig_len(const uint8_t *sig, size_t field_len) +{ + size_t len = 0; + + if (field_len < 2 || sig[0] != DER_SEQUENCE_TAG) + return field_len; + + if (sig[1] < 0x80) + len = (size_t)sig[1] + 2; + else if (sig[1] == 0x81 && field_len >= 3) + len = (size_t)sig[2] + 3; + else + return field_len; + + return len <= field_len ? len : field_len; +} + +TEE_Result pas_sig_verify_signature(uint32_t sig_algo, + uint32_t sig_hash_algo, + const uint8_t *leaf_der, + size_t leaf_der_len, + const uint8_t *msg, size_t msg_len, + const uint8_t *sig, size_t sig_len) +{ + uint8_t digest_buf[PAS_SIG_MAX_HASH_SIZE] = { }; + size_t digest_buf_len = sizeof(digest_buf); + mbedtls_md_type_t md = MBEDTLS_MD_NONE; + TEE_Result res = TEE_ERROR_SECURITY; + mbedtls_x509_crt leaf = { }; + size_t actual_sig_len = 0; + int rc = 0; + + if (!leaf_der || !leaf_der_len || !msg || !msg_len || !sig || !sig_len) + return TEE_ERROR_BAD_PARAMETERS; + + if (sig_len > PAS_SIG_MAX_SIG_SIZE) + return TEE_ERROR_SECURITY; + + res = md_from_tee(sig_hash_algo, &md); + if (res != TEE_SUCCESS) + return res; + + res = digest(sig_hash_algo, msg, msg_len, digest_buf, &digest_buf_len); + if (res != TEE_SUCCESS) + return res; + + mbedtls_x509_crt_init(&leaf); + if (mbedtls_x509_crt_parse_der(&leaf, leaf_der, leaf_der_len)) { + res = TEE_ERROR_SECURITY; + goto out; + } + + switch (sig_algo) { + case TEE_ALG_ECDSA_SHA384: + actual_sig_len = ecdsa_der_sig_len(sig, sig_len); + rc = mbedtls_pk_verify(&leaf.pk, md, digest_buf, digest_buf_len, + sig, actual_sig_len); + break; + default: + res = TEE_ERROR_NOT_SUPPORTED; + goto out; + } + + if (rc) { + EMSG("PAS auth: signature verify failed (%d)", rc); + res = TEE_ERROR_SECURITY; + goto out; + } + + res = TEE_SUCCESS; +out: + mbedtls_x509_crt_free(&leaf); + memzero_explicit(digest_buf, sizeof(digest_buf)); + + return res; +} diff --git a/ta/qcom_pas/src/auth/pas_sig_auth.c b/ta/qcom_pas/src/auth/pas_sig_auth.c new file mode 100644 index 00000000000..35a9554ab61 --- /dev/null +++ b/ta/qcom_pas/src/auth/pas_sig_auth.c @@ -0,0 +1,477 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SECBOOT_METADATA_MAJOR_V0 0U +#define SECBOOT_METADATA_MAJOR_V1 1U +#define SECBOOT_METADATA_MINOR 0U +#define SECBOOT_DEFAULT_ROOT_CERT_SEL 0U + +static TEE_Result check_metadata_version(const struct pas_oem_metadata *meta) +{ + if ((meta->major == SECBOOT_METADATA_MAJOR_V0 || + meta->major == SECBOOT_METADATA_MAJOR_V1) && + meta->minor == SECBOOT_METADATA_MINOR) + return TEE_SUCCESS; + + EMSG("PAS auth: unsupported metadata version %#"PRIx32".%#"PRIx32, + meta->major, meta->minor); + return TEE_ERROR_SECURITY; +} + +static TEE_Result check_sw_binding(const struct pas_oem_metadata *meta, + uint32_t pas_id) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t expected = 0; + + res = pas_policy_expected_swid(pas_id, &expected); + if (res) { + EMSG("PAS auth: no SW_ID binding for pas_id %#"PRIx32, pas_id); + return res; + } + + if (meta->sw_id != expected) { + EMSG("PAS auth: SW_ID got %#"PRIx32" want %#"PRIx32, + meta->sw_id, expected); + return TEE_ERROR_SECURITY; + } + + return TEE_SUCCESS; +} + +static uint32_t pas_meta_option(uint32_t flags, uint32_t shift) +{ + return (flags >> shift) & PAS_META_OPTION_MASK; +} + +static bool pas_meta_option_sn_gated(uint32_t flags, uint32_t shift) +{ + return pas_meta_option(flags, shift) == PAS_META_OPTION_ENABLE_SN; +} + +static TEE_Result check_metadata_options(const struct pas_oem_metadata *meta) +{ + if (pas_meta_option(meta->flags, + PAS_META_FLAG_ROOT_REVOKE_ACTIVATE_SHIFT) <= + PAS_META_OPTION_MAX && + pas_meta_option(meta->flags, PAS_META_FLAG_UIE_KEY_SWITCH_SHIFT) <= + PAS_META_OPTION_MAX && + pas_meta_option(meta->flags, PAS_META_FLAG_DEBUG_SHIFT) <= + PAS_META_OPTION_MAX) + return TEE_SUCCESS; + + EMSG("PAS auth: reserved metadata option value, flags=%#"PRIx32, + meta->flags); + return TEE_ERROR_SECURITY; +} + +static TEE_Result check_oem_model_binding(const struct pas_oem_metadata *meta, + const struct pas_device_ids *ids) +{ + bool oem_independent = meta->flags & + BIT32(PAS_META_FLAG_OEM_ID_INDEPENDENT); + bool model_independent = false; + + /* v0 metadata has no MODEL_ID_INDEPENDENT bit; falls back to OEM's. */ + if (meta->major == 0) + model_independent = oem_independent; + else + model_independent = meta->flags & + BIT32(PAS_META_FLAG_MODEL_ID_INDEPENDENT); + + if (!oem_independent && meta->oem_id != ids->oem_id) { + EMSG("PAS auth: OEM_ID got %#"PRIx32" want %#"PRIx32, + meta->oem_id, ids->oem_id); + return TEE_ERROR_SECURITY; + } + + if (!model_independent && meta->model_id != ids->model_id) { + EMSG("PAS auth: MODEL_ID got %#"PRIx32" want %#"PRIx32, + meta->model_id, ids->model_id); + return TEE_ERROR_SECURITY; + } + + return TEE_SUCCESS; +} + +static TEE_Result check_jtag_binding(const struct pas_oem_metadata *meta, + const struct pas_device_ids *ids) +{ + if (!(meta->flags & BIT32(PAS_META_FLAG_IN_USE_JTAG_ID))) + return TEE_SUCCESS; + + if (meta->hw_id == ids->jtag_id) + return TEE_SUCCESS; + + EMSG("PAS auth: HW_ID got %#"PRIx32" want %#"PRIx32, meta->hw_id, + ids->jtag_id); + return TEE_ERROR_SECURITY; +} + +static TEE_Result check_serial_binding(const struct pas_oem_metadata *meta, + const struct pas_device_ids *ids, + bool use_serial_num_override) +{ + static const uint32_t sn_gated_shifts[] = { + PAS_META_FLAG_DEBUG_SHIFT, + PAS_META_FLAG_ROOT_REVOKE_ACTIVATE_SHIFT, + PAS_META_FLAG_UIE_KEY_SWITCH_SHIFT, + }; + bool sn_gated = false; + size_t i = 0; + + for (i = 0; i < ARRAY_SIZE(sn_gated_shifts); i++) { + if (pas_meta_option_sn_gated(meta->flags, sn_gated_shifts[i])) { + sn_gated = true; + break; + } + } + + if (!(meta->flags & BIT32(PAS_META_FLAG_USE_SERIAL_NUMBER)) && + !use_serial_num_override && !sn_gated) + return TEE_SUCCESS; + + if (!ids->serial_num) { + EMSG("PAS auth: serial binding required, no fused serial"); + return TEE_ERROR_SECURITY; + } + + for (i = 0; i < ARRAY_SIZE(meta->serial_num); i++) { + if (meta->serial_num[i] && + meta->serial_num[i] == ids->serial_num) + return TEE_SUCCESS; + } + + EMSG("PAS auth: serial number %#"PRIx32" not in metadata allow-list", + ids->serial_num); + return TEE_ERROR_SECURITY; +} + +static TEE_Result check_soc_vers_binding(const struct pas_oem_metadata *meta) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t fam_dev = 0; + size_t i = 0; + + if (!(meta->flags & BIT32(PAS_META_FLAG_IN_USE_SOC_HW_VERSION))) + return TEE_SUCCESS; + + res = pas_fuse_get_soc_hw_version(&fam_dev); + if (res) + return res; + + for (i = 0; i < ARRAY_SIZE(meta->soc_vers); i++) { + if (meta->soc_vers[i] == fam_dev) + return TEE_SUCCESS; + } + + EMSG("PAS auth: SOC_HW_VERSION %#"PRIx32" not in metadata allow-list", + fam_dev); + return TEE_ERROR_SECURITY; +} + +static TEE_Result check_hw_binding(const struct pas_oem_metadata *meta) +{ + struct pas_fuse_hw_binding_info info = { }; + TEE_Result res = TEE_ERROR_GENERIC; + + res = check_metadata_options(meta); + if (res) + return res; + + res = pas_fuse_get_hw_binding_info(&info); + if (res) + return res; + + res = check_oem_model_binding(meta, &info.ids); + if (res) + return res; + + res = check_jtag_binding(meta, &info.ids); + if (res) + return res; + + res = check_serial_binding(meta, &info.ids, + info.use_serial_num_override); + if (res) + return res; + + return check_soc_vers_binding(meta); +} + +static TEE_Result verify_oem_signature(const struct pas_hash_segment_info *hs, + uint32_t pas_id, + const uint8_t *anchor) +{ + uint32_t rot_hash_algo = TEE_ALG_SHA384; + TEE_Result res = TEE_ERROR_GENERIC; + struct pas_oem_metadata meta = { }; + struct pas_fuse_mrc_info mrc = { }; + uint8_t *signed_copy = NULL; + const uint8_t *roots = NULL; + uint32_t sig_hash_algo = 0; + const uint8_t *leaf = NULL; + bool eku_enforced = false; + uint32_t root_cert_sel = SECBOOT_DEFAULT_ROOT_CERT_SEL; + uint32_t sig_algo = 0; + size_t signed_len = 0; + size_t roots_len = 0; + size_t leaf_len = 0; + + if (!hs->oem_certs || !hs->oem_sig || !hs->signed_region) { + EMSG("PAS auth: metadata is not OEM-signed"); + return TEE_ERROR_SECURITY; + } + + if (pas_meta_get(hs, &meta) == TEE_SUCCESS) + root_cert_sel = meta.root_cert_sel; + + res = pas_fuse_get_mrc_info(&mrc); + if (res) { + EMSG("PAS auth: cannot read MRC info: %#"PRIx32, res); + return res; + } + if (root_cert_sel >= mrc.num_roots) { + EMSG("PAS auth: root_cert_sel %#"PRIx32" >= %#"PRIx32" roots", + root_cert_sel, mrc.num_roots); + return TEE_ERROR_SECURITY; + } + + if (mrc.num_roots > 1) { + res = pas_sig_check_root_cert_index(root_cert_sel, + mrc.num_roots, + mrc.activation_list, + mrc.revocation_list); + if (res) { + EMSG("PAS auth: root cert %#"PRIx32" not usable", + root_cert_sel); + return res; + } + } + + res = pas_fuse_get_eku_enforcement_en(&eku_enforced); + if (res) { + EMSG("PAS auth: cannot read EKU enforcement fuse: %#"PRIx32, + res); + return res; + } + + res = pas_sig_verify_cert_chain(hs->oem_certs, hs->oem_certs_size, + eku_enforced, mrc.num_roots, + root_cert_sel, &leaf, + &leaf_len, &roots, &roots_len); + if (res) { + EMSG("PAS auth: OEM cert chain invalid: %#"PRIx32, res); + return res; + } + + res = pas_sig_check_root_of_trust(rot_hash_algo, + PTA_QCOM_FUSE_ROOT_OF_TRUST_SIZE, + roots, roots_len, anchor); + if (res) { + EMSG("PAS auth: root-of-trust mismatch"); + return res; + } + + res = pas_meta_get(hs, &meta); + if (res == TEE_ERROR_NO_DATA) + res = TEE_ERROR_SECURITY; + if (res) { + EMSG("PAS auth: bad or missing OEM metadata"); + return res; + } + + res = check_metadata_version(&meta); + if (res) + return res; + + res = check_sw_binding(&meta, pas_id); + if (res) + return res; + + res = check_hw_binding(&meta); + if (res) + return res; + + res = pas_sig_algo_from_leaf(leaf, leaf_len, &sig_algo, + &sig_hash_algo); + if (res) { + EMSG("PAS auth: cannot determine signature algorithm: %#"PRIx32, + res); + return res; + } + + res = pas_meta_get_signed_region_copy(hs, &signed_copy, &signed_len); + if (res) + return res; + + res = pas_sig_verify_signature(sig_algo, sig_hash_algo, leaf, + leaf_len, signed_copy, signed_len, + hs->oem_sig, hs->oem_sig_size); + TEE_Free(signed_copy); + if (res) { + EMSG("PAS auth: OEM signature verify failed: %#"PRIx32, res); + return res; + } + + return TEE_SUCCESS; +} + +static TEE_Result check_anti_rollback(const struct pas_hash_segment_info *hs, + uint32_t pas_id) +{ + enum pas_arb_fuse_bank bank = PAS_ARB_HLOS_FUSE_BANK; + TEE_Result res = TEE_ERROR_GENERIC; + struct pas_oem_metadata meta = { }; + uint32_t dev_ver = 0; + + res = pas_policy_expected_arb_bank(pas_id, &bank); + if (res) + return res; + if (bank == PAS_ARB_SEPARATE_FUSE_BANK) { + EMSG("PAS ARB: pas_id=%#"PRIx32" needs a dedicated fuse bank", + pas_id); + return TEE_ERROR_NOT_SUPPORTED; + } + + res = pas_meta_get(hs, &meta); + if (res == TEE_ERROR_NO_DATA) + return TEE_SUCCESS; + if (res) { + EMSG("PAS ARB: bad OEM metadata"); + return res; + } + + res = pas_fuse_get_pil_rollback_version(&dev_ver); + if (res) + return res; + + if (!dev_ver) + return TEE_SUCCESS; + + if (meta.anti_rollback < dev_ver) { + EMSG("PAS ARB: image version %#"PRIx32" < device %#"PRIx32, + meta.anti_rollback, dev_ver); + return TEE_ERROR_SECURITY; + } + + return TEE_SUCCESS; +} + +TEE_Result pas_sig_auth_commit_rollback(const struct pas_hash_segment_info *hs, + uint32_t pas_id) +{ + enum pas_arb_fuse_bank bank = PAS_ARB_HLOS_FUSE_BANK; + TEE_Result res = TEE_ERROR_GENERIC; + struct pas_oem_metadata meta = { }; + uint32_t dev_ver = 0; + + res = pas_policy_expected_arb_bank(pas_id, &bank); + if (res) + return res; + if (bank == PAS_ARB_SEPARATE_FUSE_BANK) { + EMSG("PAS ARB: pas_id=%#"PRIx32" needs a dedicated fuse bank", + pas_id); + return TEE_ERROR_NOT_SUPPORTED; + } + + res = pas_meta_get(hs, &meta); + if (res == TEE_ERROR_NO_DATA) + return TEE_SUCCESS; + if (res) + return res; + + res = pas_fuse_get_pil_rollback_version(&dev_ver); + if (res) + return res; + + /* + * A lower version was already rejected in check_anti_rollback(); + * an equal version is already the floor, so nothing to advance. + */ + if (meta.anti_rollback <= dev_ver) + return TEE_SUCCESS; + + res = pas_fuse_blow_pil_rollback_version(meta.anti_rollback); + if (res) { + EMSG("PAS ARB: fuse advance to %#"PRIx32" failed: %#"PRIx32, + meta.anti_rollback, res); + return res; + } + + return TEE_SUCCESS; +} + +TEE_Result pas_sig_auth_segment_hash_len(const struct pas_md_slot *slot, + uint32_t *segment_hash_len) +{ + uint32_t root_cert_sel = SECBOOT_DEFAULT_ROOT_CERT_SEL; + TEE_Result res = TEE_ERROR_GENERIC; + + res = pas_meta_get_root_cert_sel(slot->meta_data, + slot->meta_data_size, + &root_cert_sel); + if (res == TEE_ERROR_NO_DATA) + root_cert_sel = SECBOOT_DEFAULT_ROOT_CERT_SEL; + else if (res) + return res; + + res = pas_fuse_get_segment_hash_len(root_cert_sel, segment_hash_len); + if (res) { + EMSG("PAS auth: segment hash size read failed: %#"PRIx32, res); + return TEE_ERROR_NOT_SUPPORTED; + } + + return TEE_SUCCESS; +} + +TEE_Result pas_sig_auth_verify_image(const struct pas_hash_segment_info *hs, + const uint8_t *meta_data, + size_t meta_data_size, + uint32_t pas_id, + uint32_t segment_hash_len, + const uint8_t *anchor) +{ + TEE_Result res = TEE_ERROR_GENERIC; + + if (hs->uie_encrypted) { + EMSG("PAS auth: UIE image encryption not supported"); + return TEE_ERROR_NOT_SUPPORTED; + } + + if (hs->qc_certs || hs->qc_sig) { + EMSG("PAS auth: QC-countersigned images are not supported"); + return TEE_ERROR_NOT_SUPPORTED; + } + + res = verify_oem_signature(hs, pas_id, anchor); + if (res) + return res; + + res = pas_meta_verify_elf_headers_hash(meta_data, meta_data_size, + hs->hash_table, + segment_hash_len); + if (res) + return res; + + res = check_anti_rollback(hs, pas_id); + if (res) + return res; + + return TEE_SUCCESS; +} diff --git a/ta/qcom_pas/src/auth/sub.mk b/ta/qcom_pas/src/auth/sub.mk index 29db1a53d6b..42df2463c24 100644 --- a/ta/qcom_pas/src/auth/sub.mk +++ b/ta/qcom_pas/src/auth/sub.mk @@ -1 +1,9 @@ -srcs-y += pas_auth.c pas_mbn.c pas_meta.c +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +srcs-y += pas_auth.c pas_fuse.c pas_mbn.c pas_meta.c pas_policy.c pas_sig.c \ + pas_sig_auth.c + +global-incdirs-y += $(PLATFORM_FLAVOR) diff --git a/ta/qcom_pas/src/qcom_pas.c b/ta/qcom_pas/src/qcom_pas.c index 195b4db748d..36e32dbeaf8 100644 --- a/ta/qcom_pas/src/qcom_pas.c +++ b/ta/qcom_pas/src/qcom_pas.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -29,7 +30,7 @@ static TEE_Result qcom_pas_init_image(struct qcom_pas_session *s, uint32_t pt, if (res) return res; - return pas_auth_prepare(s, params[0].value.a); + return pas_auth_prepare_and_authenticate(s, params[0].value.a); } static TEE_Result qcom_pas_auth_and_reset(struct qcom_pas_session *s, @@ -121,6 +122,13 @@ TEE_Result TA_OpenSessionEntryPoint(uint32_t pt, TEE_Free(s); goto out; } + + res = pas_fuse_open(); + if (res) { + TEE_CloseTASession(pta_session); + TEE_Free(s); + goto out; + } } session_refcount++; @@ -147,8 +155,10 @@ void TA_CloseSessionEntryPoint(void *sess_ctx) session_refcount--; - if (!session_refcount) + if (!session_refcount) { + pas_fuse_close(); TEE_CloseTASession(pta_session); + } } TEE_Result TA_InvokeCommandEntryPoint(void *sess_ctx, uint32_t cmd_id, diff --git a/ta/qcom_pas/src/user_ta_header_defines.h b/ta/qcom_pas/src/user_ta_header_defines.h index f3122c028b7..3a47ec5bc48 100644 --- a/ta/qcom_pas/src/user_ta_header_defines.h +++ b/ta/qcom_pas/src/user_ta_header_defines.h @@ -14,16 +14,20 @@ TA_FLAG_SINGLE_INSTANCE | \ TA_FLAG_INSTANCE_KEEP_ALIVE) -/* Provisioned stack size */ +/* + * Provisioned stack size. mbedtls X.509/ECDSA verification recurses + * deeply and needs more than the hash-only default. + */ +#ifdef CFG_QCOM_PAS_AUTH +#define TA_STACK_SIZE (32 * 1024) +#else #define TA_STACK_SIZE (4 * 1024) +#endif -/* Provisioned heap size for TEE_Malloc() and friends */ #define TA_DATA_SIZE CFG_PAS_TA_HEAP_SIZE -/* The gpd.ta.version property */ #define TA_VERSION "1.0" -/* The gpd.ta.description property */ #define TA_DESCRIPTION "remote processor firmware management" #endif /* USER_TA_HEADER_DEFINES_H */ diff --git a/ta/qcom_pas/user_ta.mk b/ta/qcom_pas/user_ta.mk index 9591d8c4b63..e881e192e41 100644 --- a/ta/qcom_pas/user_ta.mk +++ b/ta/qcom_pas/user_ta.mk @@ -7,7 +7,7 @@ endif CFG_QCOM_PAS_AUTH ?= n ifeq ($(CFG_QCOM_PAS_AUTH),y) -CFG_PAS_TA_HEAP_SIZE ?= (512 * 1024) +CFG_PAS_TA_HEAP_SIZE ?= (1024 * 1024) else CFG_PAS_TA_HEAP_SIZE ?= (4 * 1024) endif