diff --git a/components/bootloader_support/bootloader_flash/src/bootloader_flash.c b/components/bootloader_support/bootloader_flash/src/bootloader_flash.c index f1abd50036d..d85ebcec856 100644 --- a/components/bootloader_support/bootloader_flash/src/bootloader_flash.c +++ b/components/bootloader_support/bootloader_flash/src/bootloader_flash.c @@ -416,8 +416,8 @@ void bootloader_munmap(const void *mapping) mmu_hal_unmap_all(); #else cache_hal_suspend(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); - mmu_hal_unmap_region(0, FLASH_MMAP_VADDR, current_mapped_size); cache_hal_invalidate_addr(FLASH_MMAP_VADDR, current_mapped_size); + mmu_hal_unmap_region(0, FLASH_MMAP_VADDR, current_mapped_size); cache_hal_resume(CACHE_LL_LEVEL_EXT_MEM, CACHE_TYPE_ALL); #endif #endif diff --git a/components/esp_tee/CMakeLists.txt b/components/esp_tee/CMakeLists.txt index dcba631e4d3..33dd106bc34 100644 --- a/components/esp_tee/CMakeLists.txt +++ b/components/esp_tee/CMakeLists.txt @@ -9,7 +9,7 @@ idf_build_get_property(target IDF_TARGET) # ESP-TEE is currently supported only on the ESP32-C6, H2, C5 and C61 SoCs set(SUPPORTED_TARGETS "esp32c6" "esp32h2" "esp32c5" "esp32c61") if(NOT target IN_LIST SUPPORTED_TARGETS) - message(STATUS "ESP-TEE is currently supported only on the ${SUPPORTED_TARGETS} SoCs") + # ESP-TEE Kconfig is gated on the supported targets; nothing to register elsewhere. return() endif() diff --git a/components/esp_tee/include/private/esp_tee_binary.h b/components/esp_tee/include/private/esp_tee_binary.h index aca66276090..726e87be2fc 100644 --- a/components/esp_tee/include/private/esp_tee_binary.h +++ b/components/esp_tee/include/private/esp_tee_binary.h @@ -57,6 +57,14 @@ extern "C" { #error "CONFIG_SECURE_TEE_DROM_SIZE must be a multiple of SOC_MMU_PAGE_SIZE" #endif +/* With HAL assertions disabled (level 0), a failed HAL_ASSERT() expands to + * __builtin_unreachable(): the compiler then optimizes assuming the asserted + * preconditions always hold, turning any unvalidated HAL input into undefined + * behavior. The TEE must never be built this way. */ +#if CONFIG_SECURE_ENABLE_TEE && (CONFIG_HAL_DEFAULT_ASSERTION_LEVEL < 1) +#error "ESP-TEE requires HAL assertions (CONFIG_HAL_DEFAULT_ASSERTION_LEVEL >= 1)" +#endif + /* TEE Secure Storage partition label and NVS namespace */ #define ESP_TEE_SEC_STG_PART_LABEL "secure_storage" #define ESP_TEE_SEC_STG_NVS_NAMESPACE "tee_sec_stg_ns" diff --git a/components/esp_tee/scripts/esp_tee_sec_stg_keygen/README.md b/components/esp_tee/scripts/esp_tee_sec_stg_keygen/README.md index 3e949fab7b2..9bb50b6a1ff 100644 --- a/components/esp_tee/scripts/esp_tee_sec_stg_keygen/README.md +++ b/components/esp_tee/scripts/esp_tee_sec_stg_keygen/README.md @@ -18,6 +18,8 @@ options: -o, --output OUTPUT output binary file name -i, --input INPUT input key file (.pem for ecdsa, .bin for aes) --write-once make key persistent - cannot be modified or deleted once written + --tee-only mark key as owned exclusively by the TEE - the REE cannot use, generate or clear it + -h, --help Show this message and exit ``` ### ECDSA Keys @@ -31,7 +33,7 @@ python esp_tee_sec_stg_keygen.py -k ecdsa_p384 -o ecdsa_p384_k0.bin ```bash openssl ecparam -name prime256v1 -genkey -noout -out ecdsa_p256.pem -python esp_tee_sec_stg_keygen.py -k ecdsa_p256 -o ecdsa_p256_k1.bin -i ecdsa_p256.pem --write-once +python esp_tee_sec_stg_keygen.py -k ecdsa_p256 -o ecdsa_p256_k1.bin -i ecdsa_p256.pem --write-once --tee-only ``` ### AES-256 Key diff --git a/components/esp_tee/scripts/esp_tee_sec_stg_keygen/esp_tee_sec_stg_keygen.py b/components/esp_tee/scripts/esp_tee_sec_stg_keygen/esp_tee_sec_stg_keygen.py index 49b3836a437..87781547572 100644 --- a/components/esp_tee/scripts/esp_tee_sec_stg_keygen/esp_tee_sec_stg_keygen.py +++ b/components/esp_tee/scripts/esp_tee_sec_stg_keygen/esp_tee_sec_stg_keygen.py @@ -31,6 +31,7 @@ class KeyType(Enum): class Flags(IntFlag): NONE = 0x00000000 WRITE_ONCE = 0x00000001 + TEE_ONLY = 0x00000002 # === Key Generators === @@ -112,6 +113,11 @@ def parse_args() -> argparse.Namespace: action='store_true', help='make key persistent - cannot be modified or deleted once written', ) + parser.add_argument( + '--tee-only', + action='store_true', + help='mark key as owned exclusively by the TEE - the REE cannot use, generate or clear it', + ) return parser.parse_args() @@ -122,12 +128,16 @@ def main() -> None: flags = Flags.NONE if args.write_once: flags |= Flags.WRITE_ONCE + if args.tee_only: + flags |= Flags.TEE_ONLY print(f'[+] Generating key of type: {key_type.name} (value: {key_type.value})') if args.input: print(f'[+] Using user-provided key file: {args.input}') if args.write_once: print('[+] WRITE_ONCE flag is set') + if args.tee_only: + print('[+] TEE_ONLY flag is set') key_data = generate_key_data(key_type, flags, args.input) diff --git a/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c b/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c index 6ac7557f2ab..7a5bd54f1ec 100644 --- a/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c +++ b/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c @@ -42,6 +42,8 @@ static esp_err_t gen_ecdsa_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair) esp_tee_sec_storage_key_cfg_t key_cfg = { .id = (const char *)(ESP_ATT_TK_KEY_ID), .type = ESP_SEC_STG_KEY_ECDSA_SECP256R1, + /* The attestation key must never be usable from the REE */ + .flags = SEC_STORAGE_FLAG_TEE_ONLY, }; esp_err_t err = esp_tee_sec_storage_gen_key(&key_cfg); 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 8d863f459b9..61d6ea9a3f3 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 @@ -16,6 +16,8 @@ extern "C" { #include "esp_err.h" #include "esp_bit_defs.h" +#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) */ #else @@ -25,6 +27,7 @@ extern "C" { #define SEC_STORAGE_FLAG_NONE 0 /*!< No flags */ #define SEC_STORAGE_FLAG_WRITE_ONCE BIT(0) /*!< Data can only be written once */ +#define SEC_STORAGE_FLAG_TEE_ONLY BIT(1) /*!< Key is owned exclusively by the TEE */ /** * @brief Enum to represent the type of key stored in the secure storage @@ -97,6 +100,21 @@ typedef struct { * @return esp_err_t ESP_OK on success, appropriate error code otherwise. */ esp_err_t esp_tee_sec_storage_init(void); + +/** + * @brief Check whether a key ID is owned exclusively by the TEE + * + * A key is TEE-owned if either: + * - it refers to the reserved TEE attestation key + * (`CONFIG_SECURE_TEE_ATT_KEY_STR_ID`); this also blocks the REE from + * "squatting" the ID before the TEE creates the key, or + * - the stored key carries the ::SEC_STORAGE_FLAG_TEE_ONLY flag. + * + * @param key_id NULL-terminated key identifier string (may be NULL) + * + * @return true if the key is TEE-owned (REE access must be denied), false otherwise + */ +bool esp_tee_sec_storage_is_key_tee_owned(const char *key_id); #endif /** @@ -145,11 +163,13 @@ esp_err_t esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg * * @param[in] ctx Pointer to the AEAD operation context * @param[out] iv Pointer to the output buffer for the generated initialization vector - * @param[in] iv_len Length of the initialization vector buffer + * @param[in] iv_len Length of the initialization vector buffer; must be exactly 12 bytes (96-bit IV, per NIST SP 800-38D) * @param[out] tag Pointer to the authentication tag buffer - * @param[in] tag_len Length of the authentication tag + * @param[in] tag_len Length of the authentication tag; must be 12 to 16 bytes (96- to 128-bit tag, per NIST SP 800-38D) * @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. + * * @return esp_err_t ESP_OK on success, appropriate error code otherwise. */ esp_err_t 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); @@ -159,11 +179,13 @@ esp_err_t esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ctx_t * * @param[in] ctx Pointer to the AEAD operation context * @param[in] iv Pointer to the initialization vector used during encryption - * @param[in] iv_len Length of the initialization vector + * @param[in] iv_len Length of the initialization vector; must be exactly 12 bytes (96-bit IV, per NIST SP 800-38D) * @param[in] tag Pointer to the authentication tag buffer - * @param[in] tag_len Length of the authentication tag + * @param[in] tag_len Length of the authentication tag; must be 12 to 16 bytes (96- to 128-bit tag, per NIST SP 800-38D) * @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. + * * @return esp_err_t ESP_OK on success, appropriate error code otherwise. */ esp_err_t 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); 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 8f01c8e3313..f4b61fe1800 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 @@ -37,6 +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 ECDSA_SECP384R1_KEY_LEN 48 #define ECDSA_SECP256R1_KEY_LEN 32 @@ -285,6 +287,29 @@ static esp_err_t secure_storage_read(const char *key_id, void *data, size_t *len return nvs_get_blob(tee_nvs_hdl, key_id, data, len); } +bool esp_tee_sec_storage_is_key_tee_owned(const char *key_id) +{ + if (key_id == NULL) { + return false; + } + + bool is_att_key = false, is_tee_only = false; + esp_err_t err = ESP_FAIL; + +#if CONFIG_SECURE_TEE_ATTESTATION + is_att_key = (strncmp(key_id, CONFIG_SECURE_TEE_ATT_KEY_STR_ID, NVS_KEY_NAME_MAX_SIZE) == 0); +#endif + + sec_stg_key_t keyctx = {}; + size_t keyctx_len = sizeof(keyctx); + + err = secure_storage_read(key_id, (void *)&keyctx, &keyctx_len); + is_tee_only = (err == ESP_OK) && ((keyctx.flags & SEC_STORAGE_FLAG_TEE_ONLY) != 0); + mbedtls_platform_zeroize(&keyctx, sizeof(keyctx)); + + return (is_att_key || is_tee_only); +} + /* ---------------------------------------------- Interface APIs ------------------------------------------------- */ esp_err_t esp_tee_sec_storage_init(void) @@ -658,8 +683,16 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t return ESP_ERR_INVALID_ARG; } - if (len == 0 || tag_len == 0 || iv_len == 0) { - ESP_LOGE(TAG, "Invalid input/tag/iv length"); + if (len == 0) { + ESP_LOGE(TAG, "Invalid input length"); + 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); return ESP_ERR_INVALID_SIZE; } diff --git a/components/esp_tee/subproject/main/common/multi_heap.c b/components/esp_tee/subproject/main/common/multi_heap.c index e735ec73037..f84d2bed1a7 100644 --- a/components/esp_tee/subproject/main/common/multi_heap.c +++ b/components/esp_tee/subproject/main/common/multi_heap.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "rom_patch_tlsf.h" #include "esp_rom_sys.h" #include "tlsf_block_functions.h" @@ -63,6 +64,9 @@ esp_err_t esp_tee_heap_init(void *start_ptr, size_t size) return ESP_ERR_INVALID_SIZE; } + /* Zeroize the entire region before registering it as the TEE heap*/ + memset(start_ptr, 0, size); + #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 void *heap = tlsf_create_with_pool(start_ptr + sizeof(heap_t), usable_size); size_t overhead = tlsf_size(); @@ -227,7 +231,7 @@ void esp_tee_heap_dump_info(void) /* Definitions for functions from the heap component, used in files shared with ESP-IDF */ -void *heap_caps_malloc(size_t alignment, size_t size, uint32_t caps) +void *heap_caps_malloc(size_t size, uint32_t caps) { (void) caps; return esp_tee_heap_malloc(size); 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 b56b0c7c28c..2a335938407 100644 --- a/components/esp_tee/subproject/main/core/esp_secure_services.c +++ b/components/esp_tee/subproject/main/core/esp_secure_services.c @@ -592,17 +592,24 @@ int _ss_esp_tee_ota_end(void) */ esp_err_t _ss_esp_tee_sec_storage_clear_key(const char *key_id) { + bool valid_arg = !esp_tee_sec_storage_is_key_tee_owned(key_id); + if (!valid_arg) { + return ESP_ERR_INVALID_ARG; + } + ESP_FAULT_ASSERT(valid_arg); + return esp_tee_sec_storage_clear_key(key_id); } esp_err_t _ss_esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg) { - bool valid_addr = esp_tee_buf_in_ree(cfg, sizeof(esp_tee_sec_storage_key_cfg_t)); - - if (!valid_addr) { + 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 (!valid_arg) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(valid_arg); return esp_tee_sec_storage_gen_key(cfg); } 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 b5030720fc4..cf7d56e98b6 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 @@ -8,6 +8,7 @@ #include "esp_err.h" #include "esp_log.h" +#include "esp_macros.h" #include "esp_fault.h" #include "hal/mmu_types.h" @@ -38,6 +39,9 @@ static __attribute__((unused)) const char *TAG = "esp_tee_sec_srv_iram"; +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) + /* ---------------------------------------------- Interrupts ------------------------------------------------- */ #if SOC_INT_CLIC_SUPPORTED @@ -191,67 +195,69 @@ void _ss_wdt_hal_deinit(wdt_hal_context_t *hal) */ 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_addr = (esp_tee_buf_in_ree(cfg, sizeof(esp_tee_sec_storage_key_cfg_t)) && - esp_tee_buf_in_ree(hash, hlen) && - esp_tee_buf_in_ree(out_sign, sizeof(esp_tee_sec_storage_ecdsa_sign_t))); - - if (!valid_addr) { + bool valid_arg = (esp_tee_buf_in_ree(cfg, sizeof(esp_tee_sec_storage_key_cfg_t)) && + 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)); + if (!valid_arg) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(valid_arg); return esp_tee_sec_storage_ecdsa_sign(cfg, 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_addr = (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))); - - if (!valid_addr) { + 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 (!valid_arg) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(valid_arg); return esp_tee_sec_storage_ecdsa_get_pubkey(cfg, 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_addr = (esp_tee_buf_in_ree(ctx, sizeof(esp_tee_sec_storage_aead_ctx_t)) && - esp_tee_buf_in_ree(ctx->input, ctx->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)); + 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) && + 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)); if (ctx->aad_len != 0) { - valid_addr &= esp_tee_buf_in_ree(ctx->aad, ctx->aad_len); + valid_arg &= esp_tee_buf_in_ree(ctx->aad, ctx->aad_len); } - if (!valid_addr) { + if (!valid_arg) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(valid_arg); return esp_tee_sec_storage_aead_encrypt(ctx, 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_addr = (esp_tee_buf_in_ree(ctx, sizeof(esp_tee_sec_storage_aead_ctx_t)) && - esp_tee_buf_in_ree(ctx->input, ctx->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)); + 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) && + 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)); if (ctx->aad_len != 0) { - valid_addr &= esp_tee_buf_in_ree(ctx->aad, ctx->aad_len); + valid_arg &= esp_tee_buf_in_ree(ctx->aad, ctx->aad_len); } - if (!valid_addr) { + if (!valid_arg) { return ESP_ERR_INVALID_ARG; } - ESP_FAULT_ASSERT(valid_addr); + ESP_FAULT_ASSERT(valid_arg); return esp_tee_sec_storage_aead_decrypt(ctx, iv, iv_len, tag, tag_len, output); } @@ -274,11 +280,32 @@ esp_err_t _ss_esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pb /* ---------------------------------------------- MMU HAL ------------------------------------------------- */ +static bool tee_ree_ext_vaddr_ok(uint32_t mmu_id, uint32_t vaddr, uint32_t len) +{ + uint32_t page = mmu_hal_pages_to_bytes(mmu_id, 1); + uint32_t map_len = ALIGN_UP(len, page); + + return (len != 0 && map_len >= len && (vaddr % page == 0) && + mmu_hal_check_valid_ext_vaddr_region(mmu_id, vaddr, map_len, + MMU_VADDR_DATA | MMU_VADDR_INSTRUCTION) && + !esp_tee_flash_check_vrange_in_tee_region(vaddr, map_len)); +} + +static bool tee_ree_ext_paddr_ok(uint32_t mmu_id, uint32_t paddr, uint32_t len) +{ + uint32_t page = mmu_hal_pages_to_bytes(mmu_id, 1); + uint32_t map_len = ALIGN_UP(len, page); + + return (len != 0 && map_len >= len && (paddr % page == 0) && + mmu_hal_check_valid_paddr_region(mmu_id, paddr, map_len) && + !esp_tee_flash_check_prange_in_tee_region(paddr, map_len)); +} + void _ss_mmu_hal_map_region(uint32_t mmu_id, mmu_target_t mem_type, uint32_t vaddr, uint32_t paddr, uint32_t len, uint32_t *out_len) { - bool valid_addr = (!esp_tee_flash_check_vrange_in_tee_region(vaddr, len) && - !esp_tee_flash_check_prange_in_tee_region(paddr, len) && + bool valid_addr = (tee_ree_ext_vaddr_ok(mmu_id, vaddr, len) && + tee_ree_ext_paddr_ok(mmu_id, paddr, len) && esp_tee_buf_in_ree(out_len, sizeof(uint32_t))); if (!valid_addr) { @@ -292,20 +319,21 @@ void _ss_mmu_hal_map_region(uint32_t mmu_id, mmu_target_t mem_type, uint32_t vad void _ss_mmu_hal_unmap_region(uint32_t mmu_id, uint32_t vaddr, uint32_t len) { - bool vaddr_chk = esp_tee_flash_check_vrange_in_tee_region(vaddr, len); + bool valid_addr = tee_ree_ext_vaddr_ok(mmu_id, vaddr, len); - if (vaddr_chk) { + if (!valid_addr) { ESP_LOGD(TAG, "[%s] Illegal flash access at 0x%08x", __func__, vaddr); return; } - ESP_FAULT_ASSERT(!vaddr_chk); + ESP_FAULT_ASSERT(valid_addr); mmu_hal_unmap_region(mmu_id, vaddr, len); } bool _ss_mmu_hal_vaddr_to_paddr(uint32_t mmu_id, uint32_t vaddr, uint32_t *out_paddr, mmu_target_t *out_target) { - bool valid_addr = (!esp_tee_flash_check_vaddr_in_tee_region(vaddr) && + uint32_t page = mmu_hal_pages_to_bytes(mmu_id, 1); + bool valid_addr = (tee_ree_ext_vaddr_ok(mmu_id, ALIGN_DOWN(vaddr, page), 1) && esp_tee_buf_in_ree(out_paddr, sizeof(uint32_t)) && esp_tee_buf_in_ree(out_target, sizeof(mmu_target_t))); @@ -319,7 +347,8 @@ bool _ss_mmu_hal_vaddr_to_paddr(uint32_t mmu_id, uint32_t vaddr, uint32_t *out_p bool _ss_mmu_hal_paddr_to_vaddr(uint32_t mmu_id, uint32_t paddr, mmu_target_t target, mmu_vaddr_t type, uint32_t *out_vaddr) { - bool valid_addr = (!esp_tee_flash_check_paddr_in_tee_region(paddr) && + bool valid_addr = (mmu_hal_check_valid_paddr_region(mmu_id, paddr, 1) && + !esp_tee_flash_check_paddr_in_tee_region(paddr) && esp_tee_buf_in_ree(out_vaddr, sizeof(uint32_t))); if (!valid_addr) { diff --git a/components/esp_tee/subproject/main/core/esp_tee_init.c b/components/esp_tee/subproject/main/core/esp_tee_init.c index d42e155f579..998ea0c1134 100644 --- a/components/esp_tee/subproject/main/core/esp_tee_init.c +++ b/components/esp_tee/subproject/main/core/esp_tee_init.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 */ @@ -26,6 +26,9 @@ /* TEE symbols */ extern uint32_t _tee_stack; +extern uint32_t _tee_stack_bottom; +extern uint32_t _tee_intr_stack; +extern uint32_t _tee_intr_stack_bottom; extern uint32_t _tee_bss_start; extern uint32_t _tee_bss_end; extern uint32_t _tee_s_intr_handler; @@ -119,6 +122,9 @@ void __attribute__((noreturn)) esp_tee_init(uint32_t ree_entry_addr, uint32_t re { /* Clear BSS */ memset(&_tee_bss_start, 0, (&_tee_bss_end - &_tee_bss_start) * sizeof(_tee_bss_start)); + /* Clear the TEE stack and interrupt stack */ + memset(&_tee_stack_bottom, 0, (&_tee_stack - &_tee_stack_bottom) * sizeof(_tee_stack_bottom)); + memset(&_tee_intr_stack_bottom, 0, (&_tee_intr_stack - &_tee_intr_stack_bottom) * sizeof(_tee_intr_stack_bottom)); static uint32_t btld_sp; diff --git a/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c index 7aaa2edeb13..de49b5ff24b 100644 --- a/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c +++ b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c @@ -204,7 +204,7 @@ static int tee_sec_stg_gen_key(int argc, char **argv) err = esp_tee_sec_storage_clear_key(cfg.id); if (err != ESP_OK && err != ESP_ERR_NOT_FOUND) { - ESP_LOGE(TAG, "Failed to clear key %d!", cfg.id); + ESP_LOGE(TAG, "Failed to clear key %s!", cfg.id); goto exit; } diff --git a/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py b/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py index 2acdcc3bf30..226bc637091 100644 --- a/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py +++ b/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 import hashlib import http.server @@ -135,10 +135,6 @@ def test_tee_cli_attestation(dut: Dut) -> None: dut.expect('ESP-TEE: Secure services demonstration', timeout=30) time.sleep(1) - att_key_id = dut.app.sdkconfig.get('SECURE_TEE_ATT_KEY_STR_ID') - dut.write(f'tee_sec_stg_gen_key {att_key_id} 1') - dut.expect(r'Generated ECDSA_SECP256R1 key with ID (\S+)', timeout=30) - # Get the Entity Attestation token from TEE and verify its signature dut.write('tee_att_info') dut.expect(r'Attestation token - Length: (\d+)', timeout=30) 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 eb2f53d45c5..2cff4b58aa5 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 -# 16KB -CONFIG_SECURE_TEE_DRAM_SIZE=0x4000 +# 17KB +CONFIG_SECURE_TEE_DRAM_SIZE=0x4400 # 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 f4c39acfebb..b6b8fd811d3 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 @@ -2,8 +2,8 @@ # builds across various configurations - and is not intended for production use. # Reducing TEE IRAM size -# 30KB -CONFIG_SECURE_TEE_IRAM_SIZE=0x7800 +# 31KB +CONFIG_SECURE_TEE_IRAM_SIZE=0x7C00 # TEE Secure Storage: Release mode CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE=y diff --git a/components/esp_tee/test_apps/tee_test_fw/conftest.py b/components/esp_tee/test_apps/tee_test_fw/conftest.py index b025bc4db43..585c739e042 100644 --- a/components/esp_tee/test_apps/tee_test_fw/conftest.py +++ b/components/esp_tee/test_apps/tee_test_fw/conftest.py @@ -418,6 +418,7 @@ class TEESerial(IdfSerial): 'type': 'ecdsa_p256', 'input': 'ecdsa_p256_key.pem', 'write_once': True, + 'tee_only': True, 'b64': ( 'LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlNU1VpUktHaVZjSTIvbUZFekI3eXRIOVJj' 'd0wyUThkNDhONHNFUHFYc0RvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkYxYXRZQUxrdnB4cCt4N3c1dmVPQ1Vj' @@ -493,6 +494,7 @@ class TEESerial(IdfSerial): [sys.executable, ESP_TEE_SEC_STG_KEYGEN, '-k', entry['type'], '-o', str(tmp_dir / f'{entry["key"]}.bin')] + (['-i', entry['input']] if entry['input'] else []) + (['--write-once'] if entry['write_once'] else []) + + (['--tee-only'] if entry.get('tee_only') else []) for entry in self.KEY_DEFS ] 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 c5ea5f79781..55dd7ff4b7e 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 @@ -365,6 +365,38 @@ TEST_CASE("Test TEE Secure Storage - Null Pointer and Zero Length", "[sec_storag TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id)); } +#if CONFIG_SECURE_TEE_ATTESTATION +TEST_CASE("Test TEE Secure Storage - Attestation key is not REE-accessible", "[sec_storage]") +{ + const char *att_key_id = CONFIG_SECURE_TEE_ATT_KEY_STR_ID; + + esp_tee_sec_storage_key_cfg_t key_cfg = { + .id = att_key_id, + .type = ESP_SEC_STG_KEY_ECDSA_SECP256R1 + }; + + uint8_t digest[SHA256_DIGEST_SZ]; + esp_fill_random(digest, sizeof(digest)); + + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(&key_cfg)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_clear_key(att_key_id)); + + esp_tee_sec_storage_ecdsa_sign_t sign = {}; + esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {}; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_sign(&key_cfg, digest, sizeof(digest), &sign)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey)); + + uint8_t data[31], tag[12], iv[12]; + esp_tee_sec_storage_aead_ctx_t aead_ctx = { + .key_id = att_key_id, + .input = data, + .input_len = sizeof(data), + }; + 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)); +} +#endif + TEST_CASE("Test TEE Secure Storage - Verify data encryption", "[sec_storage_encr]") { ESP_LOGI(TAG, "Populating NVS-based TEE Secure Storage; encrypted with XTS-AES-512"); @@ -423,6 +455,22 @@ TEST_CASE("Test TEE Secure Storage - WRITE_ONCE keys", "[sec_storage]") TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_clear_key(key_cfg.id)); } +TEST_CASE("Test TEE Secure Storage - TEE_ONLY keys", "[sec_storage]") +{ + const char *key_id = "key_id_tee_only"; + esp_tee_sec_storage_key_cfg_t key_cfg = { + .id = key_id, + .type = ESP_SEC_STG_KEY_ECDSA_SECP256R1, + .flags = SEC_STORAGE_FLAG_TEE_ONLY, + }; + + 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_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(&key_cfg)); + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_clear_key(key_cfg.id)); +} + static void test_aead_encrypt_decrypt(const char *key_id, const uint8_t *input, size_t len) { uint8_t *ciphertext = heap_caps_malloc(len, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); @@ -524,10 +572,19 @@ TEST_CASE("Test TEE Secure Storage - Host-generated keys", "[sec_storage_host_ke size_t token_len = 0; TEST_ESP_OK(psa_initial_attest_get_token(auth_challenge, challenge_size, token_buf, token_buf_size, &token_len)); free(token_buf); - - const char *attest_key_id = "attest_key"; - TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_clear_key(attest_key_id)); #endif /* CONFIG_SECURE_TEE_ATTESTATION */ + + const char *attest_key_id = "attest_key"; + esp_tee_sec_storage_key_cfg_t attest_cfg = { + .id = attest_key_id, + .type = ESP_SEC_STG_KEY_ECDSA_SECP256R1, + }; + esp_tee_sec_storage_ecdsa_sign_t attest_sign = {0}; + esp_tee_sec_storage_ecdsa_pubkey_t attest_pubkey = {0}; + + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_sign(&attest_cfg, digest_buf, SHA256_DIGEST_SZ, &attest_sign)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_get_pubkey(&attest_cfg, &attest_pubkey)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_clear_key(attest_key_id)); } #if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN diff --git a/components/hal/include/hal/mmu_hal.h b/components/hal/include/hal/mmu_hal.h index 779af2d068e..82551ea7152 100644 --- a/components/hal/include/hal/mmu_hal.h +++ b/components/hal/include/hal/mmu_hal.h @@ -149,6 +149,18 @@ bool mmu_hal_paddr_to_vaddr(uint32_t mmu_id, uint32_t paddr, mmu_target_t target */ bool mmu_hal_check_valid_ext_vaddr_region(uint32_t mmu_id, uint32_t vaddr_start, uint32_t len, mmu_vaddr_t type); +/** + * Check if the paddr region is valid + * + * @param mmu_id MMU ID + * @param paddr_start start of the physical address + * @param len length, in bytes + * + * @return + * True for valid + */ +bool mmu_hal_check_valid_paddr_region(uint32_t mmu_id, uint32_t paddr_start, uint32_t len); + #if SOC_MMU_PER_EXT_MEM_TARGET /** * Get MMU ID from MMU target diff --git a/components/hal/mmu_hal.c b/components/hal/mmu_hal.c index 5c83a9681fd..0cd593d8efa 100644 --- a/components/hal/mmu_hal.c +++ b/components/hal/mmu_hal.c @@ -197,6 +197,11 @@ bool mmu_hal_check_valid_ext_vaddr_region(uint32_t mmu_id, uint32_t vaddr_start, return mmu_ll_check_valid_ext_vaddr_region(mmu_id, vaddr_start, len, type); } +bool mmu_hal_check_valid_paddr_region(uint32_t mmu_id, uint32_t paddr_start, uint32_t len) +{ + return mmu_ll_check_valid_paddr_region(mmu_id, paddr_start, len); +} + #if SOC_MMU_PER_EXT_MEM_TARGET uint32_t mmu_hal_get_id_from_target(mmu_target_t target) { diff --git a/docs/en/security/tee/tee-attestation.rst b/docs/en/security/tee/tee-attestation.rst index 2d45bcf9a18..02cca2b9125 100644 --- a/docs/en/security/tee/tee-attestation.rst +++ b/docs/en/security/tee/tee-attestation.rst @@ -14,6 +14,10 @@ To ensure security, the EAT is cryptographically protected. The remote relying p - Support for Attestation can be toggled using the option :ref:`CONFIG_SECURE_TEE_ATTESTATION` (enabled by default). + - The attestation signing key (identified by :ref:`CONFIG_SECURE_TEE_ATT_KEY_STR_ID`) is owned exclusively by the TEE. When the TEE generates this key, it is marked with the ``SEC_STORAGE_FLAG_TEE_ONLY`` flag, and the REE is denied any access to it through the secure service interface - it cannot use the key for signing, regenerate it, or clear it. This ensures that the attestation evidence can only ever be signed from within the TEE. + + - In addition, the reserved key ID is treated as TEE-owned even before the key exists, which prevents the REE from "squatting" the ID with a key of its own before the TEE provisions it. If the key is pre-provisioned as part of an NVS image (see :doc:`Secure Storage `), it **must** be generated with the ``--tee-only`` flag of the :component_file:`esp_tee_sec_stg_keygen.py` tool. + Attestation Flow ---------------- diff --git a/examples/security/tee/tee_secure_storage/main/tee_main.c b/examples/security/tee/tee_secure_storage/main/tee_main.c index aebdf0e1c2b..e24433cf933 100644 --- a/examples/security/tee/tee_secure_storage/main/tee_main.c +++ b/examples/security/tee/tee_secure_storage/main/tee_main.c @@ -93,7 +93,7 @@ static void example_tee_sec_stg_sign_verify(void *pvParameter) esp_err_t err = esp_tee_sec_storage_clear_key(cfg.id); if (err != ESP_OK && err != ESP_ERR_NOT_FOUND) { - ESP_LOGE(TAG, "Failed to clear key %d!", cfg.id); + ESP_LOGE(TAG, "Failed to clear key %s!", cfg.id); goto exit; } @@ -186,7 +186,7 @@ static void example_tee_sec_stg_encrypt_decrypt(void *pvParameter) err = esp_tee_sec_storage_clear_key(cfg.id); if (err != ESP_OK && err != ESP_ERR_NOT_FOUND) { - ESP_LOGE(TAG, "Failed to clear key %d!", cfg.id); + ESP_LOGE(TAG, "Failed to clear key %s!", cfg.id); goto exit; }