mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
Merge branch 'feat/tee_c61_examples_and_docs_v6.0' into 'release/v6.0'
feat(esp_tee): Enable ESP-TEE examples and documentation for ESP32-C61 (v6.0) See merge request espressif/esp-idf!43462
This commit is contained in:
@@ -17,20 +17,35 @@ This example can be executed on any development board with a Espressif SOC chip
|
||||
|
||||
- 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`.
|
||||
|
||||
Configure the Secure Storage mode for determining how the NVS XTS encryption keys are derived at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: Mode`
|
||||
Configure the Secure Storage mode for determining how the NVS XTS-AES encryption keys are derived at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: Mode`
|
||||
|
||||
- **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.
|
||||
- Set the eFuse key ID storing the HMAC key at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: eFuse HMAC key ID for storage encryption keys`.
|
||||
- Snippet for burning the secure storage key in eFuse is given below.
|
||||
- **Release** Mode: Encryption keys are derived using a key stored in eFuse, specified by `CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`.
|
||||
- Set the eFuse key ID at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: eFuse HMAC key ID for storage encryption keys`.
|
||||
- Before running the application, users must program the required key into the configured eFuse block - refer to the instructions below.
|
||||
|
||||
```shell
|
||||
# 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 -p PORT burn-key BLOCK_KEYx hmac_key_file.bin HMAC_UP
|
||||
```
|
||||
**For targets without HMAC peripheral (ESP32-C61):**
|
||||
|
||||
```shell
|
||||
# Generate a random 32-byte key
|
||||
openssl rand -out hmac_key_file.bin 32
|
||||
# Program the USER purpose key (256-bit) in eFuse
|
||||
# Here, BLOCK_KEYx is a free eFuse key-block between BLOCK_KEY0 and BLOCK_KEY5
|
||||
espefuse -p PORT burn-key --no-read-protect BLOCK_KEYx hmac_key_file.bin USER
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When programming the key into eFuse for targets without HMAC peripheral, ensure that it is **NOT** marked as read-protected (use the `--no-read-protect` flag). If the key is read-protected, the TEE will be unable to access it. However, this does not weaken security: the APM peripheral already blocks software access to the key, and any illegal read or write attempt from the REE triggers a fault.
|
||||
|
||||
**For targets with HMAC peripheral:**
|
||||
|
||||
```shell
|
||||
# Generate a random 32-byte HMAC key
|
||||
openssl rand -out hmac_key_file.bin 32
|
||||
# Program the HMAC key (256-bit) in eFuse
|
||||
# Here, BLOCK_KEYx is a free eFuse key-block between BLOCK_KEY0 and BLOCK_KEY5
|
||||
espefuse -p PORT burn-key BLOCK_KEYx hmac_key_file.bin HMAC_UP
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
|
||||
+1
-1
@@ -309,7 +309,7 @@ ESP32C61_DOCS = [
|
||||
'api-guides/phy.rst',
|
||||
'api-reference/peripherals/sd_pullup_requirements.rst',
|
||||
'api-guides/RF_calibration.rst',
|
||||
]
|
||||
] + ESP_TEE_DOCS
|
||||
|
||||
ESP32C6_DOCS = [
|
||||
'api-guides/RF_calibration.rst',
|
||||
|
||||
@@ -5,3 +5,7 @@ INPUT += \
|
||||
$(PROJECT_PATH)/components/bt/include/esp32c6/include/esp_bt_vs.h \
|
||||
$(PROJECT_PATH)/components/esp_phy/include/esp_phy_init.h \
|
||||
$(PROJECT_PATH)/components/esp_phy/include/esp_phy_cert_test.h \
|
||||
$(PROJECT_PATH)/components/esp_tee/include/esp_tee.h \
|
||||
$(PROJECT_PATH)/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h \
|
||||
$(PROJECT_PATH)/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.h \
|
||||
$(PROJECT_PATH)/components/esp_tee/subproject/components/tee_ota_ops/include/esp_tee_ota_ops.h \
|
||||
|
||||
@@ -10,6 +10,6 @@ Security Guides
|
||||
flash-encryption
|
||||
:esp32: secure-boot-v1
|
||||
secure-boot-v2
|
||||
:esp32c6 or esp32c5: tee/index
|
||||
:esp32c6 or esp32c5 or esp32c61: tee/index
|
||||
security-features-enablement-workflows
|
||||
vulnerabilities
|
||||
|
||||
@@ -5,12 +5,18 @@ Overview
|
||||
--------
|
||||
The TEE Secure Storage service provides persistent storage for securely storing sensitive data, such as cryptographic keys, cloud credentials, or other general-purpose information. It uses a dedicated flash partition of type ``data`` and subtype ``nvs``. The TEE ensures both confidentiality and integrity of the stored data.
|
||||
|
||||
TEE Secure Storage adopts the :doc:`../../api-reference/storage/nvs_flash` partition format and uses the HMAC peripheral-based XTS-AES encryption scheme, as detailed :ref:`here <nvs_encr_hmac_scheme>`. The AES encryption keys are derived from an HMAC key programmed in eFuse with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`. Please note that the TEE Secure storage does not support the :ref:`NVS Flash Encryption-based scheme <nvs_encr_flash_enc_scheme>`.
|
||||
.. only:: SOC_HMAC_SUPPORTED
|
||||
|
||||
TEE Secure Storage adopts the :doc:`../../api-reference/storage/nvs_flash` partition format and uses the HMAC peripheral-based XTS-AES encryption scheme, as detailed :ref:`here <nvs_encr_hmac_scheme>`. The AES encryption keys are derived from an HMAC key programmed in eFuse with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_HMAC_UP`. Please note that the TEE Secure storage does not support the :ref:`NVS Flash Encryption-based scheme <nvs_encr_flash_enc_scheme>`.
|
||||
|
||||
.. only:: not SOC_HMAC_SUPPORTED
|
||||
|
||||
TEE Secure Storage adopts the :doc:`../../api-reference/storage/nvs_flash` partition format and uses the HMAC-based XTS-AES encryption scheme. There is, however, an important difference in how the HMAC step is handled. Since {IDF_TARGET_NAME} lacks a hardware HMAC peripheral, the HMAC computation is carried out in software with assistance from the SHA peripheral.The AES encryption keys are derived from an eFuse key with the purpose :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_USER`. Please note that the TEE Secure storage does not support the :ref:`NVS Flash Encryption-based scheme <nvs_encr_flash_enc_scheme>`.
|
||||
|
||||
.. important::
|
||||
|
||||
- One eFuse block is required to store the HMAC key used for deriving the NVS encryption keys. This key is exclusive to the TEE and **CANNOT** be used by the REE for any purpose.
|
||||
- The HMAC key must be programmed into eFuse before firmware execution, as TEE Secure Storage does not support generating it on-device. If no valid key with the required purpose is found in the configured eFuse block, an error will be raised at runtime.
|
||||
- The required key must be programmed into eFuse before firmware execution, as TEE Secure Storage does not support generating it on-device. If no valid key with the required purpose is found in the configured eFuse block, an error will be raised at runtime.
|
||||
|
||||
Additionally, the secure storage provides interfaces for performing the following cryptographic services from the TEE using securely stored key material:
|
||||
|
||||
@@ -21,12 +27,14 @@ Additionally, the secure storage provides interfaces for performing the followin
|
||||
|
||||
As per the current implementation, the TEE Secure Storage partition **must** have the label ``secure_storage``.
|
||||
|
||||
TEE secure storage also supports ECDSA signing with keys derived via PBKDF2 (Password-Based Key Derivation Function 2), using an HMAC key programmed in eFuse along with a user-provided salt. This mechanism enables ECDSA signing on both P-256 and P-192 curves without requiring storage of the actual private keys. The eFuse HMAC key ID for the PBKDF2 operations is specified via the :ref:`CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID` option.
|
||||
.. only:: SOC_HMAC_SUPPORTED
|
||||
|
||||
.. important::
|
||||
TEE secure storage also supports ECDSA signing with keys derived via PBKDF2 (Password-Based Key Derivation Function 2), using an HMAC key programmed in eFuse along with a user-provided salt. This mechanism enables ECDSA signing on both P-256 and P-192 curves without requiring storage of the actual private keys. The eFuse HMAC key ID for the PBKDF2 operations is specified via the :ref:`CONFIG_SECURE_TEE_PBKDF2_EFUSE_HMAC_KEY_ID` option.
|
||||
|
||||
- The eFuse HMAC key ID used for PBKDF2-based signing **CANNOT** be the same as the one used for deriving TEE secure storage encryption keys (i.e., :ref:`CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`).
|
||||
- This eFuse ID is also exclusive to the TEE and **CANNOT** be used by the REE for any purpose.
|
||||
.. important::
|
||||
|
||||
- The eFuse HMAC key ID used for PBKDF2-based signing **CANNOT** be the same as the one used for deriving TEE secure storage encryption keys (i.e., :ref:`CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`).
|
||||
- This eFuse ID is also exclusive to the TEE and **CANNOT** be used by the REE for any purpose.
|
||||
|
||||
Internals
|
||||
---------
|
||||
@@ -47,7 +55,9 @@ All assets related to TEE secure storage are protected by the APM peripheral and
|
||||
The TEE Secure Storage feature supports two modes for determining how the NVS encryption keys are derived (see :ref:`CONFIG_SECURE_TEE_SEC_STG_MODE`):
|
||||
|
||||
- **Development** Mode: Encryption keys are embedded (constant for all instances) in the ESP-TEE firmware.
|
||||
- **Release** Mode: Encryption keys are derived via the HMAC peripheral using a key stored in eFuse, specified by :ref:`CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`.
|
||||
- **Release** Mode: Encryption keys are derived via the HMAC peripheral (or software-based HMAC implementation) using a key stored in eFuse, specified by :ref:`CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`.
|
||||
|
||||
.. only:: SOC_HMAC_SUPPORTED
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -61,7 +71,28 @@ The TEE Secure Storage feature supports two modes for determining how the NVS en
|
||||
openssl rand -out hmac_key_file.bin 32
|
||||
|
||||
# Program the HMAC key into the eFuse block
|
||||
idf.py -p PORT efuse-burn-key <BLOCK_KEY0-5> hmac_key_file.bin HMAC_UP
|
||||
espefuse -p PORT burn-key <BLOCK_KEY0-5> hmac_key_file.bin HMAC_UP
|
||||
|
||||
.. only:: not SOC_HMAC_SUPPORTED
|
||||
|
||||
.. note::
|
||||
|
||||
- The valid range for :ref:`CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID` is from ``0`` (:cpp:enumerator:`esp_efuse_block_t::EFUSE_BLK_KEY0`) to ``5`` (:cpp:enumerator:`esp_efuse_block_t::EFUSE_BLK_KEY5`). By default, this config is set to ``-1`` and must be configured before building the TEE application.
|
||||
|
||||
- The following commands can be used to generate and program the USER purpose key into the required eFuse block:
|
||||
|
||||
::
|
||||
|
||||
# Generate a random 32-byte key
|
||||
openssl rand -out hmac_key_file.bin 32
|
||||
|
||||
# Program the USER purpose key into the eFuse block
|
||||
espefuse -p PORT burn-key --no-read-protect <BLOCK_KEY0-5> hmac_key_file.bin USER
|
||||
|
||||
.. warning::
|
||||
|
||||
- When programming the key into eFuse, ensure that it is **NOT** marked as read-protected (use the ``--no-read-protect`` flag). If the key is read-protected, the TEE will be unable to access it.
|
||||
- However, this does not weaken security: the APM peripheral already blocks software access to the key, and any illegal read or write attempt from the REE triggers a fault.
|
||||
|
||||
Tools
|
||||
-----
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
flash-encryption
|
||||
:esp32: secure-boot-v1
|
||||
secure-boot-v2
|
||||
:esp32c6 or esp32c5: tee/index
|
||||
:esp32c6 or esp32c5 or esp32c61: tee/index
|
||||
security-features-enablement-workflows
|
||||
vulnerabilities
|
||||
|
||||
@@ -38,7 +38,7 @@ examples/security/security_features_app:
|
||||
|
||||
examples/security/tee/tee_attestation:
|
||||
disable:
|
||||
- if: IDF_TARGET not in ["esp32c6"]
|
||||
- if: IDF_TARGET not in ["esp32c6", "esp32c61"]
|
||||
depends_components:
|
||||
- esp_tee
|
||||
depends_filepatterns:
|
||||
@@ -46,7 +46,7 @@ examples/security/tee/tee_attestation:
|
||||
|
||||
examples/security/tee/tee_basic:
|
||||
disable:
|
||||
- if: IDF_TARGET not in ["esp32c6"]
|
||||
- if: IDF_TARGET not in ["esp32c6", "esp32c61"]
|
||||
depends_components:
|
||||
- esp_tee
|
||||
depends_filepatterns:
|
||||
@@ -54,7 +54,7 @@ examples/security/tee/tee_basic:
|
||||
|
||||
examples/security/tee/tee_secure_ota:
|
||||
disable:
|
||||
- if: IDF_TARGET not in ["esp32c6"]
|
||||
- if: IDF_TARGET not in ["esp32c6", "esp32c61"]
|
||||
depends_components:
|
||||
- esp_tee
|
||||
depends_filepatterns:
|
||||
@@ -62,7 +62,7 @@ examples/security/tee/tee_secure_ota:
|
||||
|
||||
examples/security/tee/tee_secure_storage:
|
||||
disable:
|
||||
- if: IDF_TARGET not in ["esp32c6"]
|
||||
- if: IDF_TARGET not in ["esp32c6", "esp32c61"]
|
||||
depends_components:
|
||||
- esp_tee
|
||||
depends_filepatterns:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| Supported Targets | ESP32-C6 |
|
||||
| ----------------- | -------- |
|
||||
| Supported Targets | ESP32-C6 | ESP32-C61 |
|
||||
| ----------------- | -------- | --------- |
|
||||
|
||||
# TEE: Attestation example
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ def verify_att_token_signature(att_tk: str) -> Any:
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
|
||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||
def test_example_tee_attestation(dut: Dut) -> None:
|
||||
# Erase the TEE secure_storage partition
|
||||
dut.serial.erase_partition('secure_storage')
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
| Supported Targets | ESP32-C6 |
|
||||
| ----------------- | -------- |
|
||||
| Supported Targets | ESP32-C6 | ESP32-C61 |
|
||||
| ----------------- | -------- | --------- |
|
||||
|
||||
# Basic TEE example
|
||||
|
||||
## Overview
|
||||
|
||||
- This example illustrates the ESP-TEE (Trusted Execution Environment) framework to encrypt/decrypt data using AES within a secure environment.
|
||||
- The non-secure world i.e. the Rich Execution Environment (REE) raises a request for AES operation in TEE through the secure service call interface. The TEE performs encrypts/decrypts the given buffer with the AES-256-CBC mode using the key protected by TEE. If the operation is successful, the result of the AES operation is returned in the output buffer provided in the secure service call by the REE.
|
||||
- The non-secure world i.e. the Rich Execution Environment (REE) raises a request for AES operation in TEE through the secure service call interface. The TEE performs encrypts/decrypts the given buffer with the AES-256-GCM mode using the key protected by TEE. If the operation is successful, the result of the AES operation is returned in the output buffer provided in the secure service call by the REE.
|
||||
- This example also demonstrates how to add custom service calls to TEE. You can refer to `components/example_service` for more information - see the structure below.
|
||||
|
||||
```
|
||||
@@ -43,15 +43,15 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
|
||||
### Example Output
|
||||
|
||||
```log
|
||||
I (315) main_task: Calling app_main()
|
||||
I (315) example_tee_basic: AES-256-CBC operations in TEE
|
||||
TEE: Secure service call for AES-256-CBC operation
|
||||
TEE: In PROTECTED M-mode
|
||||
I (325) example_tee_basic: AES encryption successful!
|
||||
I (325) example_tee_basic: Cipher text -
|
||||
I (325) example_tee_basic: ee 04 9b ee 95 6f 25 04 1e 8c e4 4e 8e 4e 7a d3
|
||||
TEE: Secure service call for AES-256-CBC operation
|
||||
TEE: In PROTECTED M-mode
|
||||
I (345) example_tee_basic: AES decryption successful!
|
||||
I (345) main_task: Returned from app_main()
|
||||
I (353) main_task: Calling app_main()
|
||||
I (353) example_tee_basic: AES-256-GCM operations in TEE
|
||||
I (364) example_tee_service: Secure service call: PROTECTED M-mode
|
||||
I (370) example_tee_service: AES-256-GCM encryption
|
||||
I (373) ciphertext: 23 32 a6 1f ff 27 15 d7 35 70 db f5 e3 0c 13 41
|
||||
I (373) ciphertext: eb 80 d7 2c 9c f5 68 5a b4 2c 43 b0 64 68 13 36
|
||||
I (383) tag: f8 85 ab 3b 47 a6 65 0d 0a 42 bd 3d
|
||||
I (391) example_tee_service: Secure service call: PROTECTED M-mode
|
||||
I (397) example_tee_service: AES-256-GCM decryption
|
||||
I (393) example_tee_basic: AES-GCM decryption successful!
|
||||
I (403) main_task: Returned from app_main()
|
||||
```
|
||||
|
||||
@@ -2,10 +2,13 @@ cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
|
||||
|
||||
if(NOT esp_tee_build)
|
||||
return()
|
||||
set(srcs)
|
||||
set(include_dirs include)
|
||||
|
||||
if(esp_tee_build)
|
||||
list(APPEND srcs "example_service.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "example_service.c"
|
||||
INCLUDE_DIRS include
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS ${include_dirs}
|
||||
PRIV_REQUIRES main)
|
||||
|
||||
+75
-19
@@ -1,39 +1,95 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "hal/aes_hal.h"
|
||||
#include "aes/esp_aes.h"
|
||||
#include "mbedtls/gcm.h"
|
||||
|
||||
#include "esp_tee.h"
|
||||
#include "secure_service_num.h"
|
||||
#include "example_service.h"
|
||||
|
||||
/* Fixed key */
|
||||
static const uint8_t key[AES_256_KEY_BYTES] = {[0 ... 31] = 0xA5};
|
||||
#define AES256_KEY_LEN 32
|
||||
#define AES256_KEY_BITS (AES256_KEY_LEN * 8)
|
||||
#define AES256_NONCE_LEN 12
|
||||
|
||||
esp_err_t _ss_example_sec_serv_aes_op(int mode, size_t length, unsigned char iv[16], const unsigned char *input, unsigned char *output)
|
||||
static const char *TAG = "tee_secure_service";
|
||||
|
||||
/* Fixed key (only for example purposes) */
|
||||
static const uint8_t key[AES256_KEY_LEN] = {[0 ... AES256_KEY_LEN - 1] = 0xA5};
|
||||
|
||||
/* Fixed nonce (only for example purposes) */
|
||||
static const uint8_t nonce[AES256_NONCE_LEN] = {[0 ... AES256_NONCE_LEN - 1] = 0x5A};
|
||||
|
||||
static esp_err_t aes_gcm_crypt_common(example_aes_gcm_ctx_t *ctx, uint8_t *tag, size_t tag_len,
|
||||
uint8_t *output, bool is_encrypt)
|
||||
{
|
||||
if (length == 0 || iv == NULL || input == NULL || output == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (esp_cpu_get_curr_privilege_level() != ESP_CPU_S_MODE) {
|
||||
esp_rom_printf("Operation executing from illegal privilege level!\n");
|
||||
ESP_LOGE(TAG, "Operation executing from illegal privilege level!");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
esp_rom_printf("TEE: Secure service call for AES-256-CBC operation\n");
|
||||
esp_rom_printf("TEE: In PROTECTED M-mode\n");
|
||||
if (ctx == NULL || ctx->input == NULL || output == NULL || tag == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_aes_context ctx = {};
|
||||
ctx.key_bytes = AES_256_KEY_BYTES;
|
||||
ctx.key_in_hardware = 0;
|
||||
memcpy(ctx.key, key, ctx.key_bytes);
|
||||
if (ctx->input_len == 0 || tag_len == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return (esp_err_t)esp_aes_crypt_cbc(&ctx, mode, length, iv, input, output);
|
||||
ESP_LOGI(TAG, "Secure service call: PROTECTED M-mode");
|
||||
ESP_LOGI(TAG, "AES-256-GCM %s", is_encrypt ? "encryption" : "decryption");
|
||||
|
||||
mbedtls_gcm_context gcm;
|
||||
mbedtls_gcm_init(&gcm);
|
||||
|
||||
esp_err_t err = ESP_FAIL;
|
||||
|
||||
int ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, AES256_KEY_BITS);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Error in setting key: %d", ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (is_encrypt) {
|
||||
ret = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, ctx->input_len,
|
||||
nonce, AES256_NONCE_LEN,
|
||||
ctx->aad, ctx->aad_len,
|
||||
ctx->input, output,
|
||||
tag_len, tag);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Error in encrypting data: %d", ret);
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
ret = mbedtls_gcm_auth_decrypt(&gcm, ctx->input_len,
|
||||
nonce, AES256_NONCE_LEN,
|
||||
ctx->aad, ctx->aad_len,
|
||||
tag, tag_len,
|
||||
ctx->input, output);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Error in decrypting data: %d", ret);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
err = ESP_OK;
|
||||
|
||||
cleanup:
|
||||
mbedtls_gcm_free(&gcm);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t _ss_example_sec_serv_aes_gcm_encrypt(example_aes_gcm_ctx_t *ctx, uint8_t *tag, size_t tag_len, uint8_t *output)
|
||||
{
|
||||
return aes_gcm_crypt_common(ctx, tag, tag_len, output, true);
|
||||
}
|
||||
|
||||
esp_err_t _ss_example_sec_serv_aes_gcm_decrypt(example_aes_gcm_ctx_t *ctx, const uint8_t *tag, size_t tag_len, uint8_t *output)
|
||||
{
|
||||
return aes_gcm_crypt_common(ctx, (uint8_t *)tag, tag_len, output, false);
|
||||
}
|
||||
|
||||
+11
-13
@@ -1,20 +1,18 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* @brief Perform AES-256-CBC encryption/decryption operation in TEE
|
||||
*
|
||||
* @param mode ESP_AES_ENCRYPT (1) for encryption, ESP_AES_DECRYPT (0) for decryption
|
||||
* @param length Length of input data in bytes
|
||||
* @param iv Initialization vector (16 bytes)
|
||||
* @param input Input buffer containing plaintext (for encryption) or ciphertext (for decryption)
|
||||
* @param output Output buffer for ciphertext (for encryption) or plaintext (for decryption)
|
||||
*
|
||||
* @return esp_err_t ESP_OK on success, appropriate error code on failure
|
||||
*/
|
||||
esp_err_t example_sec_serv_aes_op(int mode, size_t length, unsigned char iv[16], const unsigned char *input, unsigned char *output);
|
||||
typedef struct {
|
||||
const uint8_t *aad; /*!< Additional authenticated data */
|
||||
size_t aad_len; /*!< Length of additional authenticated data */
|
||||
const uint8_t *input; /*!< Input data buffer */
|
||||
size_t input_len; /*!< Length of input data */
|
||||
} example_aes_gcm_ctx_t;
|
||||
|
||||
esp_err_t example_sec_serv_aes_gcm_encrypt(example_aes_gcm_ctx_t *ctx, uint8_t *tag, size_t tag_len, uint8_t *output);
|
||||
|
||||
esp_err_t example_sec_serv_aes_gcm_decrypt(example_aes_gcm_ctx_t *ctx, const uint8_t *tag, size_t tag_len, uint8_t *output);
|
||||
|
||||
+6
-2
@@ -3,5 +3,9 @@ secure_services:
|
||||
entries:
|
||||
- id: 200
|
||||
type: custom
|
||||
function: example_sec_serv_aes_op
|
||||
args: 5
|
||||
function: example_sec_serv_aes_gcm_encrypt
|
||||
args: 4
|
||||
- id: 201
|
||||
type: custom
|
||||
function: example_sec_serv_aes_gcm_decrypt
|
||||
args: 4
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
idf_component_register(SRCS "tee_main.c"
|
||||
INCLUDE_DIRS ""
|
||||
PRIV_REQUIRES esp_tee mbedtls)
|
||||
INCLUDE_DIRS ""
|
||||
PRIV_REQUIRES esp_tee mbedtls example_secure_service)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* ESP-TEE (Trusted Execution Environment) 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
|
||||
*/
|
||||
@@ -10,55 +10,72 @@
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "aes/esp_aes.h"
|
||||
#include "esp_random.h"
|
||||
|
||||
#include "esp_tee.h"
|
||||
#include "secure_service_num.h"
|
||||
#include "example_service.h"
|
||||
|
||||
#define BUF_SZ (16)
|
||||
#define EXAMPLE_BUF_SZ (32)
|
||||
#define AES256_GCM_TAG_LEN (12)
|
||||
#define AES256_GCM_AAD_LEN (16)
|
||||
|
||||
static const char *TAG = "example_tee_basic";
|
||||
|
||||
static const uint8_t expected_cipher[] = {
|
||||
0xee, 0x04, 0x9b, 0xee, 0x95, 0x6f, 0x25, 0x04,
|
||||
0x1e, 0x8c, 0xe4, 0x4e, 0x8e, 0x4e, 0x7a, 0xd3
|
||||
};
|
||||
|
||||
static const uint8_t nonce[IV_BYTES] = {[0 ... IV_BYTES - 1] = 0xFF};
|
||||
|
||||
/*
|
||||
* Example workflow:
|
||||
* 1. The REE initiates an AES operation request via the secure service call interface
|
||||
* 2. The TEE receives the request and performs encryption/decryption using AES-256-CBC mode
|
||||
* 3. The TEE uses a protected key that is only accessible within the secure environment
|
||||
* 4. The encrypted/decrypted result is returned to the non-secure world through an output buffer
|
||||
* provided in the secure service call
|
||||
* 1. The REE generates random plaintext and AAD (Additional Authenticated Data)
|
||||
* 2. The REE initiates an AES-256-GCM encryption request and provides output buffers
|
||||
* for ciphertext and authentication tag
|
||||
* 3. The TEE receives the request and performs encryption using a protected key and
|
||||
* nonce that are only accessible within the secure environment
|
||||
* 4. The encrypted ciphertext and authentication tag are returned to the REE
|
||||
* 5. The REE initiates a decryption request with the ciphertext and authentication tag
|
||||
* 6. The TEE performs authenticated decryption, verifying the tag and returning the plaintext
|
||||
* 7. The REE verifies that the decrypted data matches the original plaintext
|
||||
*/
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "AES-256-CBC operations in TEE");
|
||||
ESP_LOGI(TAG, "AES-256-GCM operations in TEE");
|
||||
|
||||
uint8_t plain_text[BUF_SZ] = {[0 ... BUF_SZ - 1] = 0x3A};
|
||||
uint8_t cipher_text[BUF_SZ] = {0};
|
||||
uint8_t decrypted_text[BUF_SZ] = {0};
|
||||
uint8_t iv[IV_BYTES] = {0};
|
||||
uint8_t plain_text[EXAMPLE_BUF_SZ];
|
||||
uint8_t cipher_text[EXAMPLE_BUF_SZ] = {0};
|
||||
uint8_t decrypted_text[EXAMPLE_BUF_SZ] = {0};
|
||||
uint8_t tag[AES256_GCM_TAG_LEN] = {0};
|
||||
uint8_t aad_buf[AES256_GCM_AAD_LEN];
|
||||
|
||||
memcpy(iv, nonce, sizeof(iv));
|
||||
uint32_t ret = esp_tee_service_call(6, SS_EXAMPLE_SEC_SERV_AES_OP, ESP_AES_ENCRYPT, sizeof(plain_text), iv, plain_text, cipher_text);
|
||||
if (ret != ESP_OK || memcmp(cipher_text, expected_cipher, sizeof(expected_cipher))) {
|
||||
ESP_LOGE(TAG, "Failed to encrypt data!");
|
||||
/* Generate random plaintext and AAD */
|
||||
esp_fill_random(plain_text, sizeof(EXAMPLE_BUF_SZ));
|
||||
esp_fill_random(aad_buf, AES256_GCM_AAD_LEN);
|
||||
|
||||
/* Encryption operation */
|
||||
example_aes_gcm_ctx_t enc_ctx = {
|
||||
.aad = aad_buf,
|
||||
.aad_len = sizeof(aad_buf),
|
||||
.input = plain_text,
|
||||
.input_len = sizeof(plain_text),
|
||||
};
|
||||
|
||||
uint32_t ret = esp_tee_service_call(5, SS_EXAMPLE_SEC_SERV_AES_GCM_ENCRYPT, &enc_ctx, tag, AES256_GCM_TAG_LEN, cipher_text);
|
||||
ESP_ERROR_CHECK((esp_err_t)ret);
|
||||
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("ciphertext", cipher_text, sizeof(cipher_text), ESP_LOG_INFO);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL("tag", tag, AES256_GCM_TAG_LEN, ESP_LOG_INFO);
|
||||
|
||||
/* Decryption operation */
|
||||
example_aes_gcm_ctx_t dec_ctx = {
|
||||
.aad = aad_buf,
|
||||
.aad_len = sizeof(aad_buf),
|
||||
.input = cipher_text,
|
||||
.input_len = sizeof(cipher_text),
|
||||
};
|
||||
|
||||
ret = esp_tee_service_call(5, SS_EXAMPLE_SEC_SERV_AES_GCM_DECRYPT, &dec_ctx, tag, AES256_GCM_TAG_LEN, decrypted_text);
|
||||
ESP_ERROR_CHECK((esp_err_t)ret);
|
||||
|
||||
if (memcmp(decrypted_text, plain_text, sizeof(plain_text)) != 0) {
|
||||
ESP_LOGE(TAG, "Decrypted data mismatch!");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "AES encryption successful!");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Cipher text -");
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, cipher_text, sizeof(cipher_text), ESP_LOG_INFO);
|
||||
|
||||
memcpy(iv, nonce, sizeof(iv));
|
||||
ret = esp_tee_service_call(6, SS_EXAMPLE_SEC_SERV_AES_OP, ESP_AES_DECRYPT, sizeof(cipher_text), iv, cipher_text, decrypted_text);
|
||||
if (ret != ESP_OK || memcmp(decrypted_text, plain_text, sizeof(plain_text))) {
|
||||
ESP_LOGE(TAG, "Failed to decrypt data!");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "AES decryption successful!");
|
||||
ESP_LOGI(TAG, "AES-GCM decryption successful!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
|
||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||
def test_example_tee_basic(dut: Dut) -> None:
|
||||
# Logging example binary details
|
||||
binary_files = [
|
||||
@@ -19,11 +19,12 @@ def test_example_tee_basic(dut: Dut) -> None:
|
||||
for file_name, log_label in binary_files:
|
||||
binary_file = os.path.join(dut.app.binary_path, file_name)
|
||||
bin_size = os.path.getsize(binary_file)
|
||||
logging.info('{}: {}KB'.format(log_label, bin_size // 1024))
|
||||
logging.info(f'{log_label}: {bin_size // 1024}KB')
|
||||
|
||||
# Start test
|
||||
dut.expect('AES-256-CBC operations in TEE', timeout=30)
|
||||
dut.expect('TEE: In PROTECTED M-mode', timeout=30)
|
||||
dut.expect('AES encryption successful!', timeout=30)
|
||||
dut.expect('ee 04 9b ee 95 6f 25 04 1e 8c e4 4e 8e 4e 7a d3', timeout=30)
|
||||
dut.expect('AES decryption successful!', timeout=30)
|
||||
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('Returned from app_main()', timeout=10)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Enabling TEE
|
||||
CONFIG_SECURE_ENABLE_TEE=y
|
||||
CONFIG_SECURE_TEE_LOG_LEVEL_INFO=y
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP_TEE=y
|
||||
CONFIG_SECURE_TEE_ATTESTATION=n
|
||||
CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN=n
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| Supported Targets | ESP32-C6 |
|
||||
| ----------------- | -------- |
|
||||
| Supported Targets | ESP32-C6 | ESP32-C61 |
|
||||
| ----------------- | -------- | --------- |
|
||||
|
||||
# TEE: Secure OTA example
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ def start_https_server(ota_image_dir: str, server_ip: str, server_port: int) ->
|
||||
|
||||
|
||||
@pytest.mark.wifi_high_traffic
|
||||
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
|
||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||
def test_examples_tee_secure_ota_example(dut: Dut) -> None:
|
||||
"""
|
||||
This is a positive test case, which downloads complete binary file multiple number of times.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| Supported Targets | ESP32-C6 |
|
||||
| ----------------- | -------- |
|
||||
| Supported Targets | ESP32-C6 | ESP32-C61 |
|
||||
| ----------------- | -------- | --------- |
|
||||
|
||||
# TEE: Secure Storage example
|
||||
|
||||
@@ -31,22 +31,37 @@ Open the project configuration menu (`idf.py menuconfig`).
|
||||
|
||||
- Configure the secure storage example key ID at `Example Configuration → TEE: Secure Storage Key ID`.
|
||||
|
||||
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`:
|
||||
TEE Secure Storage follows the NVS partition format and uses an XTS-AES encryption scheme derived via the HMAC peripheral or software-based HMAC implementation. It supports two key derivation modes, configurable via `CONFIG_SECURE_TEE_SEC_STG_MODE`:
|
||||
|
||||
- **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`.
|
||||
- **Release** Mode: Encryption keys are derived using a key stored in eFuse, specified by `CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`.
|
||||
|
||||
#### Configure the eFuse key ID storing the HMAC key
|
||||
#### Configure the eFuse key ID for storage encryption
|
||||
|
||||
- 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 for storage encryption keys`.
|
||||
- Set the eFuse key ID storing the HMAC/USER key at `ESP-TEE (Trusted Execution Environment) → Secure Services → Secure Storage: eFuse HMAC key ID for storage encryption keys`.
|
||||
|
||||
**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.
|
||||
**Note:** Before running the example, users must program the required 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.
|
||||
|
||||
**For targets without HMAC peripheral (ESP32-C61):**
|
||||
|
||||
```shell
|
||||
# Generate a random 32-byte key
|
||||
openssl rand -out hmac_key_file.bin 32
|
||||
# Program the USER purpose key (256-bit) in eFuse
|
||||
# Here, BLOCK_KEYx is a free eFuse key-block between BLOCK_KEY0 and BLOCK_KEY5
|
||||
espefuse -p PORT burn-key --no-read-protect BLOCK_KEYx hmac_key_file.bin USER
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When programming the key into eFuse for targets without HMAC peripheral, ensure that it is **NOT** marked as read-protected (use the `--no-read-protect` flag). If the key is read-protected, the TEE will be unable to access it. However, this does not weaken security: the APM peripheral already blocks software access to the key, and any illegal read or write attempt from the REE triggers a fault.
|
||||
|
||||
**For targets with HMAC peripheral:**
|
||||
|
||||
```shell
|
||||
# Generate a random 32-byte HMAC key
|
||||
openssl rand -out hmac_key_file.bin 32
|
||||
# Programming the HMAC key (256-bit) in eFuse
|
||||
# Program the HMAC key (256-bit) in eFuse
|
||||
# Here, BLOCK_KEYx is a free eFuse key-block between BLOCK_KEY0 and BLOCK_KEY5
|
||||
espefuse -p PORT burn-key BLOCK_KEYx hmac_key_file.bin HMAC_UP
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
|
||||
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||
def test_example_tee_secure_storage(dut: Dut) -> None:
|
||||
# Start test
|
||||
dut.expect('TEE Secure Storage', timeout=30)
|
||||
|
||||
Reference in New Issue
Block a user