Merge branch 'fix/tee_reentrant_svc_and_non_det_sign_v6.0' into 'release/v6.0'

feat(esp_tee): Backports to v6.0

See merge request espressif/esp-idf!52290
This commit is contained in:
Laukik Hase
2026-09-07 10:10:44 +05:30
30 changed files with 976 additions and 341 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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.
*/

View File

@@ -6,13 +6,13 @@
#include <string.h>
#include <stdlib.h>
#include <sys/param.h>
#include "soc/soc_caps.h"
#include "esp_log.h"
#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"
@@ -37,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
@@ -314,6 +314,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 +486,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)
@@ -569,11 +576,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);
@@ -674,43 +679,78 @@ 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);
/* 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;
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)
{
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;
}
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;
}
@@ -720,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);
@@ -737,52 +779,76 @@ 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;
goto cleanup;
}
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) {
esp_fill_random(iv, iv_len);
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;
status = psa_aead_encrypt_setup(&aead_op, psa_key_id, alg);
if (status == PSA_SUCCESS) {
status = psa_generate_random(iv_local, iv_len);
}
// Separate ciphertext and tag
memcpy(output, aead_buf, len);
memcpy(tag, aead_buf + len, tag_len);
} else {
memcpy(aead_buf, input, len);
memcpy(aead_buf + len, tag, tag_len);
memcpy(iv_local, iv, iv_len);
memcpy(tag_local, 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;
/* NOTE: TEE-resident staging - no plaintext reaches the REE until the tag verifies */
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;
}
ESP_FAULT_ASSERT(status == PSA_SUCCESS);
if (is_encrypt) {
memcpy(iv, iv_local, iv_len);
memcpy(tag, tag_local, tag_out_len);
} else {
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);

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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 ------------------------------------------------- */

View File

@@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include <string.h>
#include <sys/param.h>
#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

View File

@@ -7,6 +7,7 @@
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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();

View File

@@ -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]")
{
@@ -348,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;
@@ -686,21 +727,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);

View File

@@ -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
}

View File

@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#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

View File

@@ -0,0 +1,115 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#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

View File

@@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#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

View File

@@ -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
@@ -43,6 +42,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
@@ -55,12 +60,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
@@ -120,13 +123,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 +174,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 +208,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

View File

@@ -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();

View File

@@ -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 <entropy_poll.h>
#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;
}

View File

@@ -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;

View File

@@ -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 <assert.h>
#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;
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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)