Merge branch 'refactor/esp_tee_sec_stg' into 'master'

refactor(esp_tee): Revamp the TEE Secure Storage format

Closes IDF-9180

See merge request espressif/esp-idf!36878
This commit is contained in:
Laukik Hase
2025-05-07 13:40:06 +08:00
69 changed files with 1968 additions and 1197 deletions

View File

@@ -12,7 +12,7 @@
- Digest (SHA256)
- Public key corresponding to the private key used for signing (in compressed format)
- Signature (`r` and `s` components)
- The token is signed using the ECDSA key stored in the designated slot ID of the TEE's Secure Storage. Subsequently, the resulting token is handed back to the REE in the output buffer specified in the secure service call.
- The token is signed using the ECDSA key stored with the configured ID of the TEE's Secure Storage. Subsequently, the resulting token is handed back to the REE in the output buffer specified in the secure service call.
<details>
<summary><b>Attestation: Sample Token</b></summary>
@@ -23,7 +23,7 @@
"magic": "44fef7cc",
"encr_alg": "",
"sign_alg": "ecdsa_secp256r1_sha256",
"key_id": 0
"key_id": "tee_att_key0",
},
"eat": {
"nonce": -1582119980,
@@ -109,7 +109,7 @@ Before the project configuration and build, be sure to set the correct chip targ
Open the project configuration menu (`idf.py menuconfig`).
- Configure the secure storage slot ID for generating/fetching the ECDSA keypair for attestation token signing at `(Top) → Security features → TEE: Secure Storage slot ID for EAT signing`.
- Configure the secure storage key ID for generating/fetching the ECDSA keypair for attestation token signing at `ESP-TEE (Trusted Execution Environment) → Secure Services → Attestation: Secure Storage key ID for EAT signing`.
### Build and Flash
@@ -123,10 +123,17 @@ idf.py -p PORT flash monitor
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
### Usage
### Example Output
- Use console commands to dump the attestation info: `tee_att_info`
- The generated token's signature can be verified using the script given below.
```log
I (438) example_tee_attest: TEE Attestation Service
I (1008) example_tee_attest: Attestation token - Length: 1538
I (1018) example_tee_attest: Attestation token - Data:
'{"header":{"magic":"44fef7cc","encr_alg":"","sign_alg":"ecdsa_secp256r1_sha256","key_id":"tee_att_key0"},"eat":{"nonce":-1582119980,"client_id":262974944,"device_ver":1,"device_id":"4ecc458ef4290329552b4dcdccb99d55e5ea7624f24c87b27b71515e1666f39c","instance_id":"66571b78918f4bb7ae2723f235a9e4fe1c7070ae6261ce5df7049b44b1f8a318","psa_cert_ref":"0716053550477-10100","device_status":165,"sw_claims":{"tee":{"type":1,"ver":"1.0.0","idf_ver":"v5.5-dev-2978-gd75a0105dac-dirt","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"5213904fd8ca7538776bdf372c08c13138f20b2fac3503bc878f19c6e36a710d","digest_validated":true,"sign_verified":false,"secure_padding":false}},"app":{"type":2,"ver":"v0.1.0","idf_ver":"v5.5-dev-2978-gd75a0105dac-dirt","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"65c905fc0fc135fdfa8def210d1c186627cb3a17ecb2e7f020b56411b2d2fc76","digest_validated":true,"sign_verified":false,"secure_padding":false}},"bootloader":{"type":0,"ver":"01000000","idf_ver":"v5.5-dev-2978-gd75a0105dac-dirt","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"9efd37d29266f3239f7c6a095df880f1e85e41505f154cfd3bbfad4b8a2b18dd","digest_validated":true,"sign_verified":false}}}},"public_key":{"compressed":"02ce0188c61b0118c86ca20af7e01185dd687c6698b2265a288fee845d083e9066"},"sign":{"r":"362e2053bab26c779559793b2eae89e96c1a058e5fffc49d544d07b934ce3b32","s":"fc5f0e4d329fc6e031cbf425ef62d4756b728392b2a77282baa1f15b554d2716"}}'
I (1148) main_task: Returned from app_main()
```
**Note:** The generated token's signature can be verified using the script given below.
<details>
<summary><b>Attestation: Verifying the generated token</b></summary>
@@ -181,19 +188,3 @@ assert vk.verify_digest(signature, digest, sigdecode=sigdecode_der)
print('Token signature verified!')
```
</details>
### Example Output
```log
I (416) main_task: Calling app_main()
I (416) example_tee_attest: TEE Attestation Service
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
I (476) main_task: Returned from app_main()
esp32c6> tee_att_info
I (6206) cmd_tee_attest: Attestation token - Length: 1525
I (6206) cmd_tee_attest: Attestation token - Data:
'{"header":{"magic":"44fef7cc","encr_alg":"","sign_alg":"ecdsa_secp256r1_sha256","key_id":0},"eat":{"nonce":-1582119980,"client_id":262974944,"device_ver":1,"device_id":"4ecc458ef4290329552b4dcdccb99d55e5ea7624f24c87b27b71515e1666f39c","instance_id":"77eb3dfec7633302fe4bcf04ffe3be5e83c0513057aa070d387f1e8350271329","psa_cert_ref":"0716053550477-10100","device_status":165,"sw_claims":{"tee":{"type":1,"ver":"1.0.0","idf_ver":"v5.5-dev-727-g624f640f61d-dirty","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"6e6548a5d64cd3d6e2e6dc166384f32f73558fbd9c0c0985c6095d643f053eb5","digest_validated":true,"sign_verified":false,"secure_padding":false}},"app":{"type":2,"ver":"v0.1.0","idf_ver":"v5.5-dev-727-g624f640f61d-dirty","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"7f10992d4bb32c497184fd2da0e3a593b235d82bde24de868c8eb4636d4b7bdc","digest_validated":true,"sign_verified":false,"secure_padding":false}},"bootloader":{"type":0,"ver":"01000000","idf_ver":"v5.5-dev-727-g624f640f61d-dirty","secure_ver":0,"part_chip_rev":{"min":0,"max":99},"part_digest":{"type":0,"calc_digest":"2cdf1bac1792df04ad10d67287ef3ab7024e183dc32899a190668cbb7d21a5a8","digest_validated":true,"sign_verified":false}}}},"public_key":{"compressed":"030df5c5fd9a4096a58ba16dfc4f1d53781bab555fc307d71367f0afc663005174"},"sign":{"r":"c3a0fc8ce3cd1dec2a0e38c4a63c03bd1e044febd5847178fe304b06d48b3eaf","s":"c8e34bc5d854e728cffdfd701ea09deabc9a9a22c4b06f312a61a1448a56b8b1"}}'
```

View File

@@ -8,19 +8,15 @@
- This example demonstrates the ESP-TEE framework's Secure Storage with two workflows:
- Signing and verification
- Create and securely store an ECDSA `secp256r1` keypair in a protected memory space i.e. the secure storage partition
- Sign a message in the TEE (given its digest) using the ECDSA keypair stored in the given slot ID
- Retrieve the ECDSA public key associated with the private key for the given slot ID
- Sign a message in the TEE (given its digest) using the ECDSA keypair stored with the given ID
- Retrieve the ECDSA public key associated with the private key with the given ID
- Verify the generated signature in the REE
- Encryption and decryption
- Generate and securely store an AES-256 key in the secure storage partition
- Encrypt a message in the TEE using the AES key stored in the given slot ID with the `aes-256-gcm` algorithm and generate an authentication tag
- Encrypt a message in the TEE using the AES key stored with the given ID with the `aes-256-gcm` algorithm and generate an authentication tag
- Decrypt the ciphertext using the same AES key and validate the authentication tag
- Verify that the decrypted message matches the original
### Notes
- Secure Storage currently supports only the `ecdsa-secp256r1-sha256` algorithm for signing and the `aes-256-gcm` algorithm for encryption.
## How to use the example
### Hardware Required
@@ -33,24 +29,26 @@ Before the project configuration and build, be sure to set the correct chip targ
Open the project configuration menu (`idf.py menuconfig`).
- Configure the secure storage slot ID for storing the ECDSA keypair at `Example Configuration → TEE: Secure Storage keypair slot ID`.
- Configure the secure storage example key ID at `Example Configuration → TEE: Secure Storage Key ID`.
The TEE Secure Storage feature supports two modes for determining which eFuse block stores the encryption key:
TEE Secure Storage follows the NVS partition format and uses an AES-XTS encryption scheme derived via the HMAC peripheral. It supports two key derivation modes, configurable via `CONFIG_SECURE_TEE_SEC_STG_MODE`:
- **Development** Mode: The encryption key is embedded (constant for all instances) in the ESP-TEE firmware.
- **Release** Mode: The encryption key is stored in eFuse BLK4 - BLK9, depending on the `SECURE_TEE_SEC_STG_KEY_EFUSE_BLK` Kconfig option.
- **Development** Mode: Encryption keys are embedded in the ESP-TEE firmware (identical across all instances).
- **Release** Mode: Encryption keys are derived via the HMAC peripheral using a key stored in eFuse, specified by `CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`.
#### Configure the eFuse Block ID for Encryption Key Storage
#### Configure the eFuse key ID storing the HMAC key
- Navigate to `Security features → Trusted Execution Environment → TEE: Secure Storage Mode` and enable the Release mode configuration.
- Set the eFuse block ID to store the encryption key in `Security features → Trusted Execution Environment → TEE: Secure Storage encryption key eFuse block`.
- Navigate to `ESP-TEE (Trusted Execution Environment)Secure Services → Secure Storage: Mode` and enable the `Release` mode configuration.
- Set the eFuse key ID storing the HMAC key at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: eFuse HMAC key ID`.
**Note:** Before running the example, users must program the encryption key into the configured eFuse block - refer to the snippet below. The TEE checks whether the specified eFuse block is empty or already programmed with a key. If the block is empty, an error will be returned; otherwise, the pre-programmed key will be used.
**Note:** Before running the example, users must program the HMAC key into the configured eFuse block - refer to the snippet below. The TEE checks whether the specified eFuse block is empty or already programmed with a key. If the block is empty, an error will be returned; otherwise, the pre-programmed key will be used.
```shell
# Programming the user key (256-bit) in eFuse
# Generate a random 32-byte HMAC key
openssl rand -out hmac_key_file.bin 32
# Programming the HMAC key (256-bit) in eFuse
# Here, BLOCK_KEYx is a free eFuse key-block between BLOCK_KEY0 and BLOCK_KEY5
espefuse.py -p PORT burn_key BLOCK_KEYx user_key.bin USER
espefuse.py -p PORT burn_key BLOCK_KEYx hmac_key_file.bin HMAC_UP
```
### Build and Flash

View File

@@ -1,11 +1,10 @@
menu "Example Configuration"
config EXAMPLE_TEE_SEC_STG_SLOT_ID
int "TEE: Secure Storage keypair slot ID"
default 0
range 0 15
config EXAMPLE_TEE_SEC_STG_KEY_STR_ID
string "TEE: Secure Storage Key ID"
default "key_id_0"
help
This configuration sets the slot ID from the TEE secure storage
This configuration sets the key string identifier from the TEE secure storage
storing the ECDSA keypair for executing sign/verify operations
from the TEE side

View File

@@ -1,7 +1,7 @@
/*
* TEE Secure Storage example
*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -21,20 +21,21 @@
#include "esp_tee_sec_storage.h"
#include "secure_service_num.h"
#include "sdkconfig.h"
#define SHA256_DIGEST_SZ (32)
#define ECDSA_SECP256R1_KEY_LEN (32)
#define AES256_GCM_TAG_LEN (16)
#define AES256_GCM_AAD_LEN (16)
#define KEY_SLOT_ID (CONFIG_EXAMPLE_TEE_SEC_STG_SLOT_ID)
#define KEY_STR_ID (CONFIG_EXAMPLE_TEE_SEC_STG_KEY_STR_ID)
#define MAX_AES_PLAINTEXT_LEN (128)
static const char *message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
static const char *TAG = "example_tee_sec_stg";
static esp_err_t verify_ecdsa_secp256r1_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign)
static esp_err_t verify_ecdsa_secp256r1_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_ecdsa_pubkey_t *pubkey, const esp_tee_sec_storage_ecdsa_sign_t *sign)
{
if (pubkey == NULL || digest == NULL || sign == NULL) {
return ESP_ERR_INVALID_ARG;
@@ -112,20 +113,25 @@ static void example_tee_sec_stg_sign_verify(void *pvParameter)
goto exit;
}
esp_err_t err = esp_tee_sec_storage_clear_slot(KEY_SLOT_ID);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to clear slot %d!", KEY_SLOT_ID);
esp_tee_sec_storage_key_cfg_t cfg = {
.id = (const char *)(KEY_STR_ID),
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1
};
esp_err_t err = esp_tee_sec_storage_clear_key(cfg.id);
if (err != ESP_OK && err != ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to clear key %d!", cfg.id);
goto exit;
}
err = esp_tee_sec_storage_gen_key(KEY_SLOT_ID, ESP_SEC_STG_KEY_ECDSA_SECP256R1);
err = esp_tee_sec_storage_gen_key(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate keypair!");
goto exit;
}
esp_tee_sec_storage_sign_t sign = {};
err = esp_tee_sec_storage_get_signature(KEY_SLOT_ID, ESP_SEC_STG_KEY_ECDSA_SECP256R1, msg_digest, sizeof(msg_digest), &sign);
esp_tee_sec_storage_ecdsa_sign_t sign = {};
err = esp_tee_sec_storage_ecdsa_sign(&cfg, msg_digest, sizeof(msg_digest), &sign);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate signature!");
goto exit;
@@ -133,8 +139,8 @@ static void example_tee_sec_stg_sign_verify(void *pvParameter)
ESP_LOG_BUFFER_HEX("Signature", &sign, sizeof(sign));
esp_tee_sec_storage_pubkey_t pubkey = {};
err = esp_tee_sec_storage_get_pubkey(KEY_SLOT_ID, ESP_SEC_STG_KEY_ECDSA_SECP256R1, &pubkey);
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
err = esp_tee_sec_storage_ecdsa_get_pubkey(&cfg, &pubkey);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch public-key!");
goto exit;
@@ -175,19 +181,32 @@ static void example_tee_sec_stg_encrypt_decrypt(void *pvParameter)
goto exit;
}
err = esp_tee_sec_storage_clear_slot(KEY_SLOT_ID);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to clear slot %d!", KEY_SLOT_ID);
esp_tee_sec_storage_key_cfg_t cfg = {
.id = (const char *)(KEY_STR_ID),
.type = ESP_SEC_STG_KEY_AES256
};
err = esp_tee_sec_storage_clear_key(cfg.id);
if (err != ESP_OK && err != ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to clear key %d!", cfg.id);
goto exit;
}
err = esp_tee_sec_storage_gen_key(KEY_SLOT_ID, ESP_SEC_STG_KEY_AES256);
err = esp_tee_sec_storage_gen_key(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate key!");
goto exit;
}
err = esp_tee_sec_storage_encrypt(KEY_SLOT_ID, (uint8_t *)plaintext, plaintext_len, aad_buf, sizeof(aad_buf), tag, sizeof(tag), ciphertext);
esp_tee_sec_storage_aead_ctx_t ctx = {
.key_id = cfg.id,
.aad = aad_buf,
.aad_len = sizeof(aad_buf),
};
ctx.input = (const uint8_t *)plaintext;
ctx.input_len = plaintext_len;
err = esp_tee_sec_storage_aead_encrypt(&ctx, tag, sizeof(tag), ciphertext);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt data!");
goto exit;
@@ -195,7 +214,9 @@ static void example_tee_sec_stg_encrypt_decrypt(void *pvParameter)
ESP_LOG_BUFFER_HEX("Encrypted data", ciphertext, plaintext_len);
err = esp_tee_sec_storage_decrypt(KEY_SLOT_ID, (uint8_t *)ciphertext, plaintext_len, aad_buf, sizeof(aad_buf), tag, sizeof(tag), ciphertext);
ctx.input = (const uint8_t *)ciphertext;
ctx.input_len = plaintext_len;
err = esp_tee_sec_storage_aead_decrypt(&ctx, tag, sizeof(tag), ciphertext);
if (err != ESP_OK || memcmp(ciphertext, plaintext, plaintext_len) != 0) {
ESP_LOGE(TAG, "Encryption verification failed!");
err = ESP_FAIL;
@@ -216,8 +237,6 @@ void app_main(void)
{
ESP_LOGI(TAG, "TEE Secure Storage");
ESP_ERROR_CHECK(esp_tee_sec_storage_init());
xTaskCreate(example_tee_sec_stg_sign_verify, "tee_sec_stg_sign_verify", 4096, (void *)message, 5, NULL);
xTaskCreate(example_tee_sec_stg_encrypt_decrypt, "tee_sec_stg_encrypt_decrypt", 4096, (void *)message, 5, NULL);
}