From eda2e124c12b16b8ebe03d48e92b26f84d66b489 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 15 Aug 2026 10:00:00 +0530 Subject: [PATCH 1/8] pta: qcom: pas: validate carveout range and cache state before verify The carveout base and size come from the REE, so the range must be proven non-secure memory before it is mapped for hashing, and the REE writes it outside this mapping's coherency domain, so hashing could otherwise see a stale cached copy. Also reject an ELF with inconsistent header geometry, so segment collection cannot run on a malformed image. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 --- core/pta/qcom/pas/pas_auth_core.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/core/pta/qcom/pas/pas_auth_core.c b/core/pta/qcom/pas/pas_auth_core.c index 02c3ac1db..d4653a8d3 100644 --- a/core/pta/qcom/pas/pas_auth_core.c +++ b/core/pta/qcom/pas/pas_auth_core.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -112,8 +113,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; } @@ -124,8 +126,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; } @@ -313,6 +316,8 @@ TEE_Result pas_auth_core_verify_segments(const struct pas_auth_core_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) { @@ -367,6 +372,12 @@ 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; + } + 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, From b0a61079cadc6aefbc571e28f589351a475c2983 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 15 Aug 2026 10:01:00 +0530 Subject: [PATCH 2/8] drivers: qcom: qfprom: add Lemans platform configuration The secure-boot fuse accessors need per-target register offsets, masks and layout to operate. Keep the Lemans values as driver platform data so a future target only needs its own register layout, not a copy of the reader code. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 --- .../qcom/qfprom/lemans/qfprom_target.h | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/core/drivers/qcom/qfprom/lemans/qfprom_target.h b/core/drivers/qcom/qfprom/lemans/qfprom_target.h index 8207330b4..b662b7ca3 100644 --- a/core/drivers/qcom/qfprom/lemans/qfprom_target.h +++ b/core/drivers/qcom/qfprom/lemans/qfprom_target.h @@ -17,6 +17,36 @@ #define QFPROM_CORR_BASE 0x00784000 #define QFPROM_SIZE 0x4000 +#define SECURE_BOOT_APPS_ADDR (SECURITY_CONTROL_BASE + 0x606c) +#define SECURE_BOOT_AUTH_EN_BMSK 0x20 +#define SECURE_BOOT_USE_SERIAL_NUM_BMSK 0x40 + +#define QFPROM_ROOT_OF_TRUST_BYTE_SIZE 48 + +#define PK_HASH0_SENSE_ADDR (SECURITY_CONTROL_BASE + 0x6190) + +#define OEM_ID_SENSE_ADDR (SECURITY_CONTROL_BASE + 0x6138) +#define OEM_ID_BMSK 0xffff0000 +#define OEM_ID_SHFT 16 +#define MODEL_ID_BMSK 0x0000ffff +#define MODEL_ID_SHFT 0 +#define JTAG_ID_SENSE_ADDR (SECURITY_CONTROL_BASE + 0x6130) +#define JTAG_ID_AUTH_BMSK 0x0fffffff +#define SERIAL_NUM_SENSE_ADDR (SECURITY_CONTROL_BASE + 0x6134) + +#define TCSR_SOC_HW_VERSION_ADDR 0x01FC8000 +#define SOC_HW_VERSION_FAM_DEV_BMSK 0xffff0000 +#define SOC_HW_VERSION_FAM_DEV_SHFT 16 + +#define OEM_CONFIG2_ADDR (SECURITY_CONTROL_BASE + 0x6054) +#define EKU_ENFORCEMENT_EN_SHFT 30 + +#define SEGMENT_HASH_SELECT_SUPPORTED 1 +#define SEGMENT_HASH_FUNCTION_SELECT0_SHFT 16 + +#define OEM_CONFIG0_ADDR (SECURITY_CONTROL_BASE + 0x604c) +#define IMAGE_ENCRYPTION_ENABLE_SHFT 19 + #define QFPROM_BLOW_TIMER_OFFSET 0x2030 #define QFPROM_ACCEL_OFFSET 0x2038 From c327d28400818dae279bd4d97cf9f64061166b6d Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 15 Aug 2026 10:02:00 +0530 Subject: [PATCH 3/8] drivers: qcom: qfprom: add secure-boot fuse accessors Signature authentication has to bind an image to the device it runs on, which requires the OEM root-of-trust anchor and enable state, the device identity, and the EKU and image-encryption enforcement fuses. They are gated on CFG_QCOM_FUSE_PTA so a target without the fuse PTA carries none of this code. No caller reads them yet. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 --- core/drivers/qcom/qfprom/qfprom_secboot.c | 202 ++++++++++++++++++++++ core/drivers/qcom/qfprom/sub.mk | 1 + core/include/drivers/qcom/qfprom/qfprom.h | 24 +++ 3 files changed, 227 insertions(+) create mode 100644 core/drivers/qcom/qfprom/qfprom_secboot.c diff --git a/core/drivers/qcom/qfprom/qfprom_secboot.c b/core/drivers/qcom/qfprom/qfprom_secboot.c new file mode 100644 index 000000000..1ec3b4c01 --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_secboot.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#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); + +TEE_Result qcom_secboot_is_enabled(bool *enabled) +{ + struct qfprom_context *drv = qfprom_get_context(); + uint32_t val = 0; + + if (!enabled) + return TEE_ERROR_BAD_PARAMETERS; + + if (!drv->raw_base_va) + return TEE_ERROR_BAD_STATE; + + val = io_read32(drv->raw_base_va + + (SECURE_BOOT_APPS_ADDR - SECURITY_CONTROL_BASE)); + *enabled = (val & SECURE_BOOT_AUTH_EN_BMSK) != 0; + + return TEE_SUCCESS; +} + +TEE_Result qcom_secboot_get_use_serial_num(bool *enabled) +{ + struct qfprom_context *drv = qfprom_get_context(); + uint32_t val = 0; + + if (!enabled) + return TEE_ERROR_BAD_PARAMETERS; + + if (!drv->raw_base_va) + return TEE_ERROR_BAD_STATE; + + val = io_read32(drv->raw_base_va + + (SECURE_BOOT_APPS_ADDR - SECURITY_CONTROL_BASE)); + *enabled = (val & SECURE_BOOT_USE_SERIAL_NUM_BMSK) != 0; + + return TEE_SUCCESS; +} + +static TEE_Result read_sense_reg(paddr_t pa, 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 + (pa - SECURITY_CONTROL_BASE)); + + 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_SENSE_ADDR + off, &word); + if (res) + return res; + + memcpy(hash + off, &word, MIN(sizeof(word), len - off)); + } + + 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_SENSE_ADDR, &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_SENSE_ADDR, &val); + if (res) + return res; + ids->jtag_id = val & JTAG_ID_AUTH_BMSK; + + res = read_sense_reg(SERIAL_NUM_SENSE_ADDR, &ids->serial_num); + if (res) + return res; + + return TEE_SUCCESS; +} + +#define SEGMENT_HASH_ROOT_CERT_SEL_MAX 3U + +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 SEGMENT_HASH_SELECT_SUPPORTED + { + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t val = 0; + + res = read_sense_reg(OEM_CONFIG2_ADDR, &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; +#endif + + 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_ADDR, &val); + if (res) + return res; + + *enabled = val & BIT32(EKU_ENFORCEMENT_EN_SHFT); + + return TEE_SUCCESS; +} + +TEE_Result qcom_secboot_get_image_encryption_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_CONFIG0_ADDR, &val); + if (res) + return res; + + *enabled = val & BIT32(IMAGE_ENCRYPTION_ENABLE_SHFT); + + return TEE_SUCCESS; +} + +TEE_Result qcom_secboot_get_soc_hw_version(uint32_t *fam_dev) +{ + vaddr_t va = 0; + + if (!fam_dev) + return TEE_ERROR_BAD_PARAMETERS; + + va = (vaddr_t)phys_to_virt(TCSR_SOC_HW_VERSION_ADDR, MEM_AREA_IO_SEC, + sizeof(uint32_t)); + if (!va) + return TEE_ERROR_GENERIC; + + *fam_dev = (io_read32(va) & 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 f4bde830f..5c83e7bba 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 ff6f27ad8..2f84c4462 100644 --- a/core/include/drivers/qcom/qfprom/qfprom.h +++ b/core/include/drivers/qcom/qfprom/qfprom.h @@ -36,6 +36,30 @@ TEE_Result qfprom_read_row(uint32_t addr, enum qfprom_addr_space type, uint32_t *data); +TEE_Result qcom_secboot_is_enabled(bool *enabled); + +TEE_Result qcom_secboot_get_use_serial_num(bool *enabled); + +TEE_Result qcom_secboot_get_root_of_trust(uint8_t *hash, size_t len); + +struct qcom_secboot_device_ids { + uint32_t oem_id; + uint32_t model_id; + uint32_t jtag_id; + uint32_t serial_num; +}; + +TEE_Result qcom_secboot_get_device_ids(struct qcom_secboot_device_ids *ids); + +TEE_Result qcom_secboot_get_soc_hw_version(uint32_t *fam_dev); + +TEE_Result qcom_secboot_get_segment_hash_len(uint32_t root_cert_sel, + uint32_t *hash_len); + +TEE_Result qcom_secboot_get_eku_enforcement_en(bool *enabled); + +TEE_Result qcom_secboot_get_image_encryption_en(bool *enabled); + /* Write QFPROM row data */ TEE_Result qfprom_write_row(uint32_t addr, uint32_t *data); From ee8f9118c22de16a0752cb59e51d1e269aa42997 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 15 Aug 2026 10:03:00 +0530 Subject: [PATCH 4/8] pta: qcom: fuse: expose secure-boot fuses to user TAs The secure-boot fuse readers live in the OP-TEE core, but the PAS TA that needs them runs in user space. Expose them through a pseudo-TA restricted to a REE_KERNEL-only login domain, so the TA can obtain fuse-backed values without a driver dependency of its own. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 --- core/pta/qcom/fuse/pta_qcom_fuse.c | 243 ++++++++++++++++++++++++++++ core/pta/qcom/fuse/sub.mk | 1 + core/pta/qcom/sub.mk | 4 + lib/libutee/include/pta_qcom_fuse.h | 80 +++++++++ 4 files changed, 328 insertions(+) create mode 100644 core/pta/qcom/fuse/pta_qcom_fuse.c create mode 100644 core/pta/qcom/fuse/sub.mk create mode 100644 lib/libutee/include/pta_qcom_fuse.h 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 000000000..a5e8360a0 --- /dev/null +++ b/core/pta/qcom/fuse/pta_qcom_fuse.c @@ -0,0 +1,243 @@ +// 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} } + +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_get_use_serial_num(&en); + if (res) + return res; + + params[0].value.a = en; + + return TEE_SUCCESS; +} + +static TEE_Result get_image_encryption_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_image_encryption_en(&en); + if (res) + return res; + + params[0].value.a = en; + + 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_IMAGE_ENCRYPTION_EN: + return get_image_encryption_en(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 = "qcom_fuse.pta", + .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 000000000..d30ac3d4f --- /dev/null +++ b/core/pta/qcom/fuse/sub.mk @@ -0,0 +1 @@ +srcs-y += pta_qcom_fuse.c diff --git a/core/pta/qcom/sub.mk b/core/pta/qcom/sub.mk index 2f0ac9af9..2e09c2630 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 000000000..c6d77d17b --- /dev/null +++ b/lib/libutee/include/pta_qcom_fuse.h @@ -0,0 +1,80 @@ +/* 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 per-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 + +/* + * Query whether OEM image encryption is provisioned. + * + * [out] params[0].value.a: 1 if image encryption is enabled, 0 otherwise + */ +#define PTA_QCOM_FUSE_GET_IMAGE_ENCRYPTION_EN 8 + +#endif /* __PTA_QCOM_FUSE_H */ From 5bf049c0e5dc8e8fc7980bbee9f1c9338e2e4cca Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 15 Aug 2026 10:04:00 +0530 Subject: [PATCH 5/8] ta: qcom_pas: add MBN metadata decoder Signature authentication binds an image to its device and enforces anti-rollback using fields in the OEM metadata block. Decode them into typed values so the authentication backend never re-parses the raw block. Metadata version and root_cert_sel are readable on their own, because each selects the hash size the full parse needs as input. The OEM-signed region masks out the QTI-controlled fields, so the signature covers exactly what the OEM signed. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 --- ta/qcom_pas/include/pas_meta.h | 42 ++++++ ta/qcom_pas/src/pas_meta.c | 259 +++++++++++++++++++++++++++++++++ 2 files changed, 301 insertions(+) diff --git a/ta/qcom_pas/include/pas_meta.h b/ta/qcom_pas/include/pas_meta.h index 8d4740f2b..75587106c 100644 --- a/ta/qcom_pas/include/pas_meta.h +++ b/ta/qcom_pas/include/pas_meta.h @@ -18,4 +18,46 @@ 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_root_cert_sel(const uint8_t *meta_data, + size_t meta_data_size, + uint32_t *root_cert_sel); + +TEE_Result pas_meta_verify_preamble(const uint8_t *meta_data, + size_t meta_data_size, + const uint8_t *hash_table, + uint32_t hash_len); + +struct pas_meta { + 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; +}; + +#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 + +TEE_Result pas_meta_get(const struct pas_mbn *hs, struct pas_meta *meta); + +TEE_Result pas_meta_signed_copy(const struct pas_mbn *hs, + uint8_t **out, size_t *out_len); + #endif /* __PAS_META_H */ diff --git a/ta/qcom_pas/src/pas_meta.c b/ta/qcom_pas/src/pas_meta.c index ae4f6c8b2..941c4930d 100644 --- a/ta/qcom_pas/src/pas_meta.c +++ b/ta/qcom_pas/src/pas_meta.c @@ -3,10 +3,31 @@ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include +#include #include #include +#include +#include #include #include +#include + +#define META_OFF_MAJOR 0 +#define META_OFF_MINOR 1 +#define META_OFF_SW_ID 2 +#define META_OFF_HW_ID 3 +#define META_OFF_OEM_ID 4 +#define META_OFF_MODEL_ID 5 +#define META_OFF_SECONDARY_SW_ID 6 +#define META_OFF_FLAGS 7 +#define META_OFF_SOC_VERS 8 +#define META_NUM_SOC_VERS 12 +#define META_OFF_SERIAL_NUM (META_OFF_SOC_VERS + META_NUM_SOC_VERS) +#define META_NUM_SERIAL_NUM 8 +#define META_OFF_ANTI_ROLLBACK 29 +#define META_OFF_ROOT_CERT_SEL 28 +#define META_MIN_WORDS (META_OFF_ANTI_ROLLBACK + 1) TEE_Result pas_meta_get_version(const uint8_t *meta_data, size_t meta_data_size, uint32_t *version) @@ -52,3 +73,241 @@ TEE_Result pas_meta_segment_hash_len(const uint8_t *meta_data, return TEE_ERROR_NOT_SUPPORTED; } } + +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 res = TEE_ERROR_GENERIC; + const uint8_t *oem_meta = NULL; + const uint8_t *qc_meta = NULL; + const uint8_t *segment = NULL; + uint32_t oem_meta_size = 0; + uint32_t qc_meta_size = 0; + size_t segment_size = 0; + size_t oem_meta_len = 0; + size_t qc_meta_len = 0; + uint32_t version = 0; + size_t hdr_size = 0; + size_t offset = 0; + + if (!meta_data || !meta_data_size || !root_cert_sel) + return TEE_ERROR_BAD_PARAMETERS; + + res = pas_mbn_locate(meta_data, meta_data_size, &segment, + &segment_size); + if (res) + return res; + + if (segment_size < MBN_HDR_SIZE_V6) + return TEE_ERROR_BAD_FORMAT; + + version = pas_mbn_read_u32(segment + MBN_OFF_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; + if (segment_size < hdr_size) + return TEE_ERROR_BAD_FORMAT; + + qc_meta_size = pas_mbn_read_u32(segment + MBN_OFF_QC_META_SIZE); + oem_meta_size = pas_mbn_read_u32(segment + MBN_OFF_OEM_META_SIZE); + + offset = hdr_size; + res = pas_mbn_reserve_region(segment, segment_size, &offset, + qc_meta_size, &qc_meta, &qc_meta_len); + if (res) + return res; + res = pas_mbn_reserve_region(segment, segment_size, &offset, + oem_meta_size, &oem_meta, &oem_meta_len); + if (res) + return res; + + if (!oem_meta) + return TEE_ERROR_NO_DATA; + + if (oem_meta_len < META_MIN_WORDS * sizeof(uint32_t)) + return TEE_ERROR_BAD_FORMAT; + + *root_cert_sel = pas_mbn_read_u32(oem_meta + META_OFF_ROOT_CERT_SEL * + sizeof(uint32_t)); + + return TEE_SUCCESS; +} + +TEE_Result pas_meta_verify_preamble(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; + 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 (!meta_data || !meta_data_size || !hash_table) + return TEE_ERROR_BAD_PARAMETERS; + + switch (hash_len) { + case TEE_SHA256_HASH_SIZE: + algo = TEE_ALG_SHA256; + break; + case TEE_SHA384_HASH_SIZE: + algo = TEE_ALG_SHA384; + break; + default: + 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_mbn *hs, struct pas_meta *meta) +{ + const uint8_t *m = NULL; + size_t i = 0; + + 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 < META_MIN_WORDS * sizeof(uint32_t)) + return TEE_ERROR_BAD_FORMAT; + + m = hs->oem_meta; + meta->major = pas_mbn_read_u32(m + META_OFF_MAJOR * + sizeof(uint32_t)); + meta->minor = pas_mbn_read_u32(m + META_OFF_MINOR * + sizeof(uint32_t)); + meta->sw_id = pas_mbn_read_u32(m + META_OFF_SW_ID * + sizeof(uint32_t)); + meta->hw_id = pas_mbn_read_u32(m + META_OFF_HW_ID * + sizeof(uint32_t)); + meta->oem_id = pas_mbn_read_u32(m + META_OFF_OEM_ID * + sizeof(uint32_t)); + meta->model_id = pas_mbn_read_u32(m + META_OFF_MODEL_ID * + sizeof(uint32_t)); + meta->secondary_sw_id = pas_mbn_read_u32(m + + META_OFF_SECONDARY_SW_ID * + sizeof(uint32_t)); + meta->flags = pas_mbn_read_u32(m + META_OFF_FLAGS * + sizeof(uint32_t)); + for (i = 0; i < META_NUM_SOC_VERS; i++) + meta->soc_vers[i] = pas_mbn_read_u32(m + (META_OFF_SOC_VERS + + i) * + sizeof(uint32_t)); + for (i = 0; i < META_NUM_SERIAL_NUM; i++) + meta->serial_num[i] = pas_mbn_read_u32(m + + (META_OFF_SERIAL_NUM + + i) * sizeof(uint32_t)); + meta->root_cert_sel = pas_mbn_read_u32(m + META_OFF_ROOT_CERT_SEL * + sizeof(uint32_t)); + meta->anti_rollback = pas_mbn_read_u32(m + META_OFF_ANTI_ROLLBACK * + sizeof(uint32_t)); + + 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_signed_copy(const struct pas_mbn *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, MBN_OFF_QC_SIG_SIZE); + zero_field(copy, hs->signed_region_size, MBN_OFF_QC_CERT_SIZE); + mask_meta_block(copy, hs->signed_region_size, hs->qti_meta, + hs->qti_meta_size, hs->signed_region); + + *out = copy; + *out_len = hs->signed_region_size; + + return TEE_SUCCESS; +} From 058ea130027616c42b45b19cec89c7ece77ff043 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 15 Aug 2026 10:05:00 +0530 Subject: [PATCH 6/8] ta: qcom_pas: add fuse-PTA session helper The signature-authentication path makes several fuse reads per image, so hold one fuse-PTA session per TA session rather than opening one per read, matching how the TA already holds its PAS PTA session. The helper applies no policy and fails closed: a read failure propagates instead of substituting a default, so an untrustworthy fuse value aborts authentication rather than silently weakening it. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 --- ta/qcom_pas/include/pas_fuse.h | 50 ++++++++++ ta/qcom_pas/src/pas_fuse.c | 173 +++++++++++++++++++++++++++++++++ ta/qcom_pas/src/sub.mk | 2 +- 3 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 ta/qcom_pas/include/pas_fuse.h create mode 100644 ta/qcom_pas/src/pas_fuse.c diff --git a/ta/qcom_pas/include/pas_fuse.h b/ta/qcom_pas/include/pas_fuse.h new file mode 100644 index 000000000..2cc8bb029 --- /dev/null +++ b/ta/qcom_pas/include/pas_fuse.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_FUSE_H +#define __PAS_FUSE_H + +#include +#include +#include + +#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_and_root_anchor(uint8_t *anchor, + bool *secboot_on); + +struct pas_fuse_hw_binding_info { + uint32_t oem_id; + uint32_t model_id; + uint32_t jtag_id; + uint32_t serial_num; + bool use_serial_num_override; + uint32_t soc_fam_dev; +}; + +TEE_Result pas_fuse_get_hw_binding_info(bool need_soc_vers, + struct pas_fuse_hw_binding_info *info); + +TEE_Result pas_fuse_get_eku_enforcement_en(bool *eku_enforced); + +bool pas_fuse_get_image_encryption_en(void); + +TEE_Result pas_fuse_get_segment_hash_len(uint32_t root_cert_sel, + uint32_t *hash_len); + +#endif /* __PAS_FUSE_H */ diff --git a/ta/qcom_pas/src/pas_fuse.c b/ta/qcom_pas/src/pas_fuse.c new file mode 100644 index 000000000..241942de6 --- /dev/null +++ b/ta/qcom_pas/src/pas_fuse.c @@ -0,0 +1,173 @@ +// 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; + + 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) +{ + TEE_CloseTASession(fuse_session); +} + +static TEE_Result fuse_pta_invoke(uint32_t cmd, uint32_t param_types, + TEE_Param params[TEE_NUM_PARAMS]) +{ + return TEE_InvokeTACommand(fuse_session, TEE_TIMEOUT_INFINITE, cmd, + param_types, params, NULL); +} + +TEE_Result pas_fuse_get_secboot_and_root_anchor(uint8_t *anchor, + bool *secboot_on) +{ + TEE_Param params[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t pt = 0; + + *secboot_on = false; + + 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; + } + *secboot_on = params[0].value.a != 0; + + memset(params, 0, sizeof(params)); + 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(bool need_soc_vers, + 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->oem_id = params[0].value.a; + info->model_id = params[0].value.b; + info->jtag_id = params[1].value.a; + info->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; + + if (!need_soc_vers) + return TEE_SUCCESS; + + 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_SOC_HW_VERSION, pt, params); + if (res) { + EMSG("PAS fuse: cannot read SOC_HW_VERSION: %#"PRIx32, res); + return res; + } + info->soc_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; +} + +bool pas_fuse_get_image_encryption_en(void) +{ + 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_IMAGE_ENCRYPTION_EN, pt, + params); + if (res) { + EMSG("PAS fuse: cannot read image encryption fuse: %#"PRIx32, + res); + return true; + } + + return params[0].value.a; +} + +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; +} diff --git a/ta/qcom_pas/src/sub.mk b/ta/qcom_pas/src/sub.mk index c183ff77a..c5089810e 100644 --- a/ta/qcom_pas/src/sub.mk +++ b/ta/qcom_pas/src/sub.mk @@ -1,3 +1,3 @@ global-incdirs-y += ../include srcs-y += qcom_pas.c -srcs-$(CFG_QCOM_PAS_AUTH) += pas_auth.c pas_mbn_parser.c pas_meta.c +srcs-$(CFG_QCOM_PAS_AUTH) += pas_auth.c pas_fuse.c pas_mbn_parser.c pas_meta.c From e21e66c1cd9bf365b1f6e933103970889324ab44 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 15 Aug 2026 10:06:00 +0530 Subject: [PATCH 7/8] ta: qcom_pas: verify image signature and device bindings Segment-hash verification proves an image matches its own digest table; it cannot prove who signed it or that it is permitted on this device, so establish provenance before the peripheral leaves reset. The fuse read selecting this path fails closed: an error means secure boot enabled, so a transient failure cannot downgrade a fused board to hash-only verification. UIE-encrypted and QTI-countersigned images are refused, as neither is implemented here, and the chain is limited to one ECDSA P-384 root. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 --- ta/qcom_pas/include/pas_policy.h | 22 + ta/qcom_pas/include/pas_sig.h | 41 ++ ta/qcom_pas/include/pas_sig_auth.h | 43 ++ ta/qcom_pas/src/pas_auth.c | 31 +- ta/qcom_pas/src/pas_policy.c | 62 +++ ta/qcom_pas/src/pas_sig.c | 516 +++++++++++++++++++++++ ta/qcom_pas/src/pas_sig_auth.c | 431 +++++++++++++++++++ ta/qcom_pas/src/qcom_pas.c | 12 +- ta/qcom_pas/src/sub.mk | 2 +- ta/qcom_pas/src/user_ta_header_defines.h | 12 +- ta/qcom_pas/user_ta.mk | 2 +- 11 files changed, 1165 insertions(+), 9 deletions(-) create mode 100644 ta/qcom_pas/include/pas_policy.h create mode 100644 ta/qcom_pas/include/pas_sig.h create mode 100644 ta/qcom_pas/include/pas_sig_auth.h create mode 100644 ta/qcom_pas/src/pas_policy.c create mode 100644 ta/qcom_pas/src/pas_sig.c create mode 100644 ta/qcom_pas/src/pas_sig_auth.c diff --git a/ta/qcom_pas/include/pas_policy.h b/ta/qcom_pas/include/pas_policy.h new file mode 100644 index 000000000..2a54b6500 --- /dev/null +++ b/ta/qcom_pas/include/pas_policy.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_POLICY_H +#define __PAS_POLICY_H + +#include +#include + +enum pas_sign_authority { + PAS_OEM_SIGNED = 0, + PAS_QTI_SIGNED = 1, + PAS_DOUBLE_SIGNED = 2, +}; + +TEE_Result pas_policy_expected_swid(uint32_t pas_id, uint32_t *swid); + +enum pas_sign_authority pas_policy_signer(uint32_t swid); + +#endif /* __PAS_POLICY_H */ diff --git a/ta/qcom_pas/include/pas_sig.h b/ta/qcom_pas/include/pas_sig.h new file mode 100644 index 000000000..8ed15867e --- /dev/null +++ b/ta/qcom_pas/include/pas_sig.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_AUTH_SIG_H +#define __PAS_AUTH_SIG_H + +#include +#include +#include +#include + +#define PAS_AUTH_MAX_HASH_SIZE 48U +#define PAS_AUTH_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_of_trust(uint32_t hash_algo, size_t 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 *hash_algo); + +TEE_Result pas_sig_verify_signature(uint32_t sig_algo, uint32_t 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 /* __PAS_AUTH_SIG_H */ diff --git a/ta/qcom_pas/include/pas_sig_auth.h b/ta/qcom_pas/include/pas_sig_auth.h new file mode 100644 index 000000000..758e58f3f --- /dev/null +++ b/ta/qcom_pas/include/pas_sig_auth.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_SIG_AUTH_H +#define __PAS_SIG_AUTH_H + +#include +#include +#include + +#ifdef CFG_QCOM_PAS_AUTH +TEE_Result pas_sig_auth_hash_len(const struct pas_md_slot *slot, + uint32_t *hash_len); + +TEE_Result pas_sig_auth_authenticate(const struct pas_mbn *hs, + const uint8_t *meta_data, + size_t meta_data_size, + uint32_t pas_id, uint32_t hash_len, + const uint8_t *anchor); +#else +static inline TEE_Result +pas_sig_auth_hash_len(const struct pas_md_slot *slot __unused, + uint32_t *hash_len) +{ + *hash_len = TEE_SHA384_HASH_SIZE; + return TEE_SUCCESS; +} + +static inline TEE_Result +pas_sig_auth_authenticate(const struct pas_mbn *hs __unused, + const uint8_t *meta_data __unused, + size_t meta_data_size __unused, + uint32_t pas_id __unused, + uint32_t hash_len __unused, + const uint8_t *anchor __unused) +{ + return TEE_SUCCESS; +} +#endif /* CFG_QCOM_PAS_AUTH */ + +#endif /* __PAS_SIG_AUTH_H */ diff --git a/ta/qcom_pas/src/pas_auth.c b/ta/qcom_pas/src/pas_auth.c index 6475d89ef..bc7b1d0ca 100644 --- a/ta/qcom_pas/src/pas_auth.c +++ b/ta/qcom_pas/src/pas_auth.c @@ -4,11 +4,15 @@ */ #include +#include #include #include +#include +#include #include #include #include +#include #include #include @@ -81,7 +85,9 @@ TEE_Result pas_auth_save_metadata(struct qcom_pas_session *s, uint32_t pt, TEE_Result pas_auth_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; + bool secboot_on = false; uint32_t hash_len = 0; if (!slot) { @@ -97,16 +103,37 @@ TEE_Result pas_auth_authenticate(struct qcom_pas_session *s, uint32_t pas_id) return res; } + res = pas_fuse_get_secboot_and_root_anchor(anchor, &secboot_on); + if (res) + secboot_on = true; + + res = pas_sig_auth_hash_len(slot, &hash_len); + if (res) { + EMSG("PAS auth: cannot pick hash size: %#"PRIx32, res); + goto out; + } + res = pas_mbn_parse(slot->meta_data, slot->meta_data_size, hash_len, &slot->mbn); if (res) { EMSG("PAS auth: MBN parse failed: %#"PRIx32, res); - return res; + goto out; + } + + if (secboot_on) { + res = pas_sig_auth_authenticate(&slot->mbn, slot->meta_data, + slot->meta_data_size, pas_id, + 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_reset(struct qcom_pas_session *s, diff --git a/ta/qcom_pas/src/pas_policy.c b/ta/qcom_pas/src/pas_policy.c new file mode 100644 index 000000000..0303ab73a --- /dev/null +++ b/ta/qcom_pas/src/pas_policy.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#define PAS_ID_QDSP6 1 +#define PAS_ID_WPSS 6 +#define PAS_ID_VENUS 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 SECBOOT_ADSP_SW_TYPE 0x04 +#define SECBOOT_WCNSS_SW_TYPE 0x0D +#define SECBOOT_VIDEO_SW_TYPE 0x0E +#define SECBOOT_CDSP_SW_TYPE 0x17 +#define SECBOOT_CAMERA_FW_SW_TYPE 0x34 +#define SECBOOT_CDSP1_SW_TYPE 0x44 +#define SECBOOT_GPDSP0_SW_TYPE 0x58 +#define SECBOOT_GPDSP1_SW_TYPE 0x5A + +static const struct { + uint32_t pas_id; + uint32_t swid; +} pas_swid_map[] = { + { PAS_ID_QDSP6, SECBOOT_ADSP_SW_TYPE }, + { PAS_ID_WPSS, SECBOOT_WCNSS_SW_TYPE }, + { PAS_ID_VENUS, SECBOOT_VIDEO_SW_TYPE }, + { PAS_ID_TURING, SECBOOT_CDSP_SW_TYPE }, + { PAS_ID_TURING1, SECBOOT_CDSP1_SW_TYPE }, + { PAS_ID_CAMERA, SECBOOT_CAMERA_FW_SW_TYPE }, + { PAS_ID_GPDSP0, SECBOOT_GPDSP0_SW_TYPE }, + { PAS_ID_GPDSP1, SECBOOT_GPDSP1_SW_TYPE }, +}; + +TEE_Result pas_policy_expected_swid(uint32_t pas_id, uint32_t *swid) +{ + size_t i = 0; + + if (!swid) + return TEE_ERROR_BAD_PARAMETERS; + + for (i = 0; i < ARRAY_SIZE(pas_swid_map); i++) { + if (pas_swid_map[i].pas_id == pas_id) { + *swid = pas_swid_map[i].swid; + return TEE_SUCCESS; + } + } + + return TEE_ERROR_NOT_SUPPORTED; +} + +enum pas_sign_authority pas_policy_signer(uint32_t swid __unused) +{ + return PAS_OEM_SIGNED; +} diff --git a/ta/qcom_pas/src/pas_sig.c b/ta/qcom_pas/src/pas_sig.c new file mode 100644 index 000000000..fac087440 --- /dev/null +++ b/ta/qcom_pas/src/pas_sig.c @@ -0,0 +1,516 @@ +// 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) + +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: clear expired/future flags, keep rest fatal. */ + flags &= ~(uint32_t)(MBEDTLS_X509_BADCERT_EXPIRED | + MBEDTLS_X509_BADCERT_FUTURE); + if (rc && flags) { + 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[PAS_AUTH_MAX_HASH_SIZE] = { }; + TEE_OperationHandle op = TEE_HANDLE_NULL; + TEE_Result res = TEE_ERROR_GENERIC; + size_t len = sizeof(digest); + + if (!data || !expected || !hash_len || hash_len > sizeof(digest)) + return TEE_ERROR_BAD_PARAMETERS; + + res = TEE_AllocateOperation(&op, hash_algo, TEE_MODE_DIGEST, 0); + if (res != TEE_SUCCESS) + return res; + + res = TEE_DigestDoFinal(op, data, data_len, digest, &len); + if (res != TEE_SUCCESS) + goto out; + + if (len != hash_len) { + res = TEE_ERROR_SECURITY; + goto out; + } + + if (consttime_memcmp(digest, expected, 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_sig_check_root_of_trust(uint32_t hash_algo, size_t 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(hash_algo, root_der, root_der_len, + expected, 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 *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 || !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: + *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 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_AUTH_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_AUTH_MAX_SIG_SIZE) + return TEE_ERROR_SECURITY; + + res = md_from_tee(hash_algo, &md); + if (res != TEE_SUCCESS) + return res; + + res = digest(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/pas_sig_auth.c b/ta/qcom_pas/src/pas_sig_auth.c new file mode 100644 index 000000000..b636f8f48 --- /dev/null +++ b/ta/qcom_pas/src/pas_sig_auth.c @@ -0,0 +1,431 @@ +// 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 +#include + +#define SECBOOT_METADATA_MAJOR_V0 0U +#define SECBOOT_METADATA_MAJOR_V1 1U +#define SECBOOT_METADATA_MINOR 0U + +static TEE_Result check_metadata_version(const struct pas_mbn *hs) +{ + TEE_Result res = TEE_ERROR_GENERIC; + struct pas_meta meta = { }; + + 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 in version check"); + return res; + } + + 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_mbn *hs, uint32_t pas_id) +{ + TEE_Result res = TEE_ERROR_GENERIC; + struct pas_meta meta = { }; + uint32_t expected = 0; + + 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 = 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_meta *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; +} + +struct pas_device_ids { + uint32_t oem_id; + uint32_t model_id; + uint32_t jtag_id; + uint32_t serial_num; +}; + +static TEE_Result check_oem_model_binding(const struct pas_meta *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_meta *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_meta *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_meta *meta, + uint32_t fam_dev) +{ + size_t i = 0; + + if (!(meta->flags & BIT32(PAS_META_FLAG_IN_USE_SOC_HW_VERSION))) + return TEE_SUCCESS; + + 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_mbn *hs) +{ + struct pas_fuse_hw_binding_info info = { }; + TEE_Result res = TEE_ERROR_GENERIC; + struct pas_device_ids ids = { }; + struct pas_meta meta = { }; + bool need_soc_vers = false; + + 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 in HW binding"); + return res; + } + + res = check_metadata_options(&meta); + if (res) + return res; + + need_soc_vers = meta.flags & BIT32(PAS_META_FLAG_IN_USE_SOC_HW_VERSION); + res = pas_fuse_get_hw_binding_info(need_soc_vers, &info); + if (res) + return res; + + ids.oem_id = info.oem_id; + ids.model_id = info.model_id; + ids.jtag_id = info.jtag_id; + ids.serial_num = info.serial_num; + + res = check_oem_model_binding(&meta, &ids); + if (res) + return res; + + res = check_jtag_binding(&meta, &ids); + if (res) + return res; + + res = check_serial_binding(&meta, &ids, info.use_serial_num_override); + if (res) + return res; + + res = check_soc_vers_binding(&meta, info.soc_fam_dev); + if (res) + return res; + + return TEE_SUCCESS; +} + +static TEE_Result reject_if_encrypted(const struct pas_mbn *hs) +{ + if (!hs->uie_encrypted) + return TEE_SUCCESS; + + if (!pas_fuse_get_image_encryption_en()) + return TEE_SUCCESS; + + EMSG("PAS auth: UIE image encryption not supported"); + + return TEE_ERROR_NOT_SUPPORTED; +} + +static TEE_Result reject_if_double_signed(const struct pas_mbn *hs, + uint32_t pas_id) +{ + enum pas_sign_authority required = PAS_OEM_SIGNED; + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t swid = 0; + + res = pas_policy_expected_swid(pas_id, &swid); + if (res) { + EMSG("PAS auth: no SW_ID binding for pas_id %#"PRIx32, pas_id); + return res; + } + + required = pas_policy_signer(swid); + + if (required != PAS_OEM_SIGNED) { + EMSG("PAS auth: signer class %d for SW_ID %#"PRIx32 + " not implemented", required, swid); + return TEE_ERROR_NOT_SUPPORTED; + } + + if (hs->qti_certs || hs->qti_sig) { + EMSG("PAS auth: QTI-countersigned images are not supported"); + return TEE_ERROR_NOT_SUPPORTED; + } + + return TEE_SUCCESS; +} + +static TEE_Result verify_authenticity(const struct pas_mbn *hs, + uint32_t pas_id, + const uint8_t *anchor) +{ + uint32_t rot_hash_algo = TEE_ALG_SHA384; + TEE_Result res = TEE_ERROR_GENERIC; + 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 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; + } + + 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, 1, 0, &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 = check_metadata_version(hs); + if (res) + return res; + + res = check_sw_binding(hs, pas_id); + if (res) + return res; + + res = check_hw_binding(hs); + 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_signed_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; +} + +#define SECBOOT_DEFAULT_ROOT_CERT_SEL 0U + +TEE_Result pas_sig_auth_hash_len(const struct pas_md_slot *slot, + uint32_t *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, 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_authenticate(const struct pas_mbn *hs, + const uint8_t *meta_data, + size_t meta_data_size, + uint32_t pas_id, uint32_t hash_len, + const uint8_t *anchor) +{ + TEE_Result res = TEE_ERROR_GENERIC; + + res = reject_if_encrypted(hs); + if (res) + return res; + + res = reject_if_double_signed(hs, pas_id); + if (res) + return res; + + res = verify_authenticity(hs, pas_id, anchor); + if (res) + return res; + + res = pas_meta_verify_preamble(meta_data, meta_data_size, + hs->hash_table, hash_len); + if (res) + return res; + + return TEE_SUCCESS; +} diff --git a/ta/qcom_pas/src/qcom_pas.c b/ta/qcom_pas/src/qcom_pas.c index 0eb2185b2..3b57228eb 100644 --- a/ta/qcom_pas/src/qcom_pas.c +++ b/ta/qcom_pas/src/qcom_pas.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -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/sub.mk b/ta/qcom_pas/src/sub.mk index c5089810e..5be520cdb 100644 --- a/ta/qcom_pas/src/sub.mk +++ b/ta/qcom_pas/src/sub.mk @@ -1,3 +1,3 @@ global-incdirs-y += ../include srcs-y += qcom_pas.c -srcs-$(CFG_QCOM_PAS_AUTH) += pas_auth.c pas_fuse.c pas_mbn_parser.c pas_meta.c +srcs-$(CFG_QCOM_PAS_AUTH) += pas_auth.c pas_fuse.c pas_mbn_parser.c pas_meta.c pas_policy.c pas_sig.c pas_sig_auth.c diff --git a/ta/qcom_pas/src/user_ta_header_defines.h b/ta/qcom_pas/src/user_ta_header_defines.h index f3122c028..3a47ec5bc 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 a13d7c7a1..118d5bd79 100644 --- a/ta/qcom_pas/user_ta.mk +++ b/ta/qcom_pas/user_ta.mk @@ -1,7 +1,7 @@ user-ta-uuid := cff7d191-7ca0-4784-af13-48223b9a4fbe 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 From b6a5c63183e4142efab3c0771cec2cb356313ae7 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 15 Aug 2026 10:07:00 +0530 Subject: [PATCH 8/8] plat-qcom: hoya: lemans: wire up fuse PTA and QFPROM for PAS authentication Signature authentication reads fuses at runtime, so CFG_QCOM_PAS_AUTH alone is not enough: the fuse PTA, the qfprom driver behind it, and the CMD_DB/RPMH client its write path needs must all come up with it. Fold the existing fuse-provisioning enable into the same block so it and the new fuse-PTA consumer share one QFPROM enable and cannot drift apart. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 --- core/arch/arm/plat-qcom/hoya/lemans/target.mk | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index 95c04859f..36e824d2a 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -7,12 +7,6 @@ ifneq ($(CFG_INSECURE),y) CFG_QCOM_QFPROM_FUSEPROV ?= y endif -ifeq ($(CFG_QCOM_QFPROM_FUSEPROV),y) -$(call force,CFG_QCOM_CMD_DB,y) -$(call force,CFG_QCOM_RPMH_CLIENT,y) -$(call force,CFG_QCOM_QFPROM,y) -endif - CFG_QCOM_PAS_PTA ?= y ifeq ($(CFG_QCOM_PAS_PTA),y) @@ -24,3 +18,16 @@ 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) +endif + +ifneq ($(filter y,$(CFG_QCOM_QFPROM_FUSEPROV) $(CFG_QCOM_FUSE_PTA)),) +$(call force,CFG_QCOM_QFPROM,y) +endif + +ifeq ($(CFG_QCOM_QFPROM),y) +$(call force,CFG_QCOM_CMD_DB,y) +$(call force,CFG_QCOM_RPMH_CLIENT,y) +endif