From e01f93eec479912247997b4dd9629efa074c4acd Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Thu, 30 Jul 2026 14:29:46 +0800 Subject: [PATCH 1/8] fix(esp_tee): Reject re-entrant secure service calls from the REE --- .../main/arch/riscv/esp_tee_asm_utils.inc | 19 ++++++ .../main/arch/riscv/esp_tee_vectors_clic.S | 23 +++++++ .../main/arch/riscv/esp_tee_vectors_plic.S | 23 +++++++ .../tee_test_fw/main/test_esp_tee_interrupt.c | 60 +++++++++++++++++-- 4 files changed, 121 insertions(+), 4 deletions(-) diff --git a/components/esp_tee/subproject/main/arch/riscv/esp_tee_asm_utils.inc b/components/esp_tee/subproject/main/arch/riscv/esp_tee_asm_utils.inc index 7e638cf55af..626dfdc0bf6 100644 --- a/components/esp_tee/subproject/main/arch/riscv/esp_tee_asm_utils.inc +++ b/components/esp_tee/subproject/main/arch/riscv/esp_tee_asm_utils.inc @@ -22,6 +22,7 @@ .equ SAVE_REGS, 32 .equ CONTEXT_SIZE, (SAVE_REGS * 4) .equ MAGIC, 0x1f +.equ NS_INT_RTN_MAGIC, (MAGIC << 12) /* Macro which first allocates space on the stack to save general * purpose registers, and then save them. GP register is excluded. @@ -222,6 +223,24 @@ #endif .endm +/** + * SVC_LOCK_ACQUIRE / SVC_LOCK_RELEASE + * Hold _s_svc_lock for the duration of a secure service call, so that a call issued + * while another one is active branches to \fail instead of clobbering it. + * + * Clobbers: \tx + */ +.macro SVC_LOCK_ACQUIRE tx, fail + la \tx, _s_svc_lock + amoswap.w.aq \tx, \tx, (\tx) + bnez \tx, \fail +.endm + +.macro SVC_LOCK_RELEASE tx + la \tx, _s_svc_lock + amoswap.w.rl zero, zero, (\tx) +.endm + /** * VALIDATE_REE_SP * Validate an REE-supplied sp before the TEE stores through it. The TEE region is diff --git a/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors_clic.S b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors_clic.S index debc4f77a3d..7c56c5f0bc6 100644 --- a/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors_clic.S +++ b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors_clic.S @@ -61,6 +61,10 @@ _ns_sp_max: _ns_int_rtn: .word 0 + .global _s_svc_lock +_s_svc_lock: + .word 0 + .section .exception_vectors.text, "ax" /* Exception handler. */ @@ -178,6 +182,9 @@ _1: lui t0, ESP_TEE_M2U_SWITCH_MAGIC beq a1, t0, _skip_ctx_restore + /* The secure service has returned - the REE may issue the next one */ + SVC_LOCK_RELEASE t0 + /* Check if we need to restore the MINTTHRESH register */ la t0, _s_intr_thresh lw t1, 0(t0) @@ -218,10 +225,13 @@ _skip_ctx_restore: /* U-mode ecall handler */ _user_ecall: /* Check whether we are returning after servicing an U-mode interrupt */ + li t0, NS_INT_RTN_MAGIC + bne a0, t0, _svc_call_enter la t0, _ns_int_rtn lw t0, 0(t0) bnez t0, _rtn_from_ns_int +_svc_call_enter: /* Reject an sp whose frame would be out-of-bounds */ VALIDATE_REE_SP CONTEXT_SIZE, t0, 0 @@ -233,6 +243,9 @@ _user_ecall: save_general_regs save_mepc + /* Claim the TEE before touching any of its state */ + SVC_LOCK_ACQUIRE t0, _svc_call_reject + # Check if REE is in a critical section csrr t0, CSR_UINTTHRESH # t0 = current UINTTHRESH beqz t0, _process_ecall # if threshold == 0 -> continue @@ -314,6 +327,16 @@ _3: mret + /* Discard a service call that arrived while another one was active */ +_svc_call_reject: + addi sp, sp, CONTEXT_SIZE /* t0 is the only register clobbered past the context save */ + csrr t0, mepc + addi t0, t0, 4 /* resume the REE after its ecall */ + csrw mepc, t0 + csrr t0, mscratch + li a0, -1 + mret + .size _ecall_handler, .-_ecall_handler /* This is the interrupt handler for the U-mode interrupts. diff --git a/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors_plic.S b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors_plic.S index 28437683e35..9615594ba0d 100644 --- a/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors_plic.S +++ b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors_plic.S @@ -65,6 +65,10 @@ _ns_sp_max: _ns_int_rtn: .word 0 + .global _s_svc_lock +_s_svc_lock: + .word 0 + .section .exception_vectors.text, "ax" /* Exception handler. */ @@ -167,6 +171,9 @@ _machine_ecall: lui t0, ESP_TEE_M2U_SWITCH_MAGIC beq a1, t0, _skip_ctx_restore + /* The secure service has returned - the REE may issue the next one */ + SVC_LOCK_RELEASE t0 + /* Check if we need to restore the MXINT threshold register */ la t0, _s_intr_thresh lw t1, 0(t0) @@ -208,10 +215,13 @@ _skip_ctx_restore: /* U-mode ecall handler */ _user_ecall: /* Check whether we are returning after servicing an U-mode interrupt */ + li t0, NS_INT_RTN_MAGIC + bne a0, t0, _svc_call_enter la t0, _ns_int_rtn lw t0, 0(t0) bnez t0, _rtn_from_ns_int +_svc_call_enter: /* Reject an sp whose frame would be out-of-bounds */ VALIDATE_REE_SP CONTEXT_SIZE, t0, 0 @@ -223,6 +233,9 @@ _user_ecall: save_general_regs save_mepc + /* Claim the TEE before touching any of its state */ + SVC_LOCK_ACQUIRE t0, _svc_call_reject + # Check if REE is in a critical section li t0, PLIC_UXINT_THRESH_REG lw t1, 0(t0) # t1 = current UXINT threshold @@ -293,6 +306,16 @@ _rtn_from_ns_int: mret + /* Discard a service call that arrived while another one was active */ +_svc_call_reject: + addi sp, sp, CONTEXT_SIZE /* t0 is the only register clobbered past the context save */ + csrr t0, mepc + addi t0, t0, 4 /* resume the REE after its ecall */ + csrw mepc, t0 + csrr t0, mscratch + li a0, -1 + mret + .size _ecall_handler, .-_ecall_handler /* This is the interrupt handler for the U-mode interrupts. diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c index d8da5c75ba8..ba15ed54781 100644 --- a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -34,7 +34,7 @@ static bool IRAM_ATTR test_timer_on_alarm_cb(gptimer_handle_t timer, const gptim return true; } -static void test_timer_init(volatile uint32_t *arg) +static void test_timer_init_with_cb(gptimer_alarm_cb_t on_alarm, void *arg) { /* Select and initialize basic parameters of the timer */ gptimer_config_t timer_config = { @@ -45,9 +45,9 @@ static void test_timer_init(volatile uint32_t *arg) ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer)); gptimer_event_callbacks_t cbs = { - .on_alarm = test_timer_on_alarm_cb, + .on_alarm = on_alarm, }; - ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, (void *)arg)); + ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, arg)); ESP_ERROR_CHECK(gptimer_enable(gptimer)); @@ -60,6 +60,11 @@ static void test_timer_init(volatile uint32_t *arg) ESP_ERROR_CHECK(gptimer_start(gptimer)); } +static void test_timer_init(volatile uint32_t *arg) +{ + test_timer_init_with_cb(test_timer_on_alarm_cb, (void *)arg); +} + static void test_timer_deinit(void) { ESP_ERROR_CHECK(gptimer_stop(gptimer)); @@ -113,6 +118,53 @@ TEST_CASE("Test REE interrupt in TEE", "[basic]") TEST_ASSERT_MESSAGE((mode == ESP_CPU_NS_MODE), "Incorrect privilege mode!"); } +typedef struct { + volatile uint32_t intr_count; + volatile uint32_t accepted_count; +} test_nested_svc_call_ctx_t; + +static bool IRAM_ATTR test_nested_svc_call_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) +{ + test_nested_svc_call_ctx_t *ctx = (test_nested_svc_call_ctx_t *)user_data; + + /* Issued while the preempted service call sits parked inside the TEE */ + uint32_t ret = esp_tee_service_call(3, SS_ESP_TEE_TEST_SERVICE_ADD, 200, 100); + if (ret != UINT32_MAX) { + ctx->accepted_count = ctx->accepted_count + 1; + } + ctx->intr_count = ctx->intr_count + 1; + + esp_rom_printf("[mode: %d] Nested service call from ISR (%d) returned 0x%x\n", + esp_cpu_get_curr_privilege_level(), ctx->intr_count, ret); + return true; +} + +TEST_CASE("Test nested secure service call from an REE interrupt", "[basic]") +{ + TEST_ASSERT_EQUAL(ESP_CPU_NS_MODE, esp_cpu_get_curr_privilege_level()); + + static test_nested_svc_call_ctx_t ctx; + ctx.intr_count = 0; + ctx.accepted_count = 0; + + test_timer_init_with_cb(test_nested_svc_call_cb, &ctx); + + /* Runs in the TEE until the ISR above has fired ESP_TEE_TEST_INTR_ITER times */ + uint32_t val = esp_tee_service_call(2, SS_ESP_TEE_TEST_REE_INTR_IN_TEE, &ctx.intr_count); + TEST_ASSERT_EQUAL_UINT32(0, val); + + test_timer_deinit(); + + /* Every call made from the ISR should have been rejected by the TEE */ + TEST_ASSERT_EQUAL_UINT32(0, ctx.accepted_count); + + /* The parked call resumed and completed, so the TEE takes calls again */ + val = esp_tee_service_call(3, SS_ESP_TEE_TEST_SERVICE_ADD, 200, 100); + TEST_ASSERT_EQUAL_UINT32(300, val); + + TEST_ASSERT_EQUAL(ESP_CPU_NS_MODE, esp_cpu_get_curr_privilege_level()); +} + TEST_CASE("Test TEE interrupt in REE", "[basic]") { esp_cpu_priv_mode_t mode = esp_cpu_get_curr_privilege_level(); From eb0a81b89a2c8416c1868ede8f619dff97acf30c Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Mon, 6 Jul 2026 17:25:17 +0530 Subject: [PATCH 2/8] feat(hal): Add LL-API to check whether RNG is enabled - Also add RNG LL-APIs for ESP32-C61 --- components/hal/esp32c5/include/hal/rng_ll.h | 26 ++++- components/hal/esp32c6/include/hal/rng_ll.h | 42 +++++++ components/hal/esp32c61/include/hal/rng_ll.h | 115 +++++++++++++++++++ components/hal/esp32h2/include/hal/rng_ll.h | 42 +++++++ 4 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 components/hal/esp32c6/include/hal/rng_ll.h create mode 100644 components/hal/esp32c61/include/hal/rng_ll.h create mode 100644 components/hal/esp32h2/include/hal/rng_ll.h diff --git a/components/hal/esp32c5/include/hal/rng_ll.h b/components/hal/esp32c5/include/hal/rng_ll.h index fd3cd432fe3..43d63f54d4e 100644 --- a/components/hal/esp32c5/include/hal/rng_ll.h +++ b/components/hal/esp32c5/include/hal/rng_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -12,6 +12,10 @@ #include "hal/lp_clkrst_ll.h" #define RNG_LL_DEPENDS_ON_LP_PERIPH 1 +//For ESP32C5, RNG needs to be reset and enabled again when wakeup from sleep +#define RNG_LL_NEEDS_RESET_WHEN_WAKEUP 1 +//Default value for the RNG timer clock divider +#define RNG_LL_CFG_PSCALE 255 #ifdef __cplusplus extern "C" { @@ -27,6 +31,16 @@ static inline void rng_ll_enable_sample(bool enable) LPPERI.rng_cfg.rng_sample_enable = enable; } +/** + * @brief Set RNG timer prescaler + * + * @param prescaler Timer prescaler value (0-255) + */ +static inline void rng_ll_set_timer_prescaler(uint8_t prescaler) +{ + LPPERI.rng_cfg.rng_timer_pscale = prescaler; +} + /** * @brief Enable or disable rng xor rtc timer. * @@ -64,6 +78,7 @@ static inline void rng_ll_reset(void) static inline void rng_ll_enable(void) { _lp_clkrst_ll_enable_rng_clock(true); + rng_ll_set_timer_prescaler(RNG_LL_CFG_PSCALE); rng_ll_enable_sample(true); rng_ll_enable_rtc_timer(true); rng_ll_enable_rng_timer(true); @@ -82,6 +97,15 @@ static inline void rng_ll_disable(void) _lp_clkrst_ll_enable_rng_clock(false); } +/** + * @brief Check that the RNG is live: clocked and out of reset. + * + * @return True if the RNG is enabled and operational, false otherwise. + */ +static inline bool rng_ll_is_enabled(void) +{ + return LPPERI.clk_en.rng_ck_en && !LPPERI.reset_en.lp_rng_reset_en; +} #ifdef __cplusplus } diff --git a/components/hal/esp32c6/include/hal/rng_ll.h b/components/hal/esp32c6/include/hal/rng_ll.h new file mode 100644 index 00000000000..24c854e3ec8 --- /dev/null +++ b/components/hal/esp32c6/include/hal/rng_ll.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/soc.h" +#include "soc/lpperi_reg.h" +#include "soc/lpperi_struct.h" +#include "hal/lp_clkrst_ll.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static inline uint32_t rng_ll_read_data(void) +{ + return REG_READ(LPPERI_RNG_DATA_REG); +} + +static inline void rng_ll_enable(void) +{ + _lp_clkrst_ll_enable_rng_clock(true); +} + +static inline void rng_ll_disable(void) +{ + _lp_clkrst_ll_enable_rng_clock(false); +} + +static inline bool rng_ll_is_enabled(void) +{ + return LPPERI.clk_en.rng_ck_en; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32c61/include/hal/rng_ll.h b/components/hal/esp32c61/include/hal/rng_ll.h new file mode 100644 index 00000000000..03796b7a82e --- /dev/null +++ b/components/hal/esp32c61/include/hal/rng_ll.h @@ -0,0 +1,115 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/soc.h" +#include "soc/lpperi_reg.h" +#include "soc/lpperi_struct.h" + +//Default value for the RNG timer clock divider +#define RNG_LL_CFG_PSCALE 255 + +#ifdef __cplusplus +extern "C" { +#endif + +static inline uint32_t rng_ll_read_data(void) +{ + return REG_READ(LPPERI_RNG_DATA_SYNC_REG); +} + +/** + * @brief Enable or disable RNG sampling. + * + * @param enable True to enable, False to disable + */ +static inline void rng_ll_enable_sample(bool enable) +{ + REG_SET_FIELD(LPPERI_RNG_CFG_REG, LPPERI_RNG_SAMPLE_ENABLE, enable); +} + +/** + * @brief Set RNG timer prescaler + * + * @param prescaler Timer prescaler value (0-255) + */ +static inline void rng_ll_set_timer_prescaler(uint8_t prescaler) +{ + REG_SET_FIELD(LPPERI_RNG_CFG_REG, LPPERI_RNG_TIMER_PSCALE, prescaler); +} + +/** + * @brief Enable or disable rng xor rtc timer. + * + * @param enable True to enable, False to disable + */ +static inline void rng_ll_enable_rtc_timer(bool enable) +{ + REG_SET_FIELD(LPPERI_RNG_CFG_REG, LPPERI_RTC_TIMER_EN, enable ? 0x3 : 0x0); +} + +/** + * @brief Enable or disable rng xor async rng timer. + * + * @param enable True to enable, False to disable + */ +static inline void rng_ll_enable_rng_timer(bool enable) +{ + REG_SET_FIELD(LPPERI_RNG_CFG_REG, LPPERI_RNG_TIMER_EN, enable); +} + +/** + * @brief Reset RNG. + */ +static inline void rng_ll_reset(void) +{ + LPPERI.reset_en.lp_rng_reset_en = 1; + LPPERI.reset_en.lp_rng_reset_en = 0; +} + +/** + * @brief Enable RNG module + * + * TODO: unify in rng_hal.c + */ +static inline void rng_ll_enable(void) +{ + LPPERI.clk_en.rng_ck_en = 1; + rng_ll_set_timer_prescaler(RNG_LL_CFG_PSCALE); + rng_ll_enable_sample(true); + rng_ll_enable_rtc_timer(true); + rng_ll_enable_rng_timer(true); +} + +/** + * @brief Disable RNG module + * + * TODO: unify in rng_hal.c + */ +static inline void rng_ll_disable(void) +{ + rng_ll_enable_sample(false); + rng_ll_enable_rtc_timer(false); + rng_ll_enable_rng_timer(false); + LPPERI.clk_en.rng_ck_en = 0; +} + +/** + * @brief Check that the RNG is live: clocked and out of reset. + * + * @return True if the RNG is enabled and operational, false otherwise. + */ +static inline bool rng_ll_is_enabled(void) +{ + return LPPERI.clk_en.rng_ck_en && !LPPERI.reset_en.lp_rng_reset_en; +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32h2/include/hal/rng_ll.h b/components/hal/esp32h2/include/hal/rng_ll.h new file mode 100644 index 00000000000..d6ed15f98b7 --- /dev/null +++ b/components/hal/esp32h2/include/hal/rng_ll.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "soc/soc.h" +#include "soc/lpperi_reg.h" +#include "soc/lpperi_struct.h" +#include "hal/lp_clkrst_ll.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static inline uint32_t rng_ll_read_data(void) +{ + return REG_READ(LPPERI_RNG_DATA_REG); +} + +static inline void rng_ll_enable(void) +{ + _lp_clkrst_ll_enable_rng_clock(true); +} + +static inline void rng_ll_disable(void) +{ + _lp_clkrst_ll_enable_rng_clock(false); +} + +static inline bool rng_ll_is_enabled(void) +{ + return LPPERI_REG_GET(clk_en.rng_ck_en); +} + +#ifdef __cplusplus +} +#endif From 8e34cc16cc1c5e4eb1f09662d0c748fa1754ef2e Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Mon, 6 Jul 2026 17:27:27 +0530 Subject: [PATCH 3/8] feat(esp_tee): Use CTR-DRBG for assisting random number generation in TEE - For ESP-TEE, fault-assert in `esp_random()` if the RNG is held in a freeze state --- components/esp_hw_support/hw_random.c | 9 ++- .../tee_sec_storage/tee_sec_storage.c | 18 +++-- .../main/soc/common/esp_tee_crypto_reset.c | 10 +++ .../mbedtls/esp_tee/esp_tee_mbedtls_config.h | 15 +++- components/mbedtls/port/esp_hardware.c | 69 ++++++++++++++++++- 5 files changed, 112 insertions(+), 9 deletions(-) diff --git a/components/esp_hw_support/hw_random.c b/components/esp_hw_support/hw_random.c index de41804fd63..9d7cdfcace3 100644 --- a/components/esp_hw_support/hw_random.c +++ b/components/esp_hw_support/hw_random.c @@ -17,15 +17,17 @@ #if !ESP_TEE_BUILD #include "esp_private/startup_internal.h" +#else +#include "esp_fault.h" #endif #include "hal/rtc_timer_hal.h" #if SOC_RNG_CLOCK_IS_INDEPENDENT #include "hal/lp_clkrst_ll.h" -#if SOC_RNG_BUF_CHAIN_ENTROPY_SOURCE || SOC_RNG_RTC_TIMER_ENTROPY_SOURCE -#include "hal/rng_ll.h" #endif +#if (SOC_RNG_CLOCK_IS_INDEPENDENT && (SOC_RNG_BUF_CHAIN_ENTROPY_SOURCE || SOC_RNG_RTC_TIMER_ENTROPY_SOURCE)) || ESP_TEE_BUILD +#include "hal/rng_ll.h" #endif #if defined CONFIG_IDF_TARGET_ESP32S3 @@ -74,6 +76,9 @@ uint32_t IRAM_ATTR esp_random(void) uint32_t result = 0; for (size_t i = 0; i < sizeof(result); i++) { do { +#if ESP_TEE_BUILD + ESP_FAULT_ASSERT(rng_ll_is_enabled()); +#endif ccount = esp_cpu_get_cycle_count(); result ^= REG_READ(WDEV_RND_REG); } while (ccount - last_ccount < cpu_to_apb_freq_ratio * APB_CYCLE_WAIT_NUM); diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c index f4b61fe1800..db22b85a970 100644 --- a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c @@ -12,7 +12,6 @@ #include "esp_fault.h" #include "esp_efuse.h" #include "esp_efuse_chip.h" -#include "esp_random.h" #include "spi_flash_mmap.h" #if SOC_HMAC_SUPPORTED #include "psa_crypto_driver_esp_hmac_opaque.h" @@ -314,6 +313,13 @@ bool esp_tee_sec_storage_is_key_tee_owned(const char *key_id) esp_err_t esp_tee_sec_storage_init(void) { + /* Explicitly seeds the CTR-DRBG before any PSA operations */ + uint8_t random; + psa_status_t ret = psa_generate_random(&random, sizeof(random)); + if (ret != PSA_SUCCESS) { + return ESP_FAIL; + } + nvs_sec_cfg_t cfg = {}; esp_err_t err = read_security_cfg_hmac(&cfg); if (err != ESP_OK) { @@ -479,9 +485,9 @@ static int generate_aes256_key(sec_stg_key_t *keyctx) } ESP_LOGD(TAG, "Generating AES-256 key..."); - esp_fill_random(&keyctx->aes256.key, AES256_KEY_LEN); + psa_status_t status = psa_generate_random(keyctx->aes256.key, AES256_KEY_LEN); - return 0; + return (status == PSA_SUCCESS) ? 0 : -1; } esp_err_t esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg) @@ -747,7 +753,11 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t } if (is_encrypt) { - esp_fill_random(iv, iv_len); + status = psa_generate_random(iv, iv_len); + if (status != PSA_SUCCESS) { + err = ESP_FAIL; + goto cleanup; + } size_t output_length = 0; status = psa_aead_encrypt(psa_key_id, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_len), diff --git a/components/esp_tee/subproject/main/soc/common/esp_tee_crypto_reset.c b/components/esp_tee/subproject/main/soc/common/esp_tee_crypto_reset.c index 6ade697f6b1..612076244c3 100644 --- a/components/esp_tee/subproject/main/soc/common/esp_tee_crypto_reset.c +++ b/components/esp_tee/subproject/main/soc/common/esp_tee_crypto_reset.c @@ -27,6 +27,9 @@ #if SOC_ECDSA_SUPPORTED #include "hal/ecdsa_ll.h" #endif +#if SOC_RNG_SUPPORTED +#include "hal/rng_ll.h" +#endif #include "esp_tee.h" #include "esp_attr.h" @@ -77,4 +80,11 @@ void IRAM_ATTR esp_tee_soc_reset_crypto_peripherals(void) ecdsa_ll_reset_register(); ecdsa_ll_enable_bus_clock(false); #endif + +#if SOC_RNG_SUPPORTED + rng_ll_enable(); +#if RNG_LL_NEEDS_RESET_WHEN_WAKEUP + rng_ll_reset(); +#endif +#endif } diff --git a/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h b/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h index ed14b4d095b..9bd76166b7d 100644 --- a/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h +++ b/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h @@ -43,6 +43,12 @@ #if SOC_AES_SUPPORTED #define ESP_AES_DRIVER_ENABLED #define MBEDTLS_PSA_ACCEL_KEY_TYPE_AES +#define MBEDTLS_PSA_ACCEL_ALG_ECB_NO_PADDING +#define MBEDTLS_PSA_ACCEL_ALG_CBC_NO_PADDING +#define MBEDTLS_PSA_ACCEL_ALG_CBC_PKCS7 +#define MBEDTLS_PSA_ACCEL_ALG_CFB +#define MBEDTLS_PSA_ACCEL_ALG_CTR +#define MBEDTLS_PSA_ACCEL_ALG_OFB #endif #define MBEDTLS_CIPHER_MODE_XTS @@ -120,13 +126,16 @@ /* Disable unused cipher/algorithm types */ #undef PSA_WANT_KEY_TYPE_ARIA #undef PSA_WANT_KEY_TYPE_CAMELLIA +#undef PSA_WANT_KEY_TYPE_CHACHA20 #undef PSA_WANT_KEY_TYPE_DES #undef PSA_WANT_ALG_RIPEMD160 +#undef PSA_WANT_ALG_STREAM_CIPHER #undef PSA_WANT_ALG_CHACHA20 #undef MBEDTLS_CHACHA20_C #undef PSA_WANT_ALG_CHACHA20_POLY1305 #undef MBEDTLS_CHACHAPOLY_C #undef PSA_WANT_ALG_CCM +#undef PSA_WANT_ALG_CCM_STAR_NO_TAG #undef PSA_WANT_ALG_CMAC /* Disable unused hash algorithms */ @@ -168,6 +177,8 @@ #undef MBEDTLS_SSL_SRV_C #undef PSA_WANT_ALG_TLS12_PRF +#undef PSA_WANT_ALG_TLS12_PSK_TO_MS +#undef PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS #undef PSA_WANT_ALG_PBKDF2_HMAC #undef PSA_WANT_ALG_PBKDF2_AES_CMAC_PRF_128 @@ -200,8 +211,8 @@ /* Disable self-test functions to save code size */ #undef MBEDTLS_SELF_TEST -/* TEE uses EXTERNAL_RNG, no need for CTR-DRBG */ -#undef MBEDTLS_CTR_DRBG_C +/* CTR-DRBG for strengthening the RNG operations in TEE */ +#define MBEDTLS_CTR_DRBG_C /* Disable PEM/Base64 — TEE uses DER format */ #undef MBEDTLS_PEM_PARSE_C diff --git a/components/mbedtls/port/esp_hardware.c b/components/mbedtls/port/esp_hardware.c index db79471b2ec..46c7baff656 100644 --- a/components/mbedtls/port/esp_hardware.c +++ b/components/mbedtls/port/esp_hardware.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,62 @@ #include #include "psa/crypto.h" +#if ESP_TEE_BUILD +#include "mbedtls/private/ctr_drbg.h" +#include "mbedtls/platform_util.h" +#include "esp_cpu.h" +#include "esp_fault.h" +#include "hal/efuse_hal.h" +#include "hal/rng_ll.h" + +#define CTR_DRBG_RESEED_INTERVAL 1024 + +static mbedtls_ctr_drbg_context s_ctr_drbg; + +static int esp_tee_entropy_func(void *data, unsigned char *output, unsigned int len) +{ + (void)data; + /* Explicitly enable the RNG */ + rng_ll_enable(); + esp_fill_random(output, len); + return 0; +} + +static void esp_tee_ctr_drbg_init(void) +{ + static bool s_ctr_drbg_initialized = false; + + if (!s_ctr_drbg_initialized) { + /* Personalization data for CTR-DRBG seeding */ + struct { + uint8_t mac[6]; + uint32_t random; + uint32_t cycle_cnt; + } data = {}; + + rng_ll_enable(); + data.random = esp_random(); + efuse_hal_get_mac(data.mac); + data.cycle_cnt = esp_cpu_get_cycle_count(); + + mbedtls_ctr_drbg_init(&s_ctr_drbg); + mbedtls_ctr_drbg_set_reseed_interval(&s_ctr_drbg, CTR_DRBG_RESEED_INTERVAL); + + int ret = mbedtls_ctr_drbg_seed(&s_ctr_drbg, esp_tee_entropy_func, NULL, + (const unsigned char *)&data, sizeof(data)); + mbedtls_platform_zeroize(&data, sizeof(data)); + if (ret != 0) { + abort(); + } + ESP_FAULT_ASSERT(ret == 0); + + s_ctr_drbg_initialized = true; + } + + ESP_FAULT_ASSERT(s_ctr_drbg_initialized); +} +#endif // ESP_TEE_BUILD + int mbedtls_hardware_poll( void *data, unsigned char *output, size_t len, size_t *olen ) { @@ -27,7 +83,18 @@ psa_status_t mbedtls_psa_external_get_random( if (context == NULL || output == NULL || output_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } + +#if ESP_TEE_BUILD + esp_tee_ctr_drbg_init(); + int ret = mbedtls_ctr_drbg_random(&s_ctr_drbg, output, output_size); + if (ret != 0) { + return PSA_ERROR_HARDWARE_FAILURE; + } + ESP_FAULT_ASSERT(ret == 0); +#else esp_fill_random(output, output_size); +#endif + *output_length = output_size; return PSA_SUCCESS; } From e9cd26221795a70e1d6fc43d9a8d127905d5b7f7 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Fri, 3 Jul 2026 17:19:17 +0530 Subject: [PATCH 4/8] change(esp_tee): Force non-deterministic ECDSA signing for TEE secure storage keys --- .../tee_sec_storage/tee_sec_storage.c | 6 +-- .../tee_test_fw/main/test_esp_tee_sec_stg.c | 52 +++++++++++++------ .../mbedtls/esp_tee/esp_tee_mbedtls_config.h | 6 +-- .../esp_ecdsa/psa_crypto_driver_esp_ecdsa.c | 4 ++ 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c index db22b85a970..6136b79591a 100644 --- a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c @@ -575,11 +575,9 @@ esp_err_t esp_tee_sec_storage_ecdsa_sign(const esp_tee_sec_storage_key_cfg_t *cf psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_VERIFY_HASH); - psa_algorithm_t ecdsa_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); -#if CONFIG_MBEDTLS_ECDSA_DETERMINISTIC - ecdsa_alg = PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256); -#endif + /* ECDSA signatures over TEE secure-storage keys are compulsorily non-deterministic */ + psa_algorithm_t ecdsa_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); psa_set_key_algorithm(&key_attributes, ecdsa_alg); diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c index 55dd7ff4b7e..b4a0820e635 100644 --- a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c @@ -145,6 +145,43 @@ TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1)", "[sec_stora } } +TEST_CASE("Test TEE Secure Storage - Signatures are non-deterministic (ecdsa_secp256r1)", "[sec_storage]") +{ + const size_t sig_len = 2 * ECDSA_SECP256R1_KEY_LEN; + + uint8_t msg_digest[SHA256_DIGEST_SZ]; + esp_fill_random(msg_digest, sizeof(msg_digest)); + + esp_tee_sec_storage_key_cfg_t key_cfg = { + .id = "ecdsa_nondet", + .type = ESP_SEC_STG_KEY_ECDSA_SECP256R1 + }; + + esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id); + TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg)); + + esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {}; + TEST_ESP_OK(esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey)); + + uint8_t signatures[MAX_SEC_STG_ITER][2 * ECDSA_SECP256R1_KEY_LEN]; + + for (unsigned int i = 0; i < MAX_SEC_STG_ITER; i++) { + esp_tee_sec_storage_ecdsa_sign_t sign = {}; + TEST_ESP_OK(esp_tee_sec_storage_ecdsa_sign(&key_cfg, msg_digest, sizeof(msg_digest), &sign)); + + TEST_ESP_OK(verify_ecdsa_sign(key_cfg.type, msg_digest, sizeof(msg_digest), &pubkey, &sign)); + + for (unsigned int j = 0; j < i; j++) { + TEST_ASSERT_TRUE_MESSAGE(memcmp(sign.signature, signatures[j], sig_len) != 0, + "ECDSA signature repeated - deterministic signing detected"); + } + memcpy(signatures[i], sign.signature, sig_len); + } + + TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id)); +} + #if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP384R1_SIGN TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp384r1)", "[sec_storage]") { @@ -686,21 +723,6 @@ static void test_ecdsa_sign(esp_ecdsa_curve_t curve) TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status); TEST_ASSERT_EQUAL(signature_len, 2 * key_len); -#if CONFIG_MBEDTLS_ECDSA_DETERMINISTIC - uint8_t signature_det_verify[2 * ECDSA_SECP384R1_KEY_LEN]; - size_t signature_det_verify_len = 0; - - status = psa_sign_hash(priv_key_id, - alg, - sha, sha_len, - signature_det_verify, 2 * key_len, - &signature_det_verify_len); - - TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status); - TEST_ASSERT_EQUAL(signature_det_verify_len, signature_len); - TEST_ASSERT_EQUAL_HEX8_ARRAY(signature, signature_det_verify, signature_len); -#endif - psa_set_key_type(&pub_key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(&pub_key_attr, key_len * 8); psa_set_key_usage_flags(&pub_key_attr, PSA_KEY_USAGE_VERIFY_HASH); diff --git a/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h b/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h index 9bd76166b7d..94fd58202c6 100644 --- a/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h +++ b/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h @@ -61,12 +61,10 @@ #endif #define PSA_WANT_ECC_SECP_R1_256 1 -#ifdef CONFIG_MBEDTLS_ECDSA_DETERMINISTIC -#define PSA_WANT_ALG_DETERMINISTIC_ECDSA 1 -#else +/* ECDSA signatures over TEE secure-storage keys are compulsorily + * non-deterministic (randomized nonce) */ #undef PSA_WANT_ALG_DETERMINISTIC_ECDSA #undef MBEDTLS_HMAC_DRBG_C -#endif #if SOC_SHA_SUPPORTED #define ESP_SHA_DRIVER_ENABLED diff --git a/components/mbedtls/port/psa_driver/esp_ecdsa/psa_crypto_driver_esp_ecdsa.c b/components/mbedtls/port/psa_driver/esp_ecdsa/psa_crypto_driver_esp_ecdsa.c index 44ab52e60bf..f7900c277c9 100644 --- a/components/mbedtls/port/psa_driver/esp_ecdsa/psa_crypto_driver_esp_ecdsa.c +++ b/components/mbedtls/port/psa_driver/esp_ecdsa/psa_crypto_driver_esp_ecdsa.c @@ -1003,6 +1003,10 @@ psa_status_t esp_ecdsa_opaque_sign_hash_complete( #if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN if (key_source == ESP_ECDSA_KEY_SOURCE_TEE) { /* TEE key path */ + if (PSA_ALG_ECDSA_IS_DETERMINISTIC(operation->alg)) { + ESP_LOGW(TAG, "Deterministic ECDSA unsupported for TEE keys; using randomized nonce"); + } + const char *tee_key_id = NULL; uint8_t stored_curve; From e5c3063deee1cefdbf630b3662da1d3a344c2732 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Wed, 5 Aug 2026 19:46:53 +0530 Subject: [PATCH 5/8] fix(esp_tee): Snapshot input arguments in TEE memory before secure service execution - Also fix the `tee_cli_app` build failure due to TEE heap size overflow --- components/esp_security/src/esp_ds.c | 42 +- .../main/core/esp_secure_services.c | 80 ++-- .../main/core/esp_secure_services_iram.c | 388 ++++++++++-------- .../main/include/esp_tee_memory_utils.h | 15 + .../tee_cli_app/sdkconfig.ci.minimal_tee | 4 +- .../tee_cli_app/sdkconfig.ci.release | 4 - components/mbedtls/port/ecc/esp_ecc.c | 11 +- 7 files changed, 319 insertions(+), 225 deletions(-) diff --git a/components/esp_security/src/esp_ds.c b/components/esp_security/src/esp_ds.c index 9a7d8fd42ec..3d9dd795cb1 100644 --- a/components/esp_security/src/esp_ds.c +++ b/components/esp_security/src/esp_ds.c @@ -334,11 +334,12 @@ esp_err_t esp_ds_start_sign(const void *message, return ESP_ERR_INVALID_ARG; } - if (!(data->rsa_length == ESP_DS_RSA_1024 - || data->rsa_length == ESP_DS_RSA_2048 - || data->rsa_length == ESP_DS_RSA_3072 + const uint32_t rsa_length = data->rsa_length; + if (!(rsa_length == ESP_DS_RSA_1024 + || rsa_length == ESP_DS_RSA_2048 + || rsa_length == ESP_DS_RSA_3072 #if SOC_RSA_MAX_BIT_LEN == 4096 - || data->rsa_length == ESP_DS_RSA_4096 + || rsa_length == ESP_DS_RSA_4096 #endif )) { return ESP_ERR_INVALID_ARG; @@ -393,7 +394,7 @@ esp_err_t esp_ds_start_sign(const void *message, return ESP_ERR_NO_MEM; } - size_t rsa_len = (data->rsa_length + 1) * 4; + size_t rsa_len = (rsa_length + 1) * 4; ds_hal_write_private_key_params(data->c); ds_hal_configure_iv((uint32_t *)data->iv); ds_hal_write_message(message, rsa_len); @@ -426,22 +427,31 @@ esp_err_t esp_ds_finish_sign(void *signature, esp_ds_context_t *esp_ds_ctx) } const esp_ds_data_t *data = (const esp_ds_data_t *)esp_ds_ctx->data; - unsigned rsa_len = (data->rsa_length + 1) * 4; + esp_err_t return_value = ESP_ERR_INVALID_ARG; while (ds_hal_busy()) { } - ds_signature_check_t sig_check_result = ds_hal_read_result((uint8_t *) signature, (size_t) rsa_len); + uint32_t rsa_length = data->rsa_length; + if (rsa_length == ESP_DS_RSA_1024 + || rsa_length == ESP_DS_RSA_2048 + || rsa_length == ESP_DS_RSA_3072 +#if SOC_DS_SIGNATURE_MAX_BIT_LEN == 4096 + || rsa_length == ESP_DS_RSA_4096 +#endif + ) { + unsigned rsa_len = (rsa_length + 1) * 4; - esp_err_t return_value = ESP_OK; + ds_signature_check_t res = ds_hal_read_result((uint8_t *) signature, (size_t) rsa_len); - if (sig_check_result == DS_SIGNATURE_MD_FAIL || sig_check_result == DS_SIGNATURE_PADDING_AND_MD_FAIL) { - esp_ds_zeroize(signature, rsa_len); - return_value = ESP_ERR_HW_CRYPTO_DS_INVALID_DIGEST; - } - - if (sig_check_result == DS_SIGNATURE_PADDING_FAIL) { - esp_ds_zeroize(signature, rsa_len); - return_value = ESP_ERR_HW_CRYPTO_DS_INVALID_PADDING; + if (res == DS_SIGNATURE_MD_FAIL || res == DS_SIGNATURE_PADDING_AND_MD_FAIL) { + esp_ds_zeroize(signature, rsa_len); + return_value = ESP_ERR_HW_CRYPTO_DS_INVALID_DIGEST; + } else if (res == DS_SIGNATURE_PADDING_FAIL) { + esp_ds_zeroize(signature, rsa_len); + return_value = ESP_ERR_HW_CRYPTO_DS_INVALID_PADDING; + } else if (res == DS_SIGNATURE_OK) { + return_value = ESP_OK; + } } #if !ESP_TEE_BUILD diff --git a/components/esp_tee/subproject/main/core/esp_secure_services.c b/components/esp_tee/subproject/main/core/esp_secure_services.c index 2a335938407..38acc0e49b0 100644 --- a/components/esp_tee/subproject/main/core/esp_secure_services.c +++ b/components/esp_tee/subproject/main/core/esp_secure_services.c @@ -71,7 +71,8 @@ int _ss_esp_aes_crypt_cbc(esp_aes_context *ctx, } ESP_FAULT_ASSERT(valid_addr); - return esp_aes_crypt_cbc(ctx, mode, length, iv, input, output); + esp_aes_context ctx_local = *ctx; + return esp_aes_crypt_cbc(&ctx_local, mode, length, iv, input, output); } int _ss_esp_aes_crypt_cfb128(esp_aes_context *ctx, @@ -93,7 +94,8 @@ int _ss_esp_aes_crypt_cfb128(esp_aes_context *ctx, } ESP_FAULT_ASSERT(valid_addr); - return esp_aes_crypt_cfb128(ctx, mode, length, iv_off, iv, input, output); + esp_aes_context ctx_local = *ctx; + return esp_aes_crypt_cfb128(&ctx_local, mode, length, iv_off, iv, input, output); } int _ss_esp_aes_crypt_cfb8(esp_aes_context *ctx, @@ -113,7 +115,8 @@ int _ss_esp_aes_crypt_cfb8(esp_aes_context *ctx, } ESP_FAULT_ASSERT(valid_addr); - return esp_aes_crypt_cfb8(ctx, mode, length, iv, input, output); + esp_aes_context ctx_local = *ctx; + return esp_aes_crypt_cfb8(&ctx_local, mode, length, iv, input, output); } int _ss_esp_aes_crypt_ctr(esp_aes_context *ctx, @@ -136,7 +139,8 @@ int _ss_esp_aes_crypt_ctr(esp_aes_context *ctx, } ESP_FAULT_ASSERT(valid_addr); - return esp_aes_crypt_ctr(ctx, length, nc_off, nonce_counter, stream_block, input, output); + esp_aes_context ctx_local = *ctx; + return esp_aes_crypt_ctr(&ctx_local, length, nc_off, nonce_counter, stream_block, input, output); } int _ss_esp_aes_crypt_ecb(esp_aes_context *ctx, @@ -153,7 +157,8 @@ int _ss_esp_aes_crypt_ecb(esp_aes_context *ctx, } ESP_FAULT_ASSERT(valid_addr); - return esp_aes_crypt_ecb(ctx, mode, input, output); + esp_aes_context ctx_local = *ctx; + return esp_aes_crypt_ecb(&ctx_local, mode, input, output); } int _ss_esp_aes_crypt_ofb(esp_aes_context *ctx, @@ -174,7 +179,8 @@ int _ss_esp_aes_crypt_ofb(esp_aes_context *ctx, } ESP_FAULT_ASSERT(valid_addr); - return esp_aes_crypt_ofb(ctx, length, iv_off, iv, input, output); + esp_aes_context ctx_local = *ctx; + return esp_aes_crypt_ofb(&ctx_local, length, iv_off, iv, input, output); } #endif @@ -401,8 +407,10 @@ esp_err_t _ss_esp_ds_sign(const void *message, return ESP_ERR_INVALID_ARG; } - size_t n = get_ds_msg_sign_len(data->rsa_length); - valid_addr &= (n > 0) && esp_tee_buf_in_ree(message, n) && esp_tee_buf_in_ree(signature, n); + const size_t n_max = SOC_DS_SIGNATURE_MAX_BIT_LEN / 8; + valid_addr &= (get_ds_msg_sign_len(data->rsa_length) > 0) && + esp_tee_buf_in_ree(message, n_max) && + esp_tee_buf_in_ree(signature, n_max); #if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID); @@ -422,15 +430,21 @@ esp_err_t _ss_esp_ds_start_sign(const void *message, hmac_key_id_t key_id, esp_ds_context_t **esp_ds_ctx) { - bool valid_addr = (esp_tee_buf_in_ree(esp_ds_ctx, sizeof(esp_ds_context_t *)) && - esp_tee_buf_in_ree(*esp_ds_ctx, sizeof(esp_ds_context_t)) && + if (!esp_tee_buf_in_ree(esp_ds_ctx, sizeof(esp_ds_context_t *))) { + return ESP_ERR_INVALID_ARG; + } + + esp_ds_context_t *ds_ctx = *esp_ds_ctx; + const size_t n_max = SOC_DS_SIGNATURE_MAX_BIT_LEN / 8; + + bool valid_addr = (esp_tee_buf_in_ree(ds_ctx, sizeof(esp_ds_context_t)) && esp_tee_buf_in_ree(data, sizeof(esp_ds_data_t))); if (!valid_addr) { return ESP_ERR_INVALID_ARG; } - size_t n = get_ds_msg_sign_len(data->rsa_length); - valid_addr &= (n > 0) && esp_tee_buf_in_ree(message, n); + valid_addr &= (get_ds_msg_sign_len(data->rsa_length) > 0) && + esp_tee_buf_in_ree(message, n_max); #if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID); @@ -442,7 +456,12 @@ esp_err_t _ss_esp_ds_start_sign(const void *message, } ESP_FAULT_ASSERT(valid_addr); - return esp_ds_start_sign(message, data, key_id, esp_ds_ctx); + esp_err_t err = esp_ds_start_sign(message, data, key_id, &ds_ctx); + if (err == ESP_OK) { + *esp_ds_ctx = ds_ctx; + } + + return err; } bool _ss_esp_ds_is_busy(void) @@ -452,14 +471,16 @@ bool _ss_esp_ds_is_busy(void) esp_err_t _ss_esp_ds_finish_sign(void *signature, esp_ds_context_t *esp_ds_ctx) { - const size_t max_sign = SOC_DS_SIGNATURE_MAX_BIT_LEN / 8; - bool valid_addr = (esp_tee_buf_in_ree(signature, max_sign) && + const size_t n_max = SOC_DS_SIGNATURE_MAX_BIT_LEN / 8; + bool valid_addr = (esp_tee_buf_in_ree(signature, n_max) && esp_tee_buf_in_ree(esp_ds_ctx, sizeof(esp_ds_context_t))); if (!valid_addr) { return ESP_ERR_INVALID_ARG; } - const esp_ds_data_t *data = (const esp_ds_data_t *)esp_ds_ctx->data; + const esp_ds_context_t ctx_local = *esp_ds_ctx; + + const esp_ds_data_t *data = (const esp_ds_data_t *)ctx_local.data; valid_addr &= esp_tee_buf_in_ree(data, sizeof(esp_ds_data_t)) && (get_ds_msg_sign_len(data->rsa_length) > 0); @@ -468,7 +489,7 @@ esp_err_t _ss_esp_ds_finish_sign(void *signature, esp_ds_context_t *esp_ds_ctx) } ESP_FAULT_ASSERT(valid_addr); - return esp_ds_finish_sign(signature, esp_ds_ctx); + return esp_ds_finish_sign(signature, (esp_ds_context_t *)&ctx_local); } esp_err_t _ss_esp_ds_encrypt_params(esp_ds_data_t *data, @@ -583,15 +604,11 @@ int _ss_esp_tee_ota_end(void) /* ---------------------------------------------- Secure Storage ------------------------------------------------- */ -/* NOTE: The key-name pointers here (cfg->id/ctx->key_id) are REE-supplied, NULL-terminated - * NVS key names used read-only for key lookup (NVS compares them with strncmp bounded to - * NVS_KEY_NAME_MAX_SIZE-1) — never written through, never used as a register base. - * Pointing one at TEE memory yields at most a load-fault DoS or a useless presence oracle, - * so they are left unchecked. Argument checks cost code size and add latency to every - * service call, so we keep only the ones that close a real REE->TEE read/write/control-flow gap. - */ esp_err_t _ss_esp_tee_sec_storage_clear_key(const char *key_id) { + char id_buf[NVS_KEY_NAME_MAX_SIZE]; + tee_snapshot_ree_str(&key_id, id_buf, sizeof(id_buf)); + bool valid_arg = !esp_tee_sec_storage_is_key_tee_owned(key_id); if (!valid_arg) { return ESP_ERR_INVALID_ARG; @@ -603,15 +620,22 @@ esp_err_t _ss_esp_tee_sec_storage_clear_key(const char *key_id) esp_err_t _ss_esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg) { - bool valid_arg = esp_tee_buf_in_ree(cfg, sizeof(esp_tee_sec_storage_key_cfg_t)) && - !(cfg->flags & SEC_STORAGE_FLAG_TEE_ONLY) && - !esp_tee_sec_storage_is_key_tee_owned(cfg->id); + if (!esp_tee_buf_in_ree(cfg, sizeof(esp_tee_sec_storage_key_cfg_t))) { + return ESP_ERR_INVALID_ARG; + } + + esp_tee_sec_storage_key_cfg_t cfg_local = *cfg; + char id_buf[NVS_KEY_NAME_MAX_SIZE]; + tee_snapshot_ree_str(&cfg_local.id, id_buf, sizeof(id_buf)); + + bool valid_arg = !(cfg_local.flags & SEC_STORAGE_FLAG_TEE_ONLY) && + !esp_tee_sec_storage_is_key_tee_owned(cfg_local.id); if (!valid_arg) { return ESP_ERR_INVALID_ARG; } ESP_FAULT_ASSERT(valid_arg); - return esp_tee_sec_storage_gen_key(cfg); + return esp_tee_sec_storage_gen_key(&cfg_local); } /* ---------------------------------------------- PSA Attestation ------------------------------------------------- */ diff --git a/components/esp_tee/subproject/main/core/esp_secure_services_iram.c b/components/esp_tee/subproject/main/core/esp_secure_services_iram.c index cf7d56e98b6..4a0917712d4 100644 --- a/components/esp_tee/subproject/main/core/esp_secure_services_iram.c +++ b/components/esp_tee/subproject/main/core/esp_secure_services_iram.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ #include +#include #include #include "esp_err.h" @@ -167,70 +168,90 @@ void _ss_wdt_hal_init(wdt_hal_context_t *hal, wdt_inst_t wdt_inst, uint32_t pres } ESP_FAULT_ASSERT(valid_addr); - wdt_hal_init(hal, wdt_inst, prescaler, enable_intr); + wdt_hal_context_t hal_local; + wdt_hal_init(&hal_local, wdt_inst, prescaler, enable_intr); + + *hal = hal_local; } void _ss_wdt_hal_deinit(wdt_hal_context_t *hal) { - bool valid_addr = (esp_tee_buf_in_ree(hal, sizeof(wdt_hal_context_t)) && - is_wdt_dev_valid(hal->mwdt_dev)); + if (!esp_tee_buf_in_ree(hal, sizeof(wdt_hal_context_t))) { + return; + } + wdt_hal_context_t hal_snap = *hal; + + bool valid_addr = is_wdt_dev_valid(hal_snap.mwdt_dev); if (!valid_addr) { return; } ESP_FAULT_ASSERT(valid_addr); - wdt_hal_deinit(hal); + wdt_hal_deinit(&hal_snap); } /* ---------------------------------------------- Secure Storage ------------------------------------------------- */ -/* NOTE: The key-name pointers here (cfg->id/ctx->key_id) are REE-supplied, NULL-terminated - * NVS key names used read-only for key lookup (NVS compares them with strncmp bounded to - * NVS_KEY_NAME_MAX_SIZE-1) — never written through, never used as a register base. - * Pointing one at TEE memory yields at most a load-fault DoS or a useless presence oracle, - * so they are left unchecked. Argument checks cost code size and add latency to every - * service call, so we keep only the ones that close a real REE->TEE read/write/control-flow gap. - * The buffers alongside these ARE validated, since the TEE reads/writes them. - */ esp_err_t _ss_esp_tee_sec_storage_ecdsa_sign(const esp_tee_sec_storage_key_cfg_t *cfg, const uint8_t *hash, size_t hlen, esp_tee_sec_storage_ecdsa_sign_t *out_sign) { - bool valid_arg = (esp_tee_buf_in_ree(cfg, sizeof(esp_tee_sec_storage_key_cfg_t)) && - esp_tee_buf_in_ree(hash, hlen) && + if (!esp_tee_buf_in_ree(cfg, sizeof(esp_tee_sec_storage_key_cfg_t))) { + return ESP_ERR_INVALID_ARG; + } + + esp_tee_sec_storage_key_cfg_t cfg_local = *cfg; + char id_buf[NVS_KEY_NAME_MAX_SIZE]; + tee_snapshot_ree_str(&cfg_local.id, id_buf, sizeof(id_buf)); + + bool valid_arg = (esp_tee_buf_in_ree(hash, hlen) && esp_tee_buf_in_ree(out_sign, sizeof(esp_tee_sec_storage_ecdsa_sign_t)) && - !esp_tee_sec_storage_is_key_tee_owned(cfg->id)); + !esp_tee_sec_storage_is_key_tee_owned(cfg_local.id)); if (!valid_arg) { return ESP_ERR_INVALID_ARG; } ESP_FAULT_ASSERT(valid_arg); - return esp_tee_sec_storage_ecdsa_sign(cfg, hash, hlen, out_sign); + return esp_tee_sec_storage_ecdsa_sign(&cfg_local, hash, hlen, out_sign); } esp_err_t _ss_esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg_t *cfg, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey) { - bool valid_arg = (esp_tee_buf_in_ree(cfg, sizeof(esp_tee_sec_storage_key_cfg_t)) && - esp_tee_buf_in_ree(out_pubkey, sizeof(esp_tee_sec_storage_ecdsa_pubkey_t)) && - !esp_tee_sec_storage_is_key_tee_owned(cfg->id)); + if (!esp_tee_buf_in_ree(cfg, sizeof(esp_tee_sec_storage_key_cfg_t))) { + return ESP_ERR_INVALID_ARG; + } + + esp_tee_sec_storage_key_cfg_t cfg_local = *cfg; + char id_buf[NVS_KEY_NAME_MAX_SIZE]; + tee_snapshot_ree_str(&cfg_local.id, id_buf, sizeof(id_buf)); + + bool valid_arg = (esp_tee_buf_in_ree(out_pubkey, sizeof(esp_tee_sec_storage_ecdsa_pubkey_t)) && + !esp_tee_sec_storage_is_key_tee_owned(cfg_local.id)); if (!valid_arg) { return ESP_ERR_INVALID_ARG; } ESP_FAULT_ASSERT(valid_arg); - return esp_tee_sec_storage_ecdsa_get_pubkey(cfg, out_pubkey); + return esp_tee_sec_storage_ecdsa_get_pubkey(&cfg_local, out_pubkey); } esp_err_t _ss_esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, uint8_t *iv, size_t iv_len, uint8_t *tag, size_t tag_len, uint8_t *output) { - bool valid_arg = (esp_tee_buf_in_ree(ctx, sizeof(esp_tee_sec_storage_aead_ctx_t)) && - esp_tee_buf_in_ree(ctx->input, ctx->input_len) && + if (!esp_tee_buf_in_ree(ctx, sizeof(esp_tee_sec_storage_aead_ctx_t))) { + return ESP_ERR_INVALID_ARG; + } + + esp_tee_sec_storage_aead_ctx_t ctx_local = *ctx; + char id_buf[NVS_KEY_NAME_MAX_SIZE]; + tee_snapshot_ree_str(&ctx_local.key_id, id_buf, sizeof(id_buf)); + + bool valid_arg = (esp_tee_buf_in_ree(ctx_local.input, ctx_local.input_len) && esp_tee_buf_in_ree(iv, iv_len) && esp_tee_buf_in_ree(tag, tag_len) && - esp_tee_buf_in_ree(output, ctx->input_len) && - !esp_tee_sec_storage_is_key_tee_owned(ctx->key_id)); + esp_tee_buf_in_ree(output, ctx_local.input_len) && + !esp_tee_sec_storage_is_key_tee_owned(ctx_local.key_id)); - if (ctx->aad_len != 0) { - valid_arg &= esp_tee_buf_in_ree(ctx->aad, ctx->aad_len); + if (ctx_local.aad_len != 0) { + valid_arg &= esp_tee_buf_in_ree(ctx_local.aad, ctx_local.aad_len); } if (!valid_arg) { @@ -238,20 +259,27 @@ esp_err_t _ss_esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ct } ESP_FAULT_ASSERT(valid_arg); - return esp_tee_sec_storage_aead_encrypt(ctx, iv, iv_len, tag, tag_len, output); + return esp_tee_sec_storage_aead_encrypt(&ctx_local, iv, iv_len, tag, tag_len, output); } esp_err_t _ss_esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, const uint8_t *iv, size_t iv_len, const uint8_t *tag, size_t tag_len, uint8_t *output) { - bool valid_arg = (esp_tee_buf_in_ree(ctx, sizeof(esp_tee_sec_storage_aead_ctx_t)) && - esp_tee_buf_in_ree(ctx->input, ctx->input_len) && + if (!esp_tee_buf_in_ree(ctx, sizeof(esp_tee_sec_storage_aead_ctx_t))) { + return ESP_ERR_INVALID_ARG; + } + + esp_tee_sec_storage_aead_ctx_t ctx_local = *ctx; + char id_buf[NVS_KEY_NAME_MAX_SIZE]; + tee_snapshot_ree_str(&ctx_local.key_id, id_buf, sizeof(id_buf)); + + bool valid_arg = (esp_tee_buf_in_ree(ctx_local.input, ctx_local.input_len) && esp_tee_buf_in_ree(iv, iv_len) && esp_tee_buf_in_ree(tag, tag_len) && - esp_tee_buf_in_ree(output, ctx->input_len) && - !esp_tee_sec_storage_is_key_tee_owned(ctx->key_id)); + esp_tee_buf_in_ree(output, ctx_local.input_len) && + !esp_tee_sec_storage_is_key_tee_owned(ctx_local.key_id)); - if (ctx->aad_len != 0) { - valid_arg &= esp_tee_buf_in_ree(ctx->aad, ctx->aad_len); + if (ctx_local.aad_len != 0) { + valid_arg &= esp_tee_buf_in_ree(ctx_local.aad, ctx_local.aad_len); } if (!valid_arg) { @@ -259,23 +287,28 @@ esp_err_t _ss_esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ct } ESP_FAULT_ASSERT(valid_arg); - return esp_tee_sec_storage_aead_decrypt(ctx, iv, iv_len, tag, tag_len, output); + return esp_tee_sec_storage_aead_decrypt(&ctx_local, iv, iv_len, tag, tag_len, output); } esp_err_t _ss_esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pbkdf2_ctx_t *ctx, const uint8_t *hash, size_t hlen, esp_tee_sec_storage_ecdsa_sign_t *out_sign, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey) { - bool valid_addr = (esp_tee_buf_in_ree(ctx, sizeof(esp_tee_sec_storage_pbkdf2_ctx_t)) && - esp_tee_buf_in_ree(hash, hlen) && + if (!esp_tee_buf_in_ree(ctx, sizeof(esp_tee_sec_storage_pbkdf2_ctx_t))) { + return ESP_ERR_INVALID_ARG; + } + + const esp_tee_sec_storage_pbkdf2_ctx_t ctx_local = *ctx; + + bool valid_addr = (esp_tee_buf_in_ree(hash, hlen) && esp_tee_buf_in_ree(out_sign, sizeof(esp_tee_sec_storage_ecdsa_sign_t)) && esp_tee_buf_in_ree(out_pubkey, sizeof(esp_tee_sec_storage_ecdsa_pubkey_t)) && - esp_tee_buf_in_ree(ctx->salt, ctx->salt_len)); + esp_tee_buf_in_ree(ctx_local.salt, ctx_local.salt_len)); if (!valid_addr) { return ESP_ERR_INVALID_ARG; } ESP_FAULT_ASSERT(valid_addr); - return esp_tee_sec_storage_ecdsa_sign_pbkdf2(ctx, hash, hlen, out_sign, out_pubkey); + return esp_tee_sec_storage_ecdsa_sign_pbkdf2(&ctx_local, hash, hlen, out_sign, out_pubkey); } /* ---------------------------------------------- MMU HAL ------------------------------------------------- */ @@ -382,30 +415,6 @@ static bool is_flash_addr_readable(uint32_t paddr, uint32_t len) return !esp_tee_flash_check_prange_in_tee_region(paddr, len); } -static bool is_spi_host_in_ree(spi_flash_host_inst_t *host) -{ - const spi_flash_hal_context_t *ctx = (const spi_flash_hal_context_t *)host; - - return (esp_tee_buf_in_ree(host, sizeof(spi_flash_hal_context_t)) && - ctx->spi == spi_flash_ll_get_hw(SPI1_HOST)); -} - -static bool is_spi_trans_valid(spi_flash_host_inst_t *host, spi_flash_trans_t *trans) -{ - if (!is_spi_host_in_ree(host) || !esp_tee_buf_in_ree(trans, sizeof(spi_flash_trans_t))) { - return false; - } - - bool valid_addr = true; - if (trans->mosi_len != 0) { - valid_addr &= esp_tee_buf_in_ree(trans->mosi_data, trans->mosi_len); - } - if (trans->miso_len != 0) { - valid_addr &= esp_tee_buf_in_ree(trans->miso_data, trans->miso_len); - } - return valid_addr; -} - static bool is_spi_cmd_addr_ok(uint32_t addr_bitlen, uint32_t address, uint32_t mosi_len, uint32_t miso_len) { if (addr_bitlen == 0) { @@ -430,206 +439,246 @@ static const spi_flash_host_driver_t tee_host_driver = { .configure_host_io_mode = spi_flash_hal_configure_host_io_mode, }; -static inline const spi_flash_host_driver_t *tee_substitute_host_driver(spi_flash_host_inst_t *host) +static spi_flash_host_inst_t *tee_own_host(const spi_flash_host_inst_t *host, spi_flash_hal_context_t *snap) { - const spi_flash_host_driver_t *orig = host->driver; - host->driver = &tee_host_driver; - return orig; + if (!esp_tee_buf_in_ree(host, sizeof(spi_flash_hal_context_t))) { + return NULL; + } + + *snap = *(const spi_flash_hal_context_t *)host; + + /* Reject a host aimed at another peripheral rather than silently retargeting it */ + if (snap->spi != spi_flash_ll_get_hw(SPI1_HOST)) { + return NULL; + } + + snap->inst.driver = &tee_host_driver; + snap->spi = spi_flash_ll_get_hw(SPI1_HOST); + + return &snap->inst; } uint32_t _ss_spi_flash_hal_check_status(spi_flash_host_inst_t *host) { - bool valid_addr = is_spi_host_in_ree(host); - - if (!valid_addr) { + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { return 0; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(tee_host != NULL); - return spi_flash_hal_check_status(host); + return spi_flash_hal_check_status(tee_host); } esp_err_t _ss_spi_flash_hal_common_command(spi_flash_host_inst_t *host, spi_flash_trans_t *trans) { - bool trans_valid = is_spi_trans_valid(host, trans); + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { + return ESP_ERR_INVALID_ARG; + } + ESP_FAULT_ASSERT(tee_host != NULL); + + if (!esp_tee_buf_in_ree(trans, sizeof(spi_flash_trans_t))) { + return ESP_ERR_INVALID_ARG; + } + + spi_flash_trans_t trans_snap = *trans; + + bool trans_valid = true; + if (trans_snap.mosi_len != 0) { + trans_valid &= esp_tee_buf_in_ree(trans_snap.mosi_data, trans_snap.mosi_len); + } + if (trans_snap.miso_len != 0) { + trans_valid &= esp_tee_buf_in_ree(trans_snap.miso_data, trans_snap.miso_len); + } + trans_valid &= is_spi_cmd_addr_ok(trans_snap.address_bitlen, trans_snap.address, + trans_snap.mosi_len, trans_snap.miso_len); if (!trans_valid) { + ESP_LOGD(TAG, "[%s] Illegal flash access at 0x%08x", __func__, trans_snap.address); return ESP_ERR_INVALID_ARG; } ESP_FAULT_ASSERT(trans_valid); - bool addr_ok = is_spi_cmd_addr_ok(trans->address_bitlen, trans->address, trans->mosi_len, trans->miso_len); - if (!addr_ok) { - ESP_LOGD(TAG, "[%s] Illegal flash access at 0x%08x", __func__, trans->address); - return ESP_ERR_INVALID_ARG; - } - ESP_FAULT_ASSERT(addr_ok); - - const spi_flash_host_driver_t *orig = tee_substitute_host_driver(host); - esp_err_t r = spi_flash_hal_common_command(host, trans); - host->driver = orig; - return r; + return spi_flash_hal_common_command(tee_host, &trans_snap); } esp_err_t _ss_spi_flash_hal_device_config(spi_flash_host_inst_t *host) { - bool valid_addr = is_spi_host_in_ree(host); - - if (!valid_addr) { + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(tee_host != NULL); - return spi_flash_hal_device_config(host); + return spi_flash_hal_device_config(tee_host); } void _ss_spi_flash_hal_erase_block(spi_flash_host_inst_t *host, uint32_t start_address) { - bool valid_addr = (is_spi_host_in_ree(host) && - start_address <= FLASH_ADDR_MAX_24BIT && - is_flash_addr_writable(start_address, FLASH_BLOCK_SIZE)); + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { + return; + } + ESP_FAULT_ASSERT(tee_host != NULL); + bool valid_addr = (start_address <= FLASH_ADDR_MAX_24BIT && + is_flash_addr_writable(start_address, FLASH_BLOCK_SIZE)); if (!valid_addr) { ESP_LOGD(TAG, "[%s] Illegal flash access at 0x%08x", __func__, start_address); return; } ESP_FAULT_ASSERT(valid_addr); - const spi_flash_host_driver_t *orig = tee_substitute_host_driver(host); - spi_flash_hal_erase_block(host, start_address); - host->driver = orig; + spi_flash_hal_erase_block(tee_host, start_address); } void _ss_spi_flash_hal_erase_sector(spi_flash_host_inst_t *host, uint32_t start_address) { - bool valid_addr = (is_spi_host_in_ree(host) && - start_address <= FLASH_ADDR_MAX_24BIT && - is_flash_addr_writable(start_address, FLASH_SECTOR_SIZE)); + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { + return; + } + ESP_FAULT_ASSERT(tee_host != NULL); + bool valid_addr = (start_address <= FLASH_ADDR_MAX_24BIT && + is_flash_addr_writable(start_address, FLASH_SECTOR_SIZE)); if (!valid_addr) { ESP_LOGD(TAG, "[%s] Illegal flash access at 0x%08x", __func__, start_address); return; } ESP_FAULT_ASSERT(valid_addr); - const spi_flash_host_driver_t *orig = tee_substitute_host_driver(host); - spi_flash_hal_erase_sector(host, start_address); - host->driver = orig; + spi_flash_hal_erase_sector(tee_host, start_address); } void _ss_spi_flash_hal_program_page(spi_flash_host_inst_t *host, const void *buffer, uint32_t address, uint32_t length) { - bool valid_addr = (is_spi_host_in_ree(host) && - address <= FLASH_ADDR_MAX_24BIT && + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { + return; + } + ESP_FAULT_ASSERT(tee_host != NULL); + + bool valid_addr = (address <= FLASH_ADDR_MAX_24BIT && is_flash_addr_writable(address, length) && esp_tee_buf_in_ree(buffer, length)); - if (!valid_addr) { ESP_LOGD(TAG, "[%s] Illegal flash access at 0x%08x", __func__, address); return; } ESP_FAULT_ASSERT(valid_addr); - const spi_flash_host_driver_t *orig = tee_substitute_host_driver(host); - spi_flash_hal_program_page(host, buffer, address, length); - host->driver = orig; + spi_flash_hal_program_page(tee_host, buffer, address, length); } esp_err_t _ss_spi_flash_hal_read(spi_flash_host_inst_t *host, void *buffer, uint32_t address, uint32_t read_len) { - bool valid_addr = (is_spi_host_in_ree(host) && - is_flash_addr_readable(address, read_len) && - esp_tee_buf_in_ree(buffer, read_len)); + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { + return ESP_ERR_INVALID_ARG; + } + ESP_FAULT_ASSERT(tee_host != NULL); + bool valid_addr = (is_flash_addr_readable(address, read_len) && + esp_tee_buf_in_ree(buffer, read_len)); if (!valid_addr) { ESP_LOGD(TAG, "[%s] Illegal flash access at 0x%08x", __func__, address); return ESP_ERR_INVALID_ARG; } ESP_FAULT_ASSERT(valid_addr); - const spi_flash_host_driver_t *orig = tee_substitute_host_driver(host); - esp_err_t r = spi_flash_hal_read(host, buffer, address, read_len); - host->driver = orig; - return r; + return spi_flash_hal_read(tee_host, buffer, address, read_len); } void _ss_spi_flash_hal_resume(spi_flash_host_inst_t *host) { - bool valid_addr = is_spi_host_in_ree(host); - - if (!valid_addr) { + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { return; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(tee_host != NULL); - const spi_flash_host_driver_t *orig = tee_substitute_host_driver(host); - spi_flash_hal_resume(host); - host->driver = orig; + spi_flash_hal_resume(tee_host); } esp_err_t _ss_spi_flash_hal_set_write_protect(spi_flash_host_inst_t *host, bool wp) { - bool valid_addr = is_spi_host_in_ree(host); - - if (!valid_addr) { + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(tee_host != NULL); - const spi_flash_host_driver_t *orig = tee_substitute_host_driver(host); - esp_err_t r = spi_flash_hal_set_write_protect(host, wp); - host->driver = orig; - return r; + return spi_flash_hal_set_write_protect(tee_host, wp); } esp_err_t _ss_spi_flash_hal_setup_read_suspend(spi_flash_host_inst_t *host, const spi_flash_sus_cmd_conf *sus_conf) { - bool valid_addr = (is_spi_host_in_ree(host) && - esp_tee_buf_in_ree(sus_conf, sizeof(spi_flash_sus_cmd_conf))); - - if (!valid_addr) { + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(tee_host != NULL); - return spi_flash_hal_setup_read_suspend(host, sus_conf); + if (!esp_tee_buf_in_ree(sus_conf, sizeof(spi_flash_sus_cmd_conf))) { + return ESP_ERR_INVALID_ARG; + } + + const spi_flash_sus_cmd_conf sus_snap = *sus_conf; + return spi_flash_hal_setup_read_suspend(tee_host, &sus_snap); } bool _ss_spi_flash_hal_supports_direct_read(spi_flash_host_inst_t *host, const void *p) { - bool valid_addr = (is_spi_host_in_ree(host) && esp_tee_ptr_in_ree(p)); - - if (!valid_addr) { + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { return false; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(tee_host != NULL); - return spi_flash_hal_supports_direct_read(host, p); + if (!esp_tee_ptr_in_ree(p)) { + return false; + } + + return spi_flash_hal_supports_direct_read(tee_host, p); } bool _ss_spi_flash_hal_supports_direct_write(spi_flash_host_inst_t *host, const void *p) { - bool valid_addr = (is_spi_host_in_ree(host) && esp_tee_ptr_in_ree(p)); - - if (!valid_addr) { + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { return false; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(tee_host != NULL); - return spi_flash_hal_supports_direct_write(host, p); + if (!esp_tee_ptr_in_ree(p)) { + return false; + } + + return spi_flash_hal_supports_direct_write(tee_host, p); } void _ss_spi_flash_hal_suspend(spi_flash_host_inst_t *host) { - bool valid_addr = is_spi_host_in_ree(host); - - if (!valid_addr) { + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { return; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(tee_host != NULL); - const spi_flash_host_driver_t *orig = tee_substitute_host_driver(host); - spi_flash_hal_suspend(host); - host->driver = orig; + spi_flash_hal_suspend(tee_host); } /* ---------------------------------------------- SPI Flash Extras ------------------------------------------------- */ @@ -680,38 +729,41 @@ uint32_t _ss_bootloader_flash_execute_command_common( esp_err_t _ss_memspi_host_flush_cache(spi_flash_host_inst_t *host, uint32_t addr, uint32_t size) { - bool valid_addr = (is_spi_host_in_ree(host) && - is_flash_addr_readable(addr, size)); - - if (!valid_addr) { + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(tee_host != NULL); - return memspi_host_flush_cache(host, addr, size); + if (!is_flash_addr_readable(addr, size)) { + return ESP_ERR_INVALID_ARG; + } + + return memspi_host_flush_cache(tee_host, addr, size); } esp_err_t _ss_spi_flash_chip_generic_config_host_io_mode(esp_flash_t *chip, uint32_t flags) { - spi_flash_host_inst_t *host = NULL; - bool valid_addr = (esp_tee_buf_in_ree(chip, sizeof(struct esp_flash_t)) && - is_spi_host_in_ree((host = chip->host))); - - if (!valid_addr) { + if (!esp_tee_buf_in_ree(chip, sizeof(struct esp_flash_t))) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + + spi_flash_host_inst_t *const host = chip->host; + spi_flash_hal_context_t host_snap; + spi_flash_host_inst_t *tee_host = tee_own_host(host, &host_snap); + if (tee_host == NULL) { + return ESP_ERR_INVALID_ARG; + } + ESP_FAULT_ASSERT(tee_host != NULL); esp_flash_t chip_snap = { - .host = host, + .host = tee_host, .read_mode = chip->read_mode, .hpm_dummy_ena = chip->hpm_dummy_ena, }; - const spi_flash_host_driver_t *orig = tee_substitute_host_driver(host); - esp_err_t r = spi_flash_chip_generic_config_host_io_mode(&chip_snap, flags); - host->driver = orig; - return r; + return spi_flash_chip_generic_config_host_io_mode(&chip_snap, flags); } #if CONFIG_IDF_TARGET_ESP32C5 diff --git a/components/esp_tee/subproject/main/include/esp_tee_memory_utils.h b/components/esp_tee/subproject/main/include/esp_tee_memory_utils.h index 0d52b8fc636..903c36f15ec 100644 --- a/components/esp_tee/subproject/main/include/esp_tee_memory_utils.h +++ b/components/esp_tee/subproject/main/include/esp_tee_memory_utils.h @@ -7,6 +7,7 @@ #include #include +#include #include "esp_attr.h" #include "soc/soc.h" #include "soc/ext_mem_defs.h" @@ -42,6 +43,20 @@ FORCE_INLINE_ATTR bool esp_tee_ptr_in_ree(const void *p) return esp_tee_buf_in_ree(p, 4); } +/* NOTE: re-points a REE string argument at a TEE-resident copy */ +FORCE_INLINE_ATTR void tee_snapshot_ree_str(const char **name, char *buf, size_t buf_len) +{ + const char *src = *name; + if (src == NULL || buf_len == 0) { + return; + } + + memcpy(buf, src, buf_len); + buf[buf_len - 1] = '\0'; + + *name = buf; +} + #ifdef __cplusplus } #endif diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.minimal_tee b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.minimal_tee index 2cff4b58aa5..07779a34e2b 100644 --- a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.minimal_tee +++ b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.minimal_tee @@ -5,8 +5,8 @@ CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID=5 # Reducing TEE I/DRAM sizes # 24KB CONFIG_SECURE_TEE_IRAM_SIZE=0x6000 -# 17KB -CONFIG_SECURE_TEE_DRAM_SIZE=0x4400 +# 18KB +CONFIG_SECURE_TEE_DRAM_SIZE=0x4800 # Disable TEE logs (also disable all panic logs) CONFIG_SECURE_TEE_DEBUG_MODE=n diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.release b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.release index b6b8fd811d3..e504eeea6ed 100644 --- a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.release +++ b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.release @@ -1,10 +1,6 @@ # NOTE: This sdkconfig is intended solely for CI build purposes - to verify ESP-TEE # builds across various configurations - and is not intended for production use. -# Reducing TEE IRAM size -# 31KB -CONFIG_SECURE_TEE_IRAM_SIZE=0x7C00 - # TEE Secure Storage: Release mode CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE=y CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID=5 diff --git a/components/mbedtls/port/ecc/esp_ecc.c b/components/mbedtls/port/ecc/esp_ecc.c index 59a558fd2e7..1e7ecc2535f 100644 --- a/components/mbedtls/port/ecc/esp_ecc.c +++ b/components/mbedtls/port/ecc/esp_ecc.c @@ -75,21 +75,18 @@ int esp_ecc_point_multiply(const ecc_point_t *point, const uint8_t *scalar, ecc_ int esp_ecc_point_verify(const ecc_point_t *point) { int result; + const unsigned len = point->len; - /* point->len drives a fixed-stride MMIO write loop in the HAL; an unvalidated oversized - * value (attacker-controlled via the TEE secure service) walks past the ECC register block - * and can reach other peripheral registers (CWE-787). Reject non-curve lengths up front and - * return 0 (point not verified) -- the fail-safe value for this routine. */ - if (point->len != P192_LEN && point->len != P256_LEN + if (len != P192_LEN && len != P256_LEN #if SOC_ECC_SUPPORT_CURVE_P384 - && point->len != P384_LEN + && len != P384_LEN #endif ) { return 0; } esp_ecc_acquire_hardware(); - ecc_hal_write_verify_param(point->x, point->y, point->len); + ecc_hal_write_verify_param(point->x, point->y, len); ecc_hal_set_mode(ECC_MODE_VERIFY); ecc_hal_start_calc(); From a4d6b802a782f36c55f575761b2c18369a50a271 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Fri, 21 Aug 2026 17:30:59 +0530 Subject: [PATCH 6/8] feat(esp_tee): Disable the `MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS` option for TEE build --- .../attestation/esp_att_utils_part_info.c | 21 ++- .../tee_sec_storage/tee_sec_storage.c | 165 ++++++++++++------ .../test_apps/tee_test_fw/main/CMakeLists.txt | 3 +- .../mbedtls/esp_tee/esp_tee_mbedtls_config.h | 1 - components/mbedtls/port/sha/esp_sha.c | 15 +- .../test_apps/mbedtls_ut/main/test_sha.c | 7 - 6 files changed, 139 insertions(+), 73 deletions(-) diff --git a/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c b/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c index c6aff792ef5..114184cdb01 100644 --- a/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c +++ b/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -71,6 +71,21 @@ static size_t digest_type_to_len(esp_att_part_digest_type_t digest) } #if ESP_TEE_BUILD +#define DIGEST_CHUNK_LEN (1024) + +static psa_status_t hash_update_chunked(psa_hash_operation_t *hash_op, const void *data, uint32_t len) +{ + psa_status_t status = PSA_SUCCESS; + + for (uint32_t offset = 0; offset < len; offset += DIGEST_CHUNK_LEN) { + status = psa_hash_update(hash_op, (const uint8_t *)data + offset, MIN(DIGEST_CHUNK_LEN, len - offset)); + if (status != PSA_SUCCESS) { + break; + } + } + + return status; +} static esp_err_t read_partition(uint32_t offset, void *buf, size_t size) { @@ -99,12 +114,12 @@ esp_err_t get_flash_contents_sha256(uint32_t flash_offset, uint32_t len, uint8_t psa_hash_abort(&hash_op); return ESP_FAIL; } - status = psa_hash_update(&hash_op, image, mmap_len); + status = hash_update_chunked(&hash_op, image, mmap_len); + esp_tee_flash_munmap(image); if (status != PSA_SUCCESS) { psa_hash_abort(&hash_op); return ESP_FAIL; } - esp_tee_flash_munmap(image); flash_offset += mmap_len; len -= mmap_len; diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c index 6136b79591a..ed06175f282 100644 --- a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c @@ -6,6 +6,7 @@ #include #include +#include #include "soc/soc_caps.h" #include "esp_log.h" @@ -36,8 +37,8 @@ #define AES256_KEY_LEN 32 #define AES256_KEY_BITS (AES256_KEY_LEN * 8) #define AES256_GCM_IV_LEN 12 -#define AES256_GCM_TAG_LEN_MIN 12 /* NIST SP800-38D general-use minimum (96-bit tag) */ -#define AES256_GCM_TAG_LEN_MAX 16 /* full GCM tag (128-bit) */ +#define AES256_GCM_TAG_LEN_MIN 12 /* NIST SP800-38D general-use minimum (96-bit tag) */ +#define AES256_GCM_TAG_LEN_MAX 16 /* full GCM tag (128-bit) */ #define ECDSA_SECP384R1_KEY_LEN 48 #define ECDSA_SECP256R1_KEY_LEN 32 @@ -678,6 +679,46 @@ cleanup: return err; } +#define AEAD_CHUNK_LEN (1024) + +static psa_status_t aead_update_ad_chunked(psa_aead_operation_t *op, const uint8_t *aad, size_t aad_len) +{ + psa_status_t status = PSA_SUCCESS; + + for (size_t offset = 0; offset < aad_len; offset += AEAD_CHUNK_LEN) { + status = psa_aead_update_ad(op, aad + offset, MIN(AEAD_CHUNK_LEN, aad_len - offset)); + if (status != PSA_SUCCESS) { + break; + } + } + + return status; +} + +static psa_status_t aead_update_chunked(psa_aead_operation_t *op, psa_algorithm_t alg, + const uint8_t *input, size_t len, + uint8_t *out, size_t out_size, size_t *out_len) +{ + psa_status_t status = PSA_SUCCESS; + size_t total = 0; + + for (size_t offset = 0; offset < len; offset += AEAD_CHUNK_LEN) { + const size_t chunk = MIN(AEAD_CHUNK_LEN, len - offset); + const size_t update_osize = PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, alg, chunk); + const size_t osize = MIN(out_size - total, update_osize); + size_t olen = 0; + + status = psa_aead_update(op, input + offset, chunk, out + total, osize, &olen); + if (status != PSA_SUCCESS) { + break; + } + total += olen; + } + + *out_len = total; + return status; +} + static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t *input, size_t len, const uint8_t *aad, size_t aad_len, uint8_t *iv, size_t iv_len, uint8_t *tag, size_t tag_len, uint8_t *output, bool is_encrypt) @@ -700,21 +741,16 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t return ESP_ERR_INVALID_SIZE; } - esp_err_t err = secure_storage_find_key(key_id); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Key ID not found"); - return err; - } - psa_key_id_t psa_key_id = 0; - uint8_t *aead_buf = NULL; - size_t aead_buf_len = 0; + psa_aead_operation_t aead_op = PSA_AEAD_OPERATION_INIT; + uint8_t *plaintext = NULL; sec_stg_key_t keyctx; size_t keyctx_len = sizeof(keyctx); - err = secure_storage_read(key_id, (void *)&keyctx, &keyctx_len); + esp_err_t err = secure_storage_read(key_id, (void *)&keyctx, &keyctx_len); if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to fetch key from storage"); + ESP_LOGE(TAG, "%s", (err == ESP_ERR_NVS_NOT_FOUND) ? + "Key ID not found" : "Failed to fetch key from storage"); goto cleanup; } @@ -724,10 +760,12 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t goto cleanup; } + const psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_len); + // Setup PSA key attributes psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; - psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); - psa_set_key_algorithm(&attributes, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_len)); + psa_set_key_usage_flags(&attributes, is_encrypt ? PSA_KEY_USAGE_ENCRYPT : PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attributes, alg); psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); psa_set_key_bits(&attributes, AES256_KEY_BITS); psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE); @@ -741,56 +779,75 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t goto cleanup; } - /* PSA AEAD wants ciphertext+tag concatenated in a single buffer for both - * encrypt (output) and decrypt (input). */ - aead_buf_len = len + tag_len; - aead_buf = malloc(aead_buf_len); - if (!aead_buf) { - err = ESP_ERR_NO_MEM; + uint8_t iv_local[AES256_GCM_IV_LEN]; + uint8_t tag_local[AES256_GCM_TAG_LEN_MAX]; + + size_t out_len = 0, fin_len = 0, tag_out_len = tag_len; + + uint8_t *out_buf = output; + + if (is_encrypt) { + status = psa_aead_encrypt_setup(&aead_op, psa_key_id, alg); + if (status == PSA_SUCCESS) { + status = psa_generate_random(iv_local, iv_len); + } + } else { + memcpy(iv_local, iv, iv_len); + memcpy(tag_local, tag, tag_len); + + /* NOTE: Only the AEAD-written prefix is ever copied out; zeroized at cleanup */ + plaintext = malloc(len); + if (!plaintext) { + err = ESP_ERR_NO_MEM; + goto cleanup; + } + out_buf = plaintext; + + status = psa_aead_decrypt_setup(&aead_op, psa_key_id, alg); + } + + /* Shared spine: nonce, then AAD, then the payload in bounded chunks. */ + if (status == PSA_SUCCESS) { + status = psa_aead_set_nonce(&aead_op, iv_local, iv_len); + } + if (status == PSA_SUCCESS) { + status = aead_update_ad_chunked(&aead_op, aad, aad_len); + } + if (status == PSA_SUCCESS) { + status = aead_update_chunked(&aead_op, alg, input, len, out_buf, len, &out_len); + } + if (status == PSA_SUCCESS) { + const size_t osize_fv = PSA_AEAD_FINISH_OUTPUT_SIZE(PSA_KEY_TYPE_AES, alg); + const size_t osize = MIN(len - out_len, osize_fv); + if (is_encrypt) { + status = psa_aead_finish(&aead_op, out_buf + out_len, osize, &fin_len, + tag_local, tag_len, &tag_out_len); + } else { + status = psa_aead_verify(&aead_op, out_buf + out_len, osize, &fin_len, + tag_local, tag_len); + } + } + + if (status != PSA_SUCCESS) { + ESP_LOGE(TAG, "Error in %scrypting data: %d", is_encrypt ? "en" : "de", status); + err = ESP_FAIL; goto cleanup; } if (is_encrypt) { - status = psa_generate_random(iv, iv_len); - if (status != PSA_SUCCESS) { - err = ESP_FAIL; - goto cleanup; - } - - size_t output_length = 0; - status = psa_aead_encrypt(psa_key_id, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_len), - iv, iv_len, aad, aad_len, input, len, - aead_buf, aead_buf_len, &output_length); - if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "Error in encrypting data: %d", status); - err = ESP_FAIL; - goto cleanup; - } - - // Separate ciphertext and tag - memcpy(output, aead_buf, len); - memcpy(tag, aead_buf + len, tag_len); + memcpy(iv, iv_local, iv_len); + memcpy(tag, tag_local, tag_out_len); } else { - memcpy(aead_buf, input, len); - memcpy(aead_buf + len, tag, tag_len); - - size_t output_length = 0; - status = psa_aead_decrypt(psa_key_id, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_len), - iv, iv_len, aad, aad_len, aead_buf, aead_buf_len, - output, len, &output_length); - if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "Error in decrypting data: %d", status); - err = ESP_FAIL; - goto cleanup; - } + memcpy(output, plaintext, out_len + fin_len); } err = ESP_OK; cleanup: - if (aead_buf) { - mbedtls_platform_zeroize(aead_buf, aead_buf_len); - free(aead_buf); + psa_aead_abort(&aead_op); + if (plaintext) { + mbedtls_platform_zeroize(plaintext, len); + free(plaintext); } if (psa_key_id != 0) { psa_destroy_key(psa_key_id); diff --git a/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt b/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt index f8e9587c62d..7afd1a284dd 100644 --- a/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt +++ b/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt @@ -31,8 +31,7 @@ endif() # SHA if(CONFIG_SOC_SHA_SUPPORTED) - list(APPEND srcs "${mbedtls_test_srcs_dir}/test_sha.c" - "${mbedtls_test_srcs_dir}/test_sha_perf.c") + list(APPEND srcs "${mbedtls_test_srcs_dir}/test_sha_perf.c") endif() # Mixed diff --git a/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h b/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h index 94fd58202c6..cb96b889a4b 100644 --- a/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h +++ b/components/mbedtls/esp_tee/esp_tee_mbedtls_config.h @@ -27,7 +27,6 @@ #pragma once -#define MBEDTLS_PSA_ASSUME_EXCLUSIVE_BUFFERS #ifndef CONFIG_IDF_TARGET_LINUX #undef MBEDTLS_PSA_BUILTIN_GET_ENTROPY #define MBEDTLS_PSA_DRIVER_GET_ENTROPY diff --git a/components/mbedtls/port/sha/esp_sha.c b/components/mbedtls/port/sha/esp_sha.c index 0c39bf6e74d..00def753fa6 100644 --- a/components/mbedtls/port/sha/esp_sha.c +++ b/components/mbedtls/port/sha/esp_sha.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2018-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,6 +9,7 @@ #include #include "psa/crypto.h" +#include "mbedtls/platform_util.h" #include "hal/sha_hal.h" #include "hal/sha_types.h" @@ -64,18 +65,20 @@ void esp_sha(esp_sha_type sha_type, const unsigned char *input, size_t ilen, uns if (alg == PSA_ALG_NONE) { ESP_LOGE(TAG, "SHA type %d not supported", (int)sha_type); - abort(); + return; } size_t olen; size_t output_len = PSA_HASH_LENGTH(alg); status = psa_hash_compute(alg, input, ilen, output, output_len, &olen); if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "SHA computation failed, status %d", status); - abort(); + ESP_LOGE(TAG, "SHA computation failed (status %d), output zeroed", (int)status); + mbedtls_platform_zeroize(output, output_len); + return; } if (olen != output_len) { - ESP_LOGE(TAG, "SHA output length mismatch, expected %u, got %u", output_len, olen); - abort(); + ESP_LOGE(TAG, "SHA output length mismatch (expected %u, got %u), output zeroed", output_len, olen); + mbedtls_platform_zeroize(output, output_len); + return; } } diff --git a/components/mbedtls/test_apps/mbedtls_ut/main/test_sha.c b/components/mbedtls/test_apps/mbedtls_ut/main/test_sha.c index b6612e54a45..1b4cac0f6bd 100644 --- a/components/mbedtls/test_apps/mbedtls_ut/main/test_sha.c +++ b/components/mbedtls/test_apps/mbedtls_ut/main/test_sha.c @@ -112,12 +112,6 @@ TEST_CASE("Test esp_sha()", "[hw_crypto]") #endif } -/* NOTE: This test attempts to mmap 1MB of flash starting from address 0x00, which overlaps - * the entire TEE protected region, causing the mmap operation to fail and triggering an - * exception in the subsequent steps. - */ -#if !CONFIG_SECURE_ENABLE_TEE - TEST_CASE("Test esp_sha() function with long input", "[hw_crypto]") { int r = -1; @@ -176,4 +170,3 @@ TEST_CASE("Test esp_sha() function with long input", "[hw_crypto]") } #endif -#endif // SOC_SHA_SUPPORTED && CONFIG_MBEDTLS_HARDWARE_SHA From c7f74fd7512ff4d02c95eed7ac62979ba07c712e Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Fri, 28 Aug 2026 13:51:47 +0530 Subject: [PATCH 7/8] change(esp_tee): Limit the TEE secure storage AEAD operation input buffer length --- .../tee_sec_storage/include/esp_tee_sec_storage.h | 12 +++++++++--- .../components/tee_sec_storage/tee_sec_storage.c | 13 +++++++------ .../tee_test_fw/main/test_esp_tee_sec_stg.c | 4 ++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h b/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h index 61d6ea9a3f3..bb76be87462 100644 --- a/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h +++ b/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h @@ -19,11 +19,13 @@ extern "C" { #include "sdkconfig.h" #if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP384R1_SIGN -#define MAX_ECDSA_SUPPORTED_KEY_LEN 48 /*!< Maximum supported size for the ECDSA key (SECP384R1) */ +#define MAX_ECDSA_SUPPORTED_KEY_LEN 48 /*!< Maximum supported size for the ECDSA key (SECP384R1) */ #else -#define MAX_ECDSA_SUPPORTED_KEY_LEN 32 /*!< Maximum supported size for the ECDSA key (SECP256R1) */ +#define MAX_ECDSA_SUPPORTED_KEY_LEN 32 /*!< Maximum supported size for the ECDSA key (SECP256R1) */ #endif /* CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP384R1_SIGN */ -#define MAX_AES_SUPPORTED_KEY_LEN 32 /*!< Maximum supported size for the AES key */ +#define MAX_AES_SUPPORTED_KEY_LEN 32 /*!< Maximum supported size for the AES key */ + +#define MAX_AEAD_INPUT_LEN 4096 /*!< Maximum input length per AEAD operation */ #define SEC_STORAGE_FLAG_NONE 0 /*!< No flags */ #define SEC_STORAGE_FLAG_WRITE_ONCE BIT(0) /*!< Data can only be written once */ @@ -169,6 +171,8 @@ esp_err_t esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg * @param[out] output Pointer to the output data buffer * * @note Non-standard @p iv_len / @p tag_len values are rejected with ESP_ERR_INVALID_SIZE. + * @note The input length must not exceed ::MAX_AEAD_INPUT_LEN bytes; + * larger inputs are rejected with ESP_ERR_INVALID_SIZE. * * @return esp_err_t ESP_OK on success, appropriate error code otherwise. */ @@ -185,6 +189,8 @@ esp_err_t esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ctx_t * @param[out] output Pointer to the output data buffer * * @note Non-standard @p iv_len / @p tag_len values are rejected with ESP_ERR_INVALID_SIZE. + * @note The input length must not exceed ::MAX_AEAD_INPUT_LEN bytes; + * larger inputs are rejected with ESP_ERR_INVALID_SIZE. * * @return esp_err_t ESP_OK on success, appropriate error code otherwise. */ diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c index ed06175f282..dfdc04f4159 100644 --- a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c @@ -704,6 +704,7 @@ static psa_status_t aead_update_chunked(psa_aead_operation_t *op, psa_algorithm_ for (size_t offset = 0; offset < len; offset += AEAD_CHUNK_LEN) { const size_t chunk = MIN(AEAD_CHUNK_LEN, len - offset); + /* NOTE: tight osize keeps PSA's internal output copy chunk-sized (it allocates the declared size) */ const size_t update_osize = PSA_AEAD_UPDATE_OUTPUT_SIZE(PSA_KEY_TYPE_AES, alg, chunk); const size_t osize = MIN(out_size - total, update_osize); size_t olen = 0; @@ -724,20 +725,19 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t uint8_t *output, bool is_encrypt) { if (key_id == NULL || input == NULL || output == NULL || tag == NULL || iv == NULL) { - ESP_LOGE(TAG, "Invalid arguments"); return ESP_ERR_INVALID_ARG; } - if (len == 0) { - ESP_LOGE(TAG, "Invalid input length"); + /* NOTE: Cap applies to both directions so encryption never produces a blob decryption cannot stage */ + if (len == 0 || len > MAX_AEAD_INPUT_LEN) { + ESP_LOGE(TAG, "Invalid input length (max %u)", (unsigned)MAX_AEAD_INPUT_LEN); return ESP_ERR_INVALID_SIZE; } /* Enforce standard AES-GCM parameters */ if (iv_len != AES256_GCM_IV_LEN || tag_len < AES256_GCM_TAG_LEN_MIN || tag_len > AES256_GCM_TAG_LEN_MAX) { - ESP_LOGE(TAG, "Non-standard GCM iv_len(%u)/tag_len(%u) rejected", - (unsigned)iv_len, (unsigned)tag_len); + ESP_LOGE(TAG, "Non-standard GCM iv_len/tag_len rejected"); return ESP_ERR_INVALID_SIZE; } @@ -795,7 +795,7 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t memcpy(iv_local, iv, iv_len); memcpy(tag_local, tag, tag_len); - /* NOTE: Only the AEAD-written prefix is ever copied out; zeroized at cleanup */ + /* NOTE: TEE-resident staging - no plaintext reaches the REE until the tag verifies */ plaintext = malloc(len); if (!plaintext) { err = ESP_ERR_NO_MEM; @@ -833,6 +833,7 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t err = ESP_FAIL; goto cleanup; } + ESP_FAULT_ASSERT(status == PSA_SUCCESS); if (is_encrypt) { memcpy(iv, iv_local, iv_len); diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c index b4a0820e635..247c4c3bcec 100644 --- a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c @@ -385,6 +385,10 @@ TEST_CASE("Test TEE Secure Storage - Null Pointer and Zero Length", "[sec_storag TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_encrypt(&aead_ctx, iv, sizeof(iv), tag, sizeof(tag), data)); TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_decrypt(&aead_ctx, iv, sizeof(iv), tag, sizeof(tag), data)); + aead_ctx.input_len = MAX_AEAD_INPUT_LEN + 1; + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_encrypt(&aead_ctx, iv, sizeof(iv), tag, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_decrypt(&aead_ctx, iv, sizeof(iv), tag, sizeof(tag), data)); + TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_id)); key_cfg.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1; From 540e380321049ec1e0e776ac5905448d19411c82 Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Thu, 3 Sep 2026 16:00:03 +0530 Subject: [PATCH 8/8] fix(esp_tee): Fix AEAD output buffer slicing in the `tee_basic` example --- .../components/example_secure_service/example_service.c | 4 ++-- examples/security/tee/tee_basic/main/tee_main.c | 2 +- examples/security/tee/tee_basic/pytest_tee_basic.py | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/examples/security/tee/tee_basic/components/example_secure_service/example_service.c b/examples/security/tee/tee_basic/components/example_secure_service/example_service.c index 11bc61b2a77..198f1e213e3 100644 --- a/examples/security/tee/tee_basic/components/example_secure_service/example_service.c +++ b/examples/security/tee/tee_basic/components/example_secure_service/example_service.c @@ -98,14 +98,14 @@ static esp_err_t aes_gcm_crypt_common(example_aes_gcm_ctx_t *ctx, uint8_t *tag, if (is_encrypt) { size_t output_tag_len = 0; - status = psa_aead_finish(&operation, output + output_len, ctx->input_len + tag_len - output_len, &output_len, tag, tag_len, &output_tag_len); + status = psa_aead_finish(&operation, output + output_len, ctx->input_len - output_len, &output_len, tag, tag_len, &output_tag_len); if (status != PSA_SUCCESS) { ESP_LOGE(TAG, "Error in finishing encryption: %d", status); goto cleanup; } } else { size_t plaintext_len = 0; - status = psa_aead_verify(&operation, output, ctx->input_len, &plaintext_len, tag, tag_len); + status = psa_aead_verify(&operation, output + output_len, ctx->input_len - output_len, &plaintext_len, tag, tag_len); if (status != PSA_SUCCESS) { ESP_LOGE(TAG, "Error in verifying decryption: %d", status); goto cleanup; diff --git a/examples/security/tee/tee_basic/main/tee_main.c b/examples/security/tee/tee_basic/main/tee_main.c index 22f58a1923d..f719029248a 100644 --- a/examples/security/tee/tee_basic/main/tee_main.c +++ b/examples/security/tee/tee_basic/main/tee_main.c @@ -45,7 +45,7 @@ void app_main(void) uint8_t aad_buf[AES256_GCM_AAD_LEN]; /* Generate random plaintext and AAD */ - esp_fill_random(plain_text, sizeof(EXAMPLE_BUF_SZ)); + esp_fill_random(plain_text, sizeof(plain_text)); esp_fill_random(aad_buf, AES256_GCM_AAD_LEN); /* Encryption operation */ diff --git a/examples/security/tee/tee_basic/pytest_tee_basic.py b/examples/security/tee/tee_basic/pytest_tee_basic.py index fdc466118ed..63f1cbe0db2 100644 --- a/examples/security/tee/tee_basic/pytest_tee_basic.py +++ b/examples/security/tee/tee_basic/pytest_tee_basic.py @@ -22,9 +22,8 @@ def test_example_tee_basic(dut: Dut) -> None: logging.info(f'{log_label}: {bin_size // 1024}KB') # Start test - dut.expect('AES-256-GCM operations in TEE', timeout=10) dut.expect('Secure service call: PROTECTED M-mode', timeout=10) dut.expect('AES-256-GCM encryption', timeout=10) dut.expect('Secure service call: PROTECTED M-mode', timeout=10) - dut.expect('AES-256-GCM decryption', timeout=10) + dut.expect('AES-GCM decryption successful!', timeout=10) dut.expect('Returned from app_main()', timeout=10)