mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'feat/esp_tee_backports_v6.0' into 'release/v6.0'
feat(esp_tee): Feature/fixes backports to `release/v6.0` See merge request espressif/esp-idf!48486
This commit is contained in:
@@ -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
|
||||
*/
|
||||
@@ -174,8 +174,31 @@ esp_err_t esp_att_utils_eat_data_to_json(struct esp_att_sw_claim_list *head, con
|
||||
free(auth_challenge_hexstr);
|
||||
|
||||
json_gen_obj_set_int(&json_gen, "client_id", cfg->client_id);
|
||||
json_gen_obj_set_int(&json_gen, "chip_id", cfg->chip_id);
|
||||
json_gen_obj_set_int(&json_gen, "device_ver", cfg->device_ver);
|
||||
|
||||
json_gen_push_object(&json_gen, "ueid");
|
||||
|
||||
char mac_hexstr[ESP_ATT_EAT_UEID_MAC_SZ * 2 + 1] = {0};
|
||||
err = esp_att_utils_hexbuf_to_hexstr(cfg->ueid_mac, sizeof(cfg->ueid_mac),
|
||||
mac_hexstr, sizeof(mac_hexstr));
|
||||
if (err != ESP_OK) {
|
||||
free(json_buf);
|
||||
return err;
|
||||
}
|
||||
json_gen_obj_set_string(&json_gen, "mac", mac_hexstr);
|
||||
|
||||
char opt_id_hexstr[ESP_ATT_EAT_UEID_OPT_ID_SZ * 2 + 1] = {0};
|
||||
err = esp_att_utils_hexbuf_to_hexstr(cfg->ueid_opt_id, sizeof(cfg->ueid_opt_id),
|
||||
opt_id_hexstr, sizeof(opt_id_hexstr));
|
||||
if (err != ESP_OK) {
|
||||
free(json_buf);
|
||||
return err;
|
||||
}
|
||||
json_gen_obj_set_string(&json_gen, "optional_id", opt_id_hexstr);
|
||||
|
||||
json_gen_pop_object(&json_gen);
|
||||
|
||||
char dev_id_hexstr[ESP_ATT_EAT_DEV_ID_SZ * 2 + 1] = {0};
|
||||
err = esp_att_utils_hexbuf_to_hexstr(cfg->device_id, sizeof(cfg->device_id), dev_id_hexstr, sizeof(dev_id_hexstr));
|
||||
if (err != ESP_OK) {
|
||||
@@ -201,6 +224,7 @@ esp_err_t esp_att_utils_eat_data_to_json(struct esp_att_sw_claim_list *head, con
|
||||
esp_err_t err = part_metadata_to_json(&claim->metadata, &claim_json);
|
||||
if (err != ESP_OK || claim_json == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to format the FW metadata to JSON!");
|
||||
free(json_buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,44 +48,23 @@ static void free_sw_claim_list(void)
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t fetch_device_id(uint8_t *devid_buf)
|
||||
static esp_err_t fetch_ueids(esp_att_token_cfg_t *cfg)
|
||||
{
|
||||
if (devid_buf == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint8_t mac_addr[6] = {0};
|
||||
esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_MAC, mac_addr, sizeof(mac_addr) * 8);
|
||||
/* UEID: raw eFuse MAC */
|
||||
esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_MAC, cfg->ueid_mac,
|
||||
ESP_ATT_EAT_UEID_MAC_SZ * 8);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read MAC from eFuse!");
|
||||
goto exit;
|
||||
return err;
|
||||
}
|
||||
|
||||
psa_hash_operation_t hash_op = PSA_HASH_OPERATION_INIT;
|
||||
psa_status_t status = psa_hash_setup(&hash_op, PSA_ALG_SHA_256);
|
||||
if (status != PSA_SUCCESS) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
status = psa_hash_update(&hash_op, mac_addr, sizeof(mac_addr));
|
||||
if (status != PSA_SUCCESS) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
size_t digest_len = 0;
|
||||
status = psa_hash_finish(&hash_op, devid_buf, SHA256_DIGEST_SZ, &digest_len);
|
||||
if (status != PSA_SUCCESS) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (digest_len != SHA256_DIGEST_SZ) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
/* UEID: 128-bit OPTIONAL_UNIQUE_ID */
|
||||
err = esp_efuse_read_field_blob(ESP_EFUSE_OPTIONAL_UNIQUE_ID, cfg->ueid_opt_id,
|
||||
ESP_ATT_EAT_UEID_OPT_ID_SZ * 8);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t populate_att_token_cfg(esp_att_token_cfg_t *cfg, const esp_att_ecdsa_keypair_t *keypair)
|
||||
@@ -94,18 +73,31 @@ static esp_err_t populate_att_token_cfg(esp_att_token_cfg_t *cfg, const esp_att_
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t err = fetch_device_id(cfg->device_id);
|
||||
esp_err_t err = fetch_ueids(cfg);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get the device ID!");
|
||||
ESP_LOGE(TAG, "Failed to get the UEIDs!");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Device ID = SHA-256 of the MAC */
|
||||
size_t digest_len = 0;
|
||||
psa_status_t status = psa_hash_compute(PSA_ALG_SHA_256, cfg->ueid_mac, sizeof(cfg->ueid_mac),
|
||||
cfg->device_id, sizeof(cfg->device_id), &digest_len);
|
||||
if (status != PSA_SUCCESS || digest_len != sizeof(cfg->device_id)) {
|
||||
ESP_LOGE(TAG, "Failed to derive the device ID!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
err = esp_att_utils_ecdsa_get_pubkey_digest(keypair, cfg->instance_id, sizeof(cfg->instance_id));
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get ECDSA public key hash!");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Chip ID read from the ROM */
|
||||
extern const uint32_t _rom_chip_id;
|
||||
cfg->chip_id = _rom_chip_id;
|
||||
/* Chip revision read from eFuse */
|
||||
cfg->device_ver = efuse_hal_chip_revision();
|
||||
/* TODO: Decide what all fields we need here */
|
||||
cfg->device_stat = 0xA5;
|
||||
@@ -191,6 +183,13 @@ esp_err_t esp_att_generate_token(const uint8_t *auth_challenge, size_t challenge
|
||||
}
|
||||
|
||||
esp_att_ecdsa_keypair_t keypair = {};
|
||||
psa_hash_operation_t hash_op = PSA_HASH_OPERATION_INIT;
|
||||
psa_status_t status;
|
||||
char *hdr_json = NULL;
|
||||
char *eat_json = NULL;
|
||||
char *pubkey_json = NULL;
|
||||
char *sign_json = NULL;
|
||||
|
||||
err = esp_att_utils_ecdsa_gen_keypair_secp256r1(&keypair);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to generate ECDSA key-pair!");
|
||||
@@ -214,10 +213,10 @@ esp_err_t esp_att_generate_token(const uint8_t *auth_challenge, size_t challenge
|
||||
|
||||
memset(token_buf, 0x00, token_buf_size);
|
||||
|
||||
psa_hash_operation_t hash_op = PSA_HASH_OPERATION_INIT;
|
||||
psa_status_t status = psa_hash_setup(&hash_op, PSA_ALG_SHA_256);
|
||||
status = psa_hash_setup(&hash_op, PSA_ALG_SHA_256);
|
||||
if (status != PSA_SUCCESS) {
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
json_gen_str_t jstr;
|
||||
@@ -226,79 +225,84 @@ esp_err_t esp_att_generate_token(const uint8_t *auth_challenge, size_t challenge
|
||||
|
||||
/* Pushing the Header object */
|
||||
const esp_att_token_hdr_t tk_hdr = {};
|
||||
char *hdr_json = NULL;
|
||||
int hdr_len = -1;
|
||||
/* NOTE: Token header is not yet configurable */
|
||||
err = esp_att_utils_header_to_json(&tk_hdr, &hdr_json, &hdr_len);
|
||||
if (err != ESP_OK || hdr_json == NULL || hdr_len <= 0) {
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to format the token header as JSON!");
|
||||
return err;
|
||||
goto exit;
|
||||
}
|
||||
json_gen_push_object_str(&jstr, "header", hdr_json);
|
||||
|
||||
status = psa_hash_update(&hash_op, (const unsigned char *)hdr_json, hdr_len - 1);
|
||||
if (status != PSA_SUCCESS) {
|
||||
psa_hash_abort(&hash_op);
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
free(hdr_json);
|
||||
hdr_json = NULL;
|
||||
|
||||
/* Pushing the EAT object */
|
||||
char *eat_json = NULL;
|
||||
int eat_len = -1;
|
||||
err = esp_att_utils_eat_data_to_json(&sw_claim_data, &cfg, &eat_json, &eat_len);
|
||||
if (err != ESP_OK || eat_json == NULL || eat_len <= 0) {
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to format the EAT data to JSON!");
|
||||
return err;
|
||||
goto exit;
|
||||
}
|
||||
json_gen_push_object_str(&jstr, "eat", eat_json);
|
||||
|
||||
status = psa_hash_update(&hash_op, (const unsigned char *)eat_json, eat_len - 1);
|
||||
if (status != PSA_SUCCESS) {
|
||||
psa_hash_abort(&hash_op);
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
free(eat_json);
|
||||
eat_json = NULL;
|
||||
|
||||
char *pubkey_json = NULL;
|
||||
int pubkey_len = -1;
|
||||
err = esp_att_utils_pubkey_to_json(&keypair, &pubkey_json, &pubkey_len);
|
||||
if (err != ESP_OK || pubkey_json == NULL || pubkey_len <= 0) {
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to format the public key data to JSON!");
|
||||
return err;
|
||||
goto exit;
|
||||
}
|
||||
json_gen_push_object_str(&jstr, "public_key", pubkey_json);
|
||||
|
||||
status = psa_hash_update(&hash_op, (const unsigned char *)pubkey_json, pubkey_len - 1);
|
||||
if (status != PSA_SUCCESS) {
|
||||
psa_hash_abort(&hash_op);
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
free(pubkey_json);
|
||||
pubkey_json = NULL;
|
||||
|
||||
uint8_t digest[SHA256_DIGEST_SZ] = {0};
|
||||
size_t digest_len = 0;
|
||||
status = psa_hash_finish(&hash_op, digest, sizeof(digest), &digest_len);
|
||||
if (status != PSA_SUCCESS) {
|
||||
psa_hash_abort(&hash_op);
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
char *sign_json = NULL;
|
||||
int sign_len = -1;
|
||||
err = esp_att_utils_sign_to_json(&keypair, digest, sizeof(digest), &sign_json, &sign_len);
|
||||
if (err != ESP_OK || sign_json == NULL || sign_len <= 0) {
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to format the token signature to JSON!");
|
||||
return err;
|
||||
goto exit;
|
||||
}
|
||||
json_gen_push_object_str(&jstr, "sign", sign_json);
|
||||
free(sign_json);
|
||||
sign_json = NULL;
|
||||
|
||||
json_gen_end_object(&jstr);
|
||||
*token_size = json_gen_str_end(&jstr);
|
||||
err = ESP_OK;
|
||||
|
||||
exit:
|
||||
psa_hash_abort(&hash_op);
|
||||
free(hdr_json);
|
||||
free(eat_json);
|
||||
free(pubkey_json);
|
||||
free(sign_json);
|
||||
free_sw_claim_list();
|
||||
return err;
|
||||
}
|
||||
|
||||
+15
-10
@@ -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
|
||||
*/
|
||||
@@ -37,8 +37,10 @@ extern "C" {
|
||||
#define ESP_ATT_HDR_JSON_MAX_SZ (128)
|
||||
|
||||
#define ESP_ATT_EAT_DEV_ID_SZ (32)
|
||||
#define ESP_ATT_EAT_UEID_MAC_SZ (6) /* eFuse MAC */
|
||||
#define ESP_ATT_EAT_UEID_OPT_ID_SZ (16) /* eFuse OPTIONAL_UNIQUE_ID */
|
||||
#define ESP_ATT_CLAIM_JSON_MAX_SZ (448)
|
||||
#define ESP_ATT_EAT_JSON_MAX_SZ (1344)
|
||||
#define ESP_ATT_EAT_JSON_MAX_SZ (1600)
|
||||
|
||||
#define ESP_ATT_PUBKEY_JSON_MAX_SZ (128)
|
||||
#define ESP_ATT_SIGN_JSON_MAX_SZ (192)
|
||||
@@ -119,14 +121,17 @@ typedef struct {
|
||||
* @brief Structure to hold the Entity Attestation Token initial configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *auth_challenge; /**< Authentication challenge */
|
||||
size_t challenge_size; /**< Challenge size */
|
||||
uint32_t client_id; /**< Client identifier (Attestation relying party) */
|
||||
uint32_t device_ver; /**< Device version */
|
||||
uint8_t device_id[SHA256_DIGEST_SZ]; /**< Device identifier */
|
||||
uint8_t instance_id[SHA256_DIGEST_SZ]; /**< Instance identifier */
|
||||
char psa_cert_ref[32]; /**< PSA certificate reference */
|
||||
uint8_t device_stat; /**< Flags indicating device status */
|
||||
uint8_t *auth_challenge; /**< Authentication challenge */
|
||||
size_t challenge_size; /**< Challenge size */
|
||||
uint32_t client_id; /**< Client identifier (Attestation relying party) */
|
||||
uint32_t chip_id; /**< Chip identifier */
|
||||
uint32_t device_ver; /**< Device version */
|
||||
uint8_t ueid_mac[ESP_ATT_EAT_UEID_MAC_SZ]; /**< Device UEID: MAC from eFuse*/
|
||||
uint8_t ueid_opt_id[ESP_ATT_EAT_UEID_OPT_ID_SZ]; /**< Device UEID: OPTIONAL_UNIQUE_ID from eFuse*/
|
||||
uint8_t device_id[SHA256_DIGEST_SZ]; /**< Device identifier (SHA-256 of MAC) */
|
||||
uint8_t instance_id[SHA256_DIGEST_SZ]; /**< Instance identifier */
|
||||
char psa_cert_ref[32]; /**< PSA certificate reference */
|
||||
uint8_t device_stat; /**< Flags indicating device status */
|
||||
} esp_att_token_cfg_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "esp_hmac_pbkdf2.h"
|
||||
#include "psa/crypto.h"
|
||||
#include "mbedtls/platform_util.h"
|
||||
#include "mbedtls/psa_util.h"
|
||||
|
||||
#include "esp_rom_sys.h"
|
||||
@@ -193,7 +194,7 @@ static esp_err_t compute_nvs_keys_with_hmac(esp_efuse_block_t key_blk, nvs_sec_c
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
// Zero out the key buffer after import
|
||||
memset(key_buf, 0x00, sizeof(key_buf));
|
||||
mbedtls_platform_zeroize(key_buf, sizeof(key_buf));
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Failed to import HMAC key: %d", status);
|
||||
@@ -208,7 +209,7 @@ static esp_err_t compute_nvs_keys_with_hmac(esp_efuse_block_t key_blk, nvs_sec_c
|
||||
(uint8_t *)cfg->eky, SHA256_DIGEST_SZ, &mac_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
psa_destroy_key(psa_key_id);
|
||||
memset(cfg, 0x00, sizeof(nvs_sec_cfg_t));
|
||||
mbedtls_platform_zeroize(cfg, sizeof(nvs_sec_cfg_t));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_FAULT_ASSERT(status == PSA_SUCCESS);
|
||||
@@ -221,7 +222,7 @@ static esp_err_t compute_nvs_keys_with_hmac(esp_efuse_block_t key_blk, nvs_sec_c
|
||||
psa_destroy_key(psa_key_id);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
memset(cfg, 0x00, sizeof(nvs_sec_cfg_t));
|
||||
mbedtls_platform_zeroize(cfg, sizeof(nvs_sec_cfg_t));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_FAULT_ASSERT(status == PSA_SUCCESS);
|
||||
@@ -322,20 +323,24 @@ esp_err_t esp_tee_sec_storage_clear_key(const char *key_id)
|
||||
|
||||
esp_err_t err = secure_storage_read(key_id, (void *)&keyctx, &keyctx_len);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (keyctx.flags & SEC_STORAGE_FLAG_WRITE_ONCE) {
|
||||
ESP_LOGE(TAG, "Key is write-once only and cannot be cleared!");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = nvs_erase_key(tee_nvs_hdl, key_id);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = nvs_commit(tee_nvs_hdl);
|
||||
|
||||
cleanup:
|
||||
mbedtls_platform_zeroize(&keyctx, sizeof(keyctx));
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -465,6 +470,7 @@ esp_err_t esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg)
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_err_t err;
|
||||
sec_stg_key_t keyctx = {
|
||||
.type = cfg->type,
|
||||
.flags = cfg->flags,
|
||||
@@ -477,21 +483,28 @@ esp_err_t esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg)
|
||||
#endif
|
||||
if (generate_ecdsa_key(&keyctx, cfg->type) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to generate ECDSA keypair");
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
case ESP_SEC_STG_KEY_AES256:
|
||||
if (generate_aes256_key(&keyctx) != 0) {
|
||||
ESP_LOGE(TAG, "Failed to generate AES key");
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unsupported key-type!");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
err = ESP_ERR_NOT_SUPPORTED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return secure_storage_write(cfg->id, (void *)&keyctx, sizeof(keyctx));
|
||||
err = secure_storage_write(cfg->id, (void *)&keyctx, sizeof(keyctx));
|
||||
|
||||
cleanup:
|
||||
mbedtls_platform_zeroize(&keyctx, sizeof(keyctx));
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t 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)
|
||||
@@ -514,19 +527,21 @@ esp_err_t esp_tee_sec_storage_ecdsa_sign(const esp_tee_sec_storage_key_cfg_t *cf
|
||||
|
||||
sec_stg_key_t keyctx;
|
||||
size_t keyctx_len = sizeof(keyctx);
|
||||
psa_key_id_t key_id = 0;
|
||||
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
|
||||
err = secure_storage_read(cfg->id, (void *)&keyctx, &keyctx_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to fetch key from storage");
|
||||
return err;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (keyctx.type != cfg->type) {
|
||||
ESP_LOGE(TAG, "Key type mismatch");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
psa_key_id_t key_id = 0;
|
||||
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
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);
|
||||
@@ -571,6 +586,7 @@ esp_err_t esp_tee_sec_storage_ecdsa_sign(const esp_tee_sec_storage_key_cfg_t *cf
|
||||
exit:
|
||||
psa_destroy_key(key_id);
|
||||
psa_reset_key_attributes(&key_attributes);
|
||||
mbedtls_platform_zeroize(&keyctx, sizeof(keyctx));
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -594,12 +610,13 @@ esp_err_t esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg
|
||||
err = secure_storage_read(cfg->id, (void *)&keyctx, &keyctx_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to read key from secure storage");
|
||||
return err;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (keyctx.type != cfg->type) {
|
||||
ESP_LOGE(TAG, "Key type mismatch");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Now determine the public key source and length based on key type */
|
||||
@@ -619,13 +636,17 @@ esp_err_t esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg
|
||||
#endif
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unsupported key-type");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
err = ESP_ERR_INVALID_ARG;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memcpy(out_pubkey->pub_x, pub_key_src, pub_key_len);
|
||||
memcpy(out_pubkey->pub_y, pub_key_src + pub_key_len, pub_key_len);
|
||||
err = ESP_OK;
|
||||
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
mbedtls_platform_zeroize(&keyctx, sizeof(keyctx));
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t *input, size_t len, const uint8_t *aad,
|
||||
@@ -648,17 +669,22 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t
|
||||
return err;
|
||||
}
|
||||
|
||||
psa_key_id_t psa_key_id = 0;
|
||||
uint8_t *aead_buf = NULL;
|
||||
size_t aead_buf_len = 0;
|
||||
|
||||
sec_stg_key_t keyctx;
|
||||
size_t keyctx_len = sizeof(keyctx);
|
||||
err = secure_storage_read(key_id, (void *)&keyctx, &keyctx_len);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to fetch key from storage");
|
||||
return err;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (keyctx.type != ESP_SEC_STG_KEY_AES256) {
|
||||
ESP_LOGE(TAG, "Key type mismatch");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Setup PSA key attributes
|
||||
@@ -670,70 +696,66 @@ static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t
|
||||
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE);
|
||||
|
||||
// Import the AES key
|
||||
psa_key_id_t key_id_psa = 0;
|
||||
psa_status_t status = psa_import_key(&attributes, keyctx.aes256.key, AES256_KEY_LEN, &key_id_psa);
|
||||
psa_status_t status = psa_import_key(&attributes, keyctx.aes256.key, AES256_KEY_LEN, &psa_key_id);
|
||||
psa_reset_key_attributes(&attributes);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
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;
|
||||
}
|
||||
|
||||
if (is_encrypt) {
|
||||
// PSA AEAD encrypt outputs ciphertext+tag concatenated
|
||||
uint8_t *output_with_tag = malloc(len + tag_len);
|
||||
if (!output_with_tag) {
|
||||
psa_destroy_key(key_id_psa);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_fill_random(iv, iv_len);
|
||||
|
||||
size_t output_length = 0;
|
||||
status = psa_aead_encrypt(key_id_psa, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_len),
|
||||
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,
|
||||
output_with_tag, len + tag_len, &output_length);
|
||||
aead_buf, aead_buf_len, &output_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Error in encrypting data: %d", status);
|
||||
memset(output_with_tag, 0x00, len + tag_len);
|
||||
free(output_with_tag);
|
||||
psa_destroy_key(key_id_psa);
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Separate ciphertext and tag
|
||||
memcpy(output, output_with_tag, len);
|
||||
memcpy(tag, output_with_tag + len, tag_len);
|
||||
|
||||
memset(output_with_tag, 0x00, len + tag_len);
|
||||
free(output_with_tag);
|
||||
memcpy(output, aead_buf, len);
|
||||
memcpy(tag, aead_buf + len, tag_len);
|
||||
} else {
|
||||
// For decryption, PSA expects ciphertext + tag concatenated
|
||||
uint8_t *input_with_tag = malloc(len + tag_len);
|
||||
if (!input_with_tag) {
|
||||
psa_destroy_key(key_id_psa);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
memcpy(input_with_tag, input, len);
|
||||
memcpy(input_with_tag + len, tag, tag_len);
|
||||
memcpy(aead_buf, input, len);
|
||||
memcpy(aead_buf + len, tag, tag_len);
|
||||
|
||||
size_t output_length = 0;
|
||||
status = psa_aead_decrypt(key_id_psa, PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_len),
|
||||
iv, iv_len, aad, aad_len, input_with_tag, len + tag_len,
|
||||
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);
|
||||
|
||||
memset(input_with_tag, 0x00, len + tag_len);
|
||||
free(input_with_tag);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Error in decrypting data: %d", status);
|
||||
psa_destroy_key(key_id_psa);
|
||||
return ESP_FAIL;
|
||||
err = ESP_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
psa_destroy_key(key_id_psa);
|
||||
return ESP_OK;
|
||||
err = ESP_OK;
|
||||
|
||||
cleanup:
|
||||
if (aead_buf) {
|
||||
mbedtls_platform_zeroize(aead_buf, aead_buf_len);
|
||||
free(aead_buf);
|
||||
}
|
||||
if (psa_key_id != 0) {
|
||||
psa_destroy_key(psa_key_id);
|
||||
}
|
||||
mbedtls_platform_zeroize(&keyctx, sizeof(keyctx));
|
||||
return err;
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -819,24 +841,20 @@ esp_err_t esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pbkdf2
|
||||
}
|
||||
|
||||
// Sign the hash
|
||||
memset(out_sign, 0x00, sizeof(esp_tee_sec_storage_ecdsa_sign_t));
|
||||
size_t signature_length = 0;
|
||||
status = psa_sign_hash(psa_key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256),
|
||||
hash, hlen,
|
||||
out_sign->signature, sizeof(out_sign->signature), &signature_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
memset(out_sign, 0x00, sizeof(esp_tee_sec_storage_ecdsa_sign_t));
|
||||
err = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Export public key
|
||||
memset(out_pubkey, 0x00, sizeof(esp_tee_sec_storage_ecdsa_pubkey_t));
|
||||
uint8_t public_key[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE];
|
||||
size_t public_key_length = 0;
|
||||
status = psa_export_public_key(psa_key_id, public_key, sizeof(public_key), &public_key_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
memset(out_pubkey, 0x00, sizeof(esp_tee_sec_storage_ecdsa_pubkey_t));
|
||||
err = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
@@ -844,7 +862,6 @@ esp_err_t esp_tee_sec_storage_ecdsa_sign_pbkdf2(const esp_tee_sec_storage_pbkdf2
|
||||
// PSA exports public key in uncompressed format: 0x04 || X || Y
|
||||
// Skip the first byte (0x04) and copy X and Y coordinates
|
||||
if (public_key_length != (1 + 2 * key_len) || public_key[0] != 0x04) {
|
||||
memset(out_pubkey, 0x00, sizeof(esp_tee_sec_storage_ecdsa_pubkey_t));
|
||||
err = ESP_FAIL;
|
||||
goto exit;
|
||||
}
|
||||
@@ -859,7 +876,7 @@ exit:
|
||||
psa_destroy_key(psa_key_id);
|
||||
}
|
||||
if (derived_key) {
|
||||
memset(derived_key, 0x00, key_len);
|
||||
mbedtls_platform_zeroize(derived_key, key_len);
|
||||
free(derived_key);
|
||||
}
|
||||
return err;
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* Shared assembly helpers (macros + assembler-time constants) for the TEE
|
||||
* M-mode runtime. Included from esp_tee_vectors_{plic,clic}.S
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "riscv/rvruntime-frames.h"
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
#include "esp_private/hw_stack_guard.h"
|
||||
#endif
|
||||
|
||||
/* Shared assembler-time constants used by the macros */
|
||||
.equ SAVE_REGS, 32
|
||||
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
|
||||
.equ MAGIC, 0x1f
|
||||
|
||||
/* Macro which first allocates space on the stack to save general
|
||||
* purpose registers, and then save them. GP register is excluded.
|
||||
* The default size allocated on the stack is CONTEXT_SIZE, but it
|
||||
* can be overridden. */
|
||||
.macro save_general_regs cxt_size=CONTEXT_SIZE
|
||||
addi sp, sp, -\cxt_size
|
||||
sw ra, RV_STK_RA(sp)
|
||||
sw tp, RV_STK_TP(sp)
|
||||
sw t0, RV_STK_T0(sp)
|
||||
sw t1, RV_STK_T1(sp)
|
||||
sw t2, RV_STK_T2(sp)
|
||||
sw s0, RV_STK_S0(sp)
|
||||
sw s1, RV_STK_S1(sp)
|
||||
sw a0, RV_STK_A0(sp)
|
||||
sw a1, RV_STK_A1(sp)
|
||||
sw a2, RV_STK_A2(sp)
|
||||
sw a3, RV_STK_A3(sp)
|
||||
sw a4, RV_STK_A4(sp)
|
||||
sw a5, RV_STK_A5(sp)
|
||||
sw a6, RV_STK_A6(sp)
|
||||
sw a7, RV_STK_A7(sp)
|
||||
sw s2, RV_STK_S2(sp)
|
||||
sw s3, RV_STK_S3(sp)
|
||||
sw s4, RV_STK_S4(sp)
|
||||
sw s5, RV_STK_S5(sp)
|
||||
sw s6, RV_STK_S6(sp)
|
||||
sw s7, RV_STK_S7(sp)
|
||||
sw s8, RV_STK_S8(sp)
|
||||
sw s9, RV_STK_S9(sp)
|
||||
sw s10, RV_STK_S10(sp)
|
||||
sw s11, RV_STK_S11(sp)
|
||||
sw t3, RV_STK_T3(sp)
|
||||
sw t4, RV_STK_T4(sp)
|
||||
sw t5, RV_STK_T5(sp)
|
||||
sw t6, RV_STK_T6(sp)
|
||||
.endm
|
||||
|
||||
.macro save_mepc
|
||||
csrr t0, mepc
|
||||
sw t0, RV_STK_MEPC(sp)
|
||||
.endm
|
||||
|
||||
.macro save_mcsr
|
||||
csrr t0, mstatus
|
||||
sw t0, RV_STK_MSTATUS(sp)
|
||||
csrr t0, mtvec
|
||||
sw t0, RV_STK_MTVEC(sp)
|
||||
csrr t0, mtval
|
||||
sw t0, RV_STK_MTVAL(sp)
|
||||
csrr t0, mhartid
|
||||
sw t0, RV_STK_MHARTID(sp)
|
||||
csrr t0, mcause
|
||||
sw t0, RV_STK_MCAUSE(sp)
|
||||
.endm
|
||||
|
||||
/* Restore the general purpose registers (excluding gp) from the context on
|
||||
* the stack. The context is then deallocated. The default size is CONTEXT_SIZE
|
||||
* but it can be overridden. */
|
||||
.macro restore_general_regs cxt_size=CONTEXT_SIZE
|
||||
lw ra, RV_STK_RA(sp)
|
||||
lw tp, RV_STK_TP(sp)
|
||||
lw t0, RV_STK_T0(sp)
|
||||
lw t1, RV_STK_T1(sp)
|
||||
lw t2, RV_STK_T2(sp)
|
||||
lw s0, RV_STK_S0(sp)
|
||||
lw s1, RV_STK_S1(sp)
|
||||
lw a0, RV_STK_A0(sp)
|
||||
lw a1, RV_STK_A1(sp)
|
||||
lw a2, RV_STK_A2(sp)
|
||||
lw a3, RV_STK_A3(sp)
|
||||
lw a4, RV_STK_A4(sp)
|
||||
lw a5, RV_STK_A5(sp)
|
||||
lw a6, RV_STK_A6(sp)
|
||||
lw a7, RV_STK_A7(sp)
|
||||
lw s2, RV_STK_S2(sp)
|
||||
lw s3, RV_STK_S3(sp)
|
||||
lw s4, RV_STK_S4(sp)
|
||||
lw s5, RV_STK_S5(sp)
|
||||
lw s6, RV_STK_S6(sp)
|
||||
lw s7, RV_STK_S7(sp)
|
||||
lw s8, RV_STK_S8(sp)
|
||||
lw s9, RV_STK_S9(sp)
|
||||
lw s10, RV_STK_S10(sp)
|
||||
lw s11, RV_STK_S11(sp)
|
||||
lw t3, RV_STK_T3(sp)
|
||||
lw t4, RV_STK_T4(sp)
|
||||
lw t5, RV_STK_T5(sp)
|
||||
lw t6, RV_STK_T6(sp)
|
||||
addi sp, sp, \cxt_size
|
||||
.endm
|
||||
|
||||
.macro restore_mepc
|
||||
lw t0, RV_STK_MEPC(sp)
|
||||
csrw mepc, t0
|
||||
.endm
|
||||
|
||||
.macro store_magic_general_regs
|
||||
lui ra, MAGIC
|
||||
lui tp, MAGIC
|
||||
lui t0, MAGIC
|
||||
lui t1, MAGIC
|
||||
lui t2, MAGIC
|
||||
lui s0, MAGIC
|
||||
lui s1, MAGIC
|
||||
lui a0, MAGIC
|
||||
lui a1, MAGIC
|
||||
lui a2, MAGIC
|
||||
lui a3, MAGIC
|
||||
lui a4, MAGIC
|
||||
lui a5, MAGIC
|
||||
lui a6, MAGIC
|
||||
lui a7, MAGIC
|
||||
lui s2, MAGIC
|
||||
lui s3, MAGIC
|
||||
lui s4, MAGIC
|
||||
lui s5, MAGIC
|
||||
lui s6, MAGIC
|
||||
lui s7, MAGIC
|
||||
lui s8, MAGIC
|
||||
lui s9, MAGIC
|
||||
lui s10, MAGIC
|
||||
lui s11, MAGIC
|
||||
lui t3, MAGIC
|
||||
lui t4, MAGIC
|
||||
lui t5, MAGIC
|
||||
lui t6, MAGIC
|
||||
.endm
|
||||
|
||||
/**
|
||||
* STACK_GUARD_PRE_SWITCH
|
||||
* Stops HW stack-guard monitoring and optionally saves current bounds.
|
||||
*
|
||||
* Args:
|
||||
* op_reg – output register for "monitoring enabled" state (must be reused)
|
||||
* to_save – 1=save bounds to memory, 0=skip saving
|
||||
* sp_min – symbol to store lower bound (if to_save=1)
|
||||
* sp_max – symbol to store upper bound (if to_save=1)
|
||||
*
|
||||
* Clobbers: t0, t1, t2, op_reg
|
||||
*/
|
||||
.macro STACK_GUARD_PRE_SWITCH op_reg, to_save, sp_min, sp_max
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* Query if monitoring is enabled: result goes into \op_reg */
|
||||
ESP_HW_STACK_GUARD_MONITOR_QUERY_CUR_CORE t2 \op_reg
|
||||
beqz \op_reg, 1f
|
||||
|
||||
.if \to_save
|
||||
/* Save current REE/U-mode stack bounds */
|
||||
ESP_HW_STACK_GUARD_GET_BOUNDS_CUR_CORE t2 t0 t1
|
||||
la t2, \sp_min
|
||||
sw t0, 0(t2)
|
||||
la t2, \sp_max
|
||||
sw t1, 0(t2)
|
||||
.endif
|
||||
|
||||
/* Stop monitoring */
|
||||
ESP_HW_STACK_GUARD_MONITOR_STOP_CUR_CORE t0 t1
|
||||
fence
|
||||
1:
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/**
|
||||
* STACK_GUARD_POST_SWITCH
|
||||
* Restores or applies new bounds after switching stacks, then restarts monitoring.
|
||||
*
|
||||
* Args:
|
||||
* op_reg – saved monitoring state from PRE_SWITCH
|
||||
* to_restore – 1=restore from memory, 0=set static bounds
|
||||
* sp_min – saved bound (restore) or static lower bound (S-mode)
|
||||
* sp_max – saved bound (restore) or static upper bound (S-mode)
|
||||
*
|
||||
* Clobbers: t0, t1, t2
|
||||
*/
|
||||
.macro STACK_GUARD_POST_SWITCH op_reg, to_restore, sp_min, sp_max
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* Check if monitoring was enabled (using saved state from op_reg) */
|
||||
beqz \op_reg, 1f
|
||||
|
||||
.if \to_restore
|
||||
/* Restore saved REE/U-mode bounds from memory */
|
||||
la t2, \sp_min
|
||||
lw t0, 0(t2)
|
||||
la t2, \sp_max
|
||||
lw t1, 0(t2)
|
||||
.else
|
||||
/* Use new TEE/S-mode stack bounds (static symbols) */
|
||||
la t0, \sp_min
|
||||
la t1, \sp_max
|
||||
.endif
|
||||
|
||||
/* Apply bounds to hardware stack guard */
|
||||
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE t2 t0 t1
|
||||
/* Restart monitoring */
|
||||
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE t0 t1
|
||||
1:
|
||||
#endif
|
||||
.endm
|
||||
@@ -16,15 +16,10 @@
|
||||
#include "esp_tee_intr_defs.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
#include "esp_private/hw_stack_guard.h"
|
||||
#endif
|
||||
#include "esp_tee_asm_utils.inc"
|
||||
|
||||
.equ SAVE_REGS, 32
|
||||
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
|
||||
.equ panic_from_exception, tee_panic_from_exc
|
||||
.equ panic_from_isr, tee_panic_from_isr
|
||||
.equ MAGIC, 0x1f
|
||||
.equ RTNVAL, 0xc0de
|
||||
.equ ECALL_U_MODE, 0x8
|
||||
.equ ECALL_M_MODE, 0xb
|
||||
@@ -60,205 +55,6 @@ _ns_sp_min:
|
||||
_ns_sp_max:
|
||||
.word 0
|
||||
|
||||
/**
|
||||
* STACK_GUARD_PRE_SWITCH
|
||||
* Stops HW stack-guard monitoring and optionally saves current bounds.
|
||||
*
|
||||
* Args:
|
||||
* op_reg – output register for “monitoring enabled” state (must be reused)
|
||||
* to_save – 1=save bounds to memory, 0=skip saving
|
||||
* sp_min – symbol to store lower bound (if to_save=1)
|
||||
* sp_max – symbol to store upper bound (if to_save=1)
|
||||
*
|
||||
* Clobbers: t0, t1, t2, op_reg
|
||||
*/
|
||||
.macro STACK_GUARD_PRE_SWITCH op_reg, to_save, sp_min, sp_max
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* Query if monitoring is enabled: result goes into \op_reg */
|
||||
ESP_HW_STACK_GUARD_MONITOR_QUERY_CUR_CORE t2 \op_reg
|
||||
beqz \op_reg, 1f
|
||||
|
||||
.if \to_save
|
||||
/* Save current REE/U-mode stack bounds */
|
||||
ESP_HW_STACK_GUARD_GET_BOUNDS_CUR_CORE t2 t0 t1
|
||||
la t2, \sp_min
|
||||
sw t0, 0(t2)
|
||||
la t2, \sp_max
|
||||
sw t1, 0(t2)
|
||||
.endif
|
||||
|
||||
/* Stop monitoring */
|
||||
ESP_HW_STACK_GUARD_MONITOR_STOP_CUR_CORE t0 t1
|
||||
fence
|
||||
1:
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/**
|
||||
* STACK_GUARD_POST_SWITCH
|
||||
* Restores or applies new bounds after switching stacks, then restarts monitoring.
|
||||
*
|
||||
* Args:
|
||||
* op_reg – saved monitoring state from PRE_SWITCH
|
||||
* to_restore – 1=restore from memory, 0=set static bounds
|
||||
* sp_min – saved bound (restore) or static lower bound (S-mode)
|
||||
* sp_max – saved bound (restore) or static upper bound (S-mode)
|
||||
*
|
||||
* Clobbers: t0, t1, t2
|
||||
*/
|
||||
.macro STACK_GUARD_POST_SWITCH op_reg, to_restore, sp_min, sp_max
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* Check if monitoring was enabled (using saved state from op_reg) */
|
||||
beqz \op_reg, 1f
|
||||
|
||||
.if \to_restore
|
||||
/* Restore saved REE/U-mode bounds from memory */
|
||||
la t2, \sp_min
|
||||
lw t0, 0(t2)
|
||||
la t2, \sp_max
|
||||
lw t1, 0(t2)
|
||||
.else
|
||||
/* Use new TEE/S-mode stack bounds (static symbols) */
|
||||
la t0, \sp_min
|
||||
la t1, \sp_max
|
||||
.endif
|
||||
|
||||
/* Apply bounds to hardware stack guard */
|
||||
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE t2 t0 t1
|
||||
/* Restart monitoring */
|
||||
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE t0 t1
|
||||
1:
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/* Macro which first allocates space on the stack to save general
|
||||
* purpose registers, and then save them. GP register is excluded.
|
||||
* The default size allocated on the stack is CONTEXT_SIZE, but it
|
||||
* can be overridden. */
|
||||
.macro save_general_regs cxt_size=CONTEXT_SIZE
|
||||
addi sp, sp, -\cxt_size
|
||||
sw ra, RV_STK_RA(sp)
|
||||
sw tp, RV_STK_TP(sp)
|
||||
sw t0, RV_STK_T0(sp)
|
||||
sw t1, RV_STK_T1(sp)
|
||||
sw t2, RV_STK_T2(sp)
|
||||
sw s0, RV_STK_S0(sp)
|
||||
sw s1, RV_STK_S1(sp)
|
||||
sw a0, RV_STK_A0(sp)
|
||||
sw a1, RV_STK_A1(sp)
|
||||
sw a2, RV_STK_A2(sp)
|
||||
sw a3, RV_STK_A3(sp)
|
||||
sw a4, RV_STK_A4(sp)
|
||||
sw a5, RV_STK_A5(sp)
|
||||
sw a6, RV_STK_A6(sp)
|
||||
sw a7, RV_STK_A7(sp)
|
||||
sw s2, RV_STK_S2(sp)
|
||||
sw s3, RV_STK_S3(sp)
|
||||
sw s4, RV_STK_S4(sp)
|
||||
sw s5, RV_STK_S5(sp)
|
||||
sw s6, RV_STK_S6(sp)
|
||||
sw s7, RV_STK_S7(sp)
|
||||
sw s8, RV_STK_S8(sp)
|
||||
sw s9, RV_STK_S9(sp)
|
||||
sw s10, RV_STK_S10(sp)
|
||||
sw s11, RV_STK_S11(sp)
|
||||
sw t3, RV_STK_T3(sp)
|
||||
sw t4, RV_STK_T4(sp)
|
||||
sw t5, RV_STK_T5(sp)
|
||||
sw t6, RV_STK_T6(sp)
|
||||
.endm
|
||||
|
||||
.macro save_mepc
|
||||
csrr t0, mepc
|
||||
sw t0, RV_STK_MEPC(sp)
|
||||
.endm
|
||||
|
||||
.macro save_mcsr
|
||||
csrr t0, mstatus
|
||||
sw t0, RV_STK_MSTATUS(sp)
|
||||
csrr t0, mtvec
|
||||
sw t0, RV_STK_MTVEC(sp)
|
||||
csrr t0, mtval
|
||||
sw t0, RV_STK_MTVAL(sp)
|
||||
csrr t0, mhartid
|
||||
sw t0, RV_STK_MHARTID(sp)
|
||||
csrr t0, mcause
|
||||
sw t0, RV_STK_MCAUSE(sp)
|
||||
.endm
|
||||
|
||||
/* Restore the general purpose registers (excluding gp) from the context on
|
||||
* the stack. The context is then deallocated. The default size is CONTEXT_SIZE
|
||||
* but it can be overridden. */
|
||||
.macro restore_general_regs cxt_size=CONTEXT_SIZE
|
||||
lw ra, RV_STK_RA(sp)
|
||||
lw tp, RV_STK_TP(sp)
|
||||
lw t0, RV_STK_T0(sp)
|
||||
lw t1, RV_STK_T1(sp)
|
||||
lw t2, RV_STK_T2(sp)
|
||||
lw s0, RV_STK_S0(sp)
|
||||
lw s1, RV_STK_S1(sp)
|
||||
lw a0, RV_STK_A0(sp)
|
||||
lw a1, RV_STK_A1(sp)
|
||||
lw a2, RV_STK_A2(sp)
|
||||
lw a3, RV_STK_A3(sp)
|
||||
lw a4, RV_STK_A4(sp)
|
||||
lw a5, RV_STK_A5(sp)
|
||||
lw a6, RV_STK_A6(sp)
|
||||
lw a7, RV_STK_A7(sp)
|
||||
lw s2, RV_STK_S2(sp)
|
||||
lw s3, RV_STK_S3(sp)
|
||||
lw s4, RV_STK_S4(sp)
|
||||
lw s5, RV_STK_S5(sp)
|
||||
lw s6, RV_STK_S6(sp)
|
||||
lw s7, RV_STK_S7(sp)
|
||||
lw s8, RV_STK_S8(sp)
|
||||
lw s9, RV_STK_S9(sp)
|
||||
lw s10, RV_STK_S10(sp)
|
||||
lw s11, RV_STK_S11(sp)
|
||||
lw t3, RV_STK_T3(sp)
|
||||
lw t4, RV_STK_T4(sp)
|
||||
lw t5, RV_STK_T5(sp)
|
||||
lw t6, RV_STK_T6(sp)
|
||||
addi sp,sp, \cxt_size
|
||||
.endm
|
||||
|
||||
.macro restore_mepc
|
||||
lw t0, RV_STK_MEPC(sp)
|
||||
csrw mepc, t0
|
||||
.endm
|
||||
|
||||
.macro store_magic_general_regs
|
||||
lui ra, MAGIC
|
||||
lui tp, MAGIC
|
||||
lui t0, MAGIC
|
||||
lui t1, MAGIC
|
||||
lui t2, MAGIC
|
||||
lui s0, MAGIC
|
||||
lui s1, MAGIC
|
||||
lui a0, MAGIC
|
||||
lui a1, MAGIC
|
||||
lui a2, MAGIC
|
||||
lui a3, MAGIC
|
||||
lui a4, MAGIC
|
||||
lui a5, MAGIC
|
||||
lui a6, MAGIC
|
||||
lui a7, MAGIC
|
||||
lui s2, MAGIC
|
||||
lui s3, MAGIC
|
||||
lui s4, MAGIC
|
||||
lui s5, MAGIC
|
||||
lui s6, MAGIC
|
||||
lui s7, MAGIC
|
||||
lui s8, MAGIC
|
||||
lui s9, MAGIC
|
||||
lui s10, MAGIC
|
||||
lui s11, MAGIC
|
||||
lui t3, MAGIC
|
||||
lui t4, MAGIC
|
||||
lui t5, MAGIC
|
||||
lui t6, MAGIC
|
||||
.endm
|
||||
|
||||
.section .exception_vectors.text, "ax"
|
||||
|
||||
/* Exception handler. */
|
||||
@@ -394,13 +190,14 @@ _skip_thresh_restore:
|
||||
STACK_GUARD_POST_SWITCH t3 1 _ns_sp_min _ns_sp_max
|
||||
fence
|
||||
|
||||
call syscall_exit_tee
|
||||
|
||||
/* Backup the A0 register
|
||||
* This point is reached after an ecall is triggered after executing the secure service.
|
||||
* The A0 register contains the return value of the corresponding service.
|
||||
* After restoring the entire register context, we assign A0 the value back to the return value. */
|
||||
csrw mscratch, a0
|
||||
|
||||
call syscall_exit_tee
|
||||
|
||||
restore_general_regs
|
||||
csrrw a0, mscratch, zero
|
||||
|
||||
|
||||
@@ -16,15 +16,10 @@
|
||||
#include "esp_tee_intr_defs.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
#include "esp_private/hw_stack_guard.h"
|
||||
#endif
|
||||
#include "esp_tee_asm_utils.inc"
|
||||
|
||||
.equ SAVE_REGS, 32
|
||||
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
|
||||
.equ panic_from_exception, tee_panic_from_exc
|
||||
.equ panic_from_isr, tee_panic_from_isr
|
||||
.equ MAGIC, 0x1f
|
||||
.equ RTNVAL, 0xc0de
|
||||
.equ ECALL_U_MODE, 0x8
|
||||
.equ ECALL_M_MODE, 0xb
|
||||
@@ -65,205 +60,6 @@ _ns_sp_min:
|
||||
_ns_sp_max:
|
||||
.word 0
|
||||
|
||||
/**
|
||||
* STACK_GUARD_PRE_SWITCH
|
||||
* Stops HW stack-guard monitoring and optionally saves current bounds.
|
||||
*
|
||||
* Args:
|
||||
* op_reg – output register for “monitoring enabled” state (must be reused)
|
||||
* to_save – 1=save bounds to memory, 0=skip saving
|
||||
* sp_min – symbol to store lower bound (if to_save=1)
|
||||
* sp_max – symbol to store upper bound (if to_save=1)
|
||||
*
|
||||
* Clobbers: t0, t1, t2, op_reg
|
||||
*/
|
||||
.macro STACK_GUARD_PRE_SWITCH op_reg, to_save, sp_min, sp_max
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* Query if monitoring is enabled: result goes into \op_reg */
|
||||
ESP_HW_STACK_GUARD_MONITOR_QUERY_CUR_CORE t2 \op_reg
|
||||
beqz \op_reg, 1f
|
||||
|
||||
.if \to_save
|
||||
/* Save current REE/U-mode stack bounds */
|
||||
ESP_HW_STACK_GUARD_GET_BOUNDS_CUR_CORE t2 t0 t1
|
||||
la t2, \sp_min
|
||||
sw t0, 0(t2)
|
||||
la t2, \sp_max
|
||||
sw t1, 0(t2)
|
||||
.endif
|
||||
|
||||
/* Stop monitoring */
|
||||
ESP_HW_STACK_GUARD_MONITOR_STOP_CUR_CORE t0 t1
|
||||
fence
|
||||
1:
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/**
|
||||
* STACK_GUARD_POST_SWITCH
|
||||
* Restores or applies new bounds after switching stacks, then restarts monitoring.
|
||||
*
|
||||
* Args:
|
||||
* op_reg – saved monitoring state from PRE_SWITCH
|
||||
* to_restore – 1=restore from memory, 0=set static bounds
|
||||
* sp_min – saved bound (restore) or static lower bound (S-mode)
|
||||
* sp_max – saved bound (restore) or static upper bound (S-mode)
|
||||
*
|
||||
* Clobbers: t0, t1, t2
|
||||
*/
|
||||
.macro STACK_GUARD_POST_SWITCH op_reg, to_restore, sp_min, sp_max
|
||||
#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
|
||||
/* Check if monitoring was enabled (using saved state from op_reg) */
|
||||
beqz \op_reg, 1f
|
||||
|
||||
.if \to_restore
|
||||
/* Restore saved REE/U-mode bounds from memory */
|
||||
la t2, \sp_min
|
||||
lw t0, 0(t2)
|
||||
la t2, \sp_max
|
||||
lw t1, 0(t2)
|
||||
.else
|
||||
/* Use new TEE/S-mode stack bounds (static symbols) */
|
||||
la t0, \sp_min
|
||||
la t1, \sp_max
|
||||
.endif
|
||||
|
||||
/* Apply bounds to hardware stack guard */
|
||||
ESP_HW_STACK_GUARD_SET_BOUNDS_CUR_CORE t2 t0 t1
|
||||
/* Restart monitoring */
|
||||
ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE t0 t1
|
||||
1:
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/* Macro which first allocates space on the stack to save general
|
||||
* purpose registers, and then save them. GP register is excluded.
|
||||
* The default size allocated on the stack is CONTEXT_SIZE, but it
|
||||
* can be overridden. */
|
||||
.macro save_general_regs cxt_size=CONTEXT_SIZE
|
||||
addi sp, sp, -\cxt_size
|
||||
sw ra, RV_STK_RA(sp)
|
||||
sw tp, RV_STK_TP(sp)
|
||||
sw t0, RV_STK_T0(sp)
|
||||
sw t1, RV_STK_T1(sp)
|
||||
sw t2, RV_STK_T2(sp)
|
||||
sw s0, RV_STK_S0(sp)
|
||||
sw s1, RV_STK_S1(sp)
|
||||
sw a0, RV_STK_A0(sp)
|
||||
sw a1, RV_STK_A1(sp)
|
||||
sw a2, RV_STK_A2(sp)
|
||||
sw a3, RV_STK_A3(sp)
|
||||
sw a4, RV_STK_A4(sp)
|
||||
sw a5, RV_STK_A5(sp)
|
||||
sw a6, RV_STK_A6(sp)
|
||||
sw a7, RV_STK_A7(sp)
|
||||
sw s2, RV_STK_S2(sp)
|
||||
sw s3, RV_STK_S3(sp)
|
||||
sw s4, RV_STK_S4(sp)
|
||||
sw s5, RV_STK_S5(sp)
|
||||
sw s6, RV_STK_S6(sp)
|
||||
sw s7, RV_STK_S7(sp)
|
||||
sw s8, RV_STK_S8(sp)
|
||||
sw s9, RV_STK_S9(sp)
|
||||
sw s10, RV_STK_S10(sp)
|
||||
sw s11, RV_STK_S11(sp)
|
||||
sw t3, RV_STK_T3(sp)
|
||||
sw t4, RV_STK_T4(sp)
|
||||
sw t5, RV_STK_T5(sp)
|
||||
sw t6, RV_STK_T6(sp)
|
||||
.endm
|
||||
|
||||
.macro save_mepc
|
||||
csrr t0, mepc
|
||||
sw t0, RV_STK_MEPC(sp)
|
||||
.endm
|
||||
|
||||
.macro save_mcsr
|
||||
csrr t0, mstatus
|
||||
sw t0, RV_STK_MSTATUS(sp)
|
||||
csrr t0, mtvec
|
||||
sw t0, RV_STK_MTVEC(sp)
|
||||
csrr t0, mtval
|
||||
sw t0, RV_STK_MTVAL(sp)
|
||||
csrr t0, mhartid
|
||||
sw t0, RV_STK_MHARTID(sp)
|
||||
csrr t0, mcause
|
||||
sw t0, RV_STK_MCAUSE(sp)
|
||||
.endm
|
||||
|
||||
/* Restore the general purpose registers (excluding gp) from the context on
|
||||
* the stack. The context is then deallocated. The default size is CONTEXT_SIZE
|
||||
* but it can be overridden. */
|
||||
.macro restore_general_regs cxt_size=CONTEXT_SIZE
|
||||
lw ra, RV_STK_RA(sp)
|
||||
lw tp, RV_STK_TP(sp)
|
||||
lw t0, RV_STK_T0(sp)
|
||||
lw t1, RV_STK_T1(sp)
|
||||
lw t2, RV_STK_T2(sp)
|
||||
lw s0, RV_STK_S0(sp)
|
||||
lw s1, RV_STK_S1(sp)
|
||||
lw a0, RV_STK_A0(sp)
|
||||
lw a1, RV_STK_A1(sp)
|
||||
lw a2, RV_STK_A2(sp)
|
||||
lw a3, RV_STK_A3(sp)
|
||||
lw a4, RV_STK_A4(sp)
|
||||
lw a5, RV_STK_A5(sp)
|
||||
lw a6, RV_STK_A6(sp)
|
||||
lw a7, RV_STK_A7(sp)
|
||||
lw s2, RV_STK_S2(sp)
|
||||
lw s3, RV_STK_S3(sp)
|
||||
lw s4, RV_STK_S4(sp)
|
||||
lw s5, RV_STK_S5(sp)
|
||||
lw s6, RV_STK_S6(sp)
|
||||
lw s7, RV_STK_S7(sp)
|
||||
lw s8, RV_STK_S8(sp)
|
||||
lw s9, RV_STK_S9(sp)
|
||||
lw s10, RV_STK_S10(sp)
|
||||
lw s11, RV_STK_S11(sp)
|
||||
lw t3, RV_STK_T3(sp)
|
||||
lw t4, RV_STK_T4(sp)
|
||||
lw t5, RV_STK_T5(sp)
|
||||
lw t6, RV_STK_T6(sp)
|
||||
addi sp,sp, \cxt_size
|
||||
.endm
|
||||
|
||||
.macro restore_mepc
|
||||
lw t0, RV_STK_MEPC(sp)
|
||||
csrw mepc, t0
|
||||
.endm
|
||||
|
||||
.macro store_magic_general_regs
|
||||
lui ra, MAGIC
|
||||
lui tp, MAGIC
|
||||
lui t0, MAGIC
|
||||
lui t1, MAGIC
|
||||
lui t2, MAGIC
|
||||
lui s0, MAGIC
|
||||
lui s1, MAGIC
|
||||
lui a0, MAGIC
|
||||
lui a1, MAGIC
|
||||
lui a2, MAGIC
|
||||
lui a3, MAGIC
|
||||
lui a4, MAGIC
|
||||
lui a5, MAGIC
|
||||
lui a6, MAGIC
|
||||
lui a7, MAGIC
|
||||
lui s2, MAGIC
|
||||
lui s3, MAGIC
|
||||
lui s4, MAGIC
|
||||
lui s5, MAGIC
|
||||
lui s6, MAGIC
|
||||
lui s7, MAGIC
|
||||
lui s8, MAGIC
|
||||
lui s9, MAGIC
|
||||
lui s10, MAGIC
|
||||
lui s11, MAGIC
|
||||
lui t3, MAGIC
|
||||
lui t4, MAGIC
|
||||
lui t5, MAGIC
|
||||
lui t6, MAGIC
|
||||
.endm
|
||||
|
||||
.section .exception_vectors.text, "ax"
|
||||
|
||||
/* Exception handler. */
|
||||
@@ -386,13 +182,14 @@ _skip_thresh_restore:
|
||||
STACK_GUARD_POST_SWITCH t3 1 _ns_sp_min _ns_sp_max
|
||||
fence
|
||||
|
||||
call syscall_exit_tee
|
||||
|
||||
/* Backup the A0 register
|
||||
* This point is reached after an ecall is triggered after executing the secure service.
|
||||
* The A0 register contains the return value of the corresponding service.
|
||||
* After restoring the entire register context, we assign A0 the value back to the return value. */
|
||||
csrw mscratch, a0
|
||||
|
||||
call syscall_exit_tee
|
||||
|
||||
restore_general_regs
|
||||
csrrw a0, mscratch, zero
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ components/esp_tee/test_apps/tee_cli_app:
|
||||
disable:
|
||||
- if: IDF_TARGET not in ["esp32c6", "esp32c5", "esp32c61"]
|
||||
reason: only supported with c6, c5 and c61
|
||||
depends_components:
|
||||
- esp_tee
|
||||
|
||||
components/esp_tee/test_apps/tee_test_fw:
|
||||
disable:
|
||||
|
||||
@@ -138,9 +138,9 @@ help [<string>] [-v <0|1>]
|
||||
|
||||
```log
|
||||
esp32c6> tee_att_info
|
||||
I (8180) tee_attest: Attestation token - Length: 1587
|
||||
I (8180) tee_attest: Attestation token - Length: 1705
|
||||
I (8180) tee_attest: Attestation token - Data:
|
||||
'{"header":{"magic":"44fef7cc","encr_alg":"","sign_alg":"ecdsa_secp256r1_sha256","key_id":"tee_att_key0"},"eat":{"auth_challenge":"dcb9b53143ad6b081dad1a05c7ebda4e314d388762215799cf24ed52e9387678","client_id":262974944,"device_ver":0,"device_id":"cd9c173cb3675c7adfae243f0cd9841e4bce003237cb5321927a85a86cb4b32e","instance_id":"9616ef0ecf02cdc89a3749f8fc16b3103d5100bd42d9312fcd04593baa7bac64","psa_cert_ref":"0716053550477-10100","device_status":165,"sw_claims":{"tee":{"type":1,"ver":"v0.3.0","idf_ver":"v5.1.4-241-g7ff01fd46f-dirty","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"94536998e1dcb2a036477cb2feb01ed4fff67ba6208f30482346c62bca64b280","digest_validated":true,"sign_verified":true}},"app":{"type":2,"ver":"v0.1.0","idf_ver":"v5.1.4-241-g7ff01fd46f-dirty","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"3d4c038fcec76852b4d07acb9e94afaf5fca69fc2eb212a32032d09ce5b4f2b3","digest_validated":true,"sign_verified":true,"secure_padding":true}},"bootloader":{"type":0,"ver":"","idf_ver":"","secure_ver":-1,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"1bef421beb1a4642c6fcefb3e37fd4afad60cb4074e538f42605b012c482b946","digest_validated":true,"sign_verified":true}}}},"public_key":{"compressed":"02039c4bfab0762af1aff2fe5596b037f629cf839da8c4a9c0018afedfccf519a6"},"sign":{"r":"915e749f5a780bc21a2b21821cfeb54286dc742e9f12f2387e3de9b8b1a70bc9","s":"1e583236f2630b0fe8e291645ffa35d429f14035182e19868508d4dac0e1a441"}}'
|
||||
'{"header":{"magic":"44fef7cc","encr_alg":"","sign_alg":"ecdsa_secp256r1_sha256","key_id":"tee_att_key0"},"eat":{"auth_challenge":"dcb9b53143ad6b081dad1a05c7ebda4e314d388762215799cf24ed52e9387678","client_id":262974944,"chip_id":13,"device_ver":0,"ueid":{"mac":"d885ac67c978","optional_id":"94fa4d7e305682714d48e7bbd710c961"},"device_id":"cd9c173cb3675c7adfae243f0cd9841e4bce003237cb5321927a85a86cb4b32e","instance_id":"9616ef0ecf02cdc89a3749f8fc16b3103d5100bd42d9312fcd04593baa7bac64","psa_cert_ref":"0716053550477-10100","device_status":165,"sw_claims":{"tee":{"type":1,"ver":"v0.3.0","idf_ver":"v5.1.4-241-g7ff01fd46f-dirty","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"94536998e1dcb2a036477cb2feb01ed4fff67ba6208f30482346c62bca64b280","digest_validated":true,"sign_verified":true}},"app":{"type":2,"ver":"v0.1.0","idf_ver":"v5.1.4-241-g7ff01fd46f-dirty","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"3d4c038fcec76852b4d07acb9e94afaf5fca69fc2eb212a32032d09ce5b4f2b3","digest_validated":true,"sign_verified":true,"secure_padding":true}},"bootloader":{"type":0,"ver":"","idf_ver":"","secure_ver":-1,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"1bef421beb1a4642c6fcefb3e37fd4afad60cb4074e538f42605b012c482b946","digest_validated":true,"sign_verified":true}}}},"public_key":{"compressed":"02039c4bfab0762af1aff2fe5596b037f629cf839da8c4a9c0018afedfccf519a6"},"sign":{"r":"915e749f5a780bc21a2b21821cfeb54286dc742e9f12f2387e3de9b8b1a70bc9","s":"1e583236f2630b0fe8e291645ffa35d429f14035182e19868508d4dac0e1a441"}}'
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
@@ -20,3 +20,6 @@ CONFIG_EXAMPLE_OTA_RECV_TIMEOUT=30000
|
||||
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y
|
||||
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y
|
||||
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="test_certs/server_cert.pem"
|
||||
|
||||
# Takes effect only when Secure boot is enabled
|
||||
CONFIG_SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT=y
|
||||
|
||||
@@ -77,3 +77,7 @@ secure_services:
|
||||
type: custom
|
||||
function: esp_tee_test_stack_underflow
|
||||
args: 0
|
||||
- id: 219
|
||||
type: custom
|
||||
function: esp_tee_test_read_sec_stg
|
||||
args: 1
|
||||
|
||||
+29
-1
@@ -1,13 +1,21 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_tee.h"
|
||||
#include "esp_tee_flash.h"
|
||||
#include "esp_tee_memory_utils.h"
|
||||
#include "esp_tee_test.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_flash_partitions.h"
|
||||
|
||||
static const char *TAG = "test_sec_srv";
|
||||
|
||||
/* Sample Trusted App */
|
||||
@@ -54,3 +62,23 @@ uint32_t _ss_esp_tee_test_priv_mode_switch(uint32_t *a, uint32_t *b)
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
esp_err_t _ss_esp_tee_test_read_sec_stg(uint8_t *buf)
|
||||
{
|
||||
if (!esp_tee_buf_in_ree(buf, FLASH_SECTOR_SIZE)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_partition_info_t pinfo;
|
||||
esp_err_t err = esp_tee_flash_find_partition(PART_TYPE_DATA, PART_SUBTYPE_DATA_WIFI,
|
||||
ESP_TEE_SEC_STG_PART_LABEL, &pinfo);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (pinfo.pos.size < FLASH_SECTOR_SIZE) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
return (esp_err_t)esp_tee_flash_read(pinfo.pos.offset, (uint32_t *)buf, FLASH_SECTOR_SIZE, false);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import base64
|
||||
import csv
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -202,25 +203,6 @@ class TEESerial(IdfSerial):
|
||||
def _get_flash_size(self) -> Any:
|
||||
return self.app.sdkconfig.get('ESPTOOLPY_FLASHSIZE', '')
|
||||
|
||||
@EspSerial.use_esptool()
|
||||
def bootloader_force_flash_if_req(self) -> None:
|
||||
# Forcefully flash the bootloader only if security features are enabled
|
||||
if any(
|
||||
(
|
||||
self.app.sdkconfig.get('SECURE_BOOT', True),
|
||||
self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED', True),
|
||||
)
|
||||
):
|
||||
offs = int(self.app.sdkconfig.get('BOOTLOADER_OFFSET_IN_FLASH', 0))
|
||||
bootloader_path = os.path.join(self.app.binary_path, 'bootloader', 'bootloader.bin')
|
||||
encrypt = '--encrypt' if self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED') else ''
|
||||
flash_size = self._get_flash_size()
|
||||
|
||||
esptool.main(
|
||||
f'--no-stub write-flash {offs} {bootloader_path} --force {encrypt} --flash-size {flash_size}'.split(),
|
||||
esp=self.esp,
|
||||
)
|
||||
|
||||
@EspSerial.use_esptool()
|
||||
def custom_erase_partition(self, partition: str) -> None:
|
||||
if self.app.sdkconfig.get('SECURE_ENABLE_SECURE_ROM_DL_MODE'):
|
||||
@@ -294,29 +276,133 @@ class TEESerial(IdfSerial):
|
||||
if os.path.exists(file):
|
||||
os.remove(file)
|
||||
|
||||
@EspSerial.use_esptool()
|
||||
def custom_flash(self) -> None:
|
||||
self.bootloader_force_flash_if_req()
|
||||
self.flash()
|
||||
|
||||
@EspSerial.use_esptool()
|
||||
def custom_flash_w_test_tee_img_gen(self) -> None:
|
||||
self.bootloader_force_flash_if_req()
|
||||
self.flash()
|
||||
self.copy_test_tee_img('ota_1', False)
|
||||
|
||||
@EspSerial.use_esptool()
|
||||
def custom_flash_w_test_tee_img_rb(self) -> None:
|
||||
self.bootloader_force_flash_if_req()
|
||||
self.flash()
|
||||
self.copy_test_tee_img('ota_1', True)
|
||||
|
||||
@EspSerial.use_esptool()
|
||||
def custom_flash_with_empty_sec_stg(self) -> None:
|
||||
self.bootloader_force_flash_if_req()
|
||||
self.flash()
|
||||
self.custom_erase_partition('secure_storage')
|
||||
|
||||
KEY_DEFS_ENCRYPTION_TEST: list[str] = [
|
||||
'aes256_key0',
|
||||
'aes256_key1',
|
||||
'attest_key',
|
||||
'ecdsa_p256_key0',
|
||||
]
|
||||
|
||||
# TEE Secure Storage Development mode
|
||||
# NVS XTS-AES keys: E-key=0x33*32 || T-key=0xCC*32
|
||||
NVS_KEYS_DEV_B64 = 'MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzPMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzA=='
|
||||
|
||||
@property
|
||||
def nvs_partition_gen(self) -> str:
|
||||
return str(
|
||||
Path(os.environ['IDF_PATH'])
|
||||
/ 'components'
|
||||
/ 'nvs_flash'
|
||||
/ 'nvs_partition_generator'
|
||||
/ 'nvs_partition_gen.py'
|
||||
)
|
||||
|
||||
def derive_sec_stg_nvs_keys(self, out_path: Path) -> None:
|
||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if self.app.sdkconfig.get('SECURE_TEE_SEC_STG_MODE_RELEASE'):
|
||||
hmac_key_src = self.TEST_KEYS_DIR / 'tee_sec_stg_hmac_key.bin'
|
||||
self.run_command(
|
||||
[
|
||||
sys.executable,
|
||||
self.nvs_partition_gen,
|
||||
'generate-key',
|
||||
'--key_protect_hmac',
|
||||
'--kp_hmac_inputkey',
|
||||
str(hmac_key_src),
|
||||
'--keyfile',
|
||||
out_path.name,
|
||||
'--outdir',
|
||||
str(out_path.parent),
|
||||
]
|
||||
)
|
||||
(out_path.parent / 'keys' / out_path.name).replace(out_path)
|
||||
else:
|
||||
self.write_keys_to_file(self.NVS_KEYS_DEV_B64, out_path)
|
||||
|
||||
def decrypt_sec_stg_partition(self, dump_path: Path, keys_path: Path, decrypted_path: Path) -> None:
|
||||
self.run_command(
|
||||
[sys.executable, self.nvs_partition_gen, 'decrypt', str(dump_path), str(keys_path), str(decrypted_path)]
|
||||
)
|
||||
|
||||
_SEC_STG_HEX_LINE_RE = re.compile(
|
||||
rb'test_esp_tee_sec_storage:\s+((?:[0-9a-f]{2} ){0,15}[0-9a-f]{2})',
|
||||
)
|
||||
SEC_STG_DUMP_SZ = 4096
|
||||
|
||||
def capture_sec_stg_partition_dump(self, dut: Any, timeout: float = 60) -> bytes:
|
||||
dut.expect_exact('SEC_STG_DUMP_BEGIN', timeout=timeout)
|
||||
blob = dut.expect_exact('SEC_STG_DUMP_END', timeout=timeout, return_what_before_match=True)
|
||||
|
||||
raw = bytearray()
|
||||
for match in self._SEC_STG_HEX_LINE_RE.finditer(blob):
|
||||
for tok in match.group(1).split():
|
||||
raw.append(int(tok, 16))
|
||||
|
||||
if len(raw) != self.SEC_STG_DUMP_SZ:
|
||||
raise RuntimeError(
|
||||
f'Hex dump parse mismatch: got {len(raw)} bytes, expected {self.SEC_STG_DUMP_SZ}.\n'
|
||||
f'Blob (first 256 bytes): {blob[:256]!r}'
|
||||
)
|
||||
|
||||
# Make sure the Unity case actually passed before we trust the dump.
|
||||
m = dut.expect(re.compile(rb'(\d+) Tests (\d+) Failures (\d+) Ignored'), timeout=timeout)
|
||||
if int(m.group(2)) != 0:
|
||||
raise RuntimeError(f'Unity reported {m.group(2).decode()} failures while running encryption test')
|
||||
|
||||
return bytes(raw)
|
||||
|
||||
def _run_nvs_tool_minimal(self, partition_file: Path) -> subprocess.CompletedProcess:
|
||||
nvs_tool = Path(os.environ['IDF_PATH']) / 'components' / 'nvs_flash' / 'nvs_partition_tool' / 'nvs_tool.py'
|
||||
return subprocess.run(
|
||||
[sys.executable, str(nvs_tool), '-d', 'minimal', '--color', 'never', str(partition_file)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
def verify_tee_sec_stg_encryption(self, dut: Any) -> None:
|
||||
tmp_dir = self.TMP_DIR / 'sec_stg_encryption'
|
||||
tmp_dir.mkdir(parents=True, exist_ok=True)
|
||||
raw_path = tmp_dir / 'tee_sec_stg_dump.bin'
|
||||
keys_path = tmp_dir / self.NVS_KEYS_FILE
|
||||
decrypted_path = tmp_dir / 'tee_sec_stg_decr.bin'
|
||||
expected_key_ids = self.KEY_DEFS_ENCRYPTION_TEST
|
||||
|
||||
print('Verifying TEE Secure Storage NVS partition encryption (XTS-AES-512: 256-bit AES, 512-bit total key)')
|
||||
try:
|
||||
raw_bytes = self.capture_sec_stg_partition_dump(dut)
|
||||
raw_path.write_bytes(raw_bytes)
|
||||
|
||||
self.derive_sec_stg_nvs_keys(keys_path)
|
||||
self.decrypt_sec_stg_partition(raw_path, keys_path, decrypted_path)
|
||||
|
||||
print('Confirming key IDs are NOT present in the raw (encrypted) NVS dump')
|
||||
raw_parse = self._run_nvs_tool_minimal(raw_path)
|
||||
for key_id in expected_key_ids:
|
||||
assert key_id not in raw_parse.stdout, f'{key_id!r} surfaced in raw dump (not encrypted)'
|
||||
|
||||
print('Confirming key IDs ARE present after XTS-AES decrypt with the derived NVS keys')
|
||||
decrypted_parse = self._run_nvs_tool_minimal(decrypted_path)
|
||||
assert decrypted_parse.returncode == 0, f'nvs_tool exit {decrypted_parse.returncode} on decrypted dump'
|
||||
for key_id in expected_key_ids:
|
||||
assert key_id in decrypted_parse.stdout, f'{key_id!r} missing after decrypt (wrong XTS-AES key?)'
|
||||
finally:
|
||||
shutil.rmtree(tmp_dir, ignore_errors=True)
|
||||
|
||||
KEY_DEFS: list[dict[str, Any]] = [
|
||||
{'key': 'aes256_key0', 'type': 'aes256', 'input': None, 'write_once': True},
|
||||
{
|
||||
@@ -354,12 +440,11 @@ class TEESerial(IdfSerial):
|
||||
},
|
||||
]
|
||||
|
||||
NVS_KEYS_B64 = 'MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzPMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzA=='
|
||||
|
||||
TEST_KEYS_DIR = Path(__file__).parent / 'test_keys'
|
||||
TMP_DIR = Path('tmp')
|
||||
NVS_KEYS_PATH = TMP_DIR / 'nvs_keys.bin'
|
||||
NVS_CSV_PATH = TMP_DIR / 'tee_sec_stg_val.csv'
|
||||
NVS_BIN_PATH = TMP_DIR / 'tee_sec_stg_nvs.bin'
|
||||
NVS_KEYS_FILE = 'tee_sec_stg_nvs_keys.bin'
|
||||
|
||||
def run_command(self, command: list[str]) -> None:
|
||||
try:
|
||||
@@ -391,16 +476,19 @@ class TEESerial(IdfSerial):
|
||||
input_path = tmp_dir / entry['input']
|
||||
self.write_keys_to_file(entry['b64'], input_path)
|
||||
entry['input'] = str(input_path)
|
||||
self.write_keys_to_file(self.NVS_KEYS_B64, self.NVS_KEYS_PATH)
|
||||
|
||||
idf_path = Path(os.environ['IDF_PATH'])
|
||||
ESP_TEE_SEC_STG_KEYGEN = os.path.join(
|
||||
idf_path, 'components', 'esp_tee', 'scripts', 'esp_tee_sec_stg_keygen', 'esp_tee_sec_stg_keygen.py'
|
||||
)
|
||||
NVS_PARTITION_GEN = os.path.join(
|
||||
idf_path, 'components', 'nvs_flash', 'nvs_partition_generator', 'nvs_partition_gen.py'
|
||||
os.environ['IDF_PATH'],
|
||||
'components',
|
||||
'esp_tee',
|
||||
'scripts',
|
||||
'esp_tee_sec_stg_keygen',
|
||||
'esp_tee_sec_stg_keygen.py',
|
||||
)
|
||||
|
||||
nvs_keys = tmp_dir / self.NVS_KEYS_FILE
|
||||
self.derive_sec_stg_nvs_keys(nvs_keys)
|
||||
|
||||
cmds = [
|
||||
[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 [])
|
||||
@@ -410,13 +498,12 @@ class TEESerial(IdfSerial):
|
||||
|
||||
csv_path = self.create_tee_sec_stg_csv(tmp_dir)
|
||||
nvs_bin = self.NVS_BIN_PATH
|
||||
nvs_keys = self.NVS_KEYS_PATH
|
||||
size = self.app.partition_table['secure_storage']['size']
|
||||
|
||||
cmds.append(
|
||||
[
|
||||
sys.executable,
|
||||
NVS_PARTITION_GEN,
|
||||
self.nvs_partition_gen,
|
||||
'encrypt',
|
||||
str(csv_path),
|
||||
str(nvs_bin),
|
||||
@@ -430,10 +517,9 @@ class TEESerial(IdfSerial):
|
||||
for cmd in cmds:
|
||||
self.run_command(cmd)
|
||||
|
||||
self.bootloader_force_flash_if_req()
|
||||
self.flash()
|
||||
self.custom_erase_partition('secure_storage')
|
||||
self.custom_write_partition('secure_storage', nvs_bin)
|
||||
self.custom_write_partition('secure_storage', str(nvs_bin))
|
||||
|
||||
finally:
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
@@ -127,8 +127,8 @@ TEST_CASE("Test TEE OTA - Corrupted image", "[ota_neg_2]")
|
||||
/* Corrupting the image */
|
||||
ESP_LOGI(TAG, "Corrupting the image at some offset...");
|
||||
uint32_t corrupt[8] = {[0 ... 7] = 0x0BADC0DE};
|
||||
curr_write_offset -= (2 * FLASH_SECTOR_SIZE + sizeof(corrupt));
|
||||
TEST_ESP_OK(esp_tee_ota_write(curr_write_offset, (const void *)corrupt, sizeof(corrupt)));
|
||||
uint32_t offs = SOC_MMU_PAGE_SIZE + 0x200;
|
||||
TEST_ESP_OK(esp_tee_ota_write(offs, (const void *)corrupt, sizeof(corrupt)));
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_IMAGE_INVALID, esp_tee_ota_end());
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
@@ -364,6 +365,45 @@ 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));
|
||||
}
|
||||
|
||||
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");
|
||||
static const struct {
|
||||
const char *id;
|
||||
esp_tee_sec_storage_type_t type;
|
||||
uint32_t flags;
|
||||
} key_cfgs[] = {
|
||||
{ "aes256_key0", ESP_SEC_STG_KEY_AES256, SEC_STORAGE_FLAG_WRITE_ONCE },
|
||||
{ "aes256_key1", ESP_SEC_STG_KEY_AES256, SEC_STORAGE_FLAG_NONE },
|
||||
{ "attest_key", ESP_SEC_STG_KEY_ECDSA_SECP256R1, SEC_STORAGE_FLAG_WRITE_ONCE },
|
||||
{ "ecdsa_p256_key0", ESP_SEC_STG_KEY_ECDSA_SECP256R1, SEC_STORAGE_FLAG_NONE },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(key_cfgs) / sizeof(key_cfgs[0]); i++) {
|
||||
esp_tee_sec_storage_key_cfg_t cfg = {
|
||||
.id = key_cfgs[i].id,
|
||||
.type = key_cfgs[i].type,
|
||||
.flags = key_cfgs[i].flags,
|
||||
};
|
||||
if ((cfg.flags & SEC_STORAGE_FLAG_WRITE_ONCE) == 0) {
|
||||
esp_err_t err = esp_tee_sec_storage_clear_key(cfg.id);
|
||||
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
|
||||
}
|
||||
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&cfg));
|
||||
}
|
||||
|
||||
const size_t dump_sz = 4096;
|
||||
uint8_t *buf = heap_caps_malloc(dump_sz, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
|
||||
TEST_ESP_OK((esp_err_t)esp_tee_service_call(2, SS_ESP_TEE_TEST_READ_SEC_STG, buf));
|
||||
printf("\nSEC_STG_DUMP_BEGIN\n");
|
||||
ESP_LOG_BUFFER_HEX(TAG, buf, dump_sz);
|
||||
printf("SEC_STG_DUMP_END\n");
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
TEST_CASE("Test TEE Secure Storage - WRITE_ONCE keys", "[sec_storage]")
|
||||
{
|
||||
const char *key_id = "key_id_test_wo";
|
||||
@@ -425,6 +465,10 @@ static void do_ecdsa_sign_and_verify(const esp_tee_sec_storage_key_cfg_t *cfg, c
|
||||
TEST_ESP_OK(verify_ecdsa_sign(cfg->type, digest, digest_len, &pubkey, &sign));
|
||||
}
|
||||
|
||||
/* NOTE: In release mode (CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE), the test expects
|
||||
* the eFuse-burned HMAC key used for TEE secure storage to be available at
|
||||
* the path "test_keys/tee_sec_stg_hmac_key.bin"
|
||||
*/
|
||||
TEST_CASE("Test TEE Secure Storage - Host-generated keys", "[sec_storage_host_keygen]")
|
||||
{
|
||||
const char *aes_key_ids[] = { "aes256_key0", "aes256_key1" };
|
||||
|
||||
@@ -6,7 +6,7 @@ from enum import Enum
|
||||
import pytest
|
||||
from pytest_embedded_idf import IdfDut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
from tee_exception_cfg import TEE_EXCEPTION_TEST_MAP
|
||||
from tee_exception_test_map import TEE_EXCEPTION_TEST_MAP
|
||||
|
||||
# ---------------- Pytest build parameters ----------------
|
||||
|
||||
@@ -20,6 +20,12 @@ CONFIG_DEFAULT = [
|
||||
]
|
||||
|
||||
CONFIG_OTA = [
|
||||
# 'config, target, markers',
|
||||
('tee_ota', target, (pytest.mark.generic,))
|
||||
for target in TESTING_TARGETS
|
||||
]
|
||||
|
||||
CONFIG_OTA_NO_AUTOFLASH = [
|
||||
# 'config, target, skip_autoflash, markers',
|
||||
('tee_ota', target, 'y', (pytest.mark.generic,))
|
||||
for target in TESTING_TARGETS
|
||||
@@ -50,8 +56,10 @@ def test_esp_tee(dut: IdfDut) -> None:
|
||||
CONFIG_ALL,
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
@pytest.mark.skipif(targets=['esp32c61'], reason='Not supported')
|
||||
def test_esp_tee_crypto_aes(dut: IdfDut) -> None:
|
||||
if dut.target == 'esp32c61':
|
||||
pytest.skip(f'AES not supported on {dut.target}')
|
||||
|
||||
dut.run_all_single_board_cases(group='aes')
|
||||
dut.run_all_single_board_cases(group='aes-gcm')
|
||||
|
||||
@@ -72,8 +80,10 @@ def test_esp_tee_crypto_sha(dut: IdfDut) -> None:
|
||||
CONFIG_ALL,
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
@pytest.mark.skipif(targets=['esp32c61'], reason='Not supported')
|
||||
def test_esp_tee_aes_perf(dut: IdfDut) -> None:
|
||||
if dut.target == 'esp32c61':
|
||||
pytest.skip(f'AES not supported on {dut.target}')
|
||||
|
||||
for i in range(10):
|
||||
dut.run_all_single_board_cases(name=['mbedtls AES performance'])
|
||||
|
||||
@@ -121,6 +131,13 @@ def test_esp_tee_isolation_checks(dut: IdfDut) -> None:
|
||||
for test_name, expected in cfg.items():
|
||||
run_exception_case(dut, 'Test REE-TEE isolation', test_name, expected, check_origin=True)
|
||||
|
||||
# ESP32-C61: MMU-spillover gracefully reboots instead of panicking
|
||||
if dut.target == 'esp32c61':
|
||||
dut.skip_decode_panic = True
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
dut.write('"Test REE-TEE isolation: MMU-spillover"')
|
||||
dut.expect_exact('Failed MMU operation, rebooting!', timeout=10)
|
||||
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, markers',
|
||||
@@ -238,8 +255,6 @@ def run_flash_access_test(dut: IdfDut, api: TeeFlashAccessApi, test_name: str) -
|
||||
# Panics are expected during these tests
|
||||
dut.skip_decode_panic = True
|
||||
|
||||
dut.serial.custom_flash()
|
||||
|
||||
extra_data = dut._parse_test_menu()
|
||||
test_case = next((tc for tc in extra_data if tc.name == test_name), None)
|
||||
|
||||
@@ -250,9 +265,9 @@ def run_flash_access_test(dut: IdfDut, api: TeeFlashAccessApi, test_name: str) -
|
||||
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
'config, target, markers',
|
||||
CONFIG_OTA,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
def test_esp_tee_flash_prot_esp_partition_mmap(dut: IdfDut) -> None:
|
||||
run_flash_access_test(
|
||||
@@ -261,9 +276,9 @@ def test_esp_tee_flash_prot_esp_partition_mmap(dut: IdfDut) -> None:
|
||||
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
'config, target, markers',
|
||||
CONFIG_OTA,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
def test_esp_tee_flash_prot_spi_flash_mmap(dut: IdfDut) -> None:
|
||||
run_flash_access_test(
|
||||
@@ -272,9 +287,9 @@ def test_esp_tee_flash_prot_spi_flash_mmap(dut: IdfDut) -> None:
|
||||
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
'config, target, markers',
|
||||
CONFIG_OTA,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
def test_esp_tee_flash_prot_esp_rom_spiflash(dut: IdfDut) -> None:
|
||||
run_flash_access_test(
|
||||
@@ -283,18 +298,18 @@ def test_esp_tee_flash_prot_esp_rom_spiflash(dut: IdfDut) -> None:
|
||||
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
'config, target, markers',
|
||||
CONFIG_OTA,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
def test_esp_tee_flash_prot_esp_partition(dut: IdfDut) -> None:
|
||||
run_flash_access_test(dut, TeeFlashAccessApi.ESP_PARTITION, 'Test REE-TEE isolation: Flash - SPI1 (esp_partition)')
|
||||
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
'config, target, markers',
|
||||
CONFIG_OTA,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
def test_esp_tee_flash_prot_esp_flash(dut: IdfDut) -> None:
|
||||
run_flash_access_test(dut, TeeFlashAccessApi.ESP_FLASH, 'Test REE-TEE isolation: Flash - SPI1 (esp_flash)')
|
||||
@@ -303,9 +318,11 @@ def test_esp_tee_flash_prot_esp_flash(dut: IdfDut) -> None:
|
||||
# ---------------- TEE Local OTA tests ----------------
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('config', ['tee_ota'], indirect=['config'])
|
||||
@idf_parametrize('target', TESTING_TARGETS, indirect=['target'])
|
||||
@idf_parametrize(
|
||||
'config, target, markers',
|
||||
CONFIG_OTA,
|
||||
indirect=['config', 'target'],
|
||||
)
|
||||
def test_esp_tee_ota_negative(dut: IdfDut) -> None:
|
||||
# start test
|
||||
dut.run_all_single_board_cases(group='ota_neg_1', timeout=10)
|
||||
@@ -313,7 +330,7 @@ def test_esp_tee_ota_negative(dut: IdfDut) -> None:
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
CONFIG_OTA,
|
||||
CONFIG_OTA_NO_AUTOFLASH,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
)
|
||||
def test_esp_tee_ota_corrupted_img(dut: IdfDut) -> None:
|
||||
@@ -347,7 +364,7 @@ def tee_ota_stage_checks(dut: IdfDut, stage: TeeOtaStage, offset: str) -> None:
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
CONFIG_OTA,
|
||||
CONFIG_OTA_NO_AUTOFLASH,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
)
|
||||
def test_esp_tee_ota_reboot_without_ota_end(dut: IdfDut) -> None:
|
||||
@@ -370,7 +387,7 @@ def test_esp_tee_ota_reboot_without_ota_end(dut: IdfDut) -> None:
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
CONFIG_OTA,
|
||||
CONFIG_OTA_NO_AUTOFLASH,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
)
|
||||
def test_esp_tee_ota_valid_img(dut: IdfDut) -> None:
|
||||
@@ -401,7 +418,7 @@ def test_esp_tee_ota_valid_img(dut: IdfDut) -> None:
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
CONFIG_OTA,
|
||||
CONFIG_OTA_NO_AUTOFLASH,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
)
|
||||
def test_esp_tee_ota_rollback(dut: IdfDut) -> None:
|
||||
@@ -440,7 +457,7 @@ def test_esp_tee_ota_rollback(dut: IdfDut) -> None:
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
CONFIG_OTA,
|
||||
CONFIG_OTA_NO_AUTOFLASH,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
)
|
||||
def test_esp_tee_secure_storage(dut: IdfDut) -> None:
|
||||
@@ -452,22 +469,43 @@ def test_esp_tee_secure_storage(dut: IdfDut) -> None:
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
CONFIG_OTA,
|
||||
CONFIG_OTA_NO_AUTOFLASH,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
)
|
||||
def test_esp_tee_secure_storage_with_host_img(dut: IdfDut) -> None:
|
||||
# Flash image and write the secure_storage partition with host-generated keys
|
||||
|
||||
# NOTE: In release mode (CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE), the test
|
||||
# expects the eFuse-burned HMAC key used for TEE secure storage to be available
|
||||
# at the path "test_keys/tee_sec_stg_hmac_key.bin"
|
||||
dut.serial.custom_flash_with_host_gen_sec_stg_img()
|
||||
|
||||
dut.run_all_single_board_cases(group='sec_storage_host_keygen')
|
||||
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
CONFIG_OTA_NO_AUTOFLASH,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
)
|
||||
def test_esp_tee_secure_storage_encryption(dut: IdfDut) -> None:
|
||||
dut.serial.custom_flash_with_empty_sec_stg()
|
||||
|
||||
# NOTE: In release mode (CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE), the test
|
||||
# expects the eFuse-burned HMAC key used for TEE secure storage to be available
|
||||
# at the path "test_keys/tee_sec_stg_hmac_key.bin"
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
dut.write('"Test TEE Secure Storage - Verify data encryption"')
|
||||
|
||||
dut.serial.verify_tee_sec_stg_encryption(dut)
|
||||
|
||||
|
||||
# ---------------- TEE Attestation tests ----------------
|
||||
|
||||
|
||||
@idf_parametrize(
|
||||
'config, target, skip_autoflash, markers',
|
||||
CONFIG_OTA,
|
||||
CONFIG_OTA_NO_AUTOFLASH,
|
||||
indirect=['config', 'target', 'skip_autoflash'],
|
||||
)
|
||||
def test_esp_tee_attestation(dut: IdfDut) -> None:
|
||||
|
||||
@@ -15,3 +15,6 @@ CONFIG_PARTITION_TABLE_OFFSET=0xF000
|
||||
# Increasing TEE I/DRAM size
|
||||
CONFIG_SECURE_TEE_IRAM_SIZE=0x8800
|
||||
CONFIG_SECURE_TEE_DRAM_SIZE=0x5800
|
||||
|
||||
# Takes effect only when Secure boot is enabled
|
||||
CONFIG_SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT=y
|
||||
|
||||
@@ -78,6 +78,12 @@ _TARGET_OVERRIDES: dict[str, dict[str, Any]] = {
|
||||
},
|
||||
},
|
||||
'esp32c61': {
|
||||
# NOTE: On ESP32-C61, MMU-spillover does not raise a CPU panic — the TEE
|
||||
# test fills the bad mapping with a poison pattern and calls esp_restart().
|
||||
# Verified separately in the pytest, so drop it from the panic-driven map.
|
||||
'ree_isolation': {
|
||||
'_remove': ['MMU-spillover'],
|
||||
},
|
||||
# NOTE: ESP32-C61 does not support the following peripherals
|
||||
'apm_violation': {
|
||||
'_remove': ['AES', 'HMAC', 'DS'],
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user