ESP-TEE: User Guide
The ESP-TEE (Espressif Trusted Execution Environment) is a security framework built on top of dedicated hardware that ensures only authorized parties or applications can access protected information or services. When enabled, ESP-TEE runs in parallel with a FreeRTOS-based ESP-IDF application. In this case, the execution environment is split into the following:
TEE: the protected or trusted execution environment, managed by ESP-TEE.
REE: the unprotected or rich execution environment, where the ESP-IDF user application runs.
An example use case for ESP-TEE is securely storing a cryptographic key in the TEE for authentication in a challenge-response protocol with external entities and enabling secure communication to protect data.
ESP-TEE offers several key secure services, including:
-
ESP-TEE provides secure storage to ensure that sensitive data is stored in an encrypted external flash partition, completely inaccessible to the REE. It also supports secure key management and operations for signing and encryption.
-
Attestation is a mechanism enabling a device to make claims about its identity and security status, through a cryptographically secured token. ESP-TEE generates this token in a predefined JSON format, signed using the ECDSA scheme.
-
ESP-TEE supports a secure TEE OTA update mechanism, allowing the device to update the TEE firmware using data received during normal operation, such as over Wi-Fi or Bluetooth. The mechanism also includes support for update rollback.
For more information, see the Secure Services section.
Scope of Protection
The Secure Boot feature ensures that only authenticated software can execute on the device. When enabled, a signed TEE image is generated during the build process alongside the signed REE image. The signing scheme and key are the same for both applications.
Secure Boot establishes a robust chain of trust by verifying all mutable software entities involved in the Application Startup Flow i.e. the second stage bootloader, TEE, and REE applications. Signature verification is performed both during boot-up and OTA updates. While Secure Boot ensures boot-time security, ESP-TEE provides runtime security by safeguarding the security assets within the TEE.
In addition, the attestation service verifies the signature and firmware hash for all active software upon request, ensuring ongoing protection against unauthorized modifications.
The Flash Encryption feature encrypts the contents of the ESP32-C6's off-chip flash memory, preventing physical readout from recovering its contents. The flash encryption key is securely stored in hardware and remains inaccessible to both TEE and REE.
ESP-TEE: Boot-up Flow with Secure Boot for ESP32-C6
Important
It is highly recommended to enable both Secure Boot and Flash Encryption with ESP-TEE on all production devices.
Architecture: Simplified Overview
ESP-TEE offers a secure execution environment centered around a Root-of-Trust (RoT), which is a set of implicitly trusted functions on which the system or device relies on for security.
The Immutable RoT consists of the following components:
First stage bootloader (BootROM)
Isolation hardware
Cryptographic hardware (AES, SHA)
eFuse
The Updatable RoT comprises the second stage bootloader and the TEE firmware, which is responsible for secure configuration, initialization, interrupt handling, exception management, and providing secure services for cryptographic operations as well as features like secure storage and attestation.
For more details about the individual components and the architecture, refer to the Detailed Architecture section.
Memory Allocation
ESP-TEE divides the memory into separate regions for the TEE and REE, allocating part of the internal SRAM and external flash memory to the TEE. This separation safeguards sensitive data and operations within the TEE, preventing unauthorized access from the REE.
Internal Memory (SRAM)
Internal memory is allocated to the TEE based on the Kconfig options that are available under the Memory Configuration section in the ESP-TEE (Trusted Execution Environment) menu. All remaining memory is allocated to the REE.
Warning
Modify the above values at your own risk as it may cause build or runtime failures.
Both the TEE and REE have independent heap allocators, each operating within the memory allocated to its respective application.
External Memory (Flash)
Designated partitions in the external flash are reserved for the TEE to support functions such as code XIP (eXecute-In-Place), secure storage, and OTA data. The partition table must include at least one TEE app slot (type app and subtype tee_0) with a minimum size of 192 KB.
Example partition table is given below:
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
tee_0, app, tee_0, 0x10000, 192K,
secure_storage, data, nvs, 0x40000, 64K,
factory, app, factory, 0x50000, 1M,
nvs, data, nvs, 0x150000, 24K,
phy_init, data, phy, 0x156000, 4K,
Important
The partition following the last TEE-related partition must be aligned to the configured MMU page size. This alignment is required to prevent secure boot verification failures when validating the user application (REE) image.
Note
For more details on the default policy and scope of flash memory protection with ESP-TEE, refer to the Flash Protection - Virtual and Physical Access section from the advanced guide.
Secure Services
All features that the TEE exposes to the REE are implemented as secure services. The REE can invoke such secure services through the interface APIs esp_tee_service_call() and esp_tee_service_call_with_noniram_intr_disabled(). Secure services can be preempted by TEE or REE interrupts.
Note
Since multitasking is not currently supported in the TEE, secure service calls are serialized, and subsequent calls remain pending until the current service completes.
For ESP32-C6, a list of secure services can be found at this table. Following are the types of secure services.
Core secure services: Built-in services within the TEE firmware that provide routine functionalities to the REE, such as interrupt configuration and eFuse access.
Optional secure Services: Configurable services that extend the capabilities of the TEE and can be enabled or disabled as needed, such as attestation.
Custom secure services: User-defined secure services as required by the user application. For more details, refer to the Adding Custom Secure Services section and the tee_basic example.
Usage
Enable ESP-TEE: In the Project Configuration menu, go to ESP-TEE (Trusted Execution Environment) and set the option .
Configure the TEE logging:
Set to enable logging for TEE.
Set to select the logging verbosity level.
Configure the TEE memory regions:
Set to configure the TEE IRAM size
Set to configure the TEE DRAM size
Set and to configure the TEE task and interrupt stack from the reserved DRAM
Configure the partition table:
Select one of the simple predefined partition tables under - Single factory app, no OTA, TEE or Two OTA definitions, TEE
Alternatively, select the Custom partition table CSV option and define your own layout as per the guidelines provided in the External Memory (Flash) section.
Firmware Versioning
The TEE firmware carries its own version, independent of the ESP-IDF version, in the version field of its application descriptor (esp_app_desc_t). It is printed in the TEE boot log and reported through the attestation token.
The version is defined in esp_tee/version.cmake and esp_tee/include/esp_tee_version.h and has the form <major>.<minor>.<patch>~v<IDF major>.<IDF minor>.<IDF patch> (e.g., 1.2.0~v6.2.0), following Semantic Versioning: the TEE version identifies the TEE source, while the suffix identifies the ESP-IDF release it was built with.
TEE version |
ESP-IDF release lines |
|---|---|
|
v5.5 |
|
v6.0, v6.1 |
|
v6.2 |
Note
The TEE firmware version is distinct from the TEE secure version used by TEE anti-rollback.
Reproducible Builds and Code Hash
The TEE binary is built reproducibly, irrespective of the REE application configuration: the same ESP-IDF release, target and TEE configuration always produce a byte-identical esp_tee.bin. Options that change the compiled TEE code (e.g., the compiler optimization and assertion levels, log verbosity, Secure Boot) are part of the TEE configuration and therefore change the image and its code hash. The image still embeds packaging metadata (flash mode/frequency/size, chip revision bounds, version strings, the ESP-IDF version and the ELF digest), so its plain SHA-256 changes with them even when the compiled code does not. The code hash leaves those parts (the image header, the app descriptor, the checksum and the appended digest) out, covering the image segments alone, and so identifies the compiled code alone. It can be computed from a built image with esp_tee/scripts/esp_tee_code_hash/esp_tee_code_hash.py and is reported by the device in the attestation token (see Attestation).
Application Examples
Examples are available in the security/tee directory, showcasing the various ESP-TEE secure services. For more detailed information, please refer to the README.md file within each specific example.
API Reference
Header File
This header file can be included with:
#include "esp_tee.h"
This header file is a part of the API provided by the
esp_teecomponent. To declare that your component depends onesp_tee, add the following to your CMakeLists.txt:REQUIRES esp_tee
or
PRIV_REQUIRES esp_tee
Functions
-
uint32_t esp_tee_service_call(int argc, ...)
Interface function that allows untrusted applications to invoke secure services through TEE.
- Parameters:
argc -- Number of arguments being passed to the secure service
- Returns:
Value returned by the secure service function
-
uint32_t esp_tee_service_call_with_noniram_intr_disabled(int argc, ...)
Interface function that allows untrusted applications to invoke secure services through TEE, with the scheduler and the non-IRAM interrupts disabled.
- Parameters:
argc -- Number of arguments being passed to the secure service
- Returns:
Value returned by the secure service function
Header File
This header file can be included with:
#include "esp_tee_version.h"
This header file is a part of the API provided by the
esp_teecomponent. To declare that your component depends onesp_tee, add the following to your CMakeLists.txt:REQUIRES esp_tee
or
PRIV_REQUIRES esp_tee
Macros
-
ESP_TEE_VERSION_MAJOR
Major version number (X.x.x)
-
ESP_TEE_VERSION_MINOR
Minor version number (x.X.x)
-
ESP_TEE_VERSION_PATCH
Patch version number (x.x.X)
-
ESP_TEE_VERSION_VAL(major, minor, patch)
Macro to convert the TEE version number into an integer
To be used in comparisons, such as ESP_TEE_VERSION >= ESP_TEE_VERSION_VAL(1, 0, 0)
-
ESP_TEE_VERSION
Current TEE version, as an integer
To be used in comparisons, such as ESP_TEE_VERSION >= ESP_TEE_VERSION_VAL(1, 0, 0)
-
ESP_TEE_VERSION_STR
Current TEE version string, as stored in the TEE app descriptor
Format:
<major>.<minor>.<patch>~v<IDF major>.<IDF minor>.<IDF patch>(e.g., "1.2.0~v6.2.0"), where the numeric part identifies the TEE source and the suffix the ESP-IDF release it was built with