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

@@ -41,7 +41,6 @@ extern "C" {
#define PART_SUBTYPE_PARTITION_TABLE_OTA 0x01
#define PART_SUBTYPE_DATA_TEE_OTA 0x90
#define PART_SUBTYPE_DATA_TEE_SEC_STORAGE 0x91
#define PART_TYPE_END 0xff
#define PART_SUBTYPE_END 0xff

View File

@@ -226,9 +226,6 @@ bool bootloader_utility_load_partition_table(bootloader_state_t *bs)
bs->tee_ota_info = partition->pos;
partition_usage = "TEE OTA data";
break;
case PART_SUBTYPE_DATA_TEE_SEC_STORAGE: /* TEE secure storage */
partition_usage = "TEE secure storage";
break;
#endif
default:
partition_usage = "Unknown data";

View File

@@ -1,16 +1,23 @@
idf_build_get_property(target IDF_TARGET)
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
if(${target} STREQUAL "linux")
return() # This component is not necessary on the POSIX/Linux simulator
endif()
idf_component_register(SRCS "cxx_exception_stubs.cpp"
"cxx_guards.cpp"
"cxx_init.cpp"
# Make sure that pthread is in component list
PRIV_REQUIRES pthread esp_system)
set(srcs "cxx_exception_stubs.cpp")
set(priv_requires esp_system)
if(NOT CONFIG_CXX_EXCEPTIONS)
if(NOT esp_tee_build)
list(APPEND srcs "cxx_guards.cpp" "cxx_init.cpp")
# Make sure that pthread is in component list
list(APPEND priv_requires pthread)
endif()
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES ${priv_requires})
if(esp_tee_build OR NOT CONFIG_CXX_EXCEPTIONS)
set(WRAP_FUNCTIONS
__register_frame_info_bases
__register_frame_info
@@ -55,25 +62,31 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang")
else()
target_link_libraries(${COMPONENT_LIB} PUBLIC stdc++ gcc)
endif()
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __cxa_guard_dummy")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __cxx_init_dummy")
# Force libpthread to appear later than libstdc++ in link line since libstdc++ depends on libpthread.
# Furthermore, force libcxx to appear later than libgcc because some libgcc unwind code is wrapped, if C++
# exceptions are disabled. libcxx (this component) provides the unwind code wrappers.
# This is to prevent linking of libgcc's unwind code which considerably increases the binary size.
# Also force libnewlib to appear later than libstdc++ in link line since libstdc++ depends on
# some functions in libnewlib, e.g. getentropy().
idf_component_get_property(pthread pthread COMPONENT_LIB)
idf_component_get_property(newlib newlib COMPONENT_LIB)
idf_component_get_property(cxx cxx COMPONENT_LIB)
add_library(stdcpp_deps INTERFACE)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
target_link_libraries(stdcpp_deps INTERFACE stdc++ c $<TARGET_FILE:${pthread}> $<TARGET_FILE:${newlib}>)
else()
target_link_libraries(stdcpp_deps INTERFACE stdc++ $<TARGET_FILE:${pthread}> $<TARGET_FILE:${newlib}>)
endif()
target_link_libraries(${COMPONENT_LIB} PUBLIC stdcpp_deps)
if(NOT esp_tee_build)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __cxa_guard_dummy")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u __cxx_init_dummy")
# Force libpthread to appear later than libstdc++ in link line since libstdc++ depends on libpthread.
# Furthermore, force libcxx to appear later than libgcc because some libgcc unwind code is wrapped, if C++
# exceptions are disabled. libcxx (this component) provides the unwind code wrappers.
# This is to prevent linking of libgcc's unwind code which considerably increases the binary size.
# Also force libnewlib to appear later than libstdc++ in link line since libstdc++ depends on
# some functions in libnewlib, e.g. getentropy().
idf_component_get_property(pthread pthread COMPONENT_LIB)
idf_component_get_property(newlib newlib COMPONENT_LIB)
if(CMAKE_C_COMPILER_ID MATCHES "Clang")
target_link_libraries(stdcpp_deps INTERFACE stdc++ c $<TARGET_FILE:${pthread}> $<TARGET_FILE:${newlib}>)
else()
target_link_libraries(stdcpp_deps INTERFACE stdc++ $<TARGET_FILE:${pthread}> $<TARGET_FILE:${newlib}>)
endif()
endif()
idf_component_get_property(cxx cxx COMPONENT_LIB)
add_library(libgcc_cxx INTERFACE)
target_link_libraries(libgcc_cxx INTERFACE ${CONFIG_COMPILER_RT_LIB_NAME} $<TARGET_FILE:${cxx}>)
target_link_libraries(${COMPONENT_LIB} PUBLIC libgcc_cxx)

View File

@@ -1,3 +1,5 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
# bootloader build simplified version
if(BOOTLOADER_BUILD)
set(srcs "partition_bootloader.c")
@@ -10,7 +12,19 @@ idf_component_register(SRCS "${srcs}"
REQUIRES ${reqs}
PRIV_REQUIRES ${priv_reqs})
# regular, non bootloader build
# esp-tee build simplified version
elseif(esp_tee_build)
set(srcs "partition_tee.c")
set(reqs "spi_flash")
set(priv_reqs "tee_flash_mgr")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS ${private_include_dirs}
REQUIRES ${reqs}
PRIV_REQUIRES ${priv_reqs})
# regular, OS build
else()
set(srcs "partition.c")
set(priv_reqs esp_system spi_flash partition_table)

View File

@@ -114,7 +114,6 @@ typedef enum {
ESP_PARTITION_SUBTYPE_DATA_LITTLEFS = 0x83, //!< LITTLEFS partition
ESP_PARTITION_SUBTYPE_DATA_TEE_OTA = 0x90, //!< TEE OTA selection partition
ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE= 0x91, //!< TEE secure storage partition
#if __has_include("extra_partition_subtypes.inc")
#include "extra_partition_subtypes.inc"

View File

@@ -0,0 +1,136 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_partition.h"
#include "spi_flash_mmap.h"
#include "esp_tee_flash.h"
#include "esp_log.h"
static __attribute__((unused)) const char *TAG = "partition_tee";
const esp_partition_t *esp_partition_find_first(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label)
{
static esp_partition_t internal_partition = {0};
esp_partition_info_t partition_info = {0};
esp_err_t err = esp_tee_flash_find_partition(type, subtype, label, &partition_info);
if (err != ESP_OK) {
return NULL;
}
// Populate the internal partition structure
internal_partition.flash_chip = NULL;
internal_partition.type = partition_info.type;
internal_partition.subtype = partition_info.subtype;
internal_partition.address = partition_info.pos.offset;
internal_partition.size = partition_info.pos.size;
internal_partition.erase_size = SPI_FLASH_SEC_SIZE;
strncpy(internal_partition.label, (char *)partition_info.label, sizeof(internal_partition.label) - 1);
internal_partition.encrypted = partition_info.flags & PART_FLAG_ENCRYPTED;
internal_partition.readonly = partition_info.flags & PART_FLAG_READONLY;
return &internal_partition;
}
esp_err_t esp_partition_read(const esp_partition_t *partition,
size_t src_offset, void *dst, size_t size)
{
assert(partition != NULL);
if (src_offset > partition->size) {
return ESP_ERR_INVALID_ARG;
}
if (size > partition->size - src_offset) {
return ESP_ERR_INVALID_SIZE;
}
if (!partition->encrypted) {
return esp_tee_flash_read(partition->address + src_offset, dst, size, false);
}
const void *buf = esp_tee_flash_mmap(partition->address + src_offset, size);
if (buf == NULL) {
return ESP_ERR_NO_MEM;
}
memcpy(dst, buf, size);
esp_tee_flash_munmap(buf);
return ESP_OK;
}
esp_err_t esp_partition_write(const esp_partition_t *partition,
size_t dst_offset, const void *src, size_t size)
{
assert(partition != NULL);
if (partition->readonly) {
return ESP_ERR_NOT_ALLOWED;
}
if (dst_offset > partition->size) {
return ESP_ERR_INVALID_ARG;
}
if (size > partition->size - dst_offset) {
return ESP_ERR_INVALID_SIZE;
}
dst_offset = partition->address + dst_offset;
if (!partition->encrypted) {
return esp_tee_flash_write(dst_offset, (void *)src, size, false);
}
return esp_tee_flash_write(dst_offset, (void *)src, size, true);
}
esp_err_t esp_partition_read_raw(const esp_partition_t *partition,
size_t src_offset, void *dst, size_t size)
{
assert(partition != NULL);
if (src_offset > partition->size) {
return ESP_ERR_INVALID_ARG;
}
if (size > partition->size - src_offset) {
return ESP_ERR_INVALID_SIZE;
}
return esp_tee_flash_read(partition->address + src_offset, dst, size, false);
}
esp_err_t esp_partition_write_raw(const esp_partition_t *partition,
size_t dst_offset, const void *src, size_t size)
{
assert(partition != NULL);
if (partition->readonly) {
return ESP_ERR_NOT_ALLOWED;
}
if (dst_offset > partition->size) {
return ESP_ERR_INVALID_ARG;
}
if (size > partition->size - dst_offset) {
return ESP_ERR_INVALID_SIZE;
}
dst_offset = partition->address + dst_offset;
return esp_tee_flash_write(dst_offset, (void *)src, size, false);
}
esp_err_t esp_partition_erase_range(const esp_partition_t *partition,
size_t offset, size_t size)
{
assert(partition != NULL);
if (partition->readonly) {
return ESP_ERR_NOT_ALLOWED;
}
if (offset > partition->size) {
return ESP_ERR_INVALID_ARG;
}
if (size > partition->size - offset) {
return ESP_ERR_INVALID_SIZE;
}
return esp_tee_flash_erase_range(partition->address + offset, size);
}
uint32_t esp_partition_get_main_flash_sector_size(void)
{
return SPI_FLASH_SEC_SIZE;
}

View File

@@ -72,24 +72,24 @@ menu "ESP-TEE (Trusted Execution Environment)"
config SECURE_TEE_SEC_STG_MODE_DEVELOPMENT
bool "Development"
help
Secure storage will be encrypted by the data stored in eFuse BLK2
Secure storage will be encrypted by a constant key embedded in the TEE firmware
config SECURE_TEE_SEC_STG_MODE_RELEASE
depends on IDF_TARGET_ESP32C6
bool "Release"
help
Secure storage will be encrypted by the data stored in eFuse block
configured through the SECURE_TEE_SEC_STG_KEY_EFUSE_BLK option
configured through the SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID option
endchoice
config SECURE_TEE_SEC_STG_KEY_EFUSE_BLK
int "Secure Storage: Encryption key eFuse block"
config SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID
int "Secure Storage: eFuse HMAC key ID"
depends on SECURE_TEE_SEC_STG_MODE_RELEASE
range 4 10
default 10
range -1 5
default -1
help
eFuse block ID storing the TEE secure storage encryption key
eFuse block key ID storing the HMAC key for deriving the TEE secure storage encryption keys
config SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
bool "Secure Storage: Support signing with the ECDSA SECP192R1 curve"
@@ -104,13 +104,12 @@ menu "ESP-TEE (Trusted Execution Environment)"
This configuration enables the support for the Attestation service.
config SECURE_TEE_ATT_KEY_SLOT_ID
config SECURE_TEE_ATT_KEY_STR_ID
depends on SECURE_TEE_ATTESTATION
int "Attestation: Secure Storage slot ID for EAT signing"
default 0
range 0 14
string "Attestation: Secure Storage key ID for EAT signing"
default "tee_att_key0"
help
This configuration sets the slot ID from the TEE secure storage
This configuration sets the key ID from the TEE secure storage
storing the ECDSA keypair for executing sign/verify operations
from the TEE side for attestation.

View File

@@ -39,6 +39,10 @@ extern "C" {
#error "CONFIG_SECURE_TEE_INTR_STACK_SIZE must be 16-byte (0x10) aligned"
#endif
/* TEE Secure Storage partition label and NVS namespace */
#define ESP_TEE_SEC_STG_PART_LABEL "secure_storage"
#define ESP_TEE_SEC_STG_NVS_NAMESPACE "tee_sec_stg_ns"
/* NOTE: ESP32-C6 - TEE/REE memory regions */
/* TEE I/DRAM */
#define SOC_S_IRAM_START (SOC_IRAM_LOW)

View File

@@ -252,36 +252,28 @@ secure_services:
entries:
- id: 175
type: custom
function: esp_tee_sec_storage_init
args: 0
function: esp_tee_sec_storage_clear_key
args: 1
- id: 176
type: custom
function: esp_tee_sec_storage_gen_key
args: 2
args: 1
- id: 177
type: custom
function: esp_tee_sec_storage_get_signature
args: 5
function: esp_tee_sec_storage_ecdsa_sign
args: 4
- id: 178
type: custom
function: esp_tee_sec_storage_get_pubkey
args: 3
function: esp_tee_sec_storage_ecdsa_get_pubkey
args: 2
- id: 179
type: custom
function: esp_tee_sec_storage_encrypt
args: 8
function: esp_tee_sec_storage_aead_encrypt
args: 4
- id: 180
type: custom
function: esp_tee_sec_storage_decrypt
args: 8
- id: 181
type: custom
function: esp_tee_sec_storage_is_slot_empty
args: 1
- id: 182
type: custom
function: esp_tee_sec_storage_clear_slot
args: 1
function: esp_tee_sec_storage_aead_decrypt
args: 4
# ID: 195-199 (5) - OTA
- family: ota
entries:

View File

@@ -0,0 +1,95 @@
# TEE Secure Storage: Image Generation
## A. Generate Secure Key Blobs
### `esp_tee_sec_stg_keygen` tool
```
$ python esp_tee_sec_stg_keygen.py --help
usage: esp_tee_sec_stg_keygen.py [-h] -k {aes256,ecdsa_p256,ecdsa_p192} -o OUTPUT [-i INPUT] [--write-once]
Generate or import a cryptographic key structure for secure storage
options:
-h, --help show this help message and exit
-k, --key-type {aes256,ecdsa_p256,ecdsa_p192}
key type to be processed
-o, --output OUTPUT output binary file name
-i, --input INPUT input key file (.pem for ecdsa, .bin for aes)
--write-once make key persistent - cannot be modified or deleted once written
```
### ECDSA Keys
```bash
python esp_tee_sec_stg_keygen.py -k ecdsa_p256 -o ecdsa_p256_k0.bin
python esp_tee_sec_stg_keygen.py -k ecdsa_p192 -o ecdsa_p192_k0.bin
```
#### With custom PEM:
```bash
openssl ecparam -name prime256v1 -genkey -noout -out ecdsa_p256.pem
python esp_tee_sec_stg_keygen.py -k ecdsa_p256 -o ecdsa_p256_k1.bin -i ecdsa_p256.pem --write-once
```
### AES-256 Key
```bash
python esp_tee_sec_stg_keygen.py -k aes256 -o aes256_gcm_k0.bin --write-once
```
#### With custom key and IV
```bash
# Generate 32 bytes AES key
openssl rand 32 > aes_key.bin
# Generate 12 bytes IV (optional)
openssl rand 12 >> aes_key.bin
# Generate AES key blob using custom key + IV
python esp_tee_sec_stg_keygen.py -k aes256 -o aes256_gcm_k1.bin -i aes_key.bin
```
## B. NVS Partition Image Generation
### Create the TEE Secure Storage CSV
Prepare a CSV file that defines the data to be used as input for the NVS Partition Generator utility.
```csv
key,type,encoding,value
tee_sec_stg_ns,namespace,,
aes256_key0,file,binary,aes256_gcm_k0.bin
p256_key0,file,binary,ecdsa_p256_k0.bin
p192_key0,file,binary,ecdsa_p192_k0.bin
attest_key0,file,binary,ecdsa_p256_k1.bin
```
> [!IMPORTANT]
> As per the current implementation, all data objects in the TEE Secure Storage are to be stored in the `tee_sec_stg_ns` namespace.
### Generate NVS Encryption Keys
```bash
python nvs_partition_gen.py generate-key --key_protect_hmac --kp_hmac_inputkey hmac_key.bin --keyfile nvs_keys.bin
```
> [!IMPORTANT]
> `hmac_key.bin` must match the HMAC key programmed into the eFuse block specified by `CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID`.
### Generate encrypted NVS partition
```bash
python nvs_partition_gen.py encrypt tee_sec_stg_val.csv tee_sec_stg_nvs.bin 0x8000 --inputkey nvs_keys.bin
```
> [!NOTE]
> Replace `0x8000` with the actual size of the TEE Secure Storage partition as configured in your project's partition table.
## (Optional) Decrypt for Verification
```bash
python nvs_partition_gen.py decrypt tee_sec_stg_nvs.bin nvs_keys.bin tee_sec_stg_nvs_decr.bin
```

View File

@@ -0,0 +1,156 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import argparse
import os
import struct
from enum import Enum
from enum import IntFlag
from typing import Any
from typing import Optional
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
# === Constants ===
SEC_STG_KEY_DATA_SZ = 256
AES_KEY_LEN = 32
AES_DEFAULT_IV_LEN = 16
AES_GCM_IV_LEN = 12
ECDSA_P256_LEN = 32
ECDSA_P192_LEN = 24
# === Key Type Enum ===
class KeyType(Enum):
AES256 = 0
ECDSA_P256 = 1
ECDSA_P192 = 2
# === Bitwise Flags Enum ===
class Flags(IntFlag):
NONE = 0x00000000
WRITE_ONCE = 0x00000001
# === Key Generators ===
def generate_aes256_key(flags: Flags, key_file: Optional[str] = None) -> bytes:
if key_file:
with open(key_file, 'rb') as f:
key_data = f.read()
if len(key_data) < AES_KEY_LEN:
raise ValueError('AES key file must be at least 32 bytes long')
key = key_data[:AES_KEY_LEN]
iv_data = key_data[AES_KEY_LEN:]
iv_len = len(iv_data)
if iv_len == 0:
iv = os.urandom(AES_DEFAULT_IV_LEN)
elif iv_len == AES_GCM_IV_LEN:
iv = iv_data + b'\x00' * (AES_DEFAULT_IV_LEN - AES_GCM_IV_LEN)
elif iv_len == AES_DEFAULT_IV_LEN:
iv = iv_data
else:
raise ValueError('IV length must be exactly 12 or 16 bytes, or omitted to generate one')
else:
key = os.urandom(AES_KEY_LEN)
iv = os.urandom(AES_DEFAULT_IV_LEN)
packed = struct.pack('<II32s16s', KeyType.AES256.value, flags.value, key, iv)
return packed + b'\x00' * (SEC_STG_KEY_DATA_SZ - len(packed))
def generate_ecdsa_key(
curve: ec.EllipticCurve, key_type_enum: KeyType, key_len: int, flags: Flags, pem_file: Optional[str] = None
) -> bytes:
if pem_file:
with open(pem_file, 'rb') as f:
private_key = serialization.load_pem_private_key(f.read(), password=None, backend=default_backend())
if not isinstance(private_key, ec.EllipticCurvePrivateKey):
raise ValueError('Provided PEM does not contain an EC private key')
else:
private_key = ec.generate_private_key(curve, default_backend())
priv = private_key.private_numbers().private_value.to_bytes(key_len, 'big')
pub = private_key.public_key().public_numbers()
x = pub.x.to_bytes(key_len, 'big')
y = pub.y.to_bytes(key_len, 'big')
packed = struct.pack(f'<II{key_len}s{key_len}s{key_len}s', key_type_enum.value, flags.value, priv, x, y)
return packed + b'\x00' * (SEC_STG_KEY_DATA_SZ - len(packed))
def generate_key_data(key_type: KeyType, flags: Flags, input_file: Optional[str]) -> bytes:
if key_type == KeyType.AES256:
return generate_aes256_key(flags, input_file)
elif key_type == KeyType.ECDSA_P256:
return generate_ecdsa_key(ec.SECP256R1(), key_type, ECDSA_P256_LEN, flags, input_file)
elif key_type == KeyType.ECDSA_P192:
return generate_ecdsa_key(ec.SECP192R1(), key_type, ECDSA_P192_LEN, flags, input_file)
else:
raise ValueError(f'Unsupported key type: {key_type}')
# === CLI ===
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description='Generate or import a cryptographic key structure for secure storage')
parser.add_argument(
'-k',
'--key-type',
type=str,
choices=[e.name.lower() for e in KeyType],
required=True,
help='key type to be processed',
)
parser.add_argument(
'-o',
'--output',
required=True,
help='output binary file name',
)
parser.add_argument(
'-i',
'--input',
help='input key file (.pem for ecdsa, .bin for aes)',
)
parser.add_argument(
'--write-once',
action='store_true',
help='make key persistent - cannot be modified or deleted once written',
)
return parser.parse_args()
def main() -> None:
args: Any = parse_args()
key_type = KeyType[args.key_type.upper()]
flags = Flags.NONE
if args.write_once:
flags |= Flags.WRITE_ONCE
print(f'[+] Generating key of type: {key_type.name} (value: {key_type.value})')
if args.input:
print(f'[+] Using user-provided key file: {args.input}')
if args.write_once:
print('[+] WRITE_ONCE flag is set')
key_data = generate_key_data(key_type, flags, args.input)
with open(args.output, 'wb') as f:
f.write(key_data)
print(f'[✓] Key written to {args.output}')
if __name__ == '__main__':
main()

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -41,26 +41,23 @@ static esp_err_t gen_ecdsa_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair)
if (keypair == NULL) {
return ESP_ERR_INVALID_ARG;
}
memset(keypair, 0x00, sizeof(esp_att_ecdsa_keypair_t));
uint16_t slot_id = ESP_ATT_TK_KEY_ID;
esp_tee_sec_storage_pubkey_t pubkey = {0};
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = (const char *)(ESP_ATT_TK_KEY_ID),
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1,
};
esp_err_t err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
esp_err_t err = esp_tee_sec_storage_gen_key(&key_cfg);
if (err == ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "Using pre-existing key...");
} else if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate ECDSA keypair (%d)", err);
return err;
}
if (esp_tee_sec_storage_is_slot_empty(slot_id)) {
err = esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate ECDSA keypair (%d)", err);
return err;
}
}
err = esp_tee_sec_storage_get_pubkey(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(&key_cfg, &pubkey);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch ECDSA pubkey (%d)", err);
return err;
@@ -83,8 +80,13 @@ static esp_err_t get_ecdsa_sign_secp256r1(const esp_att_ecdsa_keypair_t *keypair
return ESP_ERR_INVALID_SIZE;
}
esp_tee_sec_storage_sign_t sign = {};
esp_err_t err = esp_tee_sec_storage_get_signature(ESP_ATT_TK_KEY_ID, ESP_SEC_STG_KEY_ECDSA_SECP256R1, (uint8_t *)digest, len, &sign);
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = (const char *)(ESP_ATT_TK_KEY_ID),
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1,
};
esp_tee_sec_storage_ecdsa_sign_t sign = {};
esp_err_t err = esp_tee_sec_storage_ecdsa_sign(&key_cfg, (uint8_t *)digest, len, &sign);
if (err != ESP_OK) {
return err;
}

View File

@@ -126,7 +126,7 @@ esp_err_t esp_att_utils_header_to_json(const esp_att_token_hdr_t *tk_hdr, char *
json_gen_obj_set_string(&json_gen, "encr_alg", NULL);
json_gen_obj_set_string(&json_gen, "sign_alg", ESP_ATT_TK_SIGN_ALG);
json_gen_obj_set_int(&json_gen, "key_id", ESP_ATT_TK_KEY_ID);
json_gen_obj_set_string(&json_gen, "key_id", ESP_ATT_TK_KEY_ID);
// End the top-level JSON object
json_gen_end_object(&json_gen);

View File

@@ -42,9 +42,9 @@ extern "C" {
#define ESP_ATT_TK_MIN_SIZE (ESP_ATT_HDR_JSON_MAX_SZ + ESP_ATT_EAT_JSON_MAX_SZ + ESP_ATT_PUBKEY_JSON_MAX_SZ + ESP_ATT_SIGN_JSON_MAX_SZ)
#if ESP_TEE_BUILD && CONFIG_SECURE_TEE_ATTESTATION
#define ESP_ATT_TK_KEY_ID (CONFIG_SECURE_TEE_ATT_KEY_SLOT_ID)
#define ESP_ATT_TK_KEY_ID (CONFIG_SECURE_TEE_ATT_KEY_STR_ID)
#else
#define ESP_ATT_TK_KEY_ID (-1)
#define ESP_ATT_TK_KEY_ID ("NULL")
#endif
/**

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -166,7 +166,14 @@ esp_err_t esp_tee_flash_setup_prot_ctx(uint8_t tee_boot_part)
if (type == PART_TYPE_APP) {
needs_protection = (subtype == PART_SUBTYPE_TEE_0 || subtype == PART_SUBTYPE_TEE_1);
} else if (type == PART_TYPE_DATA) {
needs_protection = (subtype == PART_SUBTYPE_DATA_TEE_OTA || subtype == PART_SUBTYPE_DATA_TEE_SEC_STORAGE);
if (subtype == PART_SUBTYPE_DATA_TEE_OTA) {
needs_protection = true;
} else if (subtype == PART_SUBTYPE_DATA_WIFI) {
size_t label_len = strlen(ESP_TEE_SEC_STG_PART_LABEL);
if (memcmp(partition_entry->partition.label, ESP_TEE_SEC_STG_PART_LABEL, label_len) == 0) {
needs_protection = true;
}
}
}
if (needs_protection) {

View File

@@ -1,14 +1,13 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
set(srcs)
set(priv_requires efuse mbedtls spi_flash)
set(priv_requires esp_tee)
if(esp_tee_build)
list(APPEND srcs "tee_sec_storage.c")
list(APPEND priv_requires log tee_flash_mgr)
list(APPEND priv_requires efuse esp_partition log mbedtls nvs_flash spi_flash tee_flash_mgr)
else()
list(APPEND srcs "tee_sec_storage_wrapper.c")
set(priv_requires esp_tee)
endif()
idf_component_register(SRCS ${srcs}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -12,138 +12,142 @@ extern "C" {
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#define MIN_SEC_STG_SLOT_ID 0 /*!< Minimum secure storage slot ID */
#define MAX_SEC_STG_SLOT_ID 14 /*!< Maximum secure storage slot ID */
#include "esp_err.h"
#include "esp_bit_defs.h"
#define MAX_ECDSA_SUPPORTED_KEY_LEN 32 /*!< Maximum supported size for the ECDSA key */
#define MAX_AES_SUPPORTED_KEY_LEN 32 /*!< Maximum supported size for the AES key */
#define SEC_STORAGE_FLAG_NONE 0 /*!< No flags */
#define SEC_STORAGE_FLAG_WRITE_ONCE BIT(0) /*!< Data can only be written once */
/**
* @brief Enum to represent the type of key stored in the secure storage
*
*/
typedef enum {
ESP_SEC_STG_KEY_ECDSA_SECP256R1 = 0,
ESP_SEC_STG_KEY_AES256 = 1,
ESP_SEC_STG_KEY_AES256 = 0,
ESP_SEC_STG_KEY_ECDSA_SECP256R1 = 1,
ESP_SEC_STG_KEY_ECDSA_SECP192R1 = 2,
ESP_SEC_STG_MAX,
ESP_SEC_STG_TYPE_MAX,
} esp_tee_sec_storage_type_t;
/**
* @brief Structure holding the X and Y components
* of the ECDSA public key
* @brief Configuration structure for key generation/import
*
*/
typedef struct {
const char *id; /*!< Unique identifier for the key */
esp_tee_sec_storage_type_t type; /*!< Type of key (AES256, ECDSA_SECP256R1, etc.) */
uint32_t flags; /*!< Key flags (e.g. WRITE_ONCE) */
} esp_tee_sec_storage_key_cfg_t;
/**
* @brief Context structure for AES-GCM AEAD encryption/decryption operations
*
*/
typedef struct {
const char *key_id; /*!< Identifier of the key to use */
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 */
} esp_tee_sec_storage_aead_ctx_t;
/**
* @brief Structure holding the X and Y components of the ECDSA public key
*
*/
typedef struct {
uint8_t pub_x[MAX_ECDSA_SUPPORTED_KEY_LEN]; /*!< X component */
uint8_t pub_y[MAX_ECDSA_SUPPORTED_KEY_LEN]; /*!< Y component */
} __attribute__((__packed__)) esp_tee_sec_storage_pubkey_t;
} __attribute__((__packed__)) esp_tee_sec_storage_ecdsa_pubkey_t;
/**
* @brief Structure holding the R and S components
* of the ECDSA signature
* @brief Structure holding the R and S components of the ECDSA signature
*
*/
typedef struct {
uint8_t sign_r[MAX_ECDSA_SUPPORTED_KEY_LEN]; /*!< R component */
uint8_t sign_s[MAX_ECDSA_SUPPORTED_KEY_LEN]; /*!< S component */
} __attribute__((__packed__)) esp_tee_sec_storage_sign_t;
} __attribute__((__packed__)) esp_tee_sec_storage_ecdsa_sign_t;
#if ESP_TEE_BUILD && !(__DOXYGEN__)
/**
* @brief Initialize the TEE secure storage partition
* @brief Initialize the secure storage
*
* @note Must be invoked as part of the TEE initialization sequence.
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_init(void);
#endif
/**
* @brief Generate a unique key and store it in the given secure storage slot
* @brief Clear a key from secure storage
*
* @param slot_id secure storage slot ID
* @param key_type secure storage key type to generate
* @param key_id Key identifier string
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_gen_key(uint16_t slot_id, esp_tee_sec_storage_type_t key_type);
esp_err_t esp_tee_sec_storage_clear_key(const char *key_id);
/**
* @brief Generate a unique key and store it in the secure storage
*
* @param cfg Pointer to the key configuration
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg);
/**
* @brief Generate and return the signature for the specified message digest using
* the key pair located in the given secure storage slot.
* the key pair located in the secure storage.
*
* @param[in] slot_id secure storage slot ID
* @param[in] key_type secure storage key type
* @param[in] cfg Pointer to the key configuration
* @param[in] hash Message digest
* @param[in] hlen Digest length
* @param[out] out_sign Output context holding the signature
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, esp_tee_sec_storage_type_t key_type, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *out_sign);
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);
/**
* @brief Return the public key for the given secure storage slot
* @brief Return the public key from secure storage
*
* @param[in] slot_id secure storage slot ID
* @param[in] key_type secure storage key type
* @param[out] pubkey Output context holding the public key
* @param[in] cfg Pointer to the key configuration
* @param[out] out_pubkey Output context holding the public key
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_type_t key_type, esp_tee_sec_storage_pubkey_t *pubkey);
esp_err_t esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg_t *cfg, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey);
/**
* @brief Check whether the given slot in the secure storage is empty or not
* @brief Perform encryption using AES256-GCM with the key from secure storage
*
* @param slot_id secure storage slot ID
*
* @return bool true: slot is empty; false otherwise.
*/
bool esp_tee_sec_storage_is_slot_empty(uint16_t slot_id);
/**
* @brief Erase the given secure storage slot
*
* @param slot_id secure storage slot ID
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_clear_slot(uint16_t slot_id);
/**
* @brief Perform encryption using AES256-GCM with the key stored in the specified slot
*
* @param[in] slot_id Secure storage slot ID containing the AES-GCM key
* @param[in] input Pointer to the input data buffer
* @param[in] len Length of the input data
* @param[in] aad Pointer to the Additional Authenticated Data (AAD)
* @param[in] aad_len Length of the AAD
* @param[out] tag Pointer to the authentication tag buffer
* @param[out] tag_len Length of the authentication tag
* @param[out] output Pointer to the output data buffer
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_encrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad,
uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output);
/**
* @brief Perform decryption using AES256-GCM with the key stored in the specified slot
*
* @param[in] slot_id Secure storage slot ID containing the AES-GCM key
* @param[in] input Pointer to the input data buffer
* @param[in] len Length of the input data
* @param[in] aad Pointer to the Additional Authenticated Data (AAD)
* @param[in] aad_len Length of the AAD
* @param[in] tag Pointer to the authentication tag buffer
* @param[in] tag_len Length of the authentication tag
* @param[in] ctx Pointer to the AEAD operation context
* @param[out] tag Pointer to the authentication tag buffer
* @param[in] tag_len Length of the authentication tag
* @param[out] output Pointer to the output data buffer
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_decrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad,
uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output);
esp_err_t esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, uint8_t *tag, size_t tag_len, uint8_t *output);
/**
* @brief Perform decryption using AES256-GCM with the key from secure storage
*
* @param[in] ctx Pointer to the AEAD operation context
* @param[in] tag Pointer to the authentication tag buffer
* @param[in] tag_len Length of the authentication tag
* @param[out] output Pointer to the output data buffer
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, const uint8_t *tag, size_t tag_len, uint8_t *output);
#ifdef __cplusplus
}

View File

@@ -5,53 +5,43 @@
*/
#include <string.h>
#include "esp_log.h"
#include "esp_cpu.h"
#include "esp_fault.h"
#include "esp_flash.h"
#include "esp_efuse.h"
#include "esp_random.h"
#include "spi_flash_mmap.h"
#include "mbedtls/aes.h"
#include "mbedtls/gcm.h"
#include "mbedtls/sha256.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/error.h"
#include "esp_flash.h"
#include "esp_efuse.h"
#include "soc/efuse_reg.h"
#include "rom/efuse.h"
#if SOC_HMAC_SUPPORTED
#include "rom/hmac.h"
#endif
#include "esp_rom_sys.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "esp_random.h"
#include "esp_tee.h"
#include "esp_tee_flash.h"
#include "esp_tee_sec_storage.h"
#include "secure_service_num.h"
#include "esp_rom_sys.h"
#include "esp_log.h"
#include "spi_flash_mmap.h"
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
#define SECURE_STORAGE_SIZE 2048
#define AES256_GCM_KEY_LEN 32
#define AES256_GCM_KEY_BITS (AES256_GCM_KEY_LEN * 8)
#define AES256_KEY_LEN 32
#define AES256_KEY_BITS (AES256_KEY_LEN * 8)
#define AES256_DEFAULT_IV_LEN 16
#define AES256_GCM_IV_LEN 12
#define AES256_GCM_TAG_LEN 16
#define AES256_GCM_AAD_LEN 16
#define ECDSA_SECP256R1_KEY_LEN 32
#define ECDSA_SECP192R1_KEY_LEN 24
/* Structure to hold metadata for secure storage slots */
typedef struct {
uint16_t owner_id; /* Identifier for the owner of this slot */
uint16_t slot_id; /* Unique identifier for this storage slot */
uint8_t reserved; /* Reserved for future use */
uint8_t iv[AES256_GCM_IV_LEN]; /* Initialization vector for AES-GCM */
uint8_t tag[AES256_GCM_TAG_LEN]; /* Authentication tag for AES-GCM */
uint8_t data_type; /* Type of data stored in this slot */
uint16_t data_len; /* Length of the data stored in this slot */
} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_metadata_t;
#define EKEY_SEED 0xAEBE5A5A
#define TKEY_SEED 0xCEDEA5A5
/* Structure to hold ECDSA SECP256R1 key pair */
typedef struct {
@@ -65,38 +55,33 @@ typedef struct {
uint8_t pub_key[2 * ECDSA_SECP192R1_KEY_LEN]; /* Public key for ECDSA SECP192R1 (X and Y coordinates) */
} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_ecdsa_secp192r1_t;
/* Structure to hold AES-256 GCM key and IV */
/* Structure to hold AES-256 key and IV */
typedef struct {
uint8_t key[AES256_GCM_KEY_LEN]; /* Key for AES-256 GCM */
uint8_t iv[AES256_GCM_IV_LEN]; /* Initialization vector for AES-256 GCM */
} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_aes256_gcm_t;
uint8_t key[AES256_KEY_LEN]; /* Key for AES-256 */
uint8_t iv[AES256_DEFAULT_IV_LEN]; /* Initialization vector for AES-256 */
} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_aes256_t;
/* Union to hold different types of cryptographic keys */
typedef union {
sec_stg_ecdsa_secp256r1_t ecdsa_secp256r1; /* ECDSA SECP256R1 key pair */
sec_stg_ecdsa_secp192r1_t ecdsa_secp192r1; /* ECDSA SECP192R1 key pair */
sec_stg_aes256_gcm_t aes256_gcm; /* AES-256 GCM key and IV */
/* Structure to hold the cryptographic keys in NVS */
typedef struct {
const esp_tee_sec_storage_type_t type; /* Type of the key */
uint32_t flags; /* Flags associated with the key */
union {
sec_stg_ecdsa_secp256r1_t ecdsa_secp256r1; /* ECDSA SECP256R1 key pair */
sec_stg_ecdsa_secp192r1_t ecdsa_secp192r1; /* ECDSA SECP192R1 key pair */
sec_stg_aes256_t aes256; /* AES-256 key and IV */
};
uint32_t reserved[38]; /* Reserved space for future use */
} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_key_t;
_Static_assert(sizeof(sec_stg_metadata_t) == 36, "Incorrect sec_stg_metadata_t size");
_Static_assert(sizeof(sec_stg_key_t) == 96, "Incorrect sec_stg_key_t size");
_Static_assert(sizeof(sec_stg_key_t) == 256, "Incorrect sec_stg_key_t size");
// Need this buffer to read the flash data and then modify and write it back
// esp_rom_spiflash_write requires that we erase the region before writing to it
// TODO: IDF-7586
static uint8_t tmp_buf[SECURE_STORAGE_SIZE];
// AAD buffer
static uint8_t aad_buf[AES256_GCM_AAD_LEN];
// Partition for the secure storage partition
static esp_partition_pos_t part_pos;
static nvs_handle_t tee_nvs_hdl;
static const char *TAG = "secure_storage";
/* ---------------------------------------------- Helper APIs ------------------------------------------------- */
#if CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK > 9
#error "TEE Secure Storage: Configured eFuse block for encryption key out of range! (see CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK)"
#if CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID < 0
#error "TEE Secure Storage: Configured eFuse block (CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID) out of range!"
#endif
static int buffer_hexdump(const char *label, const void *buffer, size_t length)
@@ -138,224 +123,142 @@ static int buffer_hexdump(const char *label, const void *buffer, size_t length)
return 0;
}
static esp_err_t get_sec_stg_encr_key(uint8_t *key_buf, size_t key_buf_len)
{
// NOTE: Key should strictly be of 256-bits
if (!key_buf || key_buf_len != AES256_GCM_KEY_LEN) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = ESP_OK;
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
esp_efuse_block_t blk = (esp_efuse_block_t)(CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK);
if (blk < EFUSE_BLK_KEY0 || blk >= EFUSE_BLK_KEY_MAX) {
ESP_LOGE(TAG, "Invalid eFuse block - %d", blk);
return ESP_ERR_INVALID_ARG;
}
esp_efuse_purpose_t blk_purpose = esp_efuse_get_key_purpose(blk);
if (blk_purpose != ESP_EFUSE_KEY_PURPOSE_USER) {
ESP_LOGE(TAG, "Invalid eFuse block purpose - %d", blk_purpose);
return ESP_ERR_INVALID_STATE;
}
memset(key_buf, 0x00, key_buf_len);
err = esp_efuse_read_block(blk, key_buf, 0, AES256_GCM_KEY_BITS);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to read eFuse block (err - %d)", err);
return err;
}
#else
memset(key_buf, 0xA5, key_buf_len);
#endif
// Check if eFuse is empty
uint8_t empty_key_buf[AES256_GCM_KEY_LEN] = {0};
if (memcmp(empty_key_buf, key_buf, key_buf_len) == 0) {
ESP_LOGE(TAG, "All-zeroes key read from eFuse");
return ESP_FAIL;
}
return err;
}
static int rand_func(void *rng_state, unsigned char *output, size_t len)
{
esp_fill_random(output, len);
return 0;
}
static int secure_storage_write(uint16_t slot_id, uint8_t *data, size_t len, uint8_t type)
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
static esp_err_t compute_nvs_keys_with_hmac(ets_efuse_block_t hmac_key, nvs_sec_cfg_t *cfg)
{
uint8_t iv[AES256_GCM_IV_LEN];
uint8_t tag[AES256_GCM_TAG_LEN];
uint8_t key[AES256_GCM_KEY_LEN];
uint8_t out_data[256] = {0};
uint32_t ekey_seed[8] = {[0 ... 7] = EKEY_SEED};
uint32_t tkey_seed[8] = {[0 ... 7] = TKEY_SEED};
buffer_hexdump("Plaintext data", data, len);
memset(cfg, 0x00, sizeof(nvs_sec_cfg_t));
mbedtls_gcm_context gcm;
mbedtls_gcm_init(&gcm);
int ret = -1;
ets_hmac_enable();
ret = ets_hmac_calculate_message(hmac_key, ekey_seed, sizeof(ekey_seed), (uint8_t *)cfg->eky);
ret = ets_hmac_calculate_message(hmac_key, tkey_seed, sizeof(tkey_seed), (uint8_t *)cfg->tky);
ets_hmac_disable();
esp_err_t err = get_sec_stg_encr_key(key, sizeof(key));
if (ret != 0) {
ESP_LOGE(TAG, "Failed to calculate seed HMAC");
return ESP_FAIL;
}
ESP_FAULT_ASSERT(ret == 0);
/* NOTE: If the XTS E-key and T-key are the same, we have a hash collision */
ESP_FAULT_ASSERT(memcmp(cfg->eky, cfg->tky, NVS_KEY_SIZE) != 0);
return ESP_OK;
}
#endif
static esp_err_t read_security_cfg_hmac(nvs_sec_cfg_t *cfg)
{
if (cfg == NULL) {
return ESP_ERR_INVALID_ARG;
}
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
ets_efuse_block_t hmac_key = (ets_efuse_block_t)(ETS_EFUSE_BLOCK_KEY0 + CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID);
ets_efuse_purpose_t hmac_efuse_blk_purpose = ets_efuse_get_key_purpose(hmac_key);
if (hmac_efuse_blk_purpose != ETS_EFUSE_KEY_PURPOSE_HMAC_UP) {
ESP_LOGE(TAG, "HMAC key is not burnt in eFuse block");
return ESP_ERR_NOT_FOUND;
}
esp_err_t err = compute_nvs_keys_with_hmac(hmac_key, cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch key from eFuse!");
goto exit;
return err;
}
#else
memset(&cfg->eky, 0x33, sizeof(cfg->eky));
memset(&cfg->tky, 0xCC, sizeof(cfg->tky));
#endif
return ESP_OK;
}
static esp_err_t secure_storage_find_key(const char *key_id)
{
nvs_type_t out_type;
return nvs_find_key(tee_nvs_hdl, key_id, &out_type);
}
static esp_err_t secure_storage_write(const char *key_id, const void *data, size_t len)
{
esp_err_t err = nvs_set_blob(tee_nvs_hdl, key_id, data, len);
if (err != ESP_OK) {
return err;
}
int ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, AES256_GCM_KEY_BITS);
if (ret != 0) {
ESP_LOGE(TAG, "Error in setting key: %d", ret);
err = ESP_FAIL;
goto exit;
}
// Generate different IV every time GCM encrypt is called
esp_fill_random(iv, AES256_GCM_IV_LEN);
ret = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, len, iv, AES256_GCM_IV_LEN,
aad_buf, AES256_GCM_AAD_LEN, data, out_data, AES256_GCM_TAG_LEN, tag);
if (ret != 0) {
ESP_LOGE(TAG, "Error in encrypting data: %d", ret);
err = ESP_FAIL;
goto exit;
}
buffer_hexdump("Encrypted data", out_data, len);
buffer_hexdump("TAG data", tag, sizeof(tag));
// Currently keeping the owner ID as 0
sec_stg_metadata_t metadata;
metadata.owner_id = 0;
metadata.slot_id = slot_id;
memcpy(metadata.iv, iv, AES256_GCM_IV_LEN);
memcpy(metadata.tag, tag, AES256_GCM_TAG_LEN);
metadata.data_type = type;
metadata.data_len = len;
uint32_t slot_offset = (sizeof(sec_stg_metadata_t) + sizeof(sec_stg_key_t)) * slot_id;
/* ROM flash APIs require the region to be erased before writing to it.
* For that, we read the entire sector, make changes in read buffer, and then write
* the entire data back in flash.
*
* This opens up a small window when the sector has been erased but the device resets before writing the
* data back in flash. This can lead to loss of data.
*
* TODO: IDF-7586
*/
ret = esp_tee_flash_read(part_pos.offset, (uint32_t *)tmp_buf, SECURE_STORAGE_SIZE, false);
if (ret != 0) {
ESP_LOGE(TAG, "Error reading flash contents: %d", ret);
err = ESP_ERR_FLASH_OP_FAIL;
goto exit;
}
memcpy(&tmp_buf[slot_offset], &metadata, sizeof(sec_stg_metadata_t));
memcpy(&tmp_buf[slot_offset + sizeof(sec_stg_metadata_t)], out_data, len);
ret = esp_tee_flash_erase_range(part_pos.offset, ALIGN_UP(SECURE_STORAGE_SIZE, FLASH_SECTOR_SIZE));
ret |= esp_tee_flash_write(part_pos.offset, (uint32_t *)tmp_buf, SECURE_STORAGE_SIZE, false);
if (ret != 0) {
ESP_LOGE(TAG, "Error writing encrypted data: %d", ret);
err = ESP_ERR_FLASH_OP_FAIL;
goto exit;
}
err = ESP_OK;
exit:
mbedtls_gcm_free(&gcm);
err = nvs_commit(tee_nvs_hdl);
return err;
}
static esp_err_t secure_storage_read(uint16_t slot_id, uint8_t *data, size_t len, uint8_t type)
static esp_err_t secure_storage_read(const char *key_id, void *data, size_t *len)
{
esp_err_t err;
sec_stg_metadata_t metadata;
uint32_t slot_offset = (sizeof(sec_stg_metadata_t) + sizeof(sec_stg_key_t)) * slot_id;
uint8_t key[AES256_GCM_KEY_BITS / 8];
uint8_t flash_data[256] = {0};
int ret = esp_tee_flash_read(part_pos.offset + slot_offset, (uint32_t *)&metadata, sizeof(sec_stg_metadata_t), false);
if (ret != 0) {
ESP_LOGE(TAG, "Error reading metadata: %d", ret);
err = ESP_ERR_FLASH_OP_FAIL;
goto exit;
}
if (metadata.data_type != type || metadata.data_len != len) {
ESP_LOGE(TAG, "Data type/length mismatch");
err = ESP_ERR_NOT_FOUND;
goto exit;
}
ret = esp_tee_flash_read(part_pos.offset + slot_offset + sizeof(sec_stg_metadata_t), (uint32_t *)flash_data, metadata.data_len, false);
if (ret != 0) {
ESP_LOGE(TAG, "Error reading data: %d", ret);
err = ESP_ERR_FLASH_OP_FAIL;
goto exit;
}
buffer_hexdump("Encrypted data", flash_data, len);
buffer_hexdump("TAG data", metadata.tag, AES256_GCM_TAG_LEN);
mbedtls_gcm_context gcm;
mbedtls_gcm_init(&gcm);
err = get_sec_stg_encr_key(key, sizeof(key));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch key from eFuse!");
goto cleanup;
}
ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, AES256_GCM_KEY_BITS);
if (ret != 0) {
err = ESP_FAIL;
goto cleanup;
}
ret = mbedtls_gcm_auth_decrypt(&gcm, metadata.data_len, metadata.iv, AES256_GCM_IV_LEN,
aad_buf, AES256_GCM_AAD_LEN, metadata.tag, AES256_GCM_TAG_LEN, flash_data, data);
if (ret != 0) {
ESP_LOGE(TAG, "Error in decrypting data: %d", ret);
err = ESP_FAIL;
goto cleanup;
}
buffer_hexdump("Decrypted data", data, len);
err = ESP_OK;
cleanup:
mbedtls_gcm_free(&gcm);
exit:
return err;
return nvs_get_blob(tee_nvs_hdl, key_id, data, len);
}
/* ---------------------------------------------- Interface APIs ------------------------------------------------- */
esp_err_t esp_tee_sec_storage_init(void)
{
ESP_LOGI(TAG, "Initializing secure storage...");
esp_partition_info_t part_info = {};
esp_err_t err = esp_tee_flash_find_partition(PART_TYPE_DATA, PART_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL, &part_info);
nvs_sec_cfg_t cfg = {};
esp_err_t err = read_security_cfg_hmac(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "No secure storage partition found (0x%08x)", err);
return err;
} else {
#if CONFIG_SECURE_TEE_SEC_STG_MODE_DEVELOPMENT
ESP_LOGW(TAG, "TEE Secure Storage enabled in insecure DEVELOPMENT mode");
#endif
// Take backup of the partition for future usage
part_pos = part_info.pos;
}
err = nvs_flash_secure_init_partition(ESP_TEE_SEC_STG_PART_LABEL, &cfg);
if (err != ESP_OK) {
return err;
}
err = nvs_open_from_partition(ESP_TEE_SEC_STG_PART_LABEL, ESP_TEE_SEC_STG_NVS_NAMESPACE, NVS_READWRITE, &tee_nvs_hdl);
if (err != ESP_OK) {
return err;
}
#if CONFIG_SECURE_TEE_SEC_STG_MODE_DEVELOPMENT
ESP_LOGW(TAG, "TEE Secure Storage enabled in insecure DEVELOPMENT mode");
#endif
return ESP_OK;
}
esp_err_t esp_tee_sec_storage_clear_key(const char *key_id)
{
if (secure_storage_find_key(key_id) != ESP_OK) {
return ESP_ERR_NOT_FOUND;
}
sec_stg_key_t keyctx;
size_t keyctx_len = sizeof(keyctx);
esp_err_t err = secure_storage_read(key_id, (void *)&keyctx, &keyctx_len);
if (err != ESP_OK) {
return err;
}
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 = nvs_erase_key(tee_nvs_hdl, key_id);
if (err != ESP_OK) {
return err;
}
err = nvs_commit(tee_nvs_hdl);
return err;
}
static int generate_ecdsa_key(sec_stg_key_t *keyctx, esp_tee_sec_storage_type_t key_type)
{
if (keyctx == NULL) {
@@ -370,19 +273,18 @@ static int generate_ecdsa_key(sec_stg_key_t *keyctx, esp_tee_sec_storage_type_t
curve_id = MBEDTLS_ECP_DP_SECP192R1;
key_len = ECDSA_SECP192R1_KEY_LEN;
#else
ESP_LOGE(TAG, "Unsupported key type!");
ESP_LOGE(TAG, "Unsupported key-type!");
return -1;
#endif
}
ESP_LOGI(TAG, "Generating ECDSA key for curve %d...", curve_id);
ESP_LOGD(TAG, "Generating ECDSA key for curve %d...", curve_id);
mbedtls_ecdsa_context ctxECDSA;
mbedtls_ecdsa_init(&ctxECDSA);
int ret = mbedtls_ecdsa_genkey(&ctxECDSA, curve_id, rand_func, NULL);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to generate ECDSA key");
goto exit;
}
@@ -425,48 +327,49 @@ exit:
return ret;
}
static int generate_aes256_gcm_key(sec_stg_key_t *keyctx)
static int generate_aes256_key(sec_stg_key_t *keyctx)
{
if (keyctx == NULL) {
return -1;
}
ESP_LOGI(TAG, "Generating AES-256-GCM key...");
ESP_LOGD(TAG, "Generating AES-256 key...");
esp_fill_random(&keyctx->aes256_gcm.key, AES256_GCM_KEY_LEN);
esp_fill_random(&keyctx->aes256_gcm.iv, AES256_GCM_IV_LEN);
esp_fill_random(&keyctx->aes256.key, AES256_KEY_LEN);
esp_fill_random(&keyctx->aes256.iv, AES256_DEFAULT_IV_LEN);
return 0;
}
esp_err_t esp_tee_sec_storage_gen_key(uint16_t slot_id, esp_tee_sec_storage_type_t key_type)
esp_err_t esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg)
{
if (slot_id > MAX_SEC_STG_SLOT_ID) {
ESP_LOGE(TAG, "Invalid slot ID");
if (cfg == NULL || cfg->id == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!esp_tee_sec_storage_is_slot_empty(slot_id)) {
ESP_LOGE(TAG, "Slot already occupied - clear before reuse");
if (secure_storage_find_key(cfg->id) == ESP_OK) {
ESP_LOGE(TAG, "Key ID already exists");
return ESP_ERR_INVALID_STATE;
}
int ret = -1;
sec_stg_key_t keyctx;
sec_stg_key_t keyctx = {
.type = cfg->type,
.flags = cfg->flags,
};
switch (key_type) {
switch (cfg->type) {
case ESP_SEC_STG_KEY_ECDSA_SECP256R1:
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
case ESP_SEC_STG_KEY_ECDSA_SECP192R1:
#endif
if (generate_ecdsa_key(&keyctx, key_type) != 0) {
ESP_LOGE(TAG, "Failed to generate ECDSA keypair (%d)", ret);
if (generate_ecdsa_key(&keyctx, cfg->type) != 0) {
ESP_LOGE(TAG, "Failed to generate ECDSA keypair");
return ESP_FAIL;
}
break;
case ESP_SEC_STG_KEY_AES256:
if (generate_aes256_gcm_key(&keyctx) != 0) {
ESP_LOGE(TAG, "Failed to generate AES key (%d)", ret);
if (generate_aes256_key(&keyctx) != 0) {
ESP_LOGE(TAG, "Failed to generate AES key");
return ESP_FAIL;
}
break;
@@ -475,33 +378,41 @@ esp_err_t esp_tee_sec_storage_gen_key(uint16_t slot_id, esp_tee_sec_storage_type
return ESP_ERR_NOT_SUPPORTED;
}
return secure_storage_write(slot_id, (uint8_t *)&keyctx, sizeof(keyctx), key_type);
return secure_storage_write(cfg->id, (void *)&keyctx, sizeof(keyctx));
}
esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, esp_tee_sec_storage_type_t key_type, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *out_sign)
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)
{
if (slot_id > MAX_SEC_STG_SLOT_ID || hash == NULL || out_sign == NULL) {
if (cfg == NULL || cfg->id == NULL || hash == NULL || out_sign == NULL || hlen == 0) {
return ESP_ERR_INVALID_ARG;
}
if (hlen == 0) {
return ESP_ERR_INVALID_SIZE;
}
#if !CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
if (key_type == ESP_SEC_STG_KEY_ECDSA_SECP192R1) {
ESP_LOGE(TAG, "Unsupported key type!");
if (cfg->type == ESP_SEC_STG_KEY_ECDSA_SECP192R1) {
ESP_LOGE(TAG, "Unsupported key-type!");
return ESP_ERR_NOT_SUPPORTED;
}
#endif
sec_stg_key_t keyctx;
esp_err_t err = secure_storage_read(slot_id, (uint8_t *)&keyctx, sizeof(sec_stg_key_t), key_type);
esp_err_t err = secure_storage_find_key(cfg->id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch key from slot");
ESP_LOGE(TAG, "Key ID not found");
return err;
}
sec_stg_key_t keyctx;
size_t keyctx_len = sizeof(keyctx);
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;
}
if (keyctx.type != cfg->type) {
ESP_LOGE(TAG, "Key type mismatch");
return ESP_ERR_INVALID_STATE;
}
mbedtls_mpi r, s;
mbedtls_ecp_keypair priv_key;
mbedtls_ecdsa_context sign_ctx;
@@ -513,18 +424,15 @@ esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, esp_tee_sec_storag
size_t key_len = 0;
int ret = -1;
if (key_type == ESP_SEC_STG_KEY_ECDSA_SECP256R1) {
if (cfg->type == ESP_SEC_STG_KEY_ECDSA_SECP256R1) {
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &priv_key, keyctx.ecdsa_secp256r1.priv_key, sizeof(keyctx.ecdsa_secp256r1.priv_key));
key_len = ECDSA_SECP256R1_KEY_LEN;
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
} else if (key_type == ESP_SEC_STG_KEY_ECDSA_SECP192R1) {
} else if (cfg->type == ESP_SEC_STG_KEY_ECDSA_SECP192R1) {
ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP192R1, &priv_key, keyctx.ecdsa_secp192r1.priv_key, sizeof(keyctx.ecdsa_secp192r1.priv_key));
key_len = ECDSA_SECP192R1_KEY_LEN;
#endif
} else {
ESP_LOGE(TAG, "Unsupported key type for signature generation");
err = ESP_ERR_NOT_SUPPORTED;
goto exit;
}
if (ret != 0) {
@@ -538,7 +446,7 @@ esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, esp_tee_sec_storag
goto exit;
}
ESP_LOGI(TAG, "Generating ECDSA signature...");
ESP_LOGD(TAG, "Generating ECDSA signature...");
ret = mbedtls_ecdsa_sign(&sign_ctx.MBEDTLS_PRIVATE(grp), &r, &s, &sign_ctx.MBEDTLS_PRIVATE(d), hash, hlen,
rand_func, NULL);
@@ -548,7 +456,7 @@ esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, esp_tee_sec_storag
goto exit;
}
memset(out_sign, 0x00, sizeof(esp_tee_sec_storage_sign_t));
memset(out_sign, 0x00, sizeof(esp_tee_sec_storage_ecdsa_sign_t));
ret = mbedtls_mpi_write_binary(&r, out_sign->sign_r, key_len);
if (ret != 0) {
@@ -572,142 +480,93 @@ exit:
return err;
}
esp_err_t esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_type_t key_type, esp_tee_sec_storage_pubkey_t *pubkey)
esp_err_t esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg_t *cfg, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey)
{
if (slot_id > MAX_SEC_STG_SLOT_ID || pubkey == NULL) {
if (cfg == NULL || cfg->id == NULL || out_pubkey == NULL) {
return ESP_ERR_INVALID_ARG;
}
sec_stg_key_t keyctx;
size_t key_len;
uint8_t *pub_key_src;
if (key_type == ESP_SEC_STG_KEY_ECDSA_SECP256R1) {
key_len = ECDSA_SECP256R1_KEY_LEN;
pub_key_src = keyctx.ecdsa_secp256r1.pub_key;
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
} else if (key_type == ESP_SEC_STG_KEY_ECDSA_SECP192R1) {
key_len = ECDSA_SECP192R1_KEY_LEN;
pub_key_src = keyctx.ecdsa_secp192r1.pub_key;
#endif
} else {
ESP_LOGE(TAG, "Unsupported key type");
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = secure_storage_read(slot_id, (uint8_t *)&keyctx, sizeof(sec_stg_key_t), key_type);
esp_err_t err = secure_storage_find_key(cfg->id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch key from slot");
ESP_LOGE(TAG, "Key ID not found");
return err;
}
// Copy public key components in one shot
memcpy(pubkey->pub_x, pub_key_src, key_len);
memcpy(pubkey->pub_y, pub_key_src + key_len, key_len);
sec_stg_key_t keyctx;
size_t keyctx_len = sizeof(keyctx);
uint8_t *pub_key_src = NULL;
size_t pub_key_len = 0;
switch (cfg->type) {
case ESP_SEC_STG_KEY_ECDSA_SECP256R1:
pub_key_src = keyctx.ecdsa_secp256r1.pub_key;
pub_key_len = ECDSA_SECP256R1_KEY_LEN;
break;
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
case ESP_SEC_STG_KEY_ECDSA_SECP192R1:
pub_key_src = keyctx.ecdsa_secp192r1.pub_key;
pub_key_len = ECDSA_SECP192R1_KEY_LEN;
break;
#endif
default:
ESP_LOGE(TAG, "Unsupported key-type");
return ESP_ERR_INVALID_ARG;
}
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;
}
if (keyctx.type != cfg->type) {
ESP_LOGE(TAG, "Key type mismatch");
return ESP_ERR_INVALID_STATE;
}
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);
return ESP_OK;
}
bool esp_tee_sec_storage_is_slot_empty(uint16_t slot_id)
{
if (slot_id > MAX_SEC_STG_SLOT_ID) {
ESP_LOGE(TAG, "Invalid slot ID");
return false;
}
sec_stg_metadata_t metadata, blank_metadata;
memset(&blank_metadata, 0xFF, sizeof(sec_stg_metadata_t));
uint32_t slot_offset = (sizeof(sec_stg_metadata_t) + sizeof(sec_stg_key_t)) * slot_id;
bool ret = false;
int err = esp_tee_flash_read(part_pos.offset + slot_offset, (uint32_t *)&metadata, sizeof(sec_stg_metadata_t), false);
if (err != 0) {
goto exit;
}
if (memcmp(&metadata, &blank_metadata, sizeof(sec_stg_metadata_t)) && metadata.slot_id == slot_id) {
goto exit;
}
ret = true;
exit:
return ret;
}
esp_err_t esp_tee_sec_storage_clear_slot(uint16_t slot_id)
{
if (slot_id > MAX_SEC_STG_SLOT_ID) {
ESP_LOGE(TAG, "Invalid slot ID");
return ESP_ERR_INVALID_ARG;
}
if (esp_tee_sec_storage_is_slot_empty(slot_id)) {
return ESP_OK;
}
sec_stg_key_t blank_data;
memset(&blank_data, 0xFF, sizeof(blank_data));
sec_stg_metadata_t blank_metadata;
memset(&blank_metadata, 0xFF, sizeof(sec_stg_metadata_t));
uint32_t slot_offset = (sizeof(sec_stg_metadata_t) + sizeof(sec_stg_key_t)) * slot_id;
esp_err_t err;
int ret = esp_tee_flash_read(part_pos.offset, (uint32_t *)tmp_buf, SECURE_STORAGE_SIZE, false);
if (ret != 0) {
ESP_LOGE(TAG, "Error reading flash contents: %d", ret);
err = ESP_ERR_FLASH_OP_FAIL;
goto exit;
}
memcpy(&tmp_buf[slot_offset], &blank_metadata, sizeof(sec_stg_metadata_t));
memcpy(&tmp_buf[slot_offset + sizeof(sec_stg_metadata_t)], &blank_data, sizeof(sec_stg_key_t));
ret = esp_tee_flash_erase_range(part_pos.offset, ALIGN_UP(SECURE_STORAGE_SIZE, FLASH_SECTOR_SIZE));
ret |= esp_tee_flash_write(part_pos.offset, (uint32_t *)tmp_buf, SECURE_STORAGE_SIZE, false);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error clearing slot: %d", ret);
err = ESP_ERR_FLASH_OP_FAIL;
goto exit;
}
err = ESP_OK;
exit:
return err;
}
static esp_err_t tee_sec_storage_crypt_common(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad,
uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output,
static esp_err_t tee_sec_storage_crypt_common(const char *key_id, const uint8_t *input, size_t len, const uint8_t *aad,
size_t aad_len, uint8_t *tag, size_t tag_len, uint8_t *output,
bool is_encrypt)
{
if (slot_id > MAX_SEC_STG_SLOT_ID) {
ESP_LOGE(TAG, "Invalid slot ID");
return ESP_ERR_INVALID_ARG;
}
if (input == NULL || output == NULL || tag == NULL) {
ESP_LOGE(TAG, "Invalid input/output/tag buffer");
if (key_id == NULL || input == NULL || output == NULL || tag == NULL) {
ESP_LOGE(TAG, "Invalid arguments");
return ESP_ERR_INVALID_ARG;
}
if (len == 0 || tag_len == 0) {
ESP_LOGE(TAG, "Invalid length/tag length");
ESP_LOGE(TAG, "Invalid input/tag length");
return ESP_ERR_INVALID_SIZE;
}
sec_stg_key_t keyctx;
esp_err_t err = secure_storage_read(slot_id, (uint8_t *)&keyctx, sizeof(keyctx), ESP_SEC_STG_KEY_AES256);
esp_err_t err = secure_storage_find_key(key_id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch key from slot");
ESP_LOGE(TAG, "Key ID not found");
return err;
}
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;
}
if (keyctx.type != ESP_SEC_STG_KEY_AES256) {
ESP_LOGE(TAG, "Key type mismatch");
return ESP_ERR_INVALID_STATE;
}
mbedtls_gcm_context gcm;
mbedtls_gcm_init(&gcm);
int ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, keyctx.aes256_gcm.key, AES256_GCM_KEY_BITS);
int ret = mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, keyctx.aes256.key, AES256_KEY_BITS);
if (ret != 0) {
ESP_LOGE(TAG, "Error in setting key: %d", ret);
err = ESP_FAIL;
@@ -715,7 +574,7 @@ static esp_err_t tee_sec_storage_crypt_common(uint16_t slot_id, uint8_t *input,
}
if (is_encrypt) {
ret = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, len, keyctx.aes256_gcm.iv, AES256_GCM_IV_LEN,
ret = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, len, keyctx.aes256.iv, AES256_GCM_IV_LEN,
aad, aad_len, input, output, tag_len, tag);
if (ret != 0) {
ESP_LOGE(TAG, "Error in encrypting data: %d", ret);
@@ -723,7 +582,7 @@ static esp_err_t tee_sec_storage_crypt_common(uint16_t slot_id, uint8_t *input,
goto exit;
}
} else {
ret = mbedtls_gcm_auth_decrypt(&gcm, len, keyctx.aes256_gcm.iv, AES256_GCM_IV_LEN,
ret = mbedtls_gcm_auth_decrypt(&gcm, len, keyctx.aes256.iv, AES256_GCM_IV_LEN,
aad, aad_len, tag, tag_len, input, output);
if (ret != 0) {
ESP_LOGE(TAG, "Error in decrypting data: %d", ret);
@@ -738,14 +597,12 @@ exit:
return err;
}
esp_err_t esp_tee_sec_storage_encrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad,
uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output)
esp_err_t esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, uint8_t *tag, size_t tag_len, uint8_t *output)
{
return tee_sec_storage_crypt_common(slot_id, input, len, aad, aad_len, tag, tag_len, output, true);
return tee_sec_storage_crypt_common(ctx->key_id, ctx->input, ctx->input_len, ctx->aad, ctx->aad_len, tag, tag_len, output, true);
}
esp_err_t esp_tee_sec_storage_decrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad,
uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output)
esp_err_t esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, const uint8_t *tag, size_t tag_len, uint8_t *output)
{
return tee_sec_storage_crypt_common(slot_id, input, len, aad, aad_len, tag, tag_len, output, false);
return tee_sec_storage_crypt_common(ctx->key_id, ctx->input, ctx->input_len, ctx->aad, ctx->aad_len, (uint8_t *)tag, tag_len, output, false);
}

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -8,46 +8,32 @@
#include "esp_err.h"
#include "esp_tee_sec_storage.h"
esp_err_t esp_tee_sec_storage_init(void)
esp_err_t esp_tee_sec_storage_clear_key(const char *key_id)
{
return esp_tee_service_call_with_noniram_intr_disabled(1, SS_ESP_TEE_SEC_STORAGE_INIT);
return esp_tee_service_call_with_noniram_intr_disabled(2, SS_ESP_TEE_SEC_STORAGE_CLEAR_KEY, key_id);
}
esp_err_t esp_tee_sec_storage_gen_key(uint16_t slot_id, esp_tee_sec_storage_type_t key_type)
esp_err_t esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg)
{
return esp_tee_service_call_with_noniram_intr_disabled(3, SS_ESP_TEE_SEC_STORAGE_GEN_KEY, slot_id, key_type);
return esp_tee_service_call_with_noniram_intr_disabled(2, SS_ESP_TEE_SEC_STORAGE_GEN_KEY, cfg);
}
esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, esp_tee_sec_storage_type_t key_type, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *sign)
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)
{
return esp_tee_service_call_with_noniram_intr_disabled(6, SS_ESP_TEE_SEC_STORAGE_GET_SIGNATURE, slot_id, key_type, hash, hlen, sign);
return esp_tee_service_call_with_noniram_intr_disabled(5, SS_ESP_TEE_SEC_STORAGE_ECDSA_SIGN, cfg, hash, hlen, out_sign);
}
esp_err_t esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_type_t key_type, esp_tee_sec_storage_pubkey_t *pubkey)
esp_err_t esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg_t *cfg, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey)
{
return esp_tee_service_call_with_noniram_intr_disabled(4, SS_ESP_TEE_SEC_STORAGE_GET_PUBKEY, slot_id, key_type, pubkey);
return esp_tee_service_call_with_noniram_intr_disabled(3, SS_ESP_TEE_SEC_STORAGE_ECDSA_GET_PUBKEY, cfg, out_pubkey);
}
bool esp_tee_sec_storage_is_slot_empty(uint16_t slot_id)
esp_err_t esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, uint8_t *tag, size_t tag_len, uint8_t *output)
{
return esp_tee_service_call_with_noniram_intr_disabled(2, SS_ESP_TEE_SEC_STORAGE_IS_SLOT_EMPTY, slot_id);
return esp_tee_service_call_with_noniram_intr_disabled(5, SS_ESP_TEE_SEC_STORAGE_AEAD_ENCRYPT, ctx, tag, tag_len, output);
}
esp_err_t esp_tee_sec_storage_clear_slot(uint16_t slot_id)
esp_err_t esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, const uint8_t *tag, size_t tag_len, uint8_t *output)
{
return esp_tee_service_call_with_noniram_intr_disabled(2, SS_ESP_TEE_SEC_STORAGE_CLEAR_SLOT, slot_id);
}
esp_err_t esp_tee_sec_storage_encrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad,
uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output)
{
return esp_tee_service_call_with_noniram_intr_disabled(9, SS_ESP_TEE_SEC_STORAGE_ENCRYPT, slot_id,
input, len, aad, aad_len, tag, tag_len, output);
}
esp_err_t esp_tee_sec_storage_decrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad,
uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output)
{
return esp_tee_service_call_with_noniram_intr_disabled(9, SS_ESP_TEE_SEC_STORAGE_DECRYPT, slot_id,
input, len, aad, aad_len, tag, tag_len, output);
return esp_tee_service_call_with_noniram_intr_disabled(5, SS_ESP_TEE_SEC_STORAGE_AEAD_DECRYPT, ctx, tag, tag_len, output);
}

View File

@@ -46,6 +46,9 @@ list(APPEND include "${heap_dir}/tlsf")
# esp_app_desc_t configuration structure for TEE
list(APPEND srcs "common/esp_app_desc_tee.c")
# Newlib syscalls stub implementation
list(APPEND srcs "common/syscall_stubs.c")
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include})
@@ -57,6 +60,9 @@ include(${CMAKE_CURRENT_LIST_DIR}/ld/esp_tee_ld.cmake)
# esp_app_desc_t configuration structure for TEE: Linking symbol and trimming project version and name
target_link_libraries(${COMPONENT_LIB} PRIVATE "-u esp_app_desc_tee_include_impl")
# Newlib syscalls stub implementation: Linking symbol
target_link_libraries(${COMPONENT_LIB} PRIVATE "-u esp_tee_include_syscalls_impl")
# cut PROJECT_VER and PROJECT_NAME to required 32 characters.
idf_build_get_property(project_ver PROJECT_VER)
idf_build_get_property(project_name PROJECT_NAME)

View File

@@ -0,0 +1,136 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include "esp_random.h"
// NOTE: Remove compile-time warnings for the below newlib-provided functions
struct _reent *__getreent(void)
{
return _GLOBAL_REENT;
}
int _fstat_r(struct _reent *r, int fd, struct stat *st)
{
errno = ENOSYS;
return -1;
}
int _close_r(struct _reent *r, int fd)
{
errno = ENOSYS;
return -1;
}
off_t _lseek_r(struct _reent *r, int fd, off_t offset, int whence)
{
errno = ENOSYS;
return -1;
}
ssize_t _read_r(struct _reent *r, int fd, void *ptr, size_t len)
{
errno = ENOSYS;
return -1;
}
ssize_t _write_r(struct _reent *r, int fd, const void *ptr, size_t len)
{
errno = ENOSYS;
return -1;
}
int _getpid_r(struct _reent *r)
{
return 1;
}
int _kill_r(struct _reent *r, int pid, int sig)
{
errno = ENOSYS;
return -1;
}
int _getentropy_r(struct _reent *r, void *buffer, size_t length)
{
esp_fill_random(buffer, length);
return 0;
}
void *pthread_getspecific(pthread_key_t key)
{
return NULL;
}
int pthread_setspecific(pthread_key_t key, const void *value)
{
return 0;
}
int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))
{
errno = ENOSYS;
return -1;
}
int pthread_key_delete(pthread_key_t key)
{
errno = ENOSYS;
return -1;
}
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
{
errno = ENOSYS;
return -1;
}
int pthread_mutex_destroy(pthread_mutex_t *mutex)
{
errno = ENOSYS;
return -1;
}
int pthread_mutex_lock(pthread_mutex_t *mutex)
{
errno = ENOSYS;
return -1;
}
int pthread_mutex_unlock(pthread_mutex_t *mutex)
{
errno = ENOSYS;
return -1;
}
void *__cxa_get_globals(void)
{
return NULL;
}
void *__cxa_get_globals_fast(void)
{
return NULL;
}
int __cxa_thread_atexit(void (*func)(void *), void *arg, void *dso)
{
return 0;
}
void esp_tee_include_syscalls_impl(void)
{
}

View File

@@ -205,6 +205,10 @@ esp_err_t _ss_esp_hmac_calculate(hmac_key_id_t key_id, const void *message, size
bool valid_addr = ((esp_tee_ptr_in_ree((void *)message) && esp_tee_ptr_in_ree((void *)hmac)) &&
esp_tee_ptr_in_ree((void *)((char *)message + message_len)));
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID);
#endif
if (!valid_addr) {
return ESP_ERR_INVALID_ARG;
}
@@ -217,6 +221,10 @@ esp_err_t _ss_esp_hmac_jtag_enable(hmac_key_id_t key_id, const uint8_t *token)
{
bool valid_addr = (esp_tee_ptr_in_ree((void *)token));
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID);
#endif
if (!valid_addr) {
return ESP_ERR_INVALID_ARG;
}
@@ -239,6 +247,10 @@ esp_err_t _ss_esp_ds_sign(const void *message,
esp_tee_ptr_in_ree((void *)data) &&
esp_tee_ptr_in_ree((void *)signature));
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID);
#endif
if (!valid_addr) {
return ESP_ERR_INVALID_ARG;
}
@@ -256,6 +268,10 @@ esp_err_t _ss_esp_ds_start_sign(const void *message,
esp_tee_ptr_in_ree((void *)data) &&
esp_tee_ptr_in_ree((void *)esp_ds_ctx));
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
valid_addr &= (key_id != (hmac_key_id_t)CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID);
#endif
if (!valid_addr) {
return ESP_ERR_INVALID_ARG;
}
@@ -356,22 +372,12 @@ int _ss_esp_tee_ota_end(void)
/* ---------------------------------------------- Secure Storage ------------------------------------------------- */
esp_err_t _ss_esp_tee_sec_storage_init(void)
esp_err_t _ss_esp_tee_sec_storage_clear_key(const char *key_id)
{
return esp_tee_sec_storage_init();
return esp_tee_sec_storage_clear_key(key_id);
}
esp_err_t _ss_esp_tee_sec_storage_gen_key(uint16_t slot_id, uint8_t key_type)
esp_err_t _ss_esp_tee_sec_storage_gen_key(const esp_tee_sec_storage_key_cfg_t *cfg)
{
return esp_tee_sec_storage_gen_key(slot_id, key_type);
}
bool _ss_esp_tee_sec_storage_is_slot_empty(uint16_t slot_id)
{
return esp_tee_sec_storage_is_slot_empty(slot_id);
}
esp_err_t _ss_esp_tee_sec_storage_clear_slot(uint16_t slot_id)
{
return esp_tee_sec_storage_clear_slot(slot_id);
return esp_tee_sec_storage_gen_key(cfg);
}

View File

@@ -85,46 +85,45 @@ void _ss_wdt_hal_deinit(wdt_hal_context_t *hal)
/* ---------------------------------------------- Secure Storage ------------------------------------------------- */
esp_err_t _ss_esp_tee_sec_storage_get_signature(uint16_t slot_id, esp_tee_sec_storage_type_t key_type, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *out_sign)
esp_err_t _ss_esp_tee_sec_storage_ecdsa_sign(const esp_tee_sec_storage_key_cfg_t *cfg, const uint8_t *hash, size_t hlen, esp_tee_sec_storage_ecdsa_sign_t *out_sign)
{
bool valid_addr = ((esp_tee_ptr_in_ree((void *)hash) && esp_tee_ptr_in_ree((void *)out_sign)) &
bool valid_addr = ((esp_tee_ptr_in_ree((void *)hash) && esp_tee_ptr_in_ree((void *)out_sign)) &&
(esp_tee_ptr_in_ree((void *)(hash + hlen)) &&
esp_tee_ptr_in_ree((void *)((char *)out_sign + sizeof(esp_tee_sec_storage_sign_t)))));
esp_tee_ptr_in_ree((void *)((char *)out_sign + sizeof(esp_tee_sec_storage_ecdsa_sign_t)))));
if (!valid_addr) {
return ESP_ERR_INVALID_ARG;
}
ESP_FAULT_ASSERT(valid_addr);
return esp_tee_sec_storage_get_signature(slot_id, key_type, hash, hlen, out_sign);
return esp_tee_sec_storage_ecdsa_sign(cfg, hash, hlen, out_sign);
}
esp_err_t _ss_esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_type_t key_type, esp_tee_sec_storage_pubkey_t *pubkey)
esp_err_t _ss_esp_tee_sec_storage_ecdsa_get_pubkey(const esp_tee_sec_storage_key_cfg_t *cfg, esp_tee_sec_storage_ecdsa_pubkey_t *out_pubkey)
{
bool valid_addr = ((esp_tee_ptr_in_ree((void *)pubkey)) &
(esp_tee_ptr_in_ree((void *)((char *)pubkey + sizeof(esp_tee_sec_storage_pubkey_t)))));
bool valid_addr = ((esp_tee_ptr_in_ree((void *)out_pubkey)) &&
(esp_tee_ptr_in_ree((void *)((char *)out_pubkey + sizeof(esp_tee_sec_storage_ecdsa_pubkey_t)))));
if (!valid_addr) {
return ESP_ERR_INVALID_ARG;
}
ESP_FAULT_ASSERT(valid_addr);
return esp_tee_sec_storage_get_pubkey(slot_id, key_type, pubkey);
return esp_tee_sec_storage_ecdsa_get_pubkey(cfg, out_pubkey);
}
esp_err_t _ss_esp_tee_sec_storage_encrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad,
uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output)
esp_err_t _ss_esp_tee_sec_storage_aead_encrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, uint8_t *tag, size_t tag_len, uint8_t *output)
{
bool valid_addr = (esp_tee_ptr_in_ree((void *)input) &&
bool valid_addr = (esp_tee_ptr_in_ree((void *)ctx->input) &&
esp_tee_ptr_in_ree((void *)tag) &&
esp_tee_ptr_in_ree((void *)output));
valid_addr &= (esp_tee_ptr_in_ree((void *)(input + len)) &&
valid_addr &= (esp_tee_ptr_in_ree((void *)(ctx->input + ctx->input_len)) &&
esp_tee_ptr_in_ree((void *)(tag + tag_len)) &&
esp_tee_ptr_in_ree((void *)(output + len)));
esp_tee_ptr_in_ree((void *)(output + ctx->input_len)));
if (aad) {
valid_addr &= (esp_tee_ptr_in_ree((void *)aad) && esp_tee_ptr_in_ree((void *)(aad + aad_len)));
if (ctx->aad) {
valid_addr &= (esp_tee_ptr_in_ree((void *)ctx->aad) && esp_tee_ptr_in_ree((void *)(ctx->aad + ctx->aad_len)));
}
if (!valid_addr) {
@@ -132,22 +131,21 @@ esp_err_t _ss_esp_tee_sec_storage_encrypt(uint16_t slot_id, uint8_t *input, uint
}
ESP_FAULT_ASSERT(valid_addr);
return esp_tee_sec_storage_encrypt(slot_id, input, len, aad, aad_len, tag, tag_len, output);
return esp_tee_sec_storage_aead_encrypt(ctx, tag, tag_len, output);
}
esp_err_t _ss_esp_tee_sec_storage_decrypt(uint16_t slot_id, uint8_t *input, uint8_t len, uint8_t *aad,
uint16_t aad_len, uint8_t *tag, uint16_t tag_len, uint8_t *output)
esp_err_t _ss_esp_tee_sec_storage_aead_decrypt(const esp_tee_sec_storage_aead_ctx_t *ctx, const uint8_t *tag, size_t tag_len, uint8_t *output)
{
bool valid_addr = (esp_tee_ptr_in_ree((void *)input) &&
bool valid_addr = (esp_tee_ptr_in_ree((void *)ctx->input) &&
esp_tee_ptr_in_ree((void *)tag) &&
esp_tee_ptr_in_ree((void *)output));
valid_addr &= (esp_tee_ptr_in_ree((void *)(input + len)) &&
valid_addr &= (esp_tee_ptr_in_ree((void *)(ctx->input + ctx->input_len)) &&
esp_tee_ptr_in_ree((void *)(tag + tag_len)) &&
esp_tee_ptr_in_ree((void *)(output + len)));
esp_tee_ptr_in_ree((void *)(output + ctx->input_len)));
if (aad) {
valid_addr &= (esp_tee_ptr_in_ree((void *)aad) && esp_tee_ptr_in_ree((void *)(aad + aad_len)));
if (ctx->aad) {
valid_addr &= (esp_tee_ptr_in_ree((void *)ctx->aad) && esp_tee_ptr_in_ree((void *)(ctx->aad + ctx->aad_len)));
}
if (!valid_addr) {
@@ -155,7 +153,7 @@ esp_err_t _ss_esp_tee_sec_storage_decrypt(uint16_t slot_id, uint8_t *input, uint
}
ESP_FAULT_ASSERT(valid_addr);
return esp_tee_sec_storage_decrypt(slot_id, input, len, aad, aad_len, tag, tag_len, output);
return esp_tee_sec_storage_aead_decrypt(ctx, tag, tag_len, output);
}
/* ---------------------------------------------- MMU HAL ------------------------------------------------- */

View File

@@ -14,6 +14,7 @@
#include "esp_tee_brownout.h"
#include "esp_tee_flash.h"
#include "esp_tee_sec_storage.h"
#include "bootloader_utility_tee.h"
#if __has_include("esp_app_desc.h")
@@ -23,11 +24,8 @@
/* TEE symbols */
extern uint32_t _tee_stack;
extern uint32_t _tee_intr_stack_bottom;
extern uint32_t _tee_bss_start;
extern uint32_t _tee_bss_end;
extern uint32_t _sec_world_entry;
extern uint32_t _tee_s_intr_handler;
extern uint8_t _tee_heap_start[];
@@ -159,6 +157,14 @@ void __attribute__((noreturn)) esp_tee_init(uint32_t ree_entry_addr, uint32_t re
}
ESP_FAULT_ASSERT(err == ESP_OK);
/* Initializing the secure storage */
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize the secure storage! (0x%08x)", err);
abort();
}
ESP_FAULT_ASSERT(err == ESP_OK);
/* Setting up the running non-secure app partition as per the address provided by the bootloader */
err = esp_tee_flash_set_running_ree_partition(ree_drom_addr);
if (err != ESP_OK) {
@@ -181,19 +187,3 @@ void __attribute__((noreturn)) esp_tee_init(uint32_t ree_entry_addr, uint32_t re
/* App entry function should not return here. */
ESP_INFINITE_LOOP(); /* WDT will reset us */
}
// NOTE: Remove compile-time warnings for the below newlib-provided functions
struct _reent *__getreent(void)
{
return _GLOBAL_REENT;
}
void _fstat_r(void) {}
void _close_r(void) {}
void _lseek_r(void) {}
void _read_r(void) {}
void _write_r(void) {}

View File

@@ -148,6 +148,7 @@ SECTIONS
*(.rodata_desc .rodata_desc.*) /* Should be the first. TEE App version info. DO NOT PUT ANYTHING BEFORE IT! */
*(.rodata .rodata.*)
*(.srodata .srodata.*)
*(.gcc_except_table .gcc_except_table.*)
_tee_xip_data_end = ABSOLUTE(.);
} > flash_data_seg
@@ -213,6 +214,12 @@ SECTIONS
*libmbedcrypto.a:*(.literal .text .literal.* .text.*)
/* HMAC-DS layer */
*libesp_security.a:*(.literal .text .literal.* .text.*)
/* NVS flash and related modules */
*libnvs_flash.a:*(.literal .text .literal.* .text.*)
*libstdc++.a:*(.literal .text .literal.* .text.*)
*libgcc.a:*(.literal .text .literal.* .text.*)
/* esp_partition API */
*libesp_partition.a:*(.literal .text .literal.* .text.*)
/* TEE attestation module */
*libattestation.a:*(.literal .text .literal.* .text.*)
*json_generator.a:*(.literal .text .literal.* .text.*)

View File

@@ -19,14 +19,14 @@ static const char *TAG = "esp_tee_apm_prot_cfg";
/* NOTE: Figuring out the eFuse protection range based on where the TEE secure storage key is stored */
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
#if CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK > 9
#error "TEE: eFuse protection region for APM out of range! (see CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK)"
#if CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID < 0
#error "TEE: eFuse protection region for APM out of range! (see CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID)"
#endif
#define LP_APM_EFUSE_REG_START \
(EFUSE_RD_KEY0_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 0x20))
(EFUSE_RD_KEY0_DATA0_REG + (CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID * 0x20))
#define LP_APM_EFUSE_REG_END \
(EFUSE_RD_KEY1_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 0x20))
(EFUSE_RD_KEY1_DATA0_REG + (CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID * 0x20))
#elif CONFIG_SECURE_TEE_SEC_STG_MODE_DEVELOPMENT
#define LP_APM_EFUSE_REG_START EFUSE_RD_KEY5_DATA0_REG
#if CONFIG_SECURE_TEE_TEST_MODE

View File

@@ -15,18 +15,21 @@ This example can be executed on any development board with a Espressif SOC chip
- 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`. If this configuration is not set, the slot with ID **0** will be used as default.
- 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 which eFuse block stores the encryption key at `(Top) → Security features → Trusted Execution Environment → TEE: Secure Storage Mode`.
- **Development** Mode: The encryption key is statically embedded in the 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.
- Set the eFuse block ID to store the encryption key in `Security features → Trusted Execution Environment → TEE: Secure Storage encryption key eFuse block`.
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`
- **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`.
- Snippet for burning the secure storage key in eFuse is given below.
```shell
# Programming 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
@@ -78,31 +81,33 @@ get_msg_sha256 "<msg>"
Get the SHA256 digest for the given message
"<msg>" Message for SHA256 digest calculation
tee_sec_stg_gen_key <slot_id> <key_type>
Generate and store a new key of the specified type in the given TEE secure
storage slot
<slot_id> TEE Secure storage slot for storing the key
<key_type> Key type (0: ECDSA_SECP256R1, 1: AES256)
tee_sec_stg_gen_key <key_id> <key_type>
Generate and store a new key of the specified type with the given ID
<key_id> TEE Secure storage key ID
<key_type> Key type (0: AES256, 1: ECDSA_SECP256R1)
tee_sec_stg_sign <slot_id> <msg_sha256>
Sign a message using the ECDSA keypair stored in the given slot ID and verify
the signature
<slot_id> TEE Secure storage slot storing the ecdsa-secp256r1 keypair
tee_sec_stg_sign <key_id> <msg_sha256>
Sign a message using the ECDSA keypair stored with the given key ID and
verify the signature
<key_id> TEE Secure storage key ID
<msg_sha256> SHA256 digest of the message to be signed and verified
tee_sec_stg_encrypt <slot_id> <plaintext>
Encrypt data using AES-GCM with a key from secure storage
<slot_id> TEE Secure storage slot storing the AES key
tee_sec_stg_encrypt <key_id> <plaintext>
Encrypt data using AES-GCM key with the given ID from secure storage
<key_id> TEE Secure storage key ID
<plaintext> Plaintext to be encrypted
tee_sec_stg_decrypt <slot_id> <ciphertext> <tag>
Decrypt data using AES-GCM with a key from secure storage
<slot_id> TEE Secure storage slot storing the AES key
tee_sec_stg_decrypt <key_id> <ciphertext> <tag>
Decrypt data using AES-GCM key with the given ID from secure storage
<key_id> TEE Secure storage key ID
<ciphertext> Ciphertext to be decrypted
<tag> AES-GCM authentication tag
help
Print the list of registered commands
help [<string>] [-v <0|1>]
Print the summary of all registered commands if no arguments are given,
otherwise print summary of given command.
<string> Name of command
-v, --verbose=<0|1> If specified, list console commands with given verbose level
```
## Secure Services
@@ -110,7 +115,7 @@ help
### Attestation
- The `tee_att_info` command provided by the attestation service generates and dumps an Entity Attestation Token (EAT) signed by the TEE.
- The token is signed using the ECDSA key (`secp256r1` curve) stored in the configured slot ID of the TEE Secure Storage.
- The token is signed using the ECDSA key (`secp256r1` curve) stored in the TEE Secure Storage with the configured key ID.
<details>
<summary><b>Sample output:</b> <i>tee_att_info</i></summary>
@@ -119,7 +124,7 @@ help
esp32c6> tee_att_info
I (8180) tee_attest: Attestation token - Length: 1455
I (8180) 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":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":{"nonce":-1582119980,"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"}}'
```
@@ -128,22 +133,22 @@ I (8180) tee_attest: Attestation token - Data:
### Secure Storage
- The TEE secure storage service provides the following commands:
- `tee_sec_stg_gen_key`: Generate and store a new key (ECDSA or AES) in a specified TEE secure storage slot
- `tee_sec_stg_sign`: Sign a message using an ECDSA `secp256r1` key pair stored in a specified slot and verify the signature
- `tee_sec_stg_encrypt`: Encrypt data with AES256-GCM using the key from the specified slot and outputs the ciphertext and tag
- `tee_sec_stg_decrypt`: Decrypt ciphertext using key from the specified slot and tag for integrity verification
- `tee_sec_stg_gen_key`: Generate and store a new key (ECDSA or AES) in the TEE secure storage with the specified ID
- `tee_sec_stg_sign`: Sign a message using an ECDSA `secp256r1` key pair with the specified ID and verify the signature
- `tee_sec_stg_encrypt`: Encrypt data with AES256-GCM using the key with the specified ID and outputs the ciphertext and tag
- `tee_sec_stg_decrypt`: Decrypt ciphertext using key with the specified ID and tag for integrity verification
- The `get_msg_sha256` command computes the SHA256 hash of a given message, which can be used as input for the `tee_sec_stg_sign` command.
<details>
<summary><b>Sample output:</b> <i>tee_sec_stg_gen_key + get_msg_sha256 + tee_sec_stg_sign</i></summary>
```log
esp32c6> tee_sec_stg_gen_key 7 0
I (2964) tee_sec_stg: Generated ECDSA_SECP256R1 key in slot 7
esp32c6> tee_sec_stg_gen_key ecdsa_p256_k0 1
I (2964) tee_sec_stg: Generated ECDSA_SECP256R1 key with ID ecdsa_p256_k0
esp32c6> get_msg_sha256 "hello world"
I (3984) tee_sec_stg: Message digest (SHA256) -
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
esp32c6> tee_sec_stg_sign 7 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
esp32c6> tee_sec_stg_sign ecdsa_p256_k0 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
I (5384) tee_sec_stg: Generated signature -
944684f6ddcf4c268ac6b65e34ccb8d95bd2849567a87867101bc1f09208f0885d935d7b3ba9d46014f28e4c7c988d68c775431fcb2cb2d4ca5c6862db771088
I (6404) tee_sec_stg: Public key (Uncompressed) -
@@ -157,14 +162,14 @@ I (6444) tee_sec_stg: Signature verified successfully!
<summary><b>Sample output:</b> <i>tee_sec_stg_gen_key + tee_sec_stg_encrypt + tee_sec_stg_decrypt</i></summary>
```log
esp32c6> tee_sec_stg_gen_key 8 1
I (2784) tee_sec_stg: Generated AES256 key in slot 8
esp32c6> tee_sec_stg_encrypt 8 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
esp32c6> tee_sec_stg_gen_key aes256_k0 0
I (2784) tee_sec_stg: Generated AES256 key with ID key0
esp32c6> tee_sec_stg_encrypt aes256_k0 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
I (3084) tee_sec_stg: Ciphertext -
58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1
I (3594) tee_sec_stg: Tag -
caeedb43e08dc3b4e35a58b2412908cc
esp32c6> tee_sec_stg_decrypt 8 58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 caeedb43e08dc3b4e35a58b2412908cc
esp32c6> tee_sec_stg_decrypt aes256_k0 58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 caeedb43e08dc3b4e35a58b2412908cc
I (4314) tee_sec_stg: Decrypted plaintext -
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
```

View File

@@ -18,6 +18,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_tee_sec_storage.h"
#include "example_tee_srv.h"
#define PROMPT_STR CONFIG_IDF_TARGET

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -79,7 +79,7 @@ static esp_err_t hexbuf_to_hexstr(const void *hexbuf, size_t hexbuf_sz, char *he
return ESP_OK;
}
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;
@@ -197,7 +197,7 @@ void register_cmd_msg_sha256(void)
}
static struct {
struct arg_int *slot_id;
struct arg_str *key_str_id;
struct arg_int *key_type;
struct arg_end *end;
} tee_sec_stg_gen_key_args;
@@ -211,29 +211,27 @@ static int tee_sec_stg_gen_key(int argc, char **argv)
}
esp_err_t err = ESP_FAIL;
uint16_t slot_id = (uint16_t)tee_sec_stg_gen_key_args.slot_id->ival[0];
esp_tee_sec_storage_type_t key_type = (esp_tee_sec_storage_type_t)tee_sec_stg_gen_key_args.key_type->ival[0];
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
esp_tee_sec_storage_key_cfg_t cfg = {
.id = (const char *)tee_sec_stg_gen_key_args.key_str_id->sval[0],
.type = (esp_tee_sec_storage_type_t)tee_sec_stg_gen_key_args.key_type->ival[0]
};
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_clear_slot(slot_id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to clear slot %d!", slot_id);
goto exit;
}
err = esp_tee_sec_storage_gen_key(slot_id, key_type);
err = esp_tee_sec_storage_gen_key(&cfg);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate key!");
goto exit;
}
ESP_LOGI(TAG, "Generated %s key in slot %d",
(key_type == ESP_SEC_STG_KEY_ECDSA_SECP256R1) ? "ECDSA_SECP256R1" : "AES256",
slot_id);
ESP_LOGI(TAG, "Generated %s key with ID %s",
(cfg.type == ESP_SEC_STG_KEY_ECDSA_SECP256R1) ? "ECDSA_SECP256R1" : "AES256",
cfg.id);
exit:
return err;
@@ -241,13 +239,13 @@ exit:
void register_srv_sec_stg_gen_key(void)
{
tee_sec_stg_gen_key_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot for storing the key");
tee_sec_stg_gen_key_args.key_type = arg_int1(NULL, NULL, "<key_type>", "Key type (0: ECDSA_SECP256R1, 1: AES256)");
tee_sec_stg_gen_key_args.key_str_id = arg_str1(NULL, NULL, "<key_id>", "TEE Secure storage key ID");
tee_sec_stg_gen_key_args.key_type = arg_int1(NULL, NULL, "<key_type>", "Key type (0: AES256, 1: ECDSA_SECP256R1)");
tee_sec_stg_gen_key_args.end = arg_end(2);
const esp_console_cmd_t tee_sec_stg = {
.command = "tee_sec_stg_gen_key",
.help = "Generate and store a new key of the specified type in the given TEE secure storage slot",
.help = "Generate and store a new key of the specified type with the given ID",
.hint = NULL,
.func = &tee_sec_stg_gen_key,
.argtable = &tee_sec_stg_gen_key_args,
@@ -257,7 +255,7 @@ void register_srv_sec_stg_gen_key(void)
}
static struct {
struct arg_int *slot_id;
struct arg_str *key_str_id;
struct arg_str *msg_sha256;
struct arg_end *end;
} tee_sec_stg_sign_args;
@@ -280,16 +278,13 @@ static int tee_sec_stg_sign(int argc, char **argv)
uint8_t digest[SHA256_DIGEST_SZ] = {};
hexstr_to_hexbuf(msg_sha256, msg_sha256_len, digest, sizeof(digest));
esp_err_t err = ESP_FAIL;
uint16_t slot_id = (uint16_t)tee_sec_stg_sign_args.slot_id->ival[0];
esp_tee_sec_storage_key_cfg_t cfg = {
.id = (const char *)tee_sec_stg_sign_args.key_str_id->sval[0],
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1
};
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
goto exit;
}
esp_tee_sec_storage_sign_t sign = {};
err = esp_tee_sec_storage_get_signature(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1, digest, sizeof(digest), &sign);
esp_tee_sec_storage_ecdsa_sign_t sign = {};
esp_err_t err = esp_tee_sec_storage_ecdsa_sign(&cfg, digest, sizeof(digest), &sign);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate signature!");
goto exit;
@@ -306,8 +301,8 @@ static int tee_sec_stg_sign(int argc, char **argv)
ESP_LOGI(TAG, "Generated signature -\n%s", sign_hexstr);
free(sign_hexstr);
esp_tee_sec_storage_pubkey_t pubkey = {};
err = esp_tee_sec_storage_get_pubkey(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;
@@ -338,13 +333,13 @@ exit:
void register_srv_sec_stg_sign(void)
{
tee_sec_stg_sign_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot storing the ecdsa-secp256r1 keypair");
tee_sec_stg_sign_args.key_str_id = arg_str1(NULL, NULL, "<key_id>", "TEE Secure storage key ID");
tee_sec_stg_sign_args.msg_sha256 = arg_str1(NULL, NULL, "<msg_sha256>", "SHA256 digest of the message to be signed and verified");
tee_sec_stg_sign_args.end = arg_end(2);
const esp_console_cmd_t tee_sec_stg = {
.command = "tee_sec_stg_sign",
.help = "Sign a message using the ECDSA keypair stored in the given slot ID and verify the signature",
.help = "Sign a message using the ECDSA keypair stored with the given key ID and verify the signature",
.hint = NULL,
.func = &tee_sec_stg_sign,
.argtable = &tee_sec_stg_sign_args,
@@ -354,7 +349,7 @@ void register_srv_sec_stg_sign(void)
}
static struct {
struct arg_int *slot_id;
struct arg_str *key_str_id;
struct arg_str *plaintext;
struct arg_end *end;
} tee_sec_stg_encrypt_args;
@@ -369,7 +364,7 @@ static int tee_sec_stg_encrypt(int argc, char **argv)
esp_err_t err = ESP_FAIL;
uint8_t tag[AES256_GCM_TAG_LEN];
uint16_t slot_id = (uint16_t)tee_sec_stg_encrypt_args.slot_id->ival[0];
const char *key_id = (const char *)tee_sec_stg_encrypt_args.key_str_id->sval[0];
const char *plaintext = tee_sec_stg_encrypt_args.plaintext->sval[0];
size_t plaintext_len = strnlen(plaintext, MAX_AES_PLAINTEXT_LEN);
@@ -399,13 +394,13 @@ static int tee_sec_stg_encrypt(int argc, char **argv)
goto exit;
}
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
goto exit;
}
esp_tee_sec_storage_aead_ctx_t ctx = {
.key_id = key_id,
.input = (uint8_t *)plaintext_buf,
.input_len = plaintext_buf_len
};
err = esp_tee_sec_storage_encrypt(slot_id, (uint8_t *)plaintext_buf, plaintext_buf_len,
NULL, 0, tag, sizeof(tag), ciphertext_buf);
err = esp_tee_sec_storage_aead_encrypt(&ctx, tag, sizeof(tag), ciphertext_buf);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt data: %s", esp_err_to_name(err));
goto exit;
@@ -434,13 +429,13 @@ exit:
void register_srv_sec_stg_encrypt(void)
{
tee_sec_stg_encrypt_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot storing the AES key");
tee_sec_stg_encrypt_args.key_str_id = arg_str1(NULL, NULL, "<key_id>", "TEE Secure storage key ID");
tee_sec_stg_encrypt_args.plaintext = arg_str1(NULL, NULL, "<plaintext>", "Plaintext to be encrypted");
tee_sec_stg_encrypt_args.end = arg_end(2);
const esp_console_cmd_t tee_sec_stg = {
.command = "tee_sec_stg_encrypt",
.help = "Encrypt data using AES-GCM with a key from secure storage",
.help = "Encrypt data using AES-GCM key with the given ID from secure storage",
.hint = NULL,
.func = &tee_sec_stg_encrypt,
.argtable = &tee_sec_stg_encrypt_args,
@@ -450,7 +445,7 @@ void register_srv_sec_stg_encrypt(void)
}
static struct {
struct arg_int *slot_id;
struct arg_str *key_str_id;
struct arg_str *ciphertext;
struct arg_str *tag;
struct arg_end *end;
@@ -465,7 +460,7 @@ static int tee_sec_stg_decrypt(int argc, char **argv)
}
esp_err_t err = ESP_FAIL;
uint16_t slot_id = (uint16_t)tee_sec_stg_decrypt_args.slot_id->ival[0];
const char *key_id = (const char *)tee_sec_stg_decrypt_args.key_str_id->sval[0];
const char *tag_hexstr = tee_sec_stg_decrypt_args.tag->sval[0];
uint8_t tag[AES256_GCM_TAG_LEN];
@@ -499,13 +494,13 @@ static int tee_sec_stg_decrypt(int argc, char **argv)
goto exit;
}
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
goto exit;
}
esp_tee_sec_storage_aead_ctx_t ctx = {
.key_id = key_id,
.input = (uint8_t *)ciphertext_buf,
.input_len = ciphertext_buf_len
};
err = esp_tee_sec_storage_decrypt(slot_id, ciphertext_buf, ciphertext_buf_len,
NULL, 0, tag, sizeof(tag), plaintext_buf);
err = esp_tee_sec_storage_aead_decrypt(&ctx, tag, sizeof(tag), plaintext_buf);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to decrypt data: %s", esp_err_to_name(err));
goto exit;
@@ -530,14 +525,14 @@ exit:
void register_srv_sec_stg_decrypt(void)
{
tee_sec_stg_decrypt_args.slot_id = arg_int1(NULL, NULL, "<slot_id>", "TEE Secure storage slot storing the AES key");
tee_sec_stg_decrypt_args.key_str_id = arg_str1(NULL, NULL, "<key_id>", "TEE Secure storage key ID");
tee_sec_stg_decrypt_args.ciphertext = arg_str1(NULL, NULL, "<ciphertext>", "Ciphertext to be decrypted");
tee_sec_stg_decrypt_args.tag = arg_str1(NULL, NULL, "<tag>", "AES-GCM authentication tag");
tee_sec_stg_decrypt_args.end = arg_end(3);
const esp_console_cmd_t tee_sec_stg = {
.command = "tee_sec_stg_decrypt",
.help = "Decrypt data using AES-GCM with a key from secure storage",
.help = "Decrypt data using AES-GCM key with the given ID from secure storage",
.hint = NULL,
.func = &tee_sec_stg_decrypt,
.argtable = &tee_sec_stg_decrypt_args,

View File

@@ -53,13 +53,14 @@ def test_tee_cli_secure_storage(dut: Dut) -> None:
time.sleep(1)
# Test out the TEE secure storage workflow - Message signing and verification
iterations = 3
sec_stg_slots = {0: 0, 1: 14, 2: 7}
for i in range(iterations):
dut.write(f'tee_sec_stg_gen_key {sec_stg_slots.get(i)} 0')
dut.expect(r'Generated ECDSA_SECP256R1 key in slot (\d+)', timeout=30)
sec_stg_key_ids = {0: 'key0', 1: 'key1', 2: 'key2'}
iterations = len(sec_stg_key_ids)
dut.write(f'tee_sec_stg_sign {sec_stg_slots.get(i)} {test_msg_hash}')
for i in range(iterations):
dut.write(f'tee_sec_stg_gen_key {sec_stg_key_ids.get(i)} 1')
dut.expect(r'Generated ECDSA_SECP256R1 key with ID (\S+)', timeout=30)
dut.write(f'tee_sec_stg_sign {sec_stg_key_ids.get(i)} {test_msg_hash}')
test_msg_sign = dut.expect(r'Generated signature -\s*([0-9a-fA-F]{128})', timeout=30)[1].decode()
test_msg_pubkey = dut.expect(r'Public key \(Uncompressed\) -\s*([0-9a-fA-F]{130})', timeout=30)[1].decode()
dut.expect('Signature verified successfully', timeout=30)
@@ -69,16 +70,15 @@ def test_tee_cli_secure_storage(dut: Dut) -> None:
time.sleep(1)
# Test out the TEE secure storage workflow - Encryption and decryption
sec_stg_slots = {0: 1, 1: 14, 2: 9}
for i in range(iterations):
dut.write(f'tee_sec_stg_gen_key {sec_stg_slots.get(i)} 1')
dut.expect(r'Generated AES256 key in slot (\d+)', timeout=30)
dut.write(f'tee_sec_stg_gen_key {sec_stg_key_ids.get(i)} 0')
dut.expect(r'Generated AES256 key with ID (\S+)', timeout=30)
dut.write(f'tee_sec_stg_encrypt {sec_stg_slots.get(i)} {test_msg_hash}')
dut.write(f'tee_sec_stg_encrypt {sec_stg_key_ids.get(i)} {test_msg_hash}')
test_msg_cipher = dut.expect(r'Ciphertext -\s*([0-9a-fA-F]{64})', timeout=30)[1].decode()
test_msg_tag = dut.expect(r'Tag -\s*([0-9a-fA-F]{32})', timeout=30)[1].decode()
dut.write(f'tee_sec_stg_decrypt {sec_stg_slots.get(i)} {test_msg_cipher} {test_msg_tag}')
dut.write(f'tee_sec_stg_decrypt {sec_stg_key_ids.get(i)} {test_msg_cipher} {test_msg_tag}')
test_msg_decipher = dut.expect(r'Decrypted plaintext -\s*([0-9a-fA-F]{64})', timeout=30)[1].decode()
assert test_msg_decipher == test_msg_hash
@@ -133,9 +133,9 @@ def test_tee_cli_attestation(dut: Dut) -> None:
dut.expect('ESP-TEE: Secure services demonstration', timeout=30)
time.sleep(1)
att_key_slot = dut.app.sdkconfig.get('SECURE_TEE_ATT_KEY_SLOT_ID')
dut.write(f'tee_sec_stg_gen_key {att_key_slot} 0')
dut.expect(r'Generated ECDSA_SECP256R1 key in slot (\d+)', timeout=30)
att_key_id = dut.app.sdkconfig.get('SECURE_TEE_ATT_KEY_STR_ID')
dut.write(f'tee_sec_stg_gen_key {att_key_id} 1')
dut.expect(r'Generated ECDSA_SECP256R1 key with ID (\S+)', timeout=30)
# Get the Entity Attestation token from TEE and verify its signature
dut.write('tee_att_info')

View File

@@ -1,6 +1,6 @@
# Minimal TEE configuration
CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE=y
CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK=9
CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID=5
# Reducing TEE DRAM and STACK sizes
# 21KB

View File

@@ -12,4 +12,4 @@ CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE=y
# TEE Secure Storage: Release mode
CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE=y
CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK=9
CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID=5

View File

@@ -42,7 +42,7 @@ uint32_t _ss_add_in_loop(uint32_t a, uint32_t b, uint32_t iter)
for (int i = 0; i < iter; i++) {
a += b;
esp_rom_delay_us(1000000);
ESP_LOGD(TAG, "[mode: %d] val: %d", esp_cpu_get_curr_privilege_level(), a);
esp_rom_printf("[mode: %d] val: %d\n", esp_cpu_get_curr_privilege_level(), a);
}
return a;
}

View File

@@ -1,9 +1,17 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
# pylint: disable=W0621 # redefined-outer-name
import base64
import csv
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path
from typing import Any
from typing import Dict
from typing import List
import espsecure
import esptool
@@ -13,6 +21,7 @@ from _pytest.monkeypatch import MonkeyPatch
from pytest_embedded_idf.serial import IdfSerial
from pytest_embedded_serial_esp.serial import EspSerial
# fmt: off
esp_tee_empty_bin = {
'esp32c6': [
0xE9, 0x04, 0x02, 0x10, 0x00, 0x00, 0x80, 0x40, 0xEE, 0x00, 0x00, 0x00,
@@ -56,6 +65,7 @@ esp_tee_empty_bin = {
0xFC, 0x74, 0xB2, 0xB9, 0x34, 0x72, 0xC3, 0x00
]
}
# fmt: on
# This is a custom IdfSerial class to support custom functionality
@@ -70,10 +80,12 @@ class TEESerial(IdfSerial):
@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),
)):
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 ''
@@ -81,7 +93,7 @@ class TEESerial(IdfSerial):
esptool.main(
f'--no-stub write_flash {offs} {bootloader_path} --force {encrypt} --flash_size {flash_size}'.split(),
esp=self.esp
esp=self.esp,
)
@EspSerial.use_esptool()
@@ -97,20 +109,30 @@ class TEESerial(IdfSerial):
temp_file.flush()
esptool.main(
f'--no-stub write_flash {offs} {temp_file.name} --flash_size {flash_size}'.split(),
esp=self.esp
f'--no-stub write_flash {offs} {temp_file.name} --flash_size {flash_size}'.split(), esp=self.esp
)
else:
self.erase_partition(partition)
@EspSerial.use_esptool()
def copy_test_tee_img(self, partition: str, is_rollback: bool = False) -> None:
def custom_write_partition(self, partition: str, bin_path: str, encrypt: bool = False) -> None:
offs = self.app.partition_table[partition]['offset']
no_stub = '--no-stub' if self.app.sdkconfig.get('SECURE_ENABLE_SECURE_ROM_DL_MODE') else ''
encrypt = '--encrypt' if self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED') else ''
flash_size = self._get_flash_size()
flash_file = bin_path
args = f'{no_stub} write_flash {offs} {flash_file}'.split()
if encrypt:
args.append('--encrypt')
args += f'--flash_size {flash_size}'.split()
esptool.main(args, esp=self.esp)
@EspSerial.use_esptool()
def copy_test_tee_img(self, partition: str, is_rollback: bool = False) -> None:
flash_file = os.path.join(self.app.binary_path, 'esp_tee', 'esp_tee.bin')
encrypt = self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED', False)
if is_rollback:
datafile = 'esp_tee_empty.bin'
@@ -125,22 +147,27 @@ class TEESerial(IdfSerial):
if self.app.sdkconfig.get('SECURE_BOOT'):
keyfile = self.app.sdkconfig.get('SECURE_BOOT_SIGNING_KEY')
# Signing the image with espsecure
espsecure.main(
f'sign_data --version 2 --append_signatures --keyfile {keyfile} --output {datafile_signed} {datafile}'.split()
[
'sign_data',
'--version',
'2',
'--append_signatures',
'--keyfile',
keyfile,
'--output',
datafile_signed,
datafile,
]
)
flash_file = datafile_signed
esptool.main(
f'{no_stub} write_flash {offs} {flash_file} {encrypt} --flash_size {flash_size}'.split(),
esp=self.esp
)
self.custom_write_partition(partition, flash_file, encrypt=encrypt)
if is_rollback:
if os.path.exists(datafile):
os.remove(datafile)
if os.path.exists(datafile_signed):
os.remove(datafile_signed)
for file in [datafile, datafile_signed]:
if os.path.exists(file):
os.remove(file)
@EspSerial.use_esptool()
def custom_flash(self) -> None:
@@ -165,6 +192,114 @@ class TEESerial(IdfSerial):
self.flash()
self.custom_erase_partition('secure_storage')
KEY_DEFS: List[Dict[str, Any]] = [
{'key': 'aes256_key0', 'type': 'aes256', 'input': None, 'write_once': True},
{
'key': 'aes256_key1',
'type': 'aes256',
'input': 'aes256_key.bin',
'write_once': False,
'b64': 'qZxftt2T8mOpLxALIfsDqI65srqPxrJtCVnDU8wrKXbFCJekDRzXqINlU5s=',
},
{'key': 'p256_key0', 'type': 'ecdsa_p256', 'input': None, 'write_once': False},
{
'key': 'attest_key',
'type': 'ecdsa_p256',
'input': 'ecdsa_p256_key.pem',
'write_once': True,
'b64': (
'LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlNU1VpUktHaVZjSTIvbUZFekI3eXRIOVJj'
'd0wyUThkNDhONHNFUHFYc0RvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkYxYXRZQUxrdnB4cCt4N3c1dmVPQ1Vj'
'RUhFRTY5azkvcFB5eFdTbEZkbW5wMnBmbVJpZwp5NnRTMDNaM2tnN2hYcitTQmNLbmRCV2RlZW81Vm9XV29nPT0K'
'LS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo='
),
},
]
NVS_KEYS_B64 = 'MzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzPMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzA=='
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'
def run_command(self, command: List[str]) -> None:
try:
subprocess.check_call(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
except subprocess.CalledProcessError:
print(f'Command failed: {" ".join(command)}')
raise
def write_keys_to_file(self, b64_data: str, path: Path) -> None:
path.write_bytes(base64.b64decode(b64_data))
def create_tee_sec_stg_csv(self, tmp_dir: Path) -> Path:
csv_path = self.NVS_CSV_PATH
rows: List[List[str]] = [
['key', 'type', 'encoding', 'value'],
['tee_sec_stg_ns', 'namespace', '', ''],
]
rows += [[entry['key'], 'file', 'binary', str(tmp_dir / f'{entry["key"]}.bin')] for entry in self.KEY_DEFS]
with csv_path.open('w', newline='') as f:
csv.writer(f).writerows(rows)
return csv_path
def custom_flash_with_host_gen_sec_stg_img(self) -> None:
tmp_dir = self.TMP_DIR
tmp_dir.mkdir(parents=True, exist_ok=True)
for entry in self.KEY_DEFS:
if entry['input']:
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'
)
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 [])
+ (['--write-once'] if entry['write_once'] else [])
for entry in self.KEY_DEFS
]
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,
'encrypt',
str(csv_path),
str(nvs_bin),
str(size),
'--inputkey',
str(nvs_keys),
]
)
try:
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)
finally:
shutil.rmtree(tmp_dir)
@pytest.fixture(scope='module')
def monkeypatch_module(request: FixtureRequest) -> MonkeyPatch:

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -29,14 +29,14 @@
#define ECDSA_SECP256R1_KEY_LEN (32)
#define ESP_ATT_TK_BUF_SIZE (1792)
#define ESP_ATT_TK_PSA_CERT_REF ("0716053550477-10100")
#define ESP_ATT_TK_PSA_CERT_REF ("0632793520245-10010")
#define ESP_ATT_TK_NONCE (0xABCD1234)
#define ESP_ATT_TK_CLIENT_ID (0x0FACADE0)
static const char *TAG = "test_esp_tee_att";
extern int verify_ecdsa_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign, bool is_crv_p192);
extern int verify_ecdsa_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, bool is_crv_p192);
static uint8_t hexchar_to_byte(char hex)
{
@@ -72,7 +72,7 @@ static void hexstr_to_bytes(const char *hex_str, uint8_t **hex_buf, size_t *buf_
}
}
static int decompress_ecdsa_pubkey(const mbedtls_ecp_group *grp, const unsigned char *input, size_t ilen, esp_tee_sec_storage_pubkey_t *pubkey)
static int decompress_ecdsa_pubkey(const mbedtls_ecp_group *grp, const unsigned char *input, size_t ilen, esp_tee_sec_storage_ecdsa_pubkey_t *pubkey)
{
int ret = -1;
mbedtls_mpi r, x, n;
@@ -186,7 +186,7 @@ static void prehash_token_data(const char *token_json, uint8_t *digest, size_t l
cJSON_Delete(root);
}
static void fetch_pubkey(const char *token_json, esp_tee_sec_storage_pubkey_t *pubkey_ctx)
static void fetch_pubkey(const char *token_json, esp_tee_sec_storage_ecdsa_pubkey_t *pubkey_ctx)
{
TEST_ASSERT_NOT_NULL(token_json);
TEST_ASSERT_NOT_NULL(pubkey_ctx);
@@ -218,7 +218,7 @@ static void fetch_pubkey(const char *token_json, esp_tee_sec_storage_pubkey_t *p
cJSON_Delete(root);
}
static void fetch_signature(const char *token_json, esp_tee_sec_storage_sign_t *sign_ctx)
static void fetch_signature(const char *token_json, esp_tee_sec_storage_ecdsa_sign_t *sign_ctx)
{
TEST_ASSERT_NOT_NULL(token_json);
TEST_ASSERT_NOT_NULL(sign_ctx);
@@ -267,11 +267,11 @@ TEST_CASE("Test TEE Attestation - Generate and verify the EAT", "[attestation]")
prehash_token_data((const char *)token_buf, digest, sizeof(digest));
// Fetching and decompressing the public key
esp_tee_sec_storage_pubkey_t pubkey_ctx = {};
esp_tee_sec_storage_ecdsa_pubkey_t pubkey_ctx = {};
fetch_pubkey((const char *)token_buf, &pubkey_ctx);
// Fetching the signature components
esp_tee_sec_storage_sign_t sign_ctx = {};
esp_tee_sec_storage_ecdsa_sign_t sign_ctx = {};
fetch_signature((const char *)token_buf, &sign_ctx);
// Verifying the generated token

View File

@@ -27,6 +27,8 @@
#define TEST_PART_LABEL "custom"
#define TEST_BUF_SZ 256
#define ESP_TEE_SEC_STG_PART_LABEL "secure_storage"
static const char *TAG = "test_esp_tee_flash_prot";
static void set_boot_count_in_nvs(uint8_t boot_count)
@@ -93,7 +95,7 @@ static void test_esp_partition_mmap_api(void)
TEST_FAIL_MESSAGE("System fault should have been generated");
break;
case 5:
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, ESP_TEE_SEC_STG_PART_LABEL);
TEST_ASSERT_NOT_NULL(part);
TEST_ESP_OK(esp_partition_mmap(part, 0, part->size, ESP_PARTITION_MMAP_DATA, &outptr, &out_handle));
ESP_LOG_BUFFER_HEXDUMP(TAG, outptr, 0x20, ESP_LOG_INFO);
@@ -149,7 +151,7 @@ static void test_esp_partition_api(void)
test_esp_partition_api_w(part);
break;
case 4:
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, ESP_TEE_SEC_STG_PART_LABEL);
test_esp_partition_api_w(part);
break;
case 5:
@@ -255,7 +257,7 @@ static void test_esp_flash_api(void)
test_esp_flash_api_r(part->address);
break;
case 4:
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, ESP_TEE_SEC_STG_PART_LABEL);
TEST_ASSERT_NOT_NULL(part);
test_esp_flash_api_e(part->address);
break;
@@ -327,7 +329,7 @@ static void test_esp_rom_spiflash_api(void)
TEST_FAIL_MESSAGE("System fault should have been generated");
break;
case 4:
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, ESP_TEE_SEC_STG_PART_LABEL);
TEST_ASSERT_NOT_NULL(part);
test_esp_rom_spiflash_api_e(part->address);
TEST_FAIL_MESSAGE("System fault should have been generated");

View File

@@ -30,7 +30,7 @@ extern uint32_t _instruction_reserved_start;
#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE
#define TEST_APM_EFUSE_PROT_REG \
(EFUSE_RD_KEY0_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 0x20))
(EFUSE_RD_KEY0_DATA0_REG + (CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID * 0x20))
#else
#define TEST_APM_EFUSE_PROT_REG EFUSE_RD_KEY5_DATA0_REG
#endif

View File

@@ -17,9 +17,12 @@
#include "esp_tee.h"
#include "esp_tee_sec_storage.h"
#include "secure_service_num.h"
#if CONFIG_SECURE_TEE_ATTESTATION
#include "esp_tee_attestation.h"
#endif
#include "esp_random.h"
#include "nvs.h"
#include "unity.h"
#include "sdkconfig.h"
@@ -31,9 +34,14 @@
#define ECDSA_SECP256R1_KEY_LEN (32)
#define ECDSA_SECP192R1_KEY_LEN (24)
#define ESP_ATT_TK_BUF_SIZE (1792)
#define ESP_ATT_TK_PSA_CERT_REF ("0632793520245-10010")
#define MAX_SEC_STG_ITER (16)
static const char *TAG = "test_esp_tee_sec_storage";
int verify_ecdsa_sign(const uint8_t *digest, size_t len, const esp_tee_sec_storage_pubkey_t *pubkey, const esp_tee_sec_storage_sign_t *sign, bool is_crv_p192)
int verify_ecdsa_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, bool is_crv_p192)
{
#if !CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
TEST_ASSERT_FALSE(is_crv_p192);
@@ -73,7 +81,7 @@ int verify_ecdsa_sign(const uint8_t *digest, size_t len, const esp_tee_sec_stora
return 0;
}
TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1) with all key-slot IDs", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1)", "[sec_storage]")
{
const size_t buf_sz = 16 * 1024 + 6; // NOTE: Not an exact multiple of SHA block size
unsigned char *message = heap_caps_malloc(buf_sz, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
@@ -85,27 +93,34 @@ TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1) with all key-
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, buf_sz, msg_digest, false));
free(message);
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_tee_sec_storage_key_cfg_t key_cfg = {
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1
};
for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) {
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1));
for (unsigned int i = 0; i < MAX_SEC_STG_ITER; i++) {
char key_id[32];
int ret = snprintf(key_id, sizeof(key_id), "ecdsa_key_%u", i);
TEST_ASSERT_TRUE(ret > 0 && ret < sizeof(key_id));
esp_tee_sec_storage_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_get_signature(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1, msg_digest, sizeof(msg_digest), &sign));
key_cfg.id = key_id;
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_get_pubkey(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1, &pubkey));
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_sign(&key_cfg, msg_digest, sizeof(msg_digest), &sign));
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey));
TEST_ESP_OK(verify_ecdsa_sign(msg_digest, sizeof(msg_digest), &pubkey, &sign, false));
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
}
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp192r1) with all key-slot IDs", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp192r1)", "[sec_storage]")
{
const size_t buf_sz = 16 * 1024 + 6; // NOTE: Not an exact multiple of SHA block size
unsigned char *message = heap_caps_malloc(buf_sz, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
@@ -117,27 +132,34 @@ TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp192r1) with all key-
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, buf_sz, msg_digest, false));
free(message);
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_tee_sec_storage_key_cfg_t key_cfg = {
.type = ESP_SEC_STG_KEY_ECDSA_SECP192R1
};
for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) {
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP192R1));
for (unsigned int i = 0; i < MAX_SEC_STG_ITER; i++) {
char key_id[32];
int ret = snprintf(key_id, sizeof(key_id), "ecdsa_key_%u", i);
TEST_ASSERT_TRUE(ret > 0 && ret < sizeof(key_id));
esp_tee_sec_storage_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_get_signature(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP192R1, msg_digest, sizeof(msg_digest), &sign));
key_cfg.id = key_id;
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_get_pubkey(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP192R1, &pubkey));
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_sign(&key_cfg, msg_digest, sizeof(msg_digest), &sign));
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey));
TEST_ESP_OK(verify_ecdsa_sign(msg_digest, sizeof(msg_digest), &pubkey, &sign, true));
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
}
#endif
TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm) with all key-slot IDs", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm)", "[sec_storage]")
{
const uint8_t SZ = 100;
uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
@@ -154,21 +176,37 @@ TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm) with all key-s
uint8_t aad[16];
memset(aad, 0xA5, sizeof(aad));
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_tee_sec_storage_key_cfg_t key_cfg = {
.type = ESP_SEC_STG_KEY_AES256
};
for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) {
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256));
for (unsigned int i = 0; i < MAX_SEC_STG_ITER; i++) {
char test_key_id[32];
int ret = snprintf(test_key_id, sizeof(test_key_id), "aes_key_%u", i);
TEST_ASSERT_TRUE(ret > 0 && ret < sizeof(test_key_id));
TEST_ESP_OK(esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad),
tag, sizeof(tag), ciphertext));
TEST_ESP_OK(esp_tee_sec_storage_decrypt(slot_id, ciphertext, SZ, aad, sizeof(aad),
tag, sizeof(tag), decryptedtext));
key_cfg.id = test_key_id;
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_aead_ctx_t aead_ctx = {
.key_id = test_key_id,
.aad = aad,
.aad_len = sizeof(aad),
};
aead_ctx.input = plaintext;
aead_ctx.input_len = SZ;
TEST_ESP_OK(esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), ciphertext));
aead_ctx.input = ciphertext;
aead_ctx.input_len = SZ;
TEST_ESP_OK(esp_tee_sec_storage_aead_decrypt(&aead_ctx, tag, sizeof(tag), decryptedtext));
TEST_ASSERT_EQUAL_HEX8_ARRAY(plaintext, decryptedtext, SZ);
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
free(plaintext);
@@ -176,7 +214,7 @@ TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm) with all key-s
free(decryptedtext);
}
TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys", "[sec_storage]")
{
// Setup for ECDSA
const uint8_t SZ = 100;
@@ -187,6 +225,12 @@ TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys",
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, SZ, msg_digest, false));
free(message);
const char *key_id = "key_id_misc";
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = key_id,
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1
};
// Setup for AES
uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(plaintext);
@@ -197,127 +241,195 @@ TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys",
uint8_t aad[16];
memset(aad, 0xA5, sizeof(aad));
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_tee_sec_storage_aead_ctx_t aead_ctx = {
.key_id = key_id,
.aad = aad,
.aad_len = sizeof(aad),
.input = plaintext,
.input_len = SZ
};
// Test ECDSA key with AES operation
const uint16_t slot_id = 7;
ESP_LOGI(TAG, "Slot ID: %u - Trying AES operation with ECDSA key...", slot_id);
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
ESP_LOGI(TAG, "Key ID: %s - Trying AES operation with ECDSA key...", key_cfg.id);
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
ESP_LOGW(TAG, "err: %x", err);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), ciphertext));
esp_tee_sec_storage_type_t key_type = ESP_SEC_STG_KEY_ECDSA_SECP256R1;
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, key_type));
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad),
tag, sizeof(tag), ciphertext));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
// Test AES key with ECDSA operation
ESP_LOGI(TAG, "Slot ID: %u - Trying ECDSA operation with AES key...", slot_id);
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256));
ESP_LOGI(TAG, "Key ID: %s - Trying ECDSA operation with AES key...", key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
key_cfg.type = ESP_SEC_STG_KEY_AES256;
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_sign_t sign = {};
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_get_signature(slot_id, key_type, msg_digest, sizeof(msg_digest), &sign));
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id));
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_ERR(ESP_FAIL, esp_tee_sec_storage_ecdsa_sign(&key_cfg, msg_digest, sizeof(msg_digest), &sign));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
// Test with non-existent data
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad),
tag, sizeof(tag), ciphertext));
TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), ciphertext));
free(plaintext);
free(ciphertext);
}
TEST_CASE("Test TEE Secure Storage - Invalid key-slot IDs", "[secure_storage]")
TEST_CASE("Test TEE Secure Storage - Null Pointer and Zero Length", "[sec_storage]")
{
TEST_ESP_OK(esp_tee_sec_storage_init());
uint16_t slot_id = MAX_SEC_STG_SLOT_ID + 1;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1));
slot_id = MIN_SEC_STG_SLOT_ID - 1;
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256));
}
TEST_CASE("Test TEE Secure Storage - Exhaust all key-slots", "[secure_storage]")
{
TEST_ESP_OK(esp_tee_sec_storage_init());
esp_err_t err = ESP_FAIL;
uint16_t slot_id = MIN_SEC_STG_SLOT_ID;
while (1) {
esp_tee_sec_storage_clear_slot(slot_id);
err = esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256);
if (err != ESP_OK) {
break;
}
TEST_ASSERT_FALSE(esp_tee_sec_storage_is_slot_empty(slot_id));
slot_id++;
}
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, err);
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(MAX_SEC_STG_SLOT_ID, ESP_SEC_STG_KEY_AES256));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(MIN_SEC_STG_SLOT_ID, ESP_SEC_STG_KEY_AES256));
uint16_t mid_slot = (MIN_SEC_STG_SLOT_ID + MAX_SEC_STG_SLOT_ID) / 2;
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(mid_slot, ESP_SEC_STG_KEY_AES256));
}
TEST_CASE("Test TEE Secure Storage - Null Pointer and Zero Length", "[secure_storage]")
{
TEST_ESP_OK(esp_tee_sec_storage_init());
const uint16_t slot_id = 7;
const char *key_id = "key_id_misc";
uint8_t data[31], tag[12];
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256));
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = key_id,
.type = ESP_SEC_STG_KEY_AES256
};
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_encrypt(slot_id, NULL, sizeof(data), NULL, 0, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_encrypt(slot_id, data, 0, NULL, 0, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_encrypt(slot_id, data, sizeof(data), NULL, 0, NULL, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_encrypt(slot_id, data, sizeof(data), NULL, 0, tag, 0, data));
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_decrypt(slot_id, NULL, sizeof(data), NULL, 0, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_decrypt(slot_id, data, 0, NULL, 0, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_decrypt(slot_id, data, sizeof(data), NULL, 0, NULL, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_decrypt(slot_id, data, sizeof(data), NULL, 0, tag, 0, data));
esp_tee_sec_storage_aead_ctx_t aead_ctx = {
.key_id = key_cfg.id,
.input = data,
.input_len = sizeof(data),
};
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_encrypt(&aead_ctx, NULL, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, 0, data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_decrypt(&aead_ctx, NULL, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_decrypt(&aead_ctx, tag, 0, data));
esp_tee_sec_storage_type_t key_type = ESP_SEC_STG_KEY_ECDSA_SECP256R1;
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, key_type));
aead_ctx.input = NULL;
aead_ctx.input_len = sizeof(data);
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_aead_decrypt(&aead_ctx, tag, sizeof(tag), data));
esp_tee_sec_storage_sign_t sign = {};
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_signature(slot_id, key_type, NULL, sizeof(data), &sign));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_get_signature(slot_id, key_type, data, 0, &sign));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_signature(slot_id, key_type, data, sizeof(data), NULL));
aead_ctx.input = data;
aead_ctx.input_len = 0;
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_encrypt(&aead_ctx, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_aead_decrypt(&aead_ctx, tag, sizeof(tag), data));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_pubkey(slot_id, key_type, NULL));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_id));
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
key_cfg.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1;
err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_sign(&key_cfg, NULL, sizeof(data), &sign));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_sign(&key_cfg, data, 0, &sign));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_sign(&key_cfg, data, sizeof(data), NULL));
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, NULL));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C6)
TEST_CASE("Test TEE Secure Storage - Corruption from non-secure world", "[secure_storage_neg]")
TEST_CASE("Test TEE Secure Storage - WRITE_ONCE keys", "[sec_storage]")
{
const esp_partition_t *tee_sec_stg = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL);
TEST_ASSERT_NOT_NULL(tee_sec_stg);
const char *key_id = "key_id_test_wo";
uint8_t buf_w[128];
memset(buf_w, 0xA5, sizeof(buf_w));
TEST_ESP_OK(esp_partition_write(tee_sec_stg, 0x00, buf_w, sizeof(buf_w)));
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = key_id,
.type = ESP_SEC_STG_KEY_AES256,
.flags = SEC_STORAGE_FLAG_WRITE_ONCE,
};
uint8_t buf_r[128];
memset(buf_r, 0x00, sizeof(buf_r));
TEST_ESP_OK(esp_partition_read(tee_sec_stg, 0x00, buf_r, sizeof(buf_r)));
ESP_LOG_BUFFER_HEXDUMP(TAG, buf_r, sizeof(buf_r), ESP_LOG_INFO);
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_FAIL_MESSAGE("APM violation interrupt should have been generated");
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(&key_cfg));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_clear_key(key_cfg.id));
}
static void test_aead_encrypt_decrypt(const char *key_id, const uint8_t *input, size_t len)
{
uint8_t *ciphertext = heap_caps_malloc(len, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(ciphertext);
uint8_t *decrypted = heap_caps_malloc(len, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(decrypted);
uint8_t tag[12];
uint8_t aad[16];
esp_fill_random(aad, sizeof(aad));
esp_tee_sec_storage_aead_ctx_t ctx = {
.key_id = key_id,
.aad = aad,
.aad_len = sizeof(aad),
};
ctx.input = input;
ctx.input_len = len;
TEST_ESP_OK(esp_tee_sec_storage_aead_encrypt(&ctx, tag, sizeof(tag), ciphertext));
ctx.input = ciphertext;
ctx.input_len = len;
TEST_ESP_OK(esp_tee_sec_storage_aead_decrypt(&ctx, tag, sizeof(tag), decrypted));
TEST_ASSERT_EQUAL_HEX8_ARRAY(input, decrypted, len);
free(ciphertext);
free(decrypted);
}
TEST_CASE("Test TEE Secure Storage - Host-generated keys", "[sec_storage_host_keygen]")
{
const char *aes_key_id0 = "aes256_key0";
const char *aes_key_id1 = "aes256_key1";
const char *ecdsa_key_id0 = "p256_key0";
const char *attest_key_id = "attest_key";
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_clear_key(aes_key_id0));
const size_t SZ = 100;
uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(plaintext);
esp_fill_random(plaintext, SZ);
test_aead_encrypt_decrypt(aes_key_id0, plaintext, SZ);
test_aead_encrypt_decrypt(aes_key_id1, plaintext, SZ);
free(plaintext);
TEST_ESP_OK(esp_tee_sec_storage_clear_key(aes_key_id1));
uint8_t msg_digest[SHA256_DIGEST_SZ];
esp_fill_random(msg_digest, sizeof(msg_digest));
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = ecdsa_key_id0,
.type = ESP_SEC_STG_KEY_ECDSA_SECP256R1,
};
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_sign(&key_cfg, msg_digest, sizeof(msg_digest), &sign));
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey));
TEST_ESP_OK(verify_ecdsa_sign(msg_digest, sizeof(msg_digest), &pubkey, &sign, false));
TEST_ESP_OK(esp_tee_sec_storage_clear_key(ecdsa_key_id0));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_clear_key(attest_key_id));
#if CONFIG_SECURE_TEE_ATTESTATION
uint8_t *token_buf = heap_caps_calloc(ESP_ATT_TK_BUF_SIZE, sizeof(uint8_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(token_buf);
uint32_t token_len = 0;
TEST_ESP_OK(esp_tee_att_generate_token(0xA1B2C3D4, 0x0FACADE0, (const char *)ESP_ATT_TK_PSA_CERT_REF,
token_buf, ESP_ATT_TK_BUF_SIZE, &token_len));
free(token_buf);
#endif
}
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
static const uint8_t sha[] = {
@@ -327,11 +439,8 @@ static const uint8_t sha[] = {
0x91, 0xbe, 0x58, 0x10, 0xfe, 0x80, 0x65, 0x6e
};
static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int slot_id)
static void test_ecdsa_sign(mbedtls_ecp_group_id gid)
{
TEST_ESP_OK(esp_tee_sec_storage_init());
TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id));
bool is_crv_p192 = false;
esp_tee_sec_storage_type_t key_type = ESP_SEC_STG_KEY_ECDSA_SECP256R1;
size_t key_len = ECDSA_SECP256R1_KEY_LEN;
@@ -342,10 +451,20 @@ static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int s
key_len = ECDSA_SECP192R1_KEY_LEN;
}
TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, key_type));
const char *key_id = "ecdsa_k0";
esp_tee_sec_storage_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_get_pubkey(slot_id, key_type, &pubkey));
esp_tee_sec_storage_key_cfg_t key_cfg = {
.id = key_id,
.type = key_type
};
esp_err_t err = esp_tee_sec_storage_clear_key(key_cfg.id);
TEST_ASSERT_TRUE(err == ESP_OK || err == ESP_ERR_NOT_FOUND);
TEST_ESP_OK(esp_tee_sec_storage_gen_key(&key_cfg));
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
TEST_ESP_OK(esp_tee_sec_storage_ecdsa_get_pubkey(&key_cfg, &pubkey));
mbedtls_mpi r, s;
mbedtls_mpi_init(&r);
@@ -360,7 +479,7 @@ static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int s
esp_ecdsa_pk_conf_t conf = {
.grp_id = gid,
.tee_slot_id = slot_id,
.tee_key_id = key_id,
.load_pubkey = true,
.use_tee_sec_stg_key = true,
};
@@ -371,7 +490,7 @@ static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int s
TEST_ASSERT_MBEDTLS_OK(mbedtls_ecdsa_sign(&ecdsa_context.MBEDTLS_PRIVATE(grp), &r, &s, &key_mpi, sha, SHA256_DIGEST_SZ, NULL, NULL));
esp_tee_sec_storage_sign_t sign = {};
esp_tee_sec_storage_ecdsa_sign_t sign = {};
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_write_binary(&r, sign.sign_r, key_len));
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_write_binary(&s, sign.sign_s, key_len));
@@ -381,13 +500,15 @@ static void test_ecdsa_sign(mbedtls_ecp_group_id gid, const uint8_t *hash, int s
mbedtls_ecdsa_free(&ecdsa_context);
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);
TEST_ESP_OK(esp_tee_sec_storage_clear_key(key_cfg.id));
}
TEST_CASE("Test TEE Secure Storage - mbedtls ECDSA signing", "[mbedtls]")
{
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP256R1, sha, MIN_SEC_STG_SLOT_ID);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP256R1);
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP192R1, sha, MAX_SEC_STG_SLOT_ID);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP192R1);
#endif
}
#endif

View File

@@ -1,7 +0,0 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
tee, app, tee_0, , 192K,
secure_storage, data, tee_sec_stg, , 64K,
factory, app, factory, , 512K,
nvs, data, nvs, , 24K,
custom, data, , , 1M
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 tee, app, tee_0, , 192K,
4 secure_storage, data, tee_sec_stg, , 64K,
5 factory, app, factory, , 512K,
6 nvs, data, nvs, , 24K,
7 custom, data, , , 1M

View File

@@ -3,7 +3,7 @@
tee_0, app, tee_0, , 192K,
tee_1, app, tee_1, , 192K,
tee_otadata, data, tee_ota, , 8K,
secure_storage, data, tee_sec_stg, , 56K,
secure_storage, data, nvs, , 56K,
ota_0, app, ota_0, , 512K,
ota_1, app, ota_1, , 512K,
otadata, data, ota, , 8K,
1 # ESP-IDF Partition Table
3 tee_0, app, tee_0, , 192K,
4 tee_1, app, tee_1, , 192K,
5 tee_otadata, data, tee_ota, , 8K,
6 secure_storage, data, tee_sec_stg, , 56K, secure_storage, data, nvs, , 56K,
7 ota_0, app, ota_0, , 512K,
8 ota_1, app, ota_1, , 512K,
9 otadata, data, ota, , 8K,

View File

@@ -442,7 +442,18 @@ def test_esp_tee_secure_storage(dut: IdfDut) -> None:
# Flash image and erase the secure_storage partition
dut.serial.custom_flash_with_empty_sec_stg()
dut.run_all_single_board_cases(group='secure_storage')
dut.run_all_single_board_cases(group='sec_storage')
@pytest.mark.generic
@idf_parametrize('config', ['ota'], indirect=['config'])
@idf_parametrize('skip_autoflash', ['y'], indirect=['skip_autoflash'])
@idf_parametrize('target', ['esp32c6'], indirect=['target'])
def test_esp_tee_secure_storage_with_host_img(dut: IdfDut) -> None:
# Flash image and write the secure_storage partition with host-generated keys
dut.serial.custom_flash_with_host_gen_sec_stg_img()
dut.run_all_single_board_cases(group='sec_storage_host_keygen')
# ---------------- TEE Attestation tests ----------------

View File

@@ -0,0 +1,5 @@
# Decreasing Bootloader log verbosity
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
# Disabling ATTESTATION
CONFIG_SECURE_TEE_ATTESTATION=n

View File

@@ -11,7 +11,7 @@ CONFIG_SECURE_TEE_LOG_LEVEL_DEBUG=y
CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP192R1_SIGN=y
# secure storage key slot for attestation
CONFIG_SECURE_TEE_ATT_KEY_SLOT_ID=14
CONFIG_SECURE_TEE_ATT_KEY_STR_ID="tee_att_keyN"
# Enabling flash protection over SPI1
CONFIG_SECURE_TEE_EXT_FLASH_MEMPROT_SPI1=y

View File

@@ -7,10 +7,8 @@ CONFIG_SECURE_ENABLE_TEE=y
CONFIG_SECURE_TEE_DEBUG_MODE=y
CONFIG_SECURE_TEE_TEST_MODE=y
# Custom partition table
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
# Setting partition table
CONFIG_PARTITION_TABLE_SINGLE_APP_TEE=y
# TEE IRAM size
CONFIG_SECURE_TEE_IRAM_SIZE=0x8400

View File

@@ -52,6 +52,7 @@ target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/aes/dma/esp_aes.c"
"${COMPONENT_DIR}/port/aes/dma/esp_aes_dma_core.c")
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/aes/esp_aes_common.c"
"${COMPONENT_DIR}/port/aes/esp_aes_xts.c"
"${COMPONENT_DIR}/port/aes/esp_aes_gcm.c")
# SHA implementation

View File

@@ -36,7 +36,9 @@
#define MBEDTLS_CIPHER_C
#define MBEDTLS_AES_C
#define MBEDTLS_GCM_C
#define MBEDTLS_AES_ALT
#define MBEDTLS_GCM_ALT
#define MBEDTLS_CIPHER_MODE_XTS
#define MBEDTLS_ASN1_WRITE_C
#define MBEDTLS_ASN1_PARSE_C

View File

@@ -410,7 +410,7 @@ static int esp_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi* r, mbedtls_mpi* s
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
int esp_ecdsa_tee_load_pubkey(mbedtls_ecp_keypair *keypair, int slot_id)
int esp_ecdsa_tee_load_pubkey(mbedtls_ecp_keypair *keypair, const char *tee_key_id)
{
int ret = -1;
uint16_t len;
@@ -426,8 +426,13 @@ int esp_ecdsa_tee_load_pubkey(mbedtls_ecp_keypair *keypair, int slot_id)
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
esp_tee_sec_storage_pubkey_t pubkey = {};
if (esp_tee_sec_storage_get_pubkey(slot_id, key_type, &pubkey) != ESP_OK) {
esp_tee_sec_storage_key_cfg_t cfg = {
.id = tee_key_id,
.type = key_type
};
esp_tee_sec_storage_ecdsa_pubkey_t pubkey = {};
if (esp_tee_sec_storage_ecdsa_get_pubkey(&cfg, &pubkey) != ESP_OK) {
ESP_LOGE(TAG, "Failed to get public key from secure storage");
goto cleanup;
}
@@ -457,9 +462,9 @@ int esp_ecdsa_tee_set_pk_context(mbedtls_pk_context *key_ctx, esp_ecdsa_pk_conf_
return ret;
}
int slot_id = conf->tee_slot_id;
if (slot_id < MIN_SEC_STG_SLOT_ID || slot_id > MAX_SEC_STG_SLOT_ID) {
ESP_LOGE(TAG, "Invalid slot id");
const char *key_id = conf->tee_key_id;
if (!key_id) {
ESP_LOGE(TAG, "Invalid TEE key id");
return ret;
}
@@ -477,8 +482,14 @@ int esp_ecdsa_tee_set_pk_context(mbedtls_pk_context *key_ctx, esp_ecdsa_pk_conf_
mbedtls_mpi_init(&(keypair->MBEDTLS_PRIVATE(d)));
keypair->MBEDTLS_PRIVATE(d).MBEDTLS_PRIVATE(s) = ECDSA_KEY_MAGIC_TEE;
keypair->MBEDTLS_PRIVATE(d).MBEDTLS_PRIVATE(n) = slot_id;
keypair->MBEDTLS_PRIVATE(d).MBEDTLS_PRIVATE(p) = NULL;
keypair->MBEDTLS_PRIVATE(d).MBEDTLS_PRIVATE(n) = 1;
mbedtls_mpi_uint *key_id_mpi = malloc(sizeof(mbedtls_mpi_uint));
if (!key_id_mpi) {
return -1;
}
key_id_mpi[0] = (mbedtls_mpi_uint)(uintptr_t)key_id;
keypair->MBEDTLS_PRIVATE(d).MBEDTLS_PRIVATE(p) = key_id_mpi;
if ((ret = mbedtls_ecp_group_load(&(keypair->MBEDTLS_PRIVATE(grp)), conf->grp_id)) != 0) {
ESP_LOGE(TAG, "Loading ecp group failed, mbedtls_pk_ec() returned %d", ret);
@@ -486,7 +497,7 @@ int esp_ecdsa_tee_set_pk_context(mbedtls_pk_context *key_ctx, esp_ecdsa_pk_conf_
}
if (conf->load_pubkey) {
if ((ret = esp_ecdsa_tee_load_pubkey(keypair, slot_id)) != 0) {
if ((ret = esp_ecdsa_tee_load_pubkey(keypair, key_id)) != 0) {
ESP_LOGE(TAG, "Loading public key context failed, esp_ecdsa_load_pubkey() returned %d", ret);
return ret;
}
@@ -525,10 +536,15 @@ static int esp_ecdsa_tee_sign(mbedtls_ecp_group *grp, mbedtls_mpi* r, mbedtls_mp
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
int slot_id = d->MBEDTLS_PRIVATE(n);
const char *key_id = (const char *)(uintptr_t)(d->MBEDTLS_PRIVATE(p)[0]);
esp_tee_sec_storage_sign_t sign = {};
esp_err_t err = esp_tee_sec_storage_get_signature(slot_id, key_type, (uint8_t *)msg, msg_len, &sign);
esp_tee_sec_storage_key_cfg_t cfg = {
.id = key_id,
.type = key_type
};
esp_tee_sec_storage_ecdsa_sign_t sign = {};
esp_err_t err = esp_tee_sec_storage_ecdsa_sign(&cfg, (uint8_t *)msg, msg_len, &sign);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get signature");
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;

View File

@@ -29,8 +29,8 @@ typedef struct {
mbedtls_ecp_group_id grp_id; /*!< MbedTLS ECP group identifier */
union {
uint8_t efuse_block; /*!< EFuse block id for ECDSA private key */
uint8_t tee_slot_id; /*!< TEE secure storage slot id for ECDSA private key */
}; /*!< Union to hold either EFuse block id or TEE secure storage slot id for ECDSA private key */
const char *tee_key_id; /*!< TEE secure storage key id for ECDSA private key */
}; /*!< Union to hold either EFuse block id or TEE secure storage key id for ECDSA private key */
#if SOC_ECDSA_SUPPORT_EXPORT_PUBKEY || CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
bool load_pubkey; /*!< Export ECDSA public key from the hardware */
@@ -120,11 +120,11 @@ int esp_ecdsa_set_pk_context(mbedtls_pk_context *key_ctx, esp_ecdsa_pk_conf_t *c
* the TEE secure storage.
*
* @param keypair The mbedtls ECP key-pair structure
* @param slot_id The TEE secure storage slot id that holds the private key.
* @param tee_key_id The TEE secure storage key id of the private key
*
* @return - 0 if successful else MBEDTLS_ERR_ECP_BAD_INPUT_DATA
*/
int esp_ecdsa_tee_load_pubkey(mbedtls_ecp_keypair *keypair, int slot_id);
int esp_ecdsa_tee_load_pubkey(mbedtls_ecp_keypair *keypair, const char *tee_key_id);
/**
* @brief Initialize PK context and fully populate the mbedtls_ecp_keypair context.

View File

@@ -1,3 +1,5 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
if(BOOTLOADER_BUILD)
# bootloader build simplified version
set(srcs "src/nvs_bootloader.c"
@@ -13,8 +15,33 @@ if(BOOTLOADER_BUILD)
PRIV_INCLUDE_DIRS "private_include"
)
elseif(esp_tee_build)
# esp-tee build simplified version
set(srcs "src/nvs_api.cpp"
"src/nvs_item_hash_list.cpp"
"src/nvs_page.cpp"
"src/nvs_pagemanager.cpp"
"src/nvs_storage.cpp"
"src/nvs_handle_simple.cpp"
"src/nvs_handle_locked.cpp"
"src/nvs_partition.cpp"
"src/nvs_encrypted_partition.cpp"
"src/nvs_partition_lookup.cpp"
"src/nvs_partition_manager.cpp"
"src/nvs_types.cpp"
"src/nvs_platform.cpp")
set(requires esp_partition mbedtls)
set(priv_requires spi_flash newlib cxx)
idf_component_register(SRCS "${srcs}"
REQUIRES "${requires}"
PRIV_REQUIRES "${priv_requires}"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "private_include")
else()
# regular, non bootloader build
# regular, OS build
idf_build_get_property(target IDF_TARGET)
set(srcs "src/nvs_api.cpp"
@@ -66,4 +93,4 @@ else()
target_compile_options(${COMPONENT_LIB} PUBLIC "-fno-analyzer")
endif()
endif() #bootloader build
endif() #non-OS build

View File

@@ -129,6 +129,7 @@ extern "C" esp_err_t nvs_flash_init_partition(const char *part_name)
}
Lock lock;
assert(nvs::Page::SEC_SIZE == esp_partition_get_main_flash_sector_size());
return NVSPartitionManager::get_instance()->init_partition(part_name);
}
@@ -169,6 +170,7 @@ extern "C" esp_err_t nvs_flash_secure_init_partition(const char *part_name, nvs_
}
Lock lock;
assert(nvs::Page::SEC_SIZE == esp_partition_get_main_flash_sector_size());
return NVSPartitionManager::get_instance()->secure_init_partition(part_name, cfg);
}

View File

@@ -5,7 +5,9 @@
*/
#include <cstdlib>
#if !ESP_TEE_BUILD
#include "esp_heap_caps.h"
#endif
#pragma once

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -15,7 +15,7 @@ namespace nvs {
Page::Page() : mPartition(nullptr) { }
const uint32_t nvs::Page::SEC_SIZE = esp_partition_get_main_flash_sector_size();
const uint32_t nvs::Page::SEC_SIZE = 4096;
uint32_t Page::Header::calculateCrc32()
{

View File

@@ -7,7 +7,7 @@
using namespace nvs;
#ifdef LINUX_TARGET
#if LINUX_TARGET || ESP_TEE_BUILD
Lock::Lock() {}
Lock::~Lock() {}
esp_err_t nvs::Lock::init() {return ESP_OK;}

View File

@@ -18,9 +18,9 @@ import re
import struct
import sys
MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
MD5_PARTITION_BEGIN = b'\xEB\xEB' + b'\xFF' * 14 # The first 2 bytes are like magic numbers for MD5 sum
PARTITION_TABLE_SIZE = 0x1000 # Size of partition table
MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature
MD5_PARTITION_BEGIN = b'\xeb\xeb' + b'\xff' * 14 # The first 2 bytes are like magic numbers for MD5 sum
PARTITION_TABLE_SIZE = 0x1000 # Size of partition table
MIN_PARTITION_SUBTYPE_APP_OTA = 0x10
NUM_PARTITION_SUBTYPE_APP_OTA = 16
@@ -49,7 +49,7 @@ NVS_RW_MIN_PARTITION_SIZE = 0x3000
def get_ptype_as_int(ptype):
""" Convert a string which might be numeric or the name of a partition type to an integer """
"""Convert a string which might be numeric or the name of a partition type to an integer"""
try:
return TYPES[ptype]
except KeyError:
@@ -87,13 +87,12 @@ SUBTYPES = {
'spiffs': 0x82,
'littlefs': 0x83,
'tee_ota': 0x90,
'tee_sec_stg': 0x91,
},
}
def get_subtype_as_int(ptype, subtype):
""" Convert a string which might be numeric or the name of a partition subtype to an integer """
"""Convert a string which might be numeric or the name of a partition subtype to an integer"""
try:
return SUBTYPES[get_ptype_as_int(ptype)][subtype]
except KeyError:
@@ -149,7 +148,7 @@ def add_extra_subtypes(csv):
try:
fields = [line.strip() for line in line_no.split(',')]
for subtype, subtype_values in SUBTYPES.items():
if (int(fields[2], 16) in subtype_values.values() and subtype == get_partition_type(fields[0])):
if int(fields[2], 16) in subtype_values.values() and subtype == get_partition_type(fields[0]):
raise ValueError('Found duplicate value in partition subtype')
SUBTYPES[TYPES[fields[0]]][fields[1]] = int(fields[2], 16)
except InputError as err:
@@ -165,13 +164,13 @@ recovery_bootloader_offset = None
def status(msg):
""" Print status message to stderr """
"""Print status message to stderr"""
if not quiet:
critical(msg)
def critical(msg):
""" Print critical message to stderr """
"""Print critical message to stderr"""
sys.stderr.write(msg)
sys.stderr.write('\n')
@@ -211,7 +210,10 @@ class PartitionTable(list):
try:
res.append(PartitionDefinition.from_csv(line, line_no + 1))
except InputError as err:
raise InputError('Error at line %d: %s\nPlease check extra_partition_subtypes.inc file in build/config directory' % (line_no + 1, err))
raise InputError(
'Error at line %d: %s\nPlease check extra_partition_subtypes.inc file in build/config directory'
% (line_no + 1, err)
)
except Exception:
critical('Unexpected error parsing CSV line %d: %s' % (line_no + 1, line))
raise
@@ -219,20 +221,23 @@ class PartitionTable(list):
# fix up missing offsets & negative sizes
last_end = offset_part_table + PARTITION_TABLE_SIZE # first offset after partition table
for e in res:
is_primary_bootloader = (e.type == BOOTLOADER_TYPE and e.subtype == SUBTYPES[e.type]['primary'])
is_primary_partition_table = (e.type == PARTITION_TABLE_TYPE and e.subtype == SUBTYPES[e.type]['primary'])
is_primary_bootloader = e.type == BOOTLOADER_TYPE and e.subtype == SUBTYPES[e.type]['primary']
is_primary_partition_table = e.type == PARTITION_TABLE_TYPE and e.subtype == SUBTYPES[e.type]['primary']
if is_primary_bootloader or is_primary_partition_table:
# They do not participate in the restoration of missing offsets
continue
if e.offset is not None and e.offset < last_end:
if e == res[0]:
raise InputError('CSV Error at line %d: Partitions overlap. Partition sets offset 0x%x. '
'But partition table occupies the whole sector 0x%x. '
'Use a free offset 0x%x or higher.'
% (e.line_no, e.offset, offset_part_table, last_end))
raise InputError(
'CSV Error at line %d: Partitions overlap. Partition sets offset 0x%x. '
'But partition table occupies the whole sector 0x%x. '
'Use a free offset 0x%x or higher.' % (e.line_no, e.offset, offset_part_table, last_end)
)
else:
raise InputError('CSV Error at line %d: Partitions overlap. Partition sets offset 0x%x. Previous partition ends 0x%x'
% (e.line_no, e.offset, last_end))
raise InputError(
'CSV Error at line %d: Partitions overlap. Partition sets offset 0x%x. '
'Previous partition ends 0x%x' % (e.line_no, e.offset, last_end)
)
if e.offset is None:
pad_to = get_alignment_offset_for_type(e.type)
if last_end % pad_to != 0:
@@ -245,8 +250,8 @@ class PartitionTable(list):
return res
def __getitem__(self, item):
""" Allow partition table access via name as well as by
numeric index. """
"""Allow partition table access via name as well as by
numeric index."""
if isinstance(item, str):
for x in self:
if x.name == item:
@@ -256,8 +261,8 @@ class PartitionTable(list):
return super(PartitionTable, self).__getitem__(item)
def find_by_type(self, ptype, subtype):
""" Return a partition by type & subtype, returns
None if not found """
"""Return a partition by type & subtype, returns
None if not found"""
# convert ptype & subtypes names (if supplied this way) to integer values
ptype = get_ptype_as_int(ptype)
subtype = get_subtype_as_int(ptype, subtype)
@@ -285,21 +290,25 @@ class PartitionTable(list):
# print sorted duplicate partitions by name
if len(duplicates) != 0:
critical('A list of partitions that have the same name:')
for p in sorted(self, key=lambda x:x.name):
for p in sorted(self, key=lambda x: x.name):
if len(duplicates.intersection([p.name])) != 0:
critical('%s' % (p.to_csv()))
raise InputError('Partition names must be unique')
# check for overlaps
last = None
for p in sorted(self, key=lambda x:x.offset):
for p in sorted(self, key=lambda x: x.offset):
if p.offset < offset_part_table + PARTITION_TABLE_SIZE:
is_primary_bootloader = (p.type == BOOTLOADER_TYPE and p.subtype == SUBTYPES[p.type]['primary'])
is_primary_partition_table = (p.type == PARTITION_TABLE_TYPE and p.subtype == SUBTYPES[p.type]['primary'])
is_primary_bootloader = p.type == BOOTLOADER_TYPE and p.subtype == SUBTYPES[p.type]['primary']
is_primary_partition_table = p.type == PARTITION_TABLE_TYPE and p.subtype == SUBTYPES[p.type]['primary']
if not (is_primary_bootloader or is_primary_partition_table):
raise InputError('Partition offset 0x%x is below 0x%x' % (p.offset, offset_part_table + PARTITION_TABLE_SIZE))
raise InputError(
'Partition offset 0x%x is below 0x%x' % (p.offset, offset_part_table + PARTITION_TABLE_SIZE)
)
if last is not None and p.offset < last.offset + last.size:
raise InputError('Partition at 0x%x overlaps 0x%x-0x%x' % (p.offset, last.offset, last.offset + last.size - 1))
raise InputError(
'Partition at 0x%x overlaps 0x%x-0x%x' % (p.offset, last.offset, last.offset + last.size - 1)
)
last = p
# check that otadata should be unique
@@ -307,7 +316,10 @@ class PartitionTable(list):
if len(otadata_duplicates) > 1:
for p in otadata_duplicates:
critical('%s' % (p.to_csv()))
raise InputError('Found multiple otadata partitions. Only one partition can be defined with type="data"(1) and subtype="ota"(0).')
raise InputError(
'Found multiple otadata partitions. Only one partition can be defined with '
'type="data"(1) and subtype="ota"(0).'
)
if len(otadata_duplicates) == 1 and otadata_duplicates[0].size != 0x2000:
p = otadata_duplicates[0]
@@ -315,11 +327,16 @@ class PartitionTable(list):
raise InputError('otadata partition must have size = 0x2000')
# Above checks but for TEE otadata
otadata_duplicates = [p for p in self if p.type == TYPES['data'] and p.subtype == SUBTYPES[DATA_TYPE]['tee_ota']]
otadata_duplicates = [
p for p in self if p.type == TYPES['data'] and p.subtype == SUBTYPES[DATA_TYPE]['tee_ota']
]
if len(otadata_duplicates) > 1:
for p in otadata_duplicates:
critical('%s' % (p.to_csv()))
raise InputError('Found multiple TEE otadata partitions. Only one partition can be defined with type="data"(1) and subtype="tee_ota"(0x90).')
raise InputError(
'Found multiple TEE otadata partitions. Only one partition can be defined with '
'type="data"(1) and subtype="tee_ota"(0x90).'
)
if len(otadata_duplicates) == 1 and otadata_duplicates[0].size != 0x2000:
p = otadata_duplicates[0]
@@ -327,8 +344,8 @@ class PartitionTable(list):
raise InputError('TEE otadata partition must have size = 0x2000')
def flash_size(self):
""" Return the size that partitions will occupy in flash
(ie the offset the last partition ends at)
"""Return the size that partitions will occupy in flash
(ie the offset the last partition ends at)
"""
try:
last = sorted(self, reverse=True)[0]
@@ -337,31 +354,36 @@ class PartitionTable(list):
return last.offset + last.size
def verify_size_fits(self, flash_size_bytes: int) -> None:
""" Check that partition table fits into the given flash size.
Raises InputError otherwise.
"""Check that partition table fits into the given flash size.
Raises InputError otherwise.
"""
table_size = self.flash_size()
if flash_size_bytes < table_size:
mb = 1024 * 1024
raise InputError('Partitions tables occupies %.1fMB of flash (%d bytes) which does not fit in configured '
"flash size %dMB. Change the flash size in menuconfig under the 'Serial Flasher Config' menu." %
(table_size / mb, table_size, flash_size_bytes / mb))
raise InputError(
'Partitions tables occupies %.1fMB of flash (%d bytes) which does not fit in configured '
"flash size %dMB. Change the flash size in menuconfig under the 'Serial Flasher Config' menu."
% (table_size / mb, table_size, flash_size_bytes / mb)
)
@classmethod
def from_binary(cls, b):
md5 = hashlib.md5()
result = cls()
for o in range(0,len(b),32):
data = b[o:o + 32]
for o in range(0, len(b), 32):
data = b[o : o + 32]
if len(data) != 32:
raise InputError('Partition table length must be a multiple of 32 bytes')
if data == b'\xFF' * 32:
if data == b'\xff' * 32:
return result # got end marker
if md5sum and data[:2] == MD5_PARTITION_BEGIN[:2]: # check only the magic number part
if data[16:] == md5.digest():
continue # the next iteration will check for the end marker
else:
raise InputError("MD5 checksums don't match! (computed: 0x%s, parsed: 0x%s)" % (md5.hexdigest(), binascii.hexlify(data[16:])))
raise InputError(
"MD5 checksums don't match! (computed: 0x%s, parsed: 0x%s)"
% (md5.hexdigest(), binascii.hexlify(data[16:]))
)
else:
md5.update(data)
result.append(PartitionDefinition.from_binary(data))
@@ -373,25 +395,21 @@ class PartitionTable(list):
result += MD5_PARTITION_BEGIN + hashlib.md5(result).digest()
if len(result) >= MAX_PARTITION_LENGTH:
raise InputError('Binary partition table length (%d) longer than max' % len(result))
result += b'\xFF' * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
result += b'\xff' * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing
return result
def to_csv(self, simple_formatting=False):
rows = ['# ESP-IDF Partition Table',
'# Name, Type, SubType, Offset, Size, Flags']
rows = ['# ESP-IDF Partition Table', '# Name, Type, SubType, Offset, Size, Flags']
rows += [x.to_csv(simple_formatting) for x in self]
return '\n'.join(rows) + '\n'
class PartitionDefinition(object):
MAGIC_BYTES = b'\xAA\x50'
MAGIC_BYTES = b'\xaa\x50'
# dictionary maps flag name (as used in CSV flags list, property name)
# to bit set in flags words in binary format
FLAGS = {
'encrypted': 0,
'readonly': 1
}
FLAGS = {'encrypted': 0, 'readonly': 1}
# add subtypes for the 16 OTA slot values ("ota_XX, etc.")
for ota_slot in range(NUM_PARTITION_SUBTYPE_APP_OTA):
@@ -412,7 +430,7 @@ class PartitionDefinition(object):
@classmethod
def from_csv(cls, line, line_no):
""" Parse a line from the CSV """
"""Parse a line from the CSV"""
line_w_defaults = line + ',,,,' # lazy way to support default fields
fields = [f.strip() for f in line_w_defaults.split(',')]
@@ -436,18 +454,34 @@ class PartitionDefinition(object):
return res
def __eq__(self, other):
return self.name == other.name and self.type == other.type \
and self.subtype == other.subtype and self.offset == other.offset \
return (
self.name == other.name
and self.type == other.type
and self.subtype == other.subtype
and self.offset == other.offset
and self.size == other.size
)
def __repr__(self):
def maybe_hex(x):
return '0x%x' % x if x is not None else 'None'
return "PartitionDefinition('%s', 0x%x, 0x%x, %s, %s)" % (self.name, self.type, self.subtype or 0,
maybe_hex(self.offset), maybe_hex(self.size))
return "PartitionDefinition('%s', 0x%x, 0x%x, %s, %s)" % (
self.name,
self.type,
self.subtype or 0,
maybe_hex(self.offset),
maybe_hex(self.size),
)
def __str__(self):
return "Part '%s' %d/%d @ 0x%x size 0x%x" % (self.name, self.type, self.subtype, self.offset or -1, self.size or -1)
return "Part '%s' %d/%d @ 0x%x size 0x%x" % (
self.name,
self.type,
self.subtype,
self.offset or -1,
self.size or -1,
)
def __cmp__(self, other):
return self.offset - other.offset
@@ -479,7 +513,7 @@ class PartitionDefinition(object):
def parse_size(self, strval, ptype):
if ptype == BOOTLOADER_TYPE:
if primary_bootloader_offset is None:
raise InputError(f'Primary bootloader offset is not defined. Please use --primary-bootloader-offset')
raise InputError('Primary bootloader offset is not defined. Please use --primary-bootloader-offset')
return offset_part_table - primary_bootloader_offset
if ptype == PARTITION_TABLE_TYPE:
return PARTITION_TABLE_SIZE
@@ -491,11 +525,13 @@ class PartitionDefinition(object):
if ptype == BOOTLOADER_TYPE:
if psubtype == SUBTYPES[ptype]['primary']:
if primary_bootloader_offset is None:
raise InputError(f'Primary bootloader offset is not defined. Please use --primary-bootloader-offset')
raise InputError('Primary bootloader offset is not defined. Please use --primary-bootloader-offset')
return primary_bootloader_offset
if psubtype == SUBTYPES[ptype]['recovery']:
if recovery_bootloader_offset is None:
raise InputError(f'Recovery bootloader offset is not defined. Please use --recovery-bootloader-offset')
raise InputError(
'Recovery bootloader offset is not defined. Please use --recovery-bootloader-offset'
)
return recovery_bootloader_offset
if ptype == PARTITION_TABLE_TYPE and psubtype == SUBTYPES[ptype]['primary']:
return offset_part_table
@@ -521,24 +557,36 @@ class PartitionDefinition(object):
raise ValidationError(self, 'Size 0x%x is not aligned to 0x%x' % (self.size, size_align))
if self.name in TYPES and TYPES.get(self.name, '') != self.type:
critical("WARNING: Partition has name '%s' which is a partition type, but does not match this partition's "
'type (0x%x). Mistake in partition table?' % (self.name, self.type))
critical(
"WARNING: Partition has name '%s' which is a partition type, but does not match this partition's "
'type (0x%x). Mistake in partition table?' % (self.name, self.type)
)
all_subtype_names = []
for names in (t.keys() for t in SUBTYPES.values()):
all_subtype_names += names
if self.name in all_subtype_names and SUBTYPES.get(self.type, {}).get(self.name, '') != self.subtype:
critical("WARNING: Partition has name '%s' which is a partition subtype, but this partition has "
'non-matching type 0x%x and subtype 0x%x. Mistake in partition table?' % (self.name, self.type, self.subtype))
critical(
"WARNING: Partition has name '%s' which is a partition subtype, but this partition has "
'non-matching type 0x%x and subtype 0x%x. Mistake in partition table?'
% (self.name, self.type, self.subtype)
)
always_rw_data_subtypes = [SUBTYPES[DATA_TYPE]['ota'], SUBTYPES[DATA_TYPE]['coredump']]
if self.type == TYPES['data'] and self.subtype in always_rw_data_subtypes and self.readonly is True:
raise ValidationError(self, "'%s' partition of type %s and subtype %s is always read-write and cannot be read-only" %
(self.name, self.type, self.subtype))
raise ValidationError(
self,
"'%s' partition of type %s and subtype %s is always read-write and cannot be read-only"
% (self.name, self.type, self.subtype),
)
if self.type == TYPES['data'] and self.subtype == SUBTYPES[DATA_TYPE]['nvs']:
if self.size < NVS_RW_MIN_PARTITION_SIZE and self.readonly is False:
raise ValidationError(self, """'%s' partition of type %s and subtype %s of this size (0x%x) must be flagged as 'readonly' \
(the size of read/write NVS has to be at least 0x%x)""" % (self.name, self.type, self.subtype, self.size, NVS_RW_MIN_PARTITION_SIZE))
raise ValidationError(
self,
"""'%s' partition of type %s and subtype %s of this size (0x%x) must be flagged as 'readonly' \
(the size of read/write NVS has to be at least 0x%x)"""
% (self.name, self.type, self.subtype, self.size, NVS_RW_MIN_PARTITION_SIZE),
)
STRUCT_FORMAT = b'<2sBBLL16sL'
@@ -547,14 +595,13 @@ class PartitionDefinition(object):
if len(b) != 32:
raise InputError('Partition definition length must be exactly 32 bytes. Got %d bytes.' % len(b))
res = cls()
(magic, res.type, res.subtype, res.offset,
res.size, res.name, flags) = struct.unpack(cls.STRUCT_FORMAT, b)
(magic, res.type, res.subtype, res.offset, res.size, res.name, flags) = struct.unpack(cls.STRUCT_FORMAT, b)
if b'\x00' in res.name: # strip null byte padding from name string
res.name = res.name[:res.name.index(b'\x00')]
res.name = res.name[: res.name.index(b'\x00')]
res.name = res.name.decode()
if magic != cls.MAGIC_BYTES:
raise InputError('Invalid magic bytes (%r) for partition definition' % magic)
for flag,bit in cls.FLAGS.items():
for flag, bit in cls.FLAGS.items():
if flags & (1 << bit):
setattr(res, flag, True)
flags &= ~(1 << bit)
@@ -567,37 +614,45 @@ class PartitionDefinition(object):
def to_binary(self):
flags = sum((1 << self.FLAGS[flag]) for flag in self.get_flags_list())
return struct.pack(self.STRUCT_FORMAT,
self.MAGIC_BYTES,
self.type, self.subtype,
self.offset, self.size,
self.name.encode(),
flags)
return struct.pack(
self.STRUCT_FORMAT,
self.MAGIC_BYTES,
self.type,
self.subtype,
self.offset,
self.size,
self.name.encode(),
flags,
)
def to_csv(self, simple_formatting=False):
def addr_format(a, include_sizes):
if not simple_formatting and include_sizes:
for (val, suffix) in [(0x100000, 'M'), (0x400, 'K')]:
for val, suffix in [(0x100000, 'M'), (0x400, 'K')]:
if a % val == 0:
return '%d%s' % (a // val, suffix)
return '0x%x' % a
def lookup_keyword(t, keywords):
for k,v in keywords.items():
for k, v in keywords.items():
if simple_formatting is False and t == v:
return k
return '%d' % t
def generate_text_flags():
""" colon-delimited list of flags """
"""colon-delimited list of flags"""
return ':'.join(self.get_flags_list())
return ','.join([self.name,
lookup_keyword(self.type, TYPES),
lookup_keyword(self.subtype, SUBTYPES.get(self.type, {})),
addr_format(self.offset, False),
addr_format(self.size, True),
generate_text_flags()])
return ','.join(
[
self.name,
lookup_keyword(self.type, TYPES),
lookup_keyword(self.subtype, SUBTYPES.get(self.type, {})),
addr_format(self.offset, False),
addr_format(self.size, True),
generate_text_flags(),
]
)
def parse_int(v, keywords={}):
@@ -627,21 +682,42 @@ def main():
global recovery_bootloader_offset
parser = argparse.ArgumentParser(description='ESP32 partition table utility')
parser.add_argument('--flash-size', help='Optional flash size limit, checks partition table fits in flash',
nargs='?', choices=['1MB', '2MB', '4MB', '8MB', '16MB', '32MB', '64MB', '128MB'])
parser.add_argument('--disable-md5sum', help='Disable md5 checksum for the partition table', default=False, action='store_true')
parser.add_argument(
'--flash-size',
help='Optional flash size limit, checks partition table fits in flash',
nargs='?',
choices=['1MB', '2MB', '4MB', '8MB', '16MB', '32MB', '64MB', '128MB'],
)
parser.add_argument(
'--disable-md5sum', help='Disable md5 checksum for the partition table', default=False, action='store_true'
)
parser.add_argument('--no-verify', help="Don't verify partition table fields", action='store_true')
parser.add_argument('--verify', '-v', help='Verify partition table fields (deprecated, this behaviour is '
'enabled by default and this flag does nothing.', action='store_true')
parser.add_argument(
'--verify',
'-v',
help='Verify partition table fields (deprecated, this behaviour is '
'enabled by default and this flag does nothing.',
action='store_true',
)
parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true')
parser.add_argument('--offset', '-o', help='Set offset partition table', default='0x8000')
parser.add_argument('--primary-bootloader-offset', help='Set primary bootloader offset', default=None)
parser.add_argument('--recovery-bootloader-offset', help='Set recovery bootloader offset', default=None)
parser.add_argument('--secure', help='Require app partitions to be suitable for secure boot', nargs='?', const=SECURE_V1, choices=[SECURE_V1, SECURE_V2])
parser.add_argument(
'--secure',
help='Require app partitions to be suitable for secure boot',
nargs='?',
const=SECURE_V1,
choices=[SECURE_V1, SECURE_V2],
)
parser.add_argument('--extra-partition-subtypes', help='Extra partition subtype entries', nargs='*')
parser.add_argument('input', help='Path to CSV or binary file to parse.', type=argparse.FileType('rb'))
parser.add_argument('output', help='Path to output converted binary or CSV file. Will use stdout if omitted.',
nargs='?', default='-')
parser.add_argument(
'output',
help='Path to output converted binary or CSV file. Will use stdout if omitted.',
nargs='?',
default='-',
)
args = parser.parse_args()
@@ -702,8 +778,7 @@ class InputError(RuntimeError):
class ValidationError(InputError):
def __init__(self, partition, message):
super(ValidationError, self).__init__(
'Partition %s invalid: %s' % (partition.name, message))
super(ValidationError, self).__init__('Partition %s invalid: %s' % (partition.name, message))
if __name__ == '__main__':

View File

@@ -1,7 +1,7 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
tee, app, tee_0, , 192K,
secure_storage, data, tee_sec_stg, , 64K,
secure_storage, data, nvs, , 64K,
factory, app, factory, , 1536K,
nvs, data, nvs, , 24K,
phy_init, data, phy, , 4K,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 tee, app, tee_0, , 192K,
4 secure_storage, data, tee_sec_stg, , 64K, secure_storage, data, nvs, , 64K,
5 factory, app, factory, , 1536K,
6 nvs, data, nvs, , 24K,
7 phy_init, data, phy, , 4K,

View File

@@ -3,7 +3,7 @@
tee_0, app, tee_0, , 192K,
tee_1, app, tee_1, , 192K,
tee_otadata, data, tee_ota, , 8K,
secure_storage, data, tee_sec_stg, , 56K,
secure_storage, data, nvs, , 56K,
ota_0, app, ota_0, , 1536K,
ota_1, app, ota_1, , 1536K,
otadata, data, ota, , 8K,
1 # Name, Type, SubType, Offset, Size, Flags
3 tee_0, app, tee_0, , 192K,
4 tee_1, app, tee_1, , 192K,
5 tee_otadata, data, tee_ota, , 8K,
6 secure_storage, data, tee_sec_stg, , 56K, secure_storage, data, nvs, , 56K,
7 ota_0, app, ota_0, , 1536K,
8 ota_1, app, ota_1, , 1536K,
9 otadata, data, ota, , 8K,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -185,7 +185,6 @@ See enum :cpp:type:`esp_partition_subtype_t` for the full list of subtypes defin
.. only:: esp32c6
- ``tee_ota`` (0x90) is the :ref:`TEE OTA data partition <tee-ota-data-partition>` which stores information about the currently selected TEE OTA app slot. This partition should be 0x2000 bytes in size. Refer to the :doc:`TEE OTA documentation <../security/tee/tee-ota>` for more details.
- ``tee_sec_stg`` (0x91) is the TEE secure storage partition which stores encrypted data that can only be accessed by the TEE application. This partition is used by the :doc:`TEE Secure Storage <../security/tee/tee-sec-storage>` to store sensitive data like cryptographic keys. The size of this partition depends on the application requirements.
- There are other predefined data subtypes for data storage supported by ESP-IDF. These include:

View File

@@ -14,6 +14,7 @@ Data stored in NVS partitions can be encrypted using XTS-AES in the manner simil
NVS encryption can be facilitated by enabling :ref:`CONFIG_NVS_ENCRYPTION` and :ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` > ``CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC`` or ``CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` depending on the scheme to be used.
.. _nvs_encr_flash_enc_scheme:
NVS Encryption: Flash Encryption-Based Scheme
---------------------------------------------
@@ -104,6 +105,8 @@ It is possible for an application to use different keys for different NVS partit
.. only:: SOC_HMAC_SUPPORTED
.. _nvs_encr_hmac_scheme:
NVS Encryption: HMAC Peripheral-Based Scheme
--------------------------------------------

View File

@@ -88,7 +88,7 @@ EAT: Header
- Currently, only ``ecdsa_secp256r1_sha256`` is supported
* - Key ID
- Identifies the key to be utilized by the device for encryption and signing
- TEE secure storage slot ID
- TEE secure storage key ID (string)
EAT: Claim Table
^^^^^^^^^^^^^^^^
@@ -173,7 +173,7 @@ Sample EAT in JSON format
"magic": "44fef7cc",
"encr_alg": "",
"sign_alg": "ecdsa_secp256r1_sha256",
"key_id": 0
"key_id": "tee_att_key0"
},
"eat": {
"nonce": -1582119980,

View File

@@ -3,105 +3,72 @@ Secure Storage
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.
The TEE Secure Storage service offers a persistent storage for securely holding sensitive data, such as cryptographic keys, cloud credentials or any other general-purpose information. It utilizes a dedicated flash partition of type ``data`` and subtype ``tee_sec_stg``. The confidentiality and integrity of the data is ensured by the TEE.
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>`.
For enhanced security, data stored in the secure storage is encrypted using a device-specific encryption key with ``AES-256-GCM`` algorithm. Additionally, the secure storage provides interfaces for performing the following cryptographic services from the TEE using securely stored key material:
.. important::
#. Message signing and public key retrieval with the ``ecdsa_secp256r1`` algorithm
#. Authenticated encryption and decryption using the ``aes256_gcm`` algorithm
- 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.
Additionally, the secure storage provides interfaces for performing the following cryptographic services from the TEE using securely stored key material:
#. Message signing and public key retrieval using the ``ecdsa_secp256r1`` and ``ecdsa_secp192r1`` algorithms
#. Authenticated encryption and decryption using the ``aes256_gcm`` algorithm
.. note::
As per the current implementation, the TEE Secure Storage partition **must** have the label ``secure_storage``.
Internals
---------
The secure storage partition is 4 KB in size, of which only the first half is used for storing data. The partition is divided into slots which hold data objects. Each data object within the TEE secure storage is encapsulated in a structured format, comprising the metadata and actual data.
.. figure:: ../../../_static/esp_tee/tee_sec_stg_part.png
:align: center
:scale: 80%
:alt: TEE Secure storage partition
:figclass: align-center
ESP-TEE: Secure Storage partition
Metadata is represented by the :cpp:type:`sec_stg_metadata_t` structure, which contains details related to the data stored in a specific slot of the storage. These details include information such as the owner, slot ID, data length, encryption parameters, etc.
.. list-table::
:header-rows: 1
:widths: 35 65
:align: center
* - **Element**
- **Description**
* - Owner ID
- Application ID defining the data ownership
* - Slot ID
- Slot ID for corresponding owner ID
* - Encryption: Initialization Vector (IV)
- IV for the encryption algorithm
* - Encryption: Tag
- Tag for the encryption algorithm
* - Data Type
- Type of data stored in this slot
* - Data Length
- Actual data length
.. figure:: ../../../_static/esp_tee/tee_sec_stg_metadata.png
:align: center
:scale: 80%
:alt: TEE Secure storage metadata
:figclass: align-center
ESP-TEE: Secure Storage Metadata
.. warning::
Future ESP-TEE framework releases may modify the internal data structure of the TEE secure storage, which could introduce breaking changes in existing applications.
Each data object in the secure storage is encrypted as specified in the **AES-GCM based AEAD** encryption policy with a platform instance unique key of length **256 bits**, stored in the eFuse.
The TEE Secure Storage feature supports two modes (:ref:`CONFIG_SECURE_TEE_SEC_STG_MODE`) for determining which eFuse block stores the encryption key:
- **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 :ref:`CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK` Kconfig option.
All the assets pertaining to the TEE secure storage are protected by the APM peripheral and thus, are inaccessible to the REE application. Any attempt to directly access them would result in a system fault.
Each data object consisting of the type, associated metadata flags (e.g., ``WRITE_ONCE``), and the actual payload is encapsulated in a structured format and stored as a variable-length NVS blob in the secure storage partition.
.. note::
- Currently, the TEE secure storage supports the storage of two types of cryptographic keys:
As per the current implementation, all data objects in the TEE Secure Storage are to be stored in the ``tee_sec_stg_ns`` namespace.
#. ``ecdsa_secp256r1`` curve key-pairs, including the private and public key components
#. ``aes256_gcm`` keys, including the key and initialization vector (IV)
Currently, TEE secure storage supports storing the following cryptographic keys:
The internal structures for these key types are as follows:
#. ``ecdsa_secp256r1`` and ``ecdsa_secp192r1`` curve key-pairs, including private and public key components
#. ``aes256`` keys, including the key and initialization vector (IV)
.. code-block:: c
All assets related to TEE secure storage are protected by the APM peripheral and are inaccessible to the REE application. Any direct access attempts will result in a system fault. Future updates are planned to add support for additional key types and general-purpose data storage.
#define ECDSA_SECP256R1_KEY_LEN 32
#define AES256_GCM_KEY_LEN 32
#define AES256_GCM_IV_LEN 12
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`):
typedef struct {
/* Private key */
uint8_t priv_key[ECDSA_SECP256R1_KEY_LEN];
/* Public key - X and Y components */
uint8_t pub_key[2 * ECDSA_SECP256R1_KEY_LEN];
} sec_stg_ecdsa_secp256r1_t;
- **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`.
typedef struct {
/* Key */
uint8_t key[AES256_GCM_KEY_LEN];
/* Initialization Vector */
uint8_t iv[AES256_GCM_IV_LEN];
} sec_stg_aes256_gcm_t;
.. note::
- Future updates may include support for additional key types and general-purpose data storage.
- The valid range for :ref:`CONFIG_SECURE_TEE_SEC_STG_EFUSE_HMAC_KEY_ID` is from ``0`` (:cpp:enumerator:`hmac_key_id_t::HMAC_KEY0`) to ``5`` (:cpp:enumerator:`hmac_key_id_t::HMAC_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 HMAC key into the required eFuse block:
::
# Generate a random 32-byte HMAC key
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
Tools
-----
The :doc:`../../api-reference/storage/nvs_partition_gen` tool can be used to generate binary images compatible with the NVS format for use with TEE Secure Storage. Since TEE Secure Storage stores data objects using a custom structured format, an additional step is required to convert input data into this format prior to image generation and encryption.
To support this process, the :component_file:`esp_tee_sec_stg_keygen.py<esp_tee/scripts/esp_tee_sec_stg_keygen/esp_tee_sec_stg_keygen.py>` script is provided for generating secure key blobs corresponding to the various supported cryptographic algorithms. These key blobs are then referenced in the input CSV file (format described :ref:`here <nvs-csv-file-format>`) and passed to the NVS Partition Generator utility to produce an encrypted images suitable for TEE Secure Storage.
Refer the detailed steps given :component_file:`here<esp_tee/scripts/esp_tee_sec_stg_keygen/README.md>` on generating key blobs and encrypted NVS partition images for TEE Secure Storage.
Application Example
-------------------
The :example:`tee_secure_storage <security/tee/tee_secure_storage>` example demonstrates how to generate ECDSA key pairs and AES-256-GCM keys in the TEE secure storage and use them for signing messages and encrypting/decrypting data.
The :example:`tee_secure_storage <security/tee/tee_secure_storage>` example demonstrates how to generate ECDSA key pairs and AES-256 keys in the TEE secure storage and use them for signing messages and encrypting/decrypting data.
API Reference
-------------

View File

@@ -96,7 +96,7 @@ 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, tee_sec_stg, 0x40000, 64K,
secure_storage, data, nvs, 0x40000, 64K,
factory, app, factory, 0x50000, 1M,
nvs, data, nvs, 0x150000, 24K,
phy_init, data, phy, 0x156000, 4K,

View File

@@ -185,7 +185,6 @@ SubType 字段长度为 8 bit内容与具体分区 Type 有关。目前ESP
.. only:: esp32c6
- ``tee-ota`` (0x90) 是 :ref:`TEE OTA 数据分区 <tee-ota-data-partition>`,用于存储所选 TEE OTA 应用分区的信息。此分区大小应为 0x2000 字节。详情请参阅 :doc:`TEE OTA <../security/tee/tee-ota>`
- ``tee_sec_stg`` (0x91) 是 TEE 安全存储分区,用于存储仅能被 TEE 应用程序访问的加密数据。:doc:`TEE 安全存储 <../security/tee/tee-sec-storage>` 将使用此分区存储包括加密密钥在内的敏感数据。此分区大小取决于具体的应用需求。
- ESP-IDF 还支持其他用于数据存储的预定义子类型,包括:

View File

@@ -14,6 +14,7 @@ NVS 加密
根据要使用的具体方案,可以选择启用 :ref:`CONFIG_NVS_ENCRYPTION`:ref:`CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME` > ``CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC````CONFIG_NVS_SEC_KEY_PROTECT_USING_HMAC`` 实现 NVS 加密。
.. _nvs_encr_flash_enc_scheme:
NVS 加密:基于 flash 加密的方案
-------------------------------------
@@ -104,6 +105,8 @@ NVS 密钥分区
.. only:: SOC_HMAC_SUPPORTED
.. _nvs_encr_hmac_scheme:
NVS 加密:基于 HMAC 外设的方案
--------------------------------------------

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