feat(esp_tee): Support for ESP-TEE - the main component

This commit is contained in:
Laukik Hase 2024-11-15 10:45:29 +05:30
parent 420810ee77
commit 373930655a
No known key found for this signature in database
GPG Key ID: D6F3208C06086AC8
111 changed files with 12291 additions and 0 deletions

View File

@ -0,0 +1,138 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
idf_build_get_property(custom_secure_service_tbl CUSTOM_SECURE_SERVICE_TBL)
idf_build_get_property(custom_secure_service_dir CUSTOM_SECURE_SERVICE_COMPONENT_DIR)
idf_build_get_property(custom_secure_service_component CUSTOM_SECURE_SERVICE_COMPONENT)
idf_build_get_property(target IDF_TARGET)
# headers & sources here are compiled into the app, not the esp_tee binary
# (see subproject/ for the esp_tee binary build files)
# ESP-TEE is currently supported only on the ESP32-C6 SoC
if(NOT ${target} STREQUAL "esp32c6")
return()
endif()
if(BOOTLOADER_BUILD)
idf_component_register()
return()
elseif(esp_tee_build)
# TEE build currently only uses the shared headers.
idf_component_register(INCLUDE_DIRS include)
else()
if(CONFIG_SECURE_ENABLE_TEE)
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
# Add custom flash target for TEE binary
partition_table_get_partition_info(partition "--partition-type app --partition-subtype tee_0" "name")
if(NOT partition)
message(FATAL_ERROR "Partition table missing TEE partition entry!")
endif()
add_dependencies(esp_tee partition_table_bin)
add_dependencies(flash esp_tee)
set(image_file ${TEE_BUILD_DIR}/esp_tee.bin)
partition_table_get_partition_info(offset "--partition-name ${partition}" "offset")
esptool_py_flash_target_image(flash "${partition}" "${offset}" "${image_file}")
endif()
partition_table_get_partition_info(tee_otadata_offset
"--partition-type data --partition-subtype tee_ota" "offset")
partition_table_get_partition_info(tee_otadata_size
"--partition-type data --partition-subtype tee_ota" "size")
# Add custom target for generating empty otadata partition for flashing
if(tee_otadata_offset AND tee_otadata_size)
idf_build_get_property(build_dir BUILD_DIR)
set(blank_tee_otadata_file ${build_dir}/tee_ota_data_initial.bin)
idf_build_get_property(python PYTHON)
idf_component_get_property(partition_table_dir partition_table COMPONENT_DIR)
add_custom_command(OUTPUT ${blank_tee_otadata_file}
COMMAND ${python} ${partition_table_dir}/gen_empty_partition.py
${tee_otadata_size} ${blank_tee_otadata_file})
add_custom_target(blank_tee_ota_data ALL DEPENDS ${blank_tee_otadata_file})
add_dependencies(flash blank_tee_ota_data)
add_dependencies(encrypted-flash blank_tee_ota_data)
partition_table_get_partition_info(tee_otadata_part
"--partition-type data --partition-subtype tee_ota" "name")
idf_component_get_property(main_args esptool_py FLASH_ARGS)
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
esptool_py_flash_target(tee_otadata-flash "${main_args}" "${sub_args}")
esptool_py_flash_target_image(tee_otadata-flash
"${tee_otadata_part}" "${tee_otadata_offset}" "${blank_tee_otadata_file}")
esptool_py_flash_target_image(flash
"${tee_otadata_part}" "${tee_otadata_offset}" "${blank_tee_otadata_file}")
endif()
set(srcs "src/esp_tee.c"
"src/esp_tee_config.c"
"src/esp_secure_service_wrapper.c"
"src/esp_tee_u2m_switch.S")
endif()
idf_component_register(INCLUDE_DIRS include
SRCS ${srcs}
PRIV_REQUIRES efuse esp_system spi_flash)
if(CONFIG_SECURE_ENABLE_TEE)
set(EXTRA_LINK_FLAGS)
list(APPEND EXTRA_LINK_FLAGS "-u esp_tee_app_config")
target_link_libraries(${COMPONENT_LIB} INTERFACE "${EXTRA_LINK_FLAGS}")
endif()
endif()
set(secure_service_hdr_py
${COMPONENT_DIR}/scripts/secure_service_hdr.py ${CMAKE_CURRENT_BINARY_DIR}/secure_service.tbl
)
set(secure_service_tbl_py
${COMPONENT_DIR}/scripts/secure_service_tbl.py ${CMAKE_CURRENT_BINARY_DIR}/secure_service.tbl
)
set(secure_service_wrap_py
${COMPONENT_DIR}/scripts/secure_service_wrap.py ${CMAKE_CURRENT_BINARY_DIR}/secure_service.tbl
)
set(secure_service_num_h
${CONFIG_DIR}/secure_service_num.h
)
set(secure_service_dec_h
${CONFIG_DIR}/secure_service_dec.h)
set(secure_service_h
${CONFIG_DIR}/secure_service.h
)
if(CONFIG_SECURE_ENABLE_TEE)
execute_process(COMMAND cat ${COMPONENT_DIR}/scripts/${target}/secure_service.tbl ${custom_secure_service_tbl}
OUTPUT_FILE secure_service.tbl
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
execute_process(COMMAND python ${secure_service_hdr_py} ${secure_service_num_h} ${secure_service_dec_h}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
execute_process(COMMAND python ${secure_service_tbl_py} ${secure_service_h}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
)
set_property(DIRECTORY "${COMPONENT_DIR}" APPEND PROPERTY
ADDITIONAL_MAKE_CLEAN_FILES ${secure_service_num_h} ${secure_service_dec_h} ${secure_service_h})
# For TEE implementation, we don't wrap the APIs since the TEE would also internally use the same API and
# it shouldn't route to secure service API.
# Instead of wrapping, we append _ss_* to the API name and then it must be defined in esp_secure_services.c
if(NOT esp_tee_build)
execute_process(COMMAND python ${secure_service_wrap_py}
OUTPUT_VARIABLE wrap_list
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(STRIP ${wrap_list} wrap_list)
target_link_libraries(${COMPONENT_LIB} INTERFACE "${wrap_list}")
endif()
endif()

View File

@ -0,0 +1,144 @@
menu "ESP-TEE (Trusted Execution Environment)"
depends on IDF_TARGET_ESP32C6
config SECURE_ENABLE_TEE
bool "Enable the ESP-TEE framework"
depends on IDF_TARGET_ESP32C6
select ESP_SYSTEM_MEMPROT_FEATURE_VIA_TEE
help
This configuration enables the Trusted Execution Environment (TEE) feature.
menu "Memory Configuration"
depends on SECURE_ENABLE_TEE
config SECURE_TEE_IRAM_SIZE
hex "IRAM region size"
default 0x8000
range 0x8000 0x10000
help
This configuration sets the IRAM size for the TEE module.
This should be a multiple of 0x1000.
config SECURE_TEE_DRAM_SIZE
hex "DRAM region size"
default 0x8000
range 0x8000 0x10000
help
This configuration sets the DRAM size for the TEE module.
This should be a multiple of 0x1000.
config SECURE_TEE_STACK_SIZE
hex "Stack size"
default 0xc00
range 0x800 0x1000
help
This configuration sets the stack size for the TEE module.
The TEE stack will be allocated from the TEE DRAM region.
This should be a multiple of 0x100.
config SECURE_TEE_INTR_STACK_SIZE
hex "Interrupt Stack size"
default 0x400
range 0x400 0x800
help
This configuration sets the interrupt stack size for the TEE module.
The TEE interrupt stack will be allocated from the TEE DRAM region.
This should be a multiple of 0x100.
config SECURE_TEE_IROM_SIZE
hex
default 0x10000
help
This should be a multiple of MMU_PAGE_SIZE.
config SECURE_TEE_DROM_SIZE
hex
default 0x10000
help
This should be a multiple of MMU_PAGE_SIZE.
endmenu
choice SECURE_TEE_SEC_STG_MODE
prompt "Secure Storage: Mode"
depends on SECURE_ENABLE_TEE
default SECURE_TEE_SEC_STG_MODE_DEVELOPMENT
help
Select the TEE secure storage mode
config SECURE_TEE_SEC_STG_MODE_DEVELOPMENT
bool "Development"
help
Secure storage will be encrypted by the data stored in eFuse BLK2
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
endchoice
config SECURE_TEE_SEC_STG_KEY_EFUSE_BLK
int "Secure Storage: Encryption key eFuse block"
depends on SECURE_TEE_SEC_STG_MODE_RELEASE
range 4 10
default 10
help
eFuse block ID storing the TEE secure storage encryption key
config SECURE_TEE_ATT_KEY_SLOT_ID
depends on SECURE_ENABLE_TEE
int "Attestation: Secure Storage slot ID for EAT signing"
default 0
range 0 14
help
This configuration sets the slot ID from the TEE secure storage
storing the ECDSA keypair for executing sign/verify operations
from the TEE side (E.g. Attestation)
config SECURE_TEE_DEBUG_MODE
bool "Enable Debug Mode"
default y
depends on SECURE_ENABLE_TEE
help
This configuration enables the logging from the TEE module.
choice SECURE_TEE_LOG_LEVEL
bool "Log verbosity"
default SECURE_TEE_LOG_LEVEL_WARN
depends on SECURE_TEE_DEBUG_MODE
help
Specify how much output to see in TEE logs.
config SECURE_TEE_LOG_LEVEL_NONE
bool "No output"
config SECURE_TEE_LOG_LEVEL_ERROR
bool "Error"
config SECURE_TEE_LOG_LEVEL_WARN
bool "Warning"
config SECURE_TEE_LOG_LEVEL_INFO
bool "Info"
config SECURE_TEE_LOG_LEVEL_DEBUG
bool "Debug"
config SECURE_TEE_LOG_LEVEL_VERBOSE
bool "Verbose"
endchoice
config SECURE_TEE_LOG_LEVEL
int
default 0 if SECURE_TEE_LOG_LEVEL_NONE || !SECURE_TEE_DEBUG_MODE
default 1 if SECURE_TEE_LOG_LEVEL_ERROR
default 2 if SECURE_TEE_LOG_LEVEL_WARN
default 3 if SECURE_TEE_LOG_LEVEL_INFO
default 4 if SECURE_TEE_LOG_LEVEL_DEBUG
default 5 if SECURE_TEE_LOG_LEVEL_VERBOSE
config SECURE_TEE_TEST_MODE
bool "Enable Test Mode"
depends on SECURE_ENABLE_TEE
help
This configuration sets up the TEE framework as required for executing the test suite.
endmenu

View File

@ -0,0 +1,90 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#ifndef __ASSEMBLER__
#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
#include "soc/soc.h"
#include "sdkconfig.h"
#include "esp_cpu.h"
#include "esp_attr.h"
#include "riscv/rv_utils.h"
#define ESP_TEE_APP_CFG_MAGIC 0x3348AAED
#define ESP_TEE_API_MAJOR_VER 1
#define ESP_TEE_API_MINOR_VER 0
#define ESP_TEE_API_PATCH_VER 0
/**
* @brief CPU privilege mode
*/
typedef enum {
ESP_CPU_NS_MODE = 0, /* Corresponds to the RISC-V User (U) mode */
ESP_CPU_S_MODE = 3, /* Corresponds to the RISC-V Machine (M) mode */
} esp_cpu_priv_mode_t;
/**
* @brief Configuration structure defining the interface between TEE and REE (user) app
*
* This configuration structure is embedded in the REE (user) app's IRAM section.
* The TEE reads and updates this structure before switching to the REE, and then
* write-protects it.
*
* @note All accesses to this structure must be 32-bit aligned since it resides in
* the (user app) IRAM section.
*/
typedef struct {
uint32_t magic_word;
uint32_t api_major_version;
uint32_t api_minor_version;
uint32_t reserved[2];
/* TEE-related fields */
void *s_entry_addr;
void *s_int_handler;
/* REE-related fields */
void *ns_entry_addr;
void *ns_int_handler;
void *ns_iram_end;
void *ns_irom_end;
void *ns_drom_end;
} __attribute__((aligned(4))) __attribute__((__packed__)) esp_tee_config_t;
extern esp_tee_config_t esp_tee_app_config;
#endif // ifndef __ASSEMBLER__
#if !ESP_TEE_BUILD
#include "private/esp_tee_app.h"
#else
#include "private/esp_tee_binary.h"
#endif
/* Offsets of some values in esp_tee_config_t that are used by assembly code */
#define ESP_TEE_CFG_OFFS_S_ENTRY_ADDR 0x14
#define ESP_TEE_CFG_OFFS_S_INTR_HANDLER 0x18
#define ESP_TEE_CFG_OFFS_NS_ENTRY_ADDR 0x1C
#define ESP_TEE_CFG_OFFS_NS_INTR_HANDLER 0x20
#ifndef __ASSEMBLER__
/* Check the offsets are correct using the C compiler */
ESP_STATIC_ASSERT(offsetof(esp_tee_config_t, s_entry_addr) == ESP_TEE_CFG_OFFS_S_ENTRY_ADDR, "offset macro is wrong");
ESP_STATIC_ASSERT(offsetof(esp_tee_config_t, s_int_handler) == ESP_TEE_CFG_OFFS_S_INTR_HANDLER, "offset macro is wrong");
ESP_STATIC_ASSERT(offsetof(esp_tee_config_t, ns_entry_addr) == ESP_TEE_CFG_OFFS_NS_ENTRY_ADDR, "offset macro is wrong");
ESP_STATIC_ASSERT(offsetof(esp_tee_config_t, ns_int_handler) == ESP_TEE_CFG_OFFS_NS_INTR_HANDLER, "offset macro is wrong");
#endif // ifndef __ASSEMBLER__
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,37 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stdarg.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Interface function that allows untrusted applications to invoke secure services through TEE
*
* @param argc Number of arguments being passed to the secure service
*
* @return Value returned by the secure service function
*/
uint32_t esp_tee_service_call(int argc, ...);
/**
* @brief Interface function that allows untrusted applications to invoke secure services through TEE,
* with the scheduler and the non-IRAM interrupts disabled
*
* @param argc Number of arguments being passed to the secure service
*
* @return Value returned by the secure service function
*/
uint32_t esp_tee_service_call_with_noniram_intr_disabled(int argc, ...);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,100 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "sdkconfig.h"
/* Declarations used inside TEE binary, only */
#define portNUM_PROCESSORS (1)
#define configNUM_CORES (portNUM_PROCESSORS)
#define TEE_SECURE_INUM (14)
#define ESP_TEE_M2U_SWITCH_MAGIC 0xfedef
#define ALIGN_UP_TO_MMU_PAGE_SIZE(addr) (((addr) + (SOC_MMU_PAGE_SIZE) - 1) & ~((SOC_MMU_PAGE_SIZE) - 1))
#define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ((addr) & ~((SOC_MMU_PAGE_SIZE) - 1))
/* NOTE: ESP32-C6 - TEE/REE memory regions */
/* TEE I/DRAM */
#define SOC_S_IRAM_START (SOC_IRAM_LOW)
#define SOC_S_IRAM_END (SOC_S_IRAM_START + CONFIG_SECURE_TEE_IRAM_SIZE)
#define SOC_S_DRAM_START (SOC_S_IRAM_END)
#define SOC_S_DRAM_END (SOC_S_IRAM_END + CONFIG_SECURE_TEE_DRAM_SIZE)
#define SOC_NS_IRAM_START (SOC_S_DRAM_END)
/* TEE I/DROM */
#define SOC_S_IDROM_SIZE (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE)
#define SOC_S_IDROM_MMU_PAGE_NUM (SOC_S_IDROM_SIZE / SOC_MMU_PAGE_SIZE)
#define SOC_S_IROM_LOW (SOC_IROM_LOW)
#define SOC_S_IROM_HIGH (SOC_IROM_LOW + SOC_S_IDROM_SIZE)
#define SOC_S_DROM_LOW (SOC_DROM_LOW)
#define SOC_S_DROM_HIGH (SOC_DROM_LOW + SOC_S_IDROM_SIZE)
#define SOC_MMU_TOTAL_SIZE (SOC_DRAM0_CACHE_ADDRESS_HIGH - SOC_DRAM0_CACHE_ADDRESS_LOW)
#define SOC_MMU_END_VADDR (SOC_DROM_LOW + SOC_MMU_TOTAL_SIZE)
#define SOC_S_MMU_MMAP_RESV_PAGE_NUM (SOC_S_IDROM_MMU_PAGE_NUM + 1)
#define SOC_S_MMU_MMAP_RESV_START_VADDR (SOC_MMU_END_VADDR - SOC_S_MMU_MMAP_RESV_PAGE_NUM * SOC_MMU_PAGE_SIZE)
#ifndef __ASSEMBLER__
#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>
#include "esp_rom_sys.h"
/**
* @brief TEE initialization function called by the bootloader at boot time.
* Performs secure system initialization before switching to the REE.
*
* @param ree_entry_addr entry point to the App where TEE jump after completing secure initialization
* @param ree_drom_addr DROM address of the selected non-secure app for determining the running non-secure app partition
* @param tee_boot_part partition subtype of the active TEE partition
*/
void esp_tee_init(uint32_t ree_entry_addr, uint32_t ree_drom_addr, uint8_t tee_boot_part);
/**
* @brief SoC-specific TEE secure initialization
*/
void esp_tee_soc_secure_sys_init(void);
/**
* @brief Configure region protection through RISC-V PMP/PMA for TEE
*/
void esp_tee_configure_region_protection(void);
/**
* @brief Configure APM protection for TEE
*/
void esp_tee_configure_apm_protection(void);
/**
* @brief Switch to the REE app after TEE initialization is complete
*
* @param ree_entry_addr REE app entry address
*/
void esp_tee_switch_to_ree(uint32_t ree_entry_addr);
/**
* @brief Secure service call entry point for the TEE binary.
* This function deciphers the call from the REE and
* dispatches the appropriate secure service API in the TEE.
*
* @param argc Number of arguments passed to the secure service API
* @param ap List of input arguments
*
* @return Return value from the secure service API
*/
int esp_tee_service_dispatcher(int argc, va_list ap);
#endif // ifndef __ASSEMBLER__
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,52 @@
idf_build_get_property(idf_path IDF_PATH)
idf_build_get_property(idf_target IDF_TARGET)
idf_build_get_property(build_dir BUILD_DIR)
idf_build_get_property(sdkconfig SDKCONFIG)
idf_build_get_property(python PYTHON)
idf_build_get_property(extra_cmake_args EXTRA_CMAKE_ARGS)
idf_build_get_property(project_dir PROJECT_DIR)
idf_build_get_property(non_os_build NON_OS_BUILD)
idf_build_get_property(config_dir CONFIG_DIR)
idf_build_get_property(custom_secure_service_dir CUSTOM_SECURE_SERVICE_COMPONENT_DIR)
idf_build_get_property(custom_secure_service_component CUSTOM_SECURE_SERVICE_COMPONENT)
if(NOT CONFIG_SECURE_ENABLE_TEE OR non_os_build)
return()
endif()
if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
get_filename_component(secure_boot_signing_key
"${CONFIG_SECURE_BOOT_SIGNING_KEY}"
ABSOLUTE BASE_DIR "${project_dir}")
set(SECURE_BOOT_SIGNING_KEY ${secure_boot_signing_key})
set(sign_key_arg "-DSECURE_BOOT_SIGNING_KEY=${secure_boot_signing_key}")
else()
set(sign_key_arg)
endif()
set(TEE_BUILD_DIR "${build_dir}/esp_tee")
set(tee_binary_files
"${TEE_BUILD_DIR}/esp_tee.elf"
"${TEE_BUILD_DIR}/esp_tee.bin"
"${TEE_BUILD_DIR}/esp_tee.map"
)
externalproject_add(esp_tee
SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/subproject"
BINARY_DIR "${TEE_BUILD_DIR}"
CMAKE_ARGS -DSDKCONFIG=${sdkconfig} -DIDF_PATH=${idf_path} -DIDF_TARGET=${idf_target}
-DCONFIG_DIR=${config_dir} -DCUSTOM_SECURE_SERVICE_COMPONENT=${custom_secure_service_component}
-DCUSTOM_SECURE_SERVICE_COMPONENT_DIR=${custom_secure_service_dir}
${extra_cmake_args} ${sign_key_arg}
INSTALL_COMMAND ""
BUILD_ALWAYS 1 # no easy way around this...
USES_TERMINAL_CONFIGURE TRUE
USES_TERMINAL_BUILD TRUE
BUILD_BYPRODUCTS ${tee_binary_files}
)
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY
ADDITIONAL_MAKE_CLEAN_FILES
${tee_binary_files})

View File

@ -0,0 +1,42 @@
# SS no. API type Function Args
0 custom invalid_secure_service 0
1 IDF esp_rom_route_intr_matrix 3
2 IDF rv_utils_intr_enable 1
3 IDF rv_utils_intr_disable 1
4 IDF rv_utils_intr_set_priority 2
5 IDF rv_utils_intr_set_type 2
6 IDF rv_utils_intr_set_threshold 1
7 IDF rv_utils_intr_edge_ack 1
8 IDF rv_utils_intr_global_enable 0
9 IDF efuse_hal_chip_revision 0
10 IDF efuse_hal_get_chip_ver_pkg 1
11 IDF efuse_hal_get_disable_wafer_version_major 0
12 IDF efuse_hal_get_mac 1
13 IDF esp_efuse_check_secure_version 1
14 IDF esp_efuse_read_field_blob 3
15 IDF esp_flash_encryption_enabled 0
16 IDF wdt_hal_init 4
17 IDF wdt_hal_deinit 1
18 IDF esp_aes_intr_alloc 0
19 IDF esp_aes_crypt_cbc 6
20 IDF esp_aes_crypt_cfb8 6
21 IDF esp_aes_crypt_cfb128 7
22 IDF esp_aes_crypt_ctr 7
23 IDF esp_aes_crypt_ecb 4
24 IDF esp_aes_crypt_ofb 6
25 IDF esp_sha 4
26 IDF esp_sha_dma 6
27 IDF esp_sha_read_digest_state 2
28 IDF esp_sha_write_digest_state 2
29 custom esp_tee_ota_begin 0
30 custom esp_tee_ota_write 3
31 custom esp_tee_ota_end 0
32 custom esp_tee_sec_storage_init 0
33 custom esp_tee_sec_storage_gen_key 1
34 custom esp_tee_sec_storage_get_signature 4
35 custom esp_tee_sec_storage_get_pubkey 2
36 custom esp_tee_sec_storage_encrypt 8
37 custom esp_tee_sec_storage_decrypt 8
38 custom esp_tee_sec_storage_is_slot_empty 1
39 custom esp_tee_sec_storage_clear_slot 1
40 custom esp_tee_att_generate_token 6

View File

@ -0,0 +1,76 @@
#!/usr/bin/env python
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import argparse
import re
from typing import List
from typing import Tuple
def parse_services(secure_service_tbl: str) -> List[Tuple[str, str, str]]:
services: List[Tuple[str, str, str]] = []
pattern: re.Pattern = re.compile(r'^([0-9A-Fa-fXx]+)\s+\S+\s+(\S+)\s+(\d+)')
with open(secure_service_tbl, 'r') as f:
for line in f:
if match := pattern.match(line):
services.append((match.group(1), match.group(2), match.group(3)))
return sorted(services, key=lambda x: int(x[0]))
def generate_num_header(services: List[Tuple[str, str, str]], output_file: str) -> None:
header_text: str = '''/**
* This header file is used to generate secure service number macros.
*
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
*/
#pragma once
'''
with open(output_file, 'w') as f:
f.write(header_text)
for nr, name, _ in services:
f.write(f'#define SS_{name.upper()}\t{nr}\n')
total: int = int(services[-1][0]) + 1 if services else 0
f.write(f'\n#define MAX_SECURE_SERVICES\t{total}\n\n')
def generate_dec_header(services: List[Tuple[str, str, str]], output_file: str) -> None:
header_text: str = '''/**
* This header file is used to provide function declarations
* for compiling secure_service_table.c source file. Please do not
* use it in application.
*
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
'''
with open(output_file, 'w') as f:
f.write(header_text)
for _, name, _ in services:
f.write(f'void _ss_{name}(void);\n')
f.write('''
#ifdef __cplusplus
}
#endif
''')
def main() -> None:
parser = argparse.ArgumentParser(description='Generate secure service headers')
parser.add_argument('secure_service_tbl', type=str, help='Path to secure_service.tbl generated in build directory')
parser.add_argument('secure_service_num_h', type=str, help='Path to secure_service_num.h header file')
parser.add_argument('secure_service_dec_h', type=str, help='Path to secure_service_dec.h header file')
args = parser.parse_args()
services: List[Tuple[str, str, str]] = parse_services(args.secure_service_tbl)
generate_num_header(services, args.secure_service_num_h)
generate_dec_header(services, args.secure_service_dec_h)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,50 @@
#!/usr/bin/env python
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import argparse
import re
import sys
from typing import Dict
from typing import Tuple
def emit(nr: int, entry: str, nargs: str) -> str:
return f'__SECURE_SERVICE({nr}, {entry}, {nargs})'
def main() -> None:
parser = argparse.ArgumentParser(description='Generate secure service table')
parser.add_argument('input_file', type=str, help='Path to input file')
parser.add_argument('output_file', type=str, help='Path to output file')
args = parser.parse_args()
services: Dict[int, Tuple[str, str, str]] = {}
pattern: re.Pattern = re.compile(r'^([0-9A-Fa-fXx]+)\s+(\S+)\s+(\S+)\s+(\d+)')
# Single pass through file to collect services and check duplicates
with open(args.input_file, 'r') as f:
for line in f:
if match := pattern.match(line):
nr = int(match.group(1))
if nr in services:
print('ERROR: Found duplicate secure service numbers, exiting...')
sys.exit(1)
services[nr] = (match.group(2), match.group(3), match.group(4))
# Generate output
with open(args.output_file, 'w') as f:
f.write('''/**
* This header file is used to define secure services.
*
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
*/
#pragma once
''')
for nr in sorted(services):
_, name, nargs = services[nr]
f.write(emit(nr, name, nargs) + '\n')
if __name__ == '__main__':
main()

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import argparse
import re
from typing import List
def main() -> None:
parser = argparse.ArgumentParser(description='Generate secure service wrap list')
parser.add_argument('secure_service_tbl', type=str, help='Path to secure service table file')
args = parser.parse_args()
pattern: re.Pattern = re.compile(r'^[0-9A-Fa-fXx]+\s+IDF\s+(\S+)\s+\d+')
with open(args.secure_service_tbl, 'r') as f:
wrap_list: List[str] = [f'-Wl,--wrap={match.group(1)}'
for line in f if (match := pattern.match(line))]
print(' '.join(wrap_list), end='')
if __name__ == '__main__':
main()

View File

@ -0,0 +1,218 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include "secure_service_num.h"
#include "hal/sha_types.h"
#include "hal/sha_hal.h"
#include "hal/wdt_hal.h"
#include "esp_tee.h"
#include "esp_err.h"
#include "esp_hmac.h"
#include "esp_efuse.h"
#include "esp_random.h"
#include "soc/soc_caps.h"
/* ---------------------------------------------- Interrupts ------------------------------------------------- */
IRAM_ATTR void __wrap_esp_rom_route_intr_matrix(int cpu_no, uint32_t model_num, uint32_t intr_num)
{
esp_tee_service_call(4, SS_ESP_ROM_ROUTE_INTR_MATRIX, cpu_no, model_num, intr_num);
}
/* ---------------------------------------------- eFuse ------------------------------------------------- */
uint32_t __wrap_efuse_hal_chip_revision(void)
{
return esp_tee_service_call(1, SS_EFUSE_HAL_CHIP_REVISION);
}
uint32_t __wrap_efuse_hal_get_chip_ver_pkg(void)
{
return esp_tee_service_call(1, SS_EFUSE_HAL_GET_CHIP_VER_PKG);
}
bool __wrap_efuse_hal_get_disable_wafer_version_major(void)
{
return esp_tee_service_call(1, SS_EFUSE_HAL_GET_DISABLE_WAFER_VERSION_MAJOR);
}
void __wrap_efuse_hal_get_mac(uint8_t *mac)
{
esp_tee_service_call(2, SS_EFUSE_HAL_GET_MAC, mac);
}
bool __wrap_esp_efuse_check_secure_version(uint32_t secure_version)
{
return esp_tee_service_call(4, SS_ESP_EFUSE_CHECK_SECURE_VERSION, secure_version);
}
esp_err_t __wrap_esp_efuse_read_field_blob(const esp_efuse_desc_t *field[], void *dst, size_t dst_size_bits)
{
return esp_tee_service_call(4, SS_ESP_EFUSE_READ_FIELD_BLOB, (uint32_t)field, (uint32_t)dst, (uint32_t)dst_size_bits);
}
bool __wrap_esp_flash_encryption_enabled(void)
{
return esp_tee_service_call(1, SS_ESP_FLASH_ENCRYPTION_ENABLED);
}
/* ---------------------------------------------- RTC_WDT ------------------------------------------------- */
void __wrap_wdt_hal_init(wdt_hal_context_t *hal, wdt_inst_t wdt_inst, uint32_t prescaler, bool enable_intr)
{
esp_tee_service_call(5, SS_WDT_HAL_INIT, hal, wdt_inst, prescaler, enable_intr);
}
void __wrap_wdt_hal_deinit(wdt_hal_context_t *hal)
{
esp_tee_service_call(2, SS_WDT_HAL_DEINIT, hal);
}
/* ---------------------------------------------- AES ------------------------------------------------- */
typedef struct {
uint8_t key_bytes;
volatile uint8_t key_in_hardware; /* This variable is used for fault injection checks, so marked volatile to avoid optimisation */
uint8_t key[32];
} esp_aes_context;
int __wrap_esp_aes_intr_alloc(void)
{
return esp_tee_service_call(1, SS_ESP_AES_INTR_ALLOC);
}
int __wrap_esp_aes_crypt_cbc(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output)
{
return esp_tee_service_call(7, SS_ESP_AES_CRYPT_CBC, ctx, mode, length, iv, input, output);
}
int __wrap_esp_aes_crypt_cfb128(esp_aes_context *ctx,
int mode,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output)
{
return esp_tee_service_call(8, SS_ESP_AES_CRYPT_CFB128, (uint32_t)ctx,
mode, length, iv_off, iv, (uint32_t)input, (uint32_t)output);
}
int __wrap_esp_aes_crypt_cfb8(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output)
{
return esp_tee_service_call(7, SS_ESP_AES_CRYPT_CFB8, ctx,
mode, length, iv, input, output);
}
int __wrap_esp_aes_crypt_ctr(esp_aes_context *ctx,
size_t length,
size_t *nc_off,
unsigned char nonce_counter[16],
unsigned char stream_block[16],
const unsigned char *input,
unsigned char *output)
{
return esp_tee_service_call(8, SS_ESP_AES_CRYPT_CTR, ctx, length, nc_off, nonce_counter, stream_block, input, output);
}
int __wrap_esp_aes_crypt_ecb(esp_aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16])
{
return esp_tee_service_call(5, SS_ESP_AES_CRYPT_ECB,
(uint32_t)ctx, (uint32_t)mode,
(uint32_t)input, (uint32_t)output);
}
int __wrap_esp_aes_crypt_ofb(esp_aes_context *ctx,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output)
{
return esp_tee_service_call(7, SS_ESP_AES_CRYPT_OFB, (uint32_t)ctx, length,
iv_off, iv, (uint32_t)input, (uint32_t)output);
}
/* ---------------------------------------------- SHA ------------------------------------------------- */
typedef enum {
ESP_SHA1_STATE_INIT,
ESP_SHA1_STATE_IN_PROCESS
} esp_sha1_state;
typedef enum {
ESP_SHA256_STATE_INIT,
ESP_SHA256_STATE_IN_PROCESS
} esp_sha256_state;
typedef enum {
ESP_SHA512_STATE_INIT,
ESP_SHA512_STATE_IN_PROCESS
} esp_sha512_state;
typedef struct {
uint32_t total[2]; /*!< number of bytes processed */
uint32_t state[5]; /*!< intermediate digest state */
unsigned char buffer[64]; /*!< data block being processed */
int first_block; /*!< if first then true else false */
esp_sha_type mode;
esp_sha1_state sha_state;
} esp_sha1_context;
typedef struct {
uint32_t total[2]; /*!< number of bytes processed */
uint32_t state[8]; /*!< intermediate digest state */
unsigned char buffer[64]; /*!< data block being processed */
int first_block; /*!< if first then true, else false */
esp_sha_type mode;
esp_sha256_state sha_state;
} esp_sha256_context;
typedef struct {
uint64_t total[2]; /*!< number of bytes processed */
uint64_t state[8]; /*!< intermediate digest state */
unsigned char buffer[128]; /*!< data block being processed */
int first_block;
esp_sha_type mode;
uint32_t t_val; /*!< t_val for 512/t mode */
esp_sha512_state sha_state;
} esp_sha512_context;
void __wrap_esp_sha(esp_sha_type sha_type, const unsigned char *input, size_t ilen, unsigned char *output)
{
esp_tee_service_call(5, SS_ESP_SHA,
(uint32_t)sha_type, (uint32_t)input,
(uint32_t)ilen, (uint32_t)output);
}
int __wrap_esp_sha_dma(esp_sha_type sha_type, const void *input, uint32_t ilen,
const void *buf, uint32_t buf_len, bool is_first_block)
{
return esp_tee_service_call(7, SS_ESP_SHA_DMA, sha_type, input, ilen, buf, buf_len, is_first_block);
}
void __wrap_esp_sha_read_digest_state(esp_sha_type sha_type, void *digest_state)
{
esp_tee_service_call(3, SS_ESP_SHA_READ_DIGEST_STATE, sha_type, digest_state);
}
void __wrap_esp_sha_write_digest_state(esp_sha_type sha_type, void *digest_state)
{
esp_tee_service_call(3, SS_ESP_SHA_WRITE_DIGEST_STATE, sha_type, digest_state);
}

View File

@ -0,0 +1,76 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include "esp_attr.h"
#include "esp_private/cache_utils.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "esp_tee.h"
#include "secure_service_num.h"
/* See esp_tee_u2m_switch.S */
extern uint32_t _u2m_switch(int argc, va_list ap);
static SemaphoreHandle_t s_tee_mutex;
static StaticSemaphore_t s_tee_mutex_buf;
static void init_mutex(void)
{
static bool is_first_call = true;
if (is_first_call) {
s_tee_mutex = xSemaphoreCreateMutexStatic(&s_tee_mutex_buf);
is_first_call = false;
}
}
/**
* TEE interface API used by untrusted side application
* to call secure service in trusted side
*/
uint32_t esp_tee_service_call(int argc, ...)
{
init_mutex();
uint32_t val = UINT32_MAX;
va_list ap;
va_start(ap, argc);
if (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) {
if (xSemaphoreTake(s_tee_mutex, portMAX_DELAY) == pdTRUE) {
val = _u2m_switch(argc, ap);
xSemaphoreGive(s_tee_mutex);
}
} else {
val = _u2m_switch(argc, ap);
}
va_end(ap);
return val;
}
IRAM_ATTR uint32_t esp_tee_service_call_with_noniram_intr_disabled(int argc, ...)
{
uint32_t val = UINT32_MAX;
va_list ap;
va_start(ap, argc);
/* NOTE: Disabling the scheduler and non-IRAM residing interrupts */
spi_flash_op_lock();
esp_intr_noniram_disable();
val = _u2m_switch(argc, ap);
esp_intr_noniram_enable();
spi_flash_op_unlock();
va_end(ap);
return val;
}

View File

@ -0,0 +1,34 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_tee.h"
/* U-mode interrupt handler */
extern int _tee_interrupt_handler(void);
/* U-to-M mode switch */
extern uint32_t _u2m_switch(int argc, va_list ap);
/* REE IRAM end */
extern uint32_t _iram_end;
/* REE IROM end */
extern uint32_t _instruction_reserved_end;
/* REE DROM end */
extern uint32_t _rodata_reserved_end;
esp_tee_config_t esp_tee_app_config __attribute__((section(".esp_tee_app_cfg"))) = {
.magic_word = ESP_TEE_APP_CFG_MAGIC,
.api_major_version = ESP_TEE_API_MAJOR_VER,
.api_minor_version = ESP_TEE_API_MINOR_VER,
/* .s_entry_addr and .s_intr_handler are NULL in the
app binary, but will be written by the TEE before it loads the binary
*/
.ns_int_handler = &_tee_interrupt_handler,
.ns_entry_addr = &_u2m_switch,
.ns_iram_end = &_iram_end,
.ns_irom_end = &_instruction_reserved_end,
.ns_drom_end = &_rodata_reserved_end,
};

View File

@ -0,0 +1,13 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
.section .iram1, "ax"
.balign 4
.global _u2m_switch
.type _u2m_switch, @function
_u2m_switch:
ecall
fence
ret

View File

@ -0,0 +1,87 @@
cmake_minimum_required(VERSION 3.16)
set(ESP_TEE_VERSION_MAJOR 1)
set(ESP_TEE_VERSION_MINOR 0)
set(ESP_TEE_VERSION_PATCH 0)
if(NOT SDKCONFIG)
message(FATAL_ERROR "esp_tee subproject expects the SDKCONFIG variable to be passed "
"in by the parent build process.")
endif()
if(NOT IDF_PATH)
message(FATAL_ERROR "esp_tee subproject expects the IDF_PATH variable to be passed "
"in by the parent build process.")
endif()
if(NOT IDF_TARGET)
message(FATAL_ERROR "esp_tee subproject expects the IDF_TARGET variable to be passed "
"in by the parent build process.")
endif()
set(COMPONENTS esp_tee bootloader esptool_py partition_table main ${CUSTOM_SECURE_SERVICE_COMPONENT})
list(APPEND EXTRA_COMPONENT_DIRS ${CUSTOM_SECURE_SERVICE_COMPONENT_DIR})
set(ESP_TEE_BUILD 1)
set(NON_OS_BUILD 1)
# TEE-specific components
list(APPEND COMPONENTS tee_flash_mgr tee_ota_ops tee_sec_storage attestation)
# Include sdkconfig.h derived from the parent build.
include_directories(${CONFIG_DIR})
include("${IDF_PATH}/tools/cmake/project.cmake")
set(common_req esp_common esp_hw_support esp_rom freertos hal log newlib soc spi_flash)
if(CONFIG_IDF_TARGET_ARCH_RISCV)
list(APPEND common_req riscv)
endif()
# Included for `esp_app_desc` configuration structure
list(APPEND common_req esp_app_format)
idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${common_req}")
idf_build_set_property(__OUTPUT_SDKCONFIG 0)
# NOTE: Helps to analyse the components built for the TEE binary by CMake Graphviz
idf_build_set_property(__BUILD_COMPONENT_DEPGRAPH_ENABLED 1)
project(esp_tee VERSION ${ESP_TEE_VERSION_MAJOR}.${ESP_TEE_VERSION_MINOR}.${ESP_TEE_VERSION_PATCH})
idf_build_set_property(COMPILE_DEFINITIONS "ESP_TEE_BUILD=1" APPEND)
idf_build_set_property(COMPILE_DEFINITIONS "NON_OS_BUILD=1" APPEND)
idf_build_set_property(COMPILE_OPTIONS "-fno-stack-protector" APPEND)
if(CONFIG_SECURE_BOOT_V2_ENABLED)
if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
get_filename_component(secure_boot_signing_key
"${SECURE_BOOT_SIGNING_KEY}" ABSOLUTE BASE_DIR "${project_dir}")
if(NOT EXISTS "${secure_boot_signing_key}")
message(FATAL_ERROR
"Secure Boot Signing Key Not found."
"\nGenerate the Secure Boot V2 RSA-PSS 3072 Key."
"\nTo generate one, you can use this command:"
"\n\t${espsecurepy} generate_signing_key --version 2 ${SECURE_BOOT_SIGNING_KEY}")
endif()
set(esp_tee_unsigned_bin "esp_tee-unsigned.bin")
add_custom_command(OUTPUT ".signed_bin_timestamp"
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/${PROJECT_BIN}"
"${CMAKE_BINARY_DIR}/${esp_tee_unsigned_bin}"
COMMAND ${ESPSECUREPY} sign_data --version 2 --keyfile "${secure_boot_signing_key}"
-o "${CMAKE_BINARY_DIR}/${PROJECT_BIN}" "${CMAKE_BINARY_DIR}/${esp_tee_unsigned_bin}"
COMMAND ${CMAKE_COMMAND} -E echo "Generated signed binary image ${build_dir}/${PROJECT_BIN}"
"from ${CMAKE_BINARY_DIR}/${esp_tee_unsigned_bin}"
COMMAND ${CMAKE_COMMAND} -E md5sum "${CMAKE_BINARY_DIR}/${PROJECT_BIN}"
> "${CMAKE_BINARY_DIR}/.signed_bin_timestamp"
DEPENDS "${build_dir}/.bin_timestamp"
VERBATIM
COMMENT "Generated the signed TEE")
else()
add_custom_command(OUTPUT ".signed_bin_timestamp"
VERBATIM
COMMENT "TEE generated but not signed")
endif()
add_custom_target(gen_signed_esp_tee ALL DEPENDS "${build_dir}/.signed_bin_timestamp")
endif()

View File

@ -0,0 +1,21 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
set(srcs "esp_attestation.c"
"esp_att_utils_part_info.c"
"esp_att_utils_crypto.c"
"esp_att_utils_json.c")
set(include_dirs "include")
set(priv_include_dirs "private_include")
set(priv_requires bootloader_support efuse esp_app_format esp_bootloader_format json_generator log mbedtls spi_flash)
if(esp_tee_build)
list(APPEND priv_requires tee_sec_storage tee_flash_mgr)
else()
list(APPEND priv_requires app_update esp_partition)
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include_dirs}
PRIV_INCLUDE_DIRS ${priv_include_dirs}
PRIV_REQUIRES ${priv_requires})

View File

@ -0,0 +1,341 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#if ESP_TEE_BUILD
#include "bootloader_sha.h"
#include "esp_tee_sec_storage.h"
#endif
#include "esp_random.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/sha256.h"
#include "esp_attestation_utils.h"
#define ECDSA_PUBKEY_PREFIX_SZ (0x02)
#define ECDSA_COMPRESSED_KEY_EVEN_PREFIX ("02")
#define ECDSA_COMPRESSED_KEY_ODD_PREFIX ("03")
/* Forward declaration */
static esp_err_t gen_ecdsa_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair);
static esp_err_t get_ecdsa_sign_secp256r1(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len,
uint8_t *sign_r, size_t sign_r_len, uint8_t *sign_s, size_t sign_s_len);
static const char *TAG = "esp_att_utils_crypto";
#if ESP_TEE_BUILD
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_err_t err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
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, &pubkey);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch ECDSA pubkey (%d)", err);
return err;
}
memcpy(keypair->pub_key_x, pubkey.pub_x, sizeof(pubkey.pub_x));
memcpy(keypair->pub_key_y, pubkey.pub_y, sizeof(pubkey.pub_y));
return ESP_OK;
}
static esp_err_t get_ecdsa_sign_secp256r1(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len,
uint8_t *sign_r, size_t sign_r_len, uint8_t *sign_s, size_t sign_s_len)
{
if (sign_r == NULL || sign_s == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (sign_r_len == 0 || sign_s_len == 0) {
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, (uint8_t *)digest, len, &sign);
if (err != ESP_OK) {
return err;
}
memcpy(sign_r, sign.sign_r, sign_r_len);
memcpy(sign_s, sign.sign_s, sign_s_len);
return ESP_OK;
}
#else
static int rng_func(void *rng_ctx, unsigned char *output, size_t len)
{
esp_fill_random(output, len);
return 0;
}
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));
int ret = -1;
esp_err_t err = ESP_FAIL;
mbedtls_ecdsa_context ecdsa_ctx;
mbedtls_ecdsa_init(&ecdsa_ctx);
ret = mbedtls_ecdsa_genkey(&ecdsa_ctx, MBEDTLS_ECP_DP_SECP256R1, rng_func, NULL);
if (ret != 0) {
goto exit;
}
size_t pvt_len = mbedtls_mpi_size(&ecdsa_ctx.MBEDTLS_PRIVATE(d));
ret = mbedtls_mpi_write_binary(&ecdsa_ctx.MBEDTLS_PRIVATE(d), (unsigned char *)keypair->pvt_key, pvt_len);
if (ret != 0) {
goto exit;
}
size_t pubx_len = mbedtls_mpi_size(&(ecdsa_ctx.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X)));
ret = mbedtls_mpi_write_binary(&(ecdsa_ctx.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X)), (unsigned char *)(keypair->pub_key_x), pubx_len);
if (ret != 0) {
goto exit;
}
size_t puby_len = mbedtls_mpi_size(&(ecdsa_ctx.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y)));
ret = mbedtls_mpi_write_binary(&(ecdsa_ctx.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y)), (unsigned char *)(keypair->pub_key_y), puby_len);
if (ret != 0) {
goto exit;
}
keypair->curve = 0;
err = ESP_OK;
exit:
if (ret != 0) {
ESP_LOGE(TAG, "Failed to generate ECDSA keypair (-0x%X)", -ret);
}
mbedtls_ecdsa_free(&ecdsa_ctx);
return err;
}
static esp_err_t get_ecdsa_sign_secp256r1(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len,
uint8_t *sign_r, size_t sign_r_len, uint8_t *sign_s, size_t sign_s_len)
{
if (sign_r == NULL || sign_s == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (sign_r_len == 0 || sign_s_len == 0) {
return ESP_ERR_INVALID_SIZE;
}
esp_err_t err = ESP_FAIL;
mbedtls_ecp_keypair pvt_key;
mbedtls_mpi r, s;
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
mbedtls_ecp_keypair_init(&pvt_key);
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &pvt_key, keypair->pvt_key, sizeof(keypair->pvt_key));
if (ret != 0) {
goto exit;
}
mbedtls_ecdsa_context ecdsa_ctx;
mbedtls_ecdsa_init(&ecdsa_ctx);
ret = mbedtls_ecdsa_from_keypair(&ecdsa_ctx, &pvt_key);
if (ret != 0) {
goto exit;
}
ret = mbedtls_ecdsa_sign(&ecdsa_ctx.MBEDTLS_PRIVATE(grp), &r, &s, &ecdsa_ctx.MBEDTLS_PRIVATE(d),
digest, len, rng_func, NULL);
if (ret != 0) {
return ret;
}
size_t r_len = mbedtls_mpi_size(&r);
if (r_len > sign_s_len) {
goto exit;
}
ret = mbedtls_mpi_write_binary(&r, (unsigned char *)(sign_r), r_len);
if (ret != 0) {
goto exit;
}
size_t s_len = mbedtls_mpi_size(&s);
if (s_len > sign_s_len) {
goto exit;
}
ret = mbedtls_mpi_write_binary(&s, (unsigned char *)(sign_s), s_len);
if (ret != 0) {
goto exit;
}
err = ESP_OK;
exit:
if (ret != 0) {
ESP_LOGE(TAG, "Failed to generate ECDSA signature (-0x%X)", -ret);
}
mbedtls_ecdsa_free(&ecdsa_ctx);
mbedtls_ecp_keypair_free(&pvt_key);
mbedtls_mpi_free(&s);
mbedtls_mpi_free(&r);
return err;
}
#endif
/* TODO: The public key generated here needs to be authorized with the relying party */
esp_err_t esp_att_utils_ecdsa_gen_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair)
{
return gen_ecdsa_keypair_secp256r1(keypair);
}
esp_err_t esp_att_utils_ecdsa_get_pubkey(const esp_att_ecdsa_keypair_t *keypair, char **pubkey_hexstr)
{
if (keypair == NULL || pubkey_hexstr == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = ESP_FAIL;
size_t hexstr_len = sizeof(keypair->pub_key_x) * 2 + ECDSA_PUBKEY_PREFIX_SZ + 1;
char *hexstr = calloc(hexstr_len, sizeof(uint8_t));
if (hexstr == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
/* Checking the parity of the y-component of the public key */
char *pubkey_prefix = (keypair->pub_key_y[SECP256R1_ECDSA_KEY_LEN - 1] & 1)
? ECDSA_COMPRESSED_KEY_ODD_PREFIX
: ECDSA_COMPRESSED_KEY_EVEN_PREFIX;
memcpy(hexstr, pubkey_prefix, ECDSA_PUBKEY_PREFIX_SZ);
err = esp_att_utils_hexbuf_to_hexstr(keypair->pub_key_x, sizeof(keypair->pub_key_x),
&hexstr[ECDSA_PUBKEY_PREFIX_SZ], hexstr_len - ECDSA_PUBKEY_PREFIX_SZ);
if (err != ESP_OK) {
goto exit;
}
*pubkey_hexstr = hexstr;
err = ESP_OK;
exit:
return err;
}
esp_err_t esp_att_utils_ecdsa_get_pubkey_digest(const esp_att_ecdsa_keypair_t *keypair, uint8_t *digest, const size_t len)
{
if (keypair == NULL || digest == NULL || len == 0) {
return ESP_ERR_INVALID_ARG;
}
uint8_t pubkey_c[SECP256R1_ECDSA_KEY_LEN * 2] = {0};
memcpy(pubkey_c, keypair->pub_key_x, SECP256R1_ECDSA_KEY_LEN);
memcpy(pubkey_c + SECP256R1_ECDSA_KEY_LEN, keypair->pub_key_y, SECP256R1_ECDSA_KEY_LEN);
uint8_t pubkey_digest[SHA256_DIGEST_SZ];
int ret = mbedtls_sha256((const unsigned char *)pubkey_c, sizeof(pubkey_c), pubkey_digest, false);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to calculate pubkey digest (-%X)", -ret);
return ESP_FAIL;
}
memcpy(digest, pubkey_digest, len);
return ESP_OK;
}
esp_err_t esp_att_utils_ecdsa_get_sign(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len,
char **sign_r_hexstr, char **sign_s_hexstr)
{
if (keypair == NULL || digest == NULL || sign_r_hexstr == NULL || sign_s_hexstr == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (len == 0) {
return ESP_ERR_INVALID_SIZE;
}
esp_err_t err = ESP_FAIL;
unsigned char sign_r[SECP256R1_ECDSA_KEY_LEN] = {0}, sign_s[SECP256R1_ECDSA_KEY_LEN] = {0};
err = get_ecdsa_sign_secp256r1(keypair, digest, len, sign_r, sizeof(sign_r), sign_s, sizeof(sign_s));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate ECDSA signature");
goto exit;
}
size_t sign_hexstr_len = SECP256R1_ECDSA_KEY_LEN * 2 + 1;
*sign_r_hexstr = calloc(sign_hexstr_len, sizeof(char));
if (*sign_r_hexstr == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
err = esp_att_utils_hexbuf_to_hexstr(sign_r, sizeof(sign_r), *sign_r_hexstr, sign_hexstr_len);
if (err != ESP_OK) {
goto exit;
}
*sign_s_hexstr = calloc(sign_hexstr_len, sizeof(char));
if (*sign_s_hexstr == NULL) {
free(*sign_r_hexstr);
*sign_r_hexstr = NULL;
err = ESP_ERR_NO_MEM;
goto exit;
}
err = esp_att_utils_hexbuf_to_hexstr(sign_s, sizeof(sign_s), *sign_s_hexstr, sign_hexstr_len);
if (err != ESP_OK) {
goto exit;
}
err = ESP_OK;
exit:
return err;
}

View File

@ -0,0 +1,288 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#if ESP_TEE_BUILD
#include "bootloader_sha.h"
#include "esp_tee_sec_storage.h"
#endif
#include "esp_random.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/sha256.h"
#include "json_generator.h"
#include "esp_attestation_utils.h"
#define DIGEST_HEXSTR_LEN (MAX_DIGEST_SZ * 2 + 1)
static const char *TAG = "esp_att_utils_json";
static const char *str_from_fw_type(esp_att_part_type_t fw, size_t *length)
{
if (fw >= ESP_ATT_PART_TYPE_MAX) {
if (length) {
*length = SIZE_MAX;
}
return NULL;
}
static const char *fw_type_str[] = {"bootloader", "tee", "app", "other"};
if (length) {
*length = strlen(fw_type_str[fw]);
}
return fw_type_str[fw];
}
static esp_err_t part_metadata_to_json(const esp_att_part_metadata_t *metadata, char **claim_json)
{
if (metadata == NULL || claim_json == NULL) {
return ESP_ERR_INVALID_ARG;
}
char *json_buf = calloc(ESP_ATT_CLAIM_JSON_MAX_SZ, sizeof(char));
if (json_buf == NULL) {
return ESP_ERR_NO_MEM;
}
json_gen_str_t json_gen;
// Initialize the JSON string generator
json_gen_str_start(&json_gen, json_buf, ESP_ATT_CLAIM_JSON_MAX_SZ, NULL, NULL);
// Start the top-level JSON object
json_gen_start_object(&json_gen);
// Add the properties within the "app" object
json_gen_obj_set_int(&json_gen, "type", metadata->type);
json_gen_obj_set_string(&json_gen, "ver", (char *)metadata->ver);
json_gen_obj_set_string(&json_gen, "idf_ver", (char *)metadata->idf_ver);
json_gen_obj_set_int(&json_gen, "secure_ver", metadata->secure_ver);
// Add "part_chip_rev" object
json_gen_push_object(&json_gen, "part_chip_rev");
json_gen_obj_set_int(&json_gen, "min", metadata->part_chip_rev.min_chip_rev);
json_gen_obj_set_int(&json_gen, "max", metadata->part_chip_rev.max_chip_rev);
json_gen_pop_object(&json_gen);
// Add "part_digest" object
json_gen_push_object(&json_gen, "part_digest");
json_gen_obj_set_int(&json_gen, "type", metadata->part_digest.type);
char calc_digest_hexstr[DIGEST_HEXSTR_LEN];
esp_err_t err = esp_att_utils_hexbuf_to_hexstr(metadata->part_digest.calc_digest, sizeof(metadata->part_digest.calc_digest),
calc_digest_hexstr, sizeof(calc_digest_hexstr));
if (err != ESP_OK) {
return err;
}
json_gen_obj_set_string(&json_gen, "calc_digest", calc_digest_hexstr);
json_gen_obj_set_bool(&json_gen, "digest_validated", metadata->part_digest.digest_validated);
json_gen_obj_set_bool(&json_gen, "sign_verified", metadata->part_digest.sign_verified);
if (metadata->type == ESP_ATT_PART_TYPE_TEE || metadata->type == ESP_ATT_PART_TYPE_APP) {
json_gen_obj_set_bool(&json_gen, "secure_padding", metadata->part_digest.secure_padding);
}
json_gen_pop_object(&json_gen);
// End the top-level JSON object
json_gen_end_object(&json_gen);
// Finalize the JSON string generation
json_gen_str_end(&json_gen);
*claim_json = json_buf;
return ESP_OK;
}
esp_err_t esp_att_utils_header_to_json(const esp_att_token_hdr_t *tk_hdr, char **header_json, int *len)
{
/* NOTE: Token header is not yet configurable, thus will be left empty for now */
if (tk_hdr == NULL || header_json == NULL || len == NULL) {
return ESP_ERR_INVALID_ARG;
}
char *json_buf = calloc(ESP_ATT_HDR_JSON_MAX_SZ, sizeof(char));
if (json_buf == NULL) {
return ESP_ERR_NO_MEM;
}
json_gen_str_t json_gen;
// Initialize the JSON string generator
json_gen_str_start(&json_gen, json_buf, ESP_ATT_HDR_JSON_MAX_SZ, NULL, NULL);
// Start the top-level JSON object
json_gen_start_object(&json_gen);
json_gen_obj_set_string(&json_gen, "magic", ESP_ATT_TK_MAGIC_STR);
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);
// End the top-level JSON object
json_gen_end_object(&json_gen);
// Finalize the JSON string generation
*len = json_gen_str_end(&json_gen);
*header_json = json_buf;
return ESP_OK;
}
esp_err_t esp_att_utils_eat_data_to_json(struct esp_att_sw_claim_list *head, const esp_att_token_cfg_t *cfg, char **eat_json, int *len)
{
if (head == NULL || eat_json == NULL || len == NULL || cfg == NULL) {
return ESP_ERR_INVALID_ARG;
}
char *json_buf = calloc(ESP_ATT_EAT_JSON_MAX_SZ, sizeof(char));
if (json_buf == NULL) {
return ESP_ERR_NO_MEM;
}
json_gen_str_t json_gen;
// Initialize the JSON string generator
json_gen_str_start(&json_gen, json_buf, ESP_ATT_EAT_JSON_MAX_SZ, NULL, NULL);
// Start the top-level JSON object
json_gen_start_object(&json_gen);
esp_att_sw_claim_list_t *claim = NULL;
char *claim_json = NULL;
json_gen_obj_set_int(&json_gen, "nonce", cfg->nonce);
json_gen_obj_set_int(&json_gen, "client_id", cfg->client_id);
json_gen_obj_set_int(&json_gen, "device_ver", cfg->device_ver);
char dev_id_hexstr[ESP_ATT_EAT_DEV_ID_SZ * 2 + 1] = {0};
esp_err_t err = esp_att_utils_hexbuf_to_hexstr(cfg->device_id, sizeof(cfg->device_id), dev_id_hexstr, sizeof(dev_id_hexstr));
if (err != ESP_OK) {
return err;
}
json_gen_obj_set_string(&json_gen, "device_id", dev_id_hexstr);
/* NOTE: Instance ID is the SHA256 of the ECDSA public key in usage */
char inst_id_hexstr[DIGEST_HEXSTR_LEN] = {0};
err = esp_att_utils_hexbuf_to_hexstr(cfg->instance_id, sizeof(cfg->instance_id), inst_id_hexstr, sizeof(inst_id_hexstr));
if (err != ESP_OK) {
return err;
}
json_gen_obj_set_string(&json_gen, "instance_id", inst_id_hexstr);
json_gen_obj_set_string(&json_gen, "psa_cert_ref", cfg->psa_cert_ref);
json_gen_obj_set_int(&json_gen, "device_status", cfg->device_stat);
json_gen_push_object(&json_gen, "sw_claims");
SLIST_FOREACH(claim, head, next) {
esp_err_t err = part_metadata_to_json(&claim->metadata, &claim_json);
if (err != ESP_OK || claim_json == NULL) {
ESP_LOGE(TAG, "Failed to format the FW metadata to JSON!");
return err;
}
json_gen_push_object_str(&json_gen, (char *)str_from_fw_type(claim->metadata.type, NULL), claim_json);
free(claim_json);
}
json_gen_pop_object(&json_gen);
// End the top-level JSON object
json_gen_end_object(&json_gen);
// Finalize the JSON string generation
*len = json_gen_str_end(&json_gen);
*eat_json = json_buf;
return ESP_OK;
}
esp_err_t esp_att_utils_pubkey_to_json(const esp_att_ecdsa_keypair_t *keypair, char **pubkey_json, int *len)
{
if (keypair == NULL || pubkey_json == NULL || len == NULL) {
return ESP_ERR_INVALID_ARG;
}
char *json_buf = calloc(ESP_ATT_PUBKEY_JSON_MAX_SZ, sizeof(char));
if (json_buf == NULL) {
return ESP_ERR_NO_MEM;
}
json_gen_str_t json_gen;
// Initialize the JSON string generator
json_gen_str_start(&json_gen, json_buf, ESP_ATT_PUBKEY_JSON_MAX_SZ, NULL, NULL);
// Start the top-level JSON object
json_gen_start_object(&json_gen);
char *pubkey_hexstr = NULL;
esp_err_t err = esp_att_utils_ecdsa_get_pubkey(keypair, &pubkey_hexstr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get public key!");
return err;
}
json_gen_obj_set_string(&json_gen, "compressed", pubkey_hexstr);
// End the top-level JSON object
json_gen_end_object(&json_gen);
// Finalize the JSON string generation
*len = json_gen_str_end(&json_gen);
*pubkey_json = json_buf;
free(pubkey_hexstr);
return ESP_OK;
}
esp_err_t esp_att_utils_sign_to_json(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t digest_len,
char **sign_json, int *len)
{
if (keypair == NULL || digest == NULL || sign_json == NULL || len == NULL) {
return ESP_ERR_INVALID_ARG;
}
char *json_buf = calloc(ESP_ATT_SIGN_JSON_MAX_SZ, sizeof(char));
if (json_buf == NULL) {
return ESP_ERR_NO_MEM;
}
json_gen_str_t json_gen;
// Initialize the JSON string generator
json_gen_str_start(&json_gen, json_buf, ESP_ATT_SIGN_JSON_MAX_SZ, NULL, NULL);
// Start the top-level JSON object
json_gen_start_object(&json_gen);
char *sign_r_hexstr = NULL, *sign_s_hexstr = NULL;
esp_err_t err = esp_att_utils_ecdsa_get_sign(keypair, digest, digest_len, &sign_r_hexstr, &sign_s_hexstr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to sign token!");
return err;
}
json_gen_obj_set_string(&json_gen, "r", sign_r_hexstr);
json_gen_obj_set_string(&json_gen, "s", sign_s_hexstr);
// End the top-level JSON object
json_gen_end_object(&json_gen);
// Finalize the JSON string generation
*len = json_gen_str_end(&json_gen);
*sign_json = json_buf;
free(sign_r_hexstr);
free(sign_s_hexstr);
return ESP_OK;
}

View File

@ -0,0 +1,545 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <sys/param.h>
#include <sys/queue.h>
#include "esp_log.h"
#include "esp_err.h"
#include "hal/efuse_hal.h"
#include "esp_app_format.h"
#include "esp_bootloader_desc.h"
#include "esp_image_format.h"
#include "esp_app_desc.h"
#if ESP_TEE_BUILD
#include "esp_flash_partitions.h"
#include "bootloader_utility_tee.h"
#include "esp_tee_flash.h"
#else
#include "esp_partition.h"
#include "esp_ota_ops.h"
#endif
#if CONFIG_SECURE_BOOT_V2_ENABLED
#include "esp_secure_boot.h"
#include "bootloader_utility.h"
#if CONFIG_IDF_TARGET_ESP32C6
#include "esp32c6/rom/secure_boot.h"
#endif
#endif
#include "mbedtls/sha256.h"
#include "bootloader_flash_priv.h"
#include "esp_attestation_utils.h"
#define SECURE_BOOT_V2 (0x02)
#define ALIGN_UP(num, align) (((num) + ((align)-1)) & ~((align)-1))
static const char *TAG = "esp_att_utils";
/* Forward declaration */
static esp_err_t read_partition(uint32_t offset, void *buf, size_t size);
esp_err_t get_flash_contents_sha256(uint32_t flash_offset, uint32_t len, uint8_t *digest);
static esp_err_t get_active_app_part_pos(esp_partition_pos_t *pos);
static esp_err_t get_active_tee_part_pos(esp_partition_pos_t *pos);
/* ---------------------------------------------- Helper APIs ------------------------------------------------- */
static size_t digest_type_to_len(esp_att_part_digest_type_t digest)
{
size_t digest_len = 0;
switch (digest) {
case ESP_ATT_DIGEST_TYPE_SHA256:
digest_len = SHA256_DIGEST_SZ;
break;
default:
break;
}
return digest_len;
}
#if ESP_TEE_BUILD
static esp_err_t read_partition(uint32_t offset, void *buf, size_t size)
{
return (esp_err_t)esp_tee_flash_read(offset, buf, size, true);
}
esp_err_t get_flash_contents_sha256(uint32_t flash_offset, uint32_t len, uint8_t *digest)
{
if (digest == NULL) {
return ESP_ERR_INVALID_ARG;
}
uint32_t mmu_free_pages_count = esp_tee_flash_mmap_get_free_pages();
uint32_t partial_image_len = mmu_free_pages_count * CONFIG_MMU_PAGE_SIZE;
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
int ret = mbedtls_sha256_starts(&ctx, false);
if (ret != 0) {
mbedtls_sha256_free(&ctx);
return ESP_FAIL;
}
while (len > 0) {
uint32_t mmap_len = MIN(len, partial_image_len);
const void *image = esp_tee_flash_mmap(flash_offset, mmap_len);
if (image == NULL) {
mbedtls_sha256_finish(&ctx, NULL);
return ESP_FAIL;
}
mbedtls_sha256_update(&ctx, image, mmap_len);
esp_tee_flash_munmap(image);
flash_offset += mmap_len;
len -= mmap_len;
}
mbedtls_sha256_finish(&ctx, digest);
mbedtls_sha256_free(&ctx);
return ESP_OK;
}
static esp_err_t get_active_app_part_pos(esp_partition_pos_t *pos)
{
if (pos == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_partition_info_t *running_app = esp_tee_flash_get_running_ree_partition();
if (running_app->magic != ESP_PARTITION_MAGIC) {
return ESP_ERR_NOT_FOUND;
}
memcpy(pos, &running_app->pos, sizeof(esp_partition_pos_t));
return ESP_OK;
}
static esp_err_t get_active_tee_part_pos(esp_partition_pos_t *pos)
{
uint8_t tee_active_part = bootloader_utility_tee_get_boot_partition(NULL);
if (tee_active_part > PART_SUBTYPE_TEE_1) {
return ESP_ERR_NOT_FOUND;
}
esp_partition_info_t part_info = {};
esp_err_t err = esp_tee_flash_find_partition(PART_TYPE_APP, tee_active_part, NULL, &part_info);
if (err != ESP_OK) {
return err;
}
memcpy(pos, &part_info.pos, sizeof(esp_partition_pos_t));
return ESP_OK;
}
#else
static esp_err_t read_partition(uint32_t offset, void *buf, size_t size)
{
return esp_flash_read(NULL, buf, offset, size);
}
esp_err_t get_flash_contents_sha256(uint32_t flash_offset, uint32_t len, uint8_t *digest)
{
if (digest == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = ESP_FAIL;
uint32_t mmu_free_pages_count = bootloader_mmap_get_free_pages();
uint32_t partial_image_len = mmu_free_pages_count * CONFIG_MMU_PAGE_SIZE;
mbedtls_sha256_context sha256_ctx;
mbedtls_sha256_init(&sha256_ctx);
if (mbedtls_sha256_starts(&sha256_ctx, false) != 0) {
goto exit;
}
while (len > 0) {
uint32_t mmap_len = MIN(len, partial_image_len);
const void *image = bootloader_mmap(flash_offset, mmap_len);
if (image == NULL) {
goto exit;
}
if (mbedtls_sha256_update(&sha256_ctx, image, mmap_len) != 0) {
goto exit;
}
bootloader_munmap(image);
flash_offset += mmap_len;
len -= mmap_len;
}
if (mbedtls_sha256_finish(&sha256_ctx, digest) != 0) {
goto exit;
}
err = ESP_OK;
exit:
mbedtls_sha256_free(&sha256_ctx);
return err;
}
static esp_err_t get_active_app_part_pos(esp_partition_pos_t *pos)
{
if (pos == NULL) {
return ESP_ERR_INVALID_ARG;
}
const esp_partition_t *running_part = esp_ota_get_running_partition();
if (running_part == NULL) {
return ESP_ERR_NOT_FOUND;
}
pos->offset = running_part->address;
pos->size = running_part->size;
return ESP_OK;
}
static esp_err_t get_active_tee_part_pos(esp_partition_pos_t *pos)
{
return ESP_ERR_NOT_SUPPORTED;
}
#endif
static esp_err_t get_active_part_pos(esp_att_part_type_t part_type, esp_partition_pos_t *active_pos)
{
if (active_pos == NULL || part_type > ESP_ATT_PART_TYPE_APP) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err;
esp_partition_pos_t pos = {};
switch (part_type) {
case ESP_ATT_PART_TYPE_BOOTLOADER:
pos.offset = CONFIG_BOOTLOADER_OFFSET_IN_FLASH;
pos.size = CONFIG_PARTITION_TABLE_OFFSET - CONFIG_BOOTLOADER_OFFSET_IN_FLASH;
break;
case ESP_ATT_PART_TYPE_APP:
err = get_active_app_part_pos(&pos);
if (err != ESP_OK) {
return err;
}
break;
case ESP_ATT_PART_TYPE_TEE:
err = get_active_tee_part_pos(&pos);
if (err != ESP_OK) {
return err;
}
break;
default:
ESP_LOGE(TAG, "Unsupported partition type!");
return ESP_ERR_NOT_FOUND;
}
memcpy(active_pos, &pos, sizeof(esp_partition_pos_t));
return ESP_OK;
}
static esp_err_t get_part_digest(const esp_partition_pos_t *pos, esp_att_part_digest_info_t *part_digest)
{
if (pos == NULL || part_digest == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_image_metadata_t metadata = {};
esp_err_t err = esp_image_get_metadata(pos, &metadata);
if (err != ESP_OK) {
return err;
}
memset(part_digest, 0x00, sizeof(esp_att_part_digest_info_t));
part_digest->type = ESP_ATT_DIGEST_TYPE_SHA256;
size_t digest_len = digest_type_to_len(part_digest->type);
size_t image_len = metadata.image_len - digest_len;
uint8_t *digest = calloc(digest_len, sizeof(uint8_t));
if (digest == NULL) {
return ESP_ERR_NO_MEM;
}
err = get_flash_contents_sha256(pos->offset, image_len, digest);
if (err != ESP_OK) {
goto exit;
}
if (!memcmp(digest, &metadata.image_digest, digest_len)) {
part_digest->digest_validated = true;
}
memset(part_digest->calc_digest, 0x00, sizeof(part_digest->calc_digest));
memcpy(part_digest->calc_digest, digest, digest_len);
#if CONFIG_SECURE_BOOT_V2_ENABLED
uint32_t signed_image_len = ALIGN_UP(metadata.image_len, FLASH_SECTOR_SIZE);
if (signed_image_len % CONFIG_MMU_PAGE_SIZE == 0) {
part_digest->secure_padding = true;
} else {
part_digest->secure_padding = false;
}
err = esp_secure_boot_verify_signature(metadata.start_addr, signed_image_len);
if (err == ESP_OK) {
part_digest->sign_verified = true;
}
#endif
err = ESP_OK;
exit:
free(digest);
return err;
}
static esp_err_t get_part_btl_desc(const esp_partition_pos_t *pos, esp_bootloader_desc_t *btl_desc)
{
if (pos == NULL || btl_desc == NULL) {
return ESP_ERR_INVALID_ARG;
}
uint32_t btl_desc_offset = pos->offset + sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t);
esp_err_t err = read_partition(btl_desc_offset, btl_desc, sizeof(esp_bootloader_desc_t));
if (err != ESP_OK) {
return err;
}
if (btl_desc->magic_byte != ESP_BOOTLOADER_DESC_MAGIC_BYTE) {
return ESP_ERR_NOT_FOUND;
}
return ESP_OK;
}
static esp_err_t get_part_app_desc(const esp_partition_pos_t *pos, esp_app_desc_t *app_desc)
{
if (pos == NULL || app_desc == NULL) {
return ESP_ERR_INVALID_ARG;
}
uint32_t app_desc_offset = pos->offset + sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t);
esp_err_t err = read_partition(app_desc_offset, app_desc, sizeof(esp_app_desc_t));
if (err != ESP_OK) {
return err;
}
if (app_desc->magic_word != ESP_APP_DESC_MAGIC_WORD) {
return ESP_ERR_NOT_FOUND;
}
return ESP_OK;
}
static esp_err_t get_part_chip_rev(const esp_partition_pos_t *pos, esp_att_part_chip_rev_t *chip_rev)
{
if (pos == NULL || chip_rev == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_image_header_t img_hdr = {};
esp_err_t err = read_partition(pos->offset, &img_hdr, sizeof(esp_image_header_t));
if (err != ESP_OK) {
return err;
}
if (img_hdr.magic != ESP_IMAGE_HEADER_MAGIC) {
return ESP_ERR_NOT_FOUND;
}
chip_rev->min_chip_rev = img_hdr.min_chip_rev_full;
chip_rev->max_chip_rev = img_hdr.max_chip_rev_full;
return ESP_OK;
}
esp_err_t esp_att_utils_hexbuf_to_hexstr(const void *hexbuf, size_t hexbuf_sz, char *hexstr, size_t hexstr_len)
{
if (hexbuf == NULL || hexstr == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (hexbuf_sz == 0 || hexstr_len < (hexbuf_sz * 2 + 1)) {
return ESP_ERR_INVALID_SIZE;
}
const uint8_t *bytes = (const uint8_t *)hexbuf;
for (size_t i = 0; i < hexbuf_sz; i++) {
for (int shift = 0; shift < 2; shift++) {
uint8_t nibble = (bytes[i] >> (shift ? 0 : 4)) & 0x0F;
if (nibble < 10) {
hexstr[i * 2 + shift] = '0' + nibble;
} else {
hexstr[i * 2 + shift] = 'a' + nibble - 10;
}
}
}
hexstr[hexbuf_sz * 2] = '\0';
return ESP_OK;
}
esp_err_t esp_att_utils_get_btl_claim_data(esp_att_part_metadata_t *btl_metadata)
{
esp_partition_pos_t btl_pos = {};
esp_err_t err = get_active_part_pos(ESP_ATT_PART_TYPE_BOOTLOADER, &btl_pos);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get running app partition!");
return err;
}
esp_att_part_digest_info_t btl_digest = {};
err = get_part_digest(&btl_pos, &btl_digest);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get bootloader image digest!");
return err;
}
esp_bootloader_desc_t btl_desc = {};
err = get_part_btl_desc(&btl_pos, &btl_desc);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get bootloader partition description!");
return err;
}
esp_att_part_chip_rev_t btl_chip_rev = {};
err = get_part_chip_rev(&btl_pos, &btl_chip_rev);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get bootloader image chip rev!");
return err;
}
/* Clearing and populating the bootloader FW metadata */
memset(btl_metadata, 0x00, sizeof(esp_att_part_metadata_t));
btl_metadata->type = ESP_ATT_PART_TYPE_BOOTLOADER;
btl_metadata->secure_ver = btl_desc.secure_version;
err = esp_att_utils_hexbuf_to_hexstr(&btl_desc.version, sizeof(btl_desc.version), btl_metadata->ver, sizeof(btl_metadata->ver));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch bootloader version!");
return err;
}
memcpy(btl_metadata->idf_ver, &btl_desc.idf_ver, sizeof(btl_metadata->idf_ver));
memcpy(&btl_metadata->part_digest, &btl_digest, sizeof(esp_att_part_digest_info_t));
memcpy(&btl_metadata->part_chip_rev, &btl_chip_rev, sizeof(esp_att_part_chip_rev_t));
return ESP_OK;
}
esp_err_t esp_att_utils_get_app_claim_data(esp_att_part_metadata_t *app_metadata)
{
esp_partition_pos_t app_pos = {};
esp_err_t err = get_active_part_pos(ESP_ATT_PART_TYPE_APP, &app_pos);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get running app partition!");
return err;
}
esp_att_part_digest_info_t app_digest = {};
err = get_part_digest(&app_pos, &app_digest);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get app image digest!");
return err;
}
esp_app_desc_t ns_app_desc = {};
err = get_part_app_desc(&app_pos, &ns_app_desc);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get app partition description!");
return err;
}
esp_att_part_chip_rev_t app_chip_rev = {};
err = get_part_chip_rev(&app_pos, &app_chip_rev);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get app image chip rev!");
return err;
}
/* Clearing and populating the bootloader FW metadata */
memset(app_metadata, 0x00, sizeof(esp_att_part_metadata_t));
app_metadata->type = ESP_ATT_PART_TYPE_APP;
app_metadata->secure_ver = ns_app_desc.secure_version;
memcpy(app_metadata->ver, &ns_app_desc.version, sizeof(app_metadata->ver));
memcpy(app_metadata->idf_ver, &ns_app_desc.idf_ver, sizeof(app_metadata->idf_ver));
memcpy(&app_metadata->part_digest, &app_digest, sizeof(esp_att_part_digest_info_t));
memcpy(&app_metadata->part_chip_rev, &app_chip_rev, sizeof(esp_att_part_chip_rev_t));
return ESP_OK;
}
esp_err_t esp_att_utils_get_tee_claim_data(esp_att_part_metadata_t *tee_metadata)
{
#if ESP_TEE_BUILD
esp_partition_pos_t tee_pos = {};
esp_err_t err = get_active_part_pos(ESP_ATT_PART_TYPE_TEE, &tee_pos);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get running tee partition!");
return err;
}
esp_att_part_digest_info_t tee_digest = {};
err = get_part_digest(&tee_pos, &tee_digest);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get TEE image digest!");
return err;
}
esp_app_desc_t tee_app_desc = {};
err = get_part_app_desc(&tee_pos, &tee_app_desc);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get TEE partition description!");
return err;
}
esp_att_part_chip_rev_t tee_chip_rev = {};
err = get_part_chip_rev(&tee_pos, &tee_chip_rev);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get TEE image chip rev!");
return err;
}
/* Clearing and populating the TEE FW metadata */
memset(tee_metadata, 0x00, sizeof(esp_att_part_metadata_t));
tee_metadata->type = ESP_ATT_PART_TYPE_TEE;
tee_metadata->secure_ver = tee_app_desc.secure_version;
memcpy(tee_metadata->ver, &tee_app_desc.version, sizeof(tee_metadata->ver));
memcpy(tee_metadata->idf_ver, &tee_app_desc.idf_ver, sizeof(tee_metadata->idf_ver));
memcpy(&tee_metadata->part_digest, &tee_digest, sizeof(esp_att_part_digest_info_t));
memcpy(&tee_metadata->part_chip_rev, &tee_chip_rev, sizeof(esp_att_part_chip_rev_t));
return ESP_OK;
#else
ESP_LOGE(TAG, "TEE not supported!");
return ESP_ERR_NOT_SUPPORTED;
#endif
}

View File

@ -0,0 +1,304 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#include "hal/efuse_hal.h"
#include "mbedtls/sha256.h"
#include "esp_attestation.h"
#include "esp_attestation_utils.h"
#include "json_generator.h"
#include "sdkconfig.h"
static const char *TAG = "esp_attestation";
/* ---------------------------------------------- Interface APIs ------------------------------------------------- */
static struct esp_att_sw_claim_list sw_claim_data = SLIST_HEAD_INITIALIZER(sw_claim_data);
static esp_att_sw_claim_list_t *create_sw_claim_entry(esp_att_part_metadata_t *metadata)
{
esp_att_sw_claim_list_t *sw_claim_entry = calloc(1, sizeof(esp_att_sw_claim_list_t));
if (sw_claim_entry == NULL) {
ESP_LOGE(TAG, "Failed to allocate claim data!");
return NULL;
}
memcpy(&sw_claim_entry->metadata, metadata, sizeof(esp_att_part_metadata_t));
return sw_claim_entry;
}
static void free_sw_claim_list(void)
{
while (!SLIST_EMPTY(&sw_claim_data)) {
esp_att_sw_claim_list_t *claim = SLIST_FIRST(&sw_claim_data);
SLIST_REMOVE_HEAD(&sw_claim_data, next);
free(claim);
}
}
static esp_err_t fetch_device_id(uint8_t *devid_buf)
{
if (devid_buf == NULL) {
return ESP_ERR_INVALID_ARG;
}
uint8_t mac_addr[6] = {0};
esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_MAC, mac_addr, sizeof(mac_addr) * 8);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to read MAC from eFuse!");
goto exit;
}
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
int ret = mbedtls_sha256_starts(&ctx, false);
if (ret != 0) {
mbedtls_sha256_free(&ctx);
err = ESP_FAIL;
goto exit;
}
ret = mbedtls_sha256_update(&ctx, (const unsigned char *)mac_addr, sizeof(mac_addr));
if (ret != 0) {
mbedtls_sha256_free(&ctx);
err = ESP_FAIL;
goto exit;
}
uint8_t digest[SHA256_DIGEST_SZ] = {0};
ret = mbedtls_sha256_finish(&ctx, digest);
if (ret != 0) {
mbedtls_sha256_free(&ctx);
err = ESP_FAIL;
goto exit;
}
memcpy(devid_buf, digest, SHA256_DIGEST_SZ);
mbedtls_sha256_free(&ctx);
exit:
return err;
}
static esp_err_t populate_att_token_cfg(esp_att_token_cfg_t *cfg, const esp_att_ecdsa_keypair_t *keypair)
{
if (cfg == NULL || keypair == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = fetch_device_id(cfg->device_id);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get the device ID!");
return err;
}
err = esp_att_utils_ecdsa_get_pubkey_digest(keypair, cfg->instance_id, sizeof(cfg->instance_id));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get ECDSA public key hash!");
return err;
}
cfg->device_ver = efuse_hal_chip_revision();
/* TODO: Decide what all fields we need here */
cfg->device_stat = 0xA5;
return ESP_OK;
}
static esp_err_t populate_sw_claim_data(void)
{
esp_att_part_metadata_t metadata = {};
esp_att_sw_claim_list_t *claim = NULL;
esp_err_t err = esp_att_utils_get_btl_claim_data(&metadata);
if (err != ESP_OK) {
return err;
}
SLIST_INIT(&sw_claim_data);
claim = create_sw_claim_entry(&metadata);
if (claim == NULL) {
return ESP_ERR_NO_MEM;
}
SLIST_INSERT_HEAD(&sw_claim_data, claim, next);
err = esp_att_utils_get_app_claim_data(&metadata);
if (err != ESP_OK) {
goto exit;
}
claim = create_sw_claim_entry(&metadata);
if (claim == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
SLIST_INSERT_HEAD(&sw_claim_data, claim, next);
err = esp_att_utils_get_tee_claim_data(&metadata);
if (err != ESP_OK) {
if (err == ESP_ERR_NOT_SUPPORTED) {
return ESP_OK;
}
return err;
}
claim = create_sw_claim_entry(&metadata);
if (claim == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
SLIST_INSERT_HEAD(&sw_claim_data, claim, next);
return ESP_OK;
exit:
free_sw_claim_list();
return err;
}
esp_err_t esp_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref,
uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len)
{
if (token_buf == NULL || token_len == NULL || psa_cert_ref == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (token_buf_size < ESP_ATT_TK_MIN_SIZE) {
ESP_LOGE(TAG, "EAT buffer too small: got %luB, need > %dB", token_buf_size, ESP_ATT_TK_MIN_SIZE);
return ESP_ERR_INVALID_SIZE;
}
esp_err_t err = populate_sw_claim_data();
if (err != ESP_OK || SLIST_EMPTY(&sw_claim_data)) {
ESP_LOGE(TAG, "Failed to fetch S/W claim data!");
return err;
}
esp_att_ecdsa_keypair_t keypair = {};
err = esp_att_utils_ecdsa_gen_keypair_secp256r1(&keypair);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate ECDSA key-pair!");
goto exit;
}
esp_att_token_cfg_t cfg = {
.nonce = nonce,
.client_id = client_id,
};
memcpy(cfg.psa_cert_ref, psa_cert_ref, sizeof(cfg.psa_cert_ref));
err = populate_att_token_cfg(&cfg, &keypair);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to populate token config data!");
goto exit;
}
memset(token_buf, 0x00, token_buf_size);
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
int ret = mbedtls_sha256_starts(&ctx, false);
if (ret != 0) {
mbedtls_sha256_free(&ctx);
return ESP_FAIL;
}
json_gen_str_t jstr;
json_gen_str_start(&jstr, (char *)token_buf, token_buf_size, NULL, NULL);
json_gen_start_object(&jstr);
/* Pushing the Header object */
const esp_att_token_hdr_t tk_hdr = {};
char *hdr_json = NULL;
int hdr_len = -1;
/* NOTE: Token header is not yet configurable */
err = esp_att_utils_header_to_json(&tk_hdr, &hdr_json, &hdr_len);
if (err != ESP_OK || hdr_json == NULL || hdr_len <= 0) {
ESP_LOGE(TAG, "Failed to format the token header as JSON!");
return err;
}
json_gen_push_object_str(&jstr, "header", hdr_json);
ret = mbedtls_sha256_update(&ctx, (const unsigned char *)hdr_json, hdr_len - 1);
if (ret != 0) {
mbedtls_sha256_free(&ctx);
return ESP_FAIL;
}
free(hdr_json);
/* Pushing the EAT object */
char *eat_json = NULL;
int eat_len = -1;
err = esp_att_utils_eat_data_to_json(&sw_claim_data, &cfg, &eat_json, &eat_len);
if (err != ESP_OK || eat_json == NULL || eat_len <= 0) {
ESP_LOGE(TAG, "Failed to format the EAT data to JSON!");
return err;
}
json_gen_push_object_str(&jstr, "eat", eat_json);
ret = mbedtls_sha256_update(&ctx, (const unsigned char *)eat_json, eat_len - 1);
if (ret != 0) {
mbedtls_sha256_free(&ctx);
return ESP_FAIL;
}
free(eat_json);
char *pubkey_json = NULL;
int pubkey_len = -1;
err = esp_att_utils_pubkey_to_json(&keypair, &pubkey_json, &pubkey_len);
if (err != ESP_OK || pubkey_json == NULL || pubkey_len <= 0) {
ESP_LOGE(TAG, "Failed to format the public key data to JSON!");
return err;
}
json_gen_push_object_str(&jstr, "public_key", pubkey_json);
ret = mbedtls_sha256_update(&ctx, (const unsigned char *)pubkey_json, pubkey_len - 1);
if (ret != 0) {
mbedtls_sha256_free(&ctx);
return ESP_FAIL;
}
free(pubkey_json);
uint8_t digest[SHA256_DIGEST_SZ] = {0};
ret = mbedtls_sha256_finish(&ctx, digest);
if (ret != 0) {
mbedtls_sha256_free(&ctx);
return ESP_FAIL;
}
mbedtls_sha256_free(&ctx);
char *sign_json = NULL;
int sign_len = -1;
err = esp_att_utils_sign_to_json(&keypair, digest, sizeof(digest), &sign_json, &sign_len);
if (err != ESP_OK || sign_json == NULL || sign_len <= 0) {
ESP_LOGE(TAG, "Failed to format the token signature to JSON!");
return err;
}
json_gen_push_object_str(&jstr, "sign", sign_json);
free(sign_json);
json_gen_end_object(&jstr);
*token_len = json_gen_str_end(&jstr);
err = ESP_OK;
exit:
free_sw_claim_list();
return err;
}

View File

@ -0,0 +1,31 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Generate an entity attestation token
*
* @param[in] nonce Nonce value to include in the token
* @param[in] client_id Client identifier to include in the token
* @param[in] psa_cert_ref PSA certificate reference to include in the token
* @param[in] token_buf Buffer to store the generated token
* @param[in] token_buf_size Size of the token buffer
* @param[out] token_len Pointer to store the actual length of the generated token
*
* @return esp_err_t ESP_OK on success, or an error code on failure
*/
esp_err_t esp_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref,
uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,278 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <sys/queue.h>
#include "esp_err.h"
#include "esp_flash_partitions.h"
#include "esp_app_desc.h"
#include "esp_attestation.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ESP_ATT_TK_MAGIC 0x44FEF7CC
#define ESP_ATT_TK_MAGIC_STR "44fef7cc"
#define ESP_ATT_TK_SIGN_ALG "ecdsa_secp256r1_sha256"
#define SECP256R1_ECDSA_KEY_LEN (32)
#define SECP256R1_ECDSA_SIG_LEN (64)
#define MAX_ECDSA_KEY_LEN (32)
#define MAX_ECDSA_SIG_LEN (64)
#define SHA256_DIGEST_SZ (32)
#define MAX_DIGEST_SZ (32)
#define ESP_ATT_HDR_JSON_MAX_SZ (128)
#define ESP_ATT_EAT_DEV_ID_SZ (32)
#define ESP_ATT_CLAIM_JSON_MAX_SZ (448)
#define ESP_ATT_EAT_JSON_MAX_SZ (1344)
#define ESP_ATT_PUBKEY_JSON_MAX_SZ (128)
#define ESP_ATT_SIGN_JSON_MAX_SZ (192)
#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
#define ESP_ATT_TK_KEY_ID (CONFIG_SECURE_TEE_ATT_KEY_SLOT_ID)
#else
#define ESP_ATT_TK_KEY_ID (-1)
#endif
/**
* @brief Enumeration of partition types for attestation
*/
typedef enum {
ESP_ATT_PART_TYPE_BOOTLOADER = 0,
ESP_ATT_PART_TYPE_TEE = 1,
ESP_ATT_PART_TYPE_APP = 2,
ESP_ATT_PART_TYPE_OTHER = 3,
ESP_ATT_PART_TYPE_MAX = 4,
} esp_att_part_type_t;
/**
* @brief Enumeration of digest types for attestation
*/
typedef enum {
ESP_ATT_DIGEST_TYPE_SHA256 = 0, /**< SHA-256 digest type */
} esp_att_part_digest_type_t;
/**
* @brief Structure to hold digest information for a partition
*/
typedef struct {
esp_att_part_digest_type_t type; /**< Type of digest */
uint8_t calc_digest[MAX_DIGEST_SZ]; /**< Calculated digest */
bool digest_validated; /**< Flag indicating if digest is validated */
bool sign_verified; /**< Flag indicating if signature is verified */
bool secure_padding; /**< Flag indicating if secure padding is present */
} esp_att_part_digest_info_t;
/**
* @brief Structure to hold chip revision information
*/
typedef struct {
uint16_t min_chip_rev; /**< Minimum chip revision */
uint16_t max_chip_rev; /**< Maximum chip revision */
} esp_att_part_chip_rev_t;
/**
* @brief Structure to hold EAT claim metadata for a partition
*/
typedef struct {
esp_att_part_type_t type; /**< Type of partition */
char ver[32]; /**< Version string */
char idf_ver[32]; /**< ESP-IDF version string */
uint32_t secure_ver; /**< Secure version number */
esp_att_part_chip_rev_t part_chip_rev; /**< Chip revision information */
esp_att_part_digest_info_t part_digest; /**< Digest information */
} esp_att_part_metadata_t;
/**
* @brief Structure to hold token header information
*/
typedef struct {
uint32_t magic; /**< Magic number for token identification */
char encr_alg[32]; /**< Encryption algorithm */
char sign_alg[32]; /**< Signing algorithm */
uint16_t key_id; /**< Key identifier */
} esp_att_token_hdr_t;
/**
* @brief Structure to hold the Entity Attestation Token initial configuration
*/
typedef struct {
uint32_t nonce; /**< Nonce value */
uint32_t client_id; /**< Client identifier (Attestation relying party) */
uint32_t device_ver; /**< Device version */
uint8_t device_id[SHA256_DIGEST_SZ]; /**< Device identifier */
uint8_t instance_id[SHA256_DIGEST_SZ]; /**< Instance identifier */
char psa_cert_ref[32]; /**< PSA certificate reference */
uint8_t device_stat; /**< Flags indicating device status */
} esp_att_token_cfg_t;
/**
* @brief Structure to hold an ECDSA key pair
*/
typedef struct {
uint32_t curve; /**< The elliptic curve used */
uint8_t pvt_key[MAX_ECDSA_KEY_LEN]; /**< The private key */
uint8_t pub_key_x[MAX_ECDSA_KEY_LEN]; /**< The x-coordinate of the public key */
uint8_t pub_key_y[MAX_ECDSA_KEY_LEN]; /**< The y-coordinate of the public key */
} __attribute__((aligned(4))) __attribute__((__packed__)) esp_att_ecdsa_keypair_t;
/**
* @brief Structure for linked list element of software claims
*/
typedef struct _esp_att_sw_claim_list {
esp_att_part_metadata_t metadata; /**< Metadata for the partition */
SLIST_ENTRY(_esp_att_sw_claim_list) next; /**< Pointer to next item in the list */
} esp_att_sw_claim_list_t;
/**
* @brief Linked list of software claims
*/
struct esp_att_sw_claim_list {
struct _esp_att_sw_claim_list *slh_first;
};
/**
* @brief Convert a hexadecimal buffer to a hexadecimal string
*
* @param hexbuf Input hexadecimal buffer
* @param hexbuf_sz Size of the input hexadecimal buffer
* @param hexstr Output hexadecimal string buffer
* @param hexstr_len Length of the output hexadecimal string buffer
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_hexbuf_to_hexstr(const void *hexbuf, size_t hexbuf_sz, char *hexstr, size_t hexstr_len);
/**
* @brief Get claim data for the bootloader
*
* @param btl_metadata Bootloader metadata output context
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_get_btl_claim_data(esp_att_part_metadata_t *btl_metadata);
/**
* @brief Get claim data for the REE/user application
*
* @param app_metadata REE/user application metadata output context
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_get_app_claim_data(esp_att_part_metadata_t *app_metadata);
/**
* @brief Get claim data for the TEE application
*
* @param tee_metadata TEE metadata output context
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_get_tee_claim_data(esp_att_part_metadata_t *tee_metadata);
/**
* @brief Convert token header to JSON format
*
* @param tk_hdr Token header structure
* @param header_json Output buffer to store the JSON string
* @param len Length of the generated JSON string
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_header_to_json(const esp_att_token_hdr_t *tk_hdr, char **header_json, int *len);
/**
* @brief Convert token claim data to JSON
*
* @param head Software claim list
* @param cfg Token configuration
* @param eat_json Output buffer to store the JSON string
* @param len Length of the generated JSON string
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_eat_data_to_json(struct esp_att_sw_claim_list *head, const esp_att_token_cfg_t *cfg, char **eat_json, int *len);
/**
* @brief Convert token public key to JSON
*
* @param keypair ECDSA key pair
* @param pubkey_json Output buffer to store the JSON string
* @param len Length of the generated JSON string
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_pubkey_to_json(const esp_att_ecdsa_keypair_t *keypair, char **pubkey_json, int *len);
/**
* @brief Convert token signature to JSON
*
* @param keypair ECDSA key pair
* @param digest Digest to be signed
* @param digest_len Length of the digest
* @param sign_json Output buffer to store the JSON string
* @param len Length of the generated JSON string
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_sign_to_json(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t digest_len, char **sign_json, int *len);
/**
* @brief Generate an ECDSA key pair using the secp256r1 curve
*
* @param keypair Context to store the generated key pair
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_ecdsa_gen_keypair_secp256r1(esp_att_ecdsa_keypair_t *keypair);
/**
* @brief Get ECDSA signature for a given digest
*
* @param keypair ECDSA key pair
* @param digest Digest to be signed
* @param len Length of the digest
* @param sign_r_hexstr Output buffer holding the signature r component as a hex string
* @param sign_s_hexstr Output buffer holding the signature s component as a hex string
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_ecdsa_get_sign(const esp_att_ecdsa_keypair_t *keypair, const uint8_t *digest, const size_t len,
char **sign_r_hexstr, char **sign_s_hexstr);
/**
* @brief Get the public key as a hex string
*
* @param keypair ECDSA key pair
* @param pubkey_hexstr Output buffer holding the public key as a hex string
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_ecdsa_get_pubkey(const esp_att_ecdsa_keypair_t *keypair, char **pubkey_hexstr);
/**
* @brief Get the digest of the public key
*
* @param keypair ECDSA key pair
* @param digest Digest buffer
* @param len Length of the digest buffer
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise
*/
esp_err_t esp_att_utils_ecdsa_get_pubkey_digest(const esp_att_ecdsa_keypair_t *keypair, uint8_t *digest, const size_t len);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,13 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
if(esp_tee_build)
return()
endif()
set(srcs "esp_tee_attestation.c")
set(include_dirs ".")
set(priv_requires esp_tee)
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include_dirs}
PRIV_REQUIRES ${priv_requires})

View File

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_tee.h"
#include "secure_service_num.h"
#include "sdkconfig.h"
static __attribute__((unused)) const char *TAG = "esp_tee_att";
esp_err_t esp_tee_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref,
uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len)
{
return (esp_err_t)esp_tee_service_call_with_noniram_intr_disabled(7, SS_ESP_TEE_ATT_GENERATE_TOKEN, nonce, client_id,
psa_cert_ref, token_buf, token_buf_size, token_len);
}

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Generate and return an entity attestation token (EAT) from the TEE
*
* The EAT consists of the below details:
* - For all firmware images (Bootloader, active TEE and non-secure app)
* - Project and ESP-IDF version
* - Digest (SHA256)
* - Public key corresponding to the private key used for signing (in compressed format)
* - Signature generated using the ECDSA key stored in the configured slot of the TEE's Secure Storage (`r` and `s` components)
*
* @param[in] nonce Attestation request identification
* @param[in] client_id Relying Party identification
* @param[in] psa_cert_ref PSA certification ID
* @param[in] token_buf Output buffer which will hold the resultant EAT in JSON format
* @param[in] token_buf_size Size of the output buffer
* @param[out] token_len Actual length of the output EAT JSON
*
* @return
* - `ESP_OK` on success
* - `ESP_ERR_INVALID_ARG` in case token_buf or token_len are NULL or token_buf_size is 0
* - `ESP_ERR_NO_MEM` in case memory could not be allocated for the internal structures
* - `ESP_FAIL` other errors
*/
esp_err_t esp_tee_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref,
uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,7 @@
set(srcs "esp_tee_flash.c")
set(include_dirs "include")
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include_dirs}
PRIV_REQUIRES esp_tee log
REQUIRES bootloader_support)

View File

@ -0,0 +1,151 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <sys/queue.h>
#include "esp_err.h"
#include "esp_log.h"
#include "bootloader_utility_tee.h"
#include "esp_tee_ota_utils.h"
#include "esp_tee.h"
#include "esp_tee_flash.h"
#include "sdkconfig.h"
static const char *TAG = "esp_tee_flash";
// Structure containing the valid flash address range for flash operations through TEE
typedef struct {
uint32_t reserved;
} tee_flash_protect_info_t;
static tee_flash_protect_info_t tee_prot_ctx;
// Running REE (user) app partition
static esp_partition_info_t ree_running;
// List storing all the partition table entries
typedef struct _partition_list {
SLIST_ENTRY(_partition_list) next;
esp_partition_info_t partition;
} partition_list_t;
SLIST_HEAD(partition_list, _partition_list) partition_table_list;
esp_err_t esp_tee_flash_setup_prot_ctx(uint8_t tee_boot_part)
{
static bool is_first_call = true;
if (is_first_call) {
// TODO: To-be-implemented for C6
(void)tee_boot_part;
tee_prot_ctx.reserved = UINT32_MAX;
is_first_call = false;
}
return ESP_OK;
}
static partition_list_t *create_partition_entry(const esp_partition_info_t* partition_info)
{
partition_list_t *partition_entry = calloc(1, sizeof(partition_list_t));
assert(partition_entry != NULL);
memcpy(&partition_entry->partition, partition_info, sizeof(esp_partition_info_t));
return partition_entry;
}
static esp_err_t get_partition_table(void)
{
if (SLIST_EMPTY(&partition_table_list)) {
const esp_partition_info_t *partition_table = esp_tee_flash_mmap(ESP_PARTITION_TABLE_OFFSET, ESP_PARTITION_TABLE_MAX_LEN);
if (partition_table == NULL) {
ESP_LOGE(TAG, "esp_tee_flash_mmap failed!");
return ESP_FAIL;
}
int num_partitions = 0;
esp_err_t err = esp_partition_table_verify(partition_table, false, &num_partitions);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Partition table verification failed!");
return err;
}
SLIST_INIT(&partition_table_list);
for (size_t num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) {
const esp_partition_info_t *part = &partition_table[num_parts];
if (part->type == PART_TYPE_END && part->subtype == PART_SUBTYPE_END) {
break;
}
partition_list_t *partition = create_partition_entry(part);
SLIST_INSERT_HEAD(&partition_table_list, partition, next);
}
esp_tee_flash_munmap(partition_table);
}
return ESP_OK;
}
esp_err_t esp_tee_flash_find_partition(uint8_t type, uint8_t subtype, const char *label, esp_partition_info_t *dest_ptr)
{
if (dest_ptr == NULL) {
return ESP_ERR_INVALID_ARG;
}
/* NOTE: Fetch the partition table */
esp_err_t err = get_partition_table();
if (err != ESP_OK) {
return err;
}
partition_list_t *partition_entry;
SLIST_FOREACH(partition_entry, &partition_table_list, next) {
if (partition_entry->partition.type == type && partition_entry->partition.subtype == subtype) {
if (label == NULL || !memcmp(label, partition_entry->partition.label, strnlen(label, sizeof(partition_entry->partition.label)))) {
memcpy(dest_ptr, &partition_entry->partition, sizeof(esp_partition_info_t));
return ESP_OK;
}
}
}
return ESP_ERR_NOT_FOUND;
}
esp_err_t esp_tee_flash_get_part_info_for_addr(uint32_t paddr, esp_partition_info_t *part_info)
{
if (part_info == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = get_partition_table();
if (err != ESP_OK) {
return err;
}
partition_list_t *partition_entry;
SLIST_FOREACH(partition_entry, &partition_table_list, next) {
uint32_t start_addr = partition_entry->partition.pos.offset;
uint32_t end_addr = start_addr + partition_entry->partition.pos.size;
if (paddr >= start_addr && paddr < end_addr) {
memcpy(part_info, &partition_entry->partition, sizeof(esp_partition_info_t));
return ESP_OK;
}
}
return ESP_ERR_NOT_FOUND;
}
esp_err_t esp_tee_flash_set_running_ree_partition(uint32_t paddr)
{
return esp_tee_flash_get_part_info_for_addr(paddr, &ree_running);
}
esp_partition_info_t *esp_tee_flash_get_running_ree_partition(void)
{
return &ree_running;
}

View File

@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
#include "esp_err.h"
#include "bootloader_flash_priv.h"
#include "esp_flash_partitions.h"
#include "esp_image_format.h"
/* Defining placeholders for the bootloader flash APIs */
#define esp_tee_flash_mmap_get_free_pages bootloader_mmap_get_free_pages
#define esp_tee_flash_mmap bootloader_mmap
#define esp_tee_flash_munmap bootloader_munmap
#define esp_tee_flash_read bootloader_flash_read
#define esp_tee_flash_write bootloader_flash_write
#define esp_tee_flash_erase_range bootloader_flash_erase_range
/**
* @brief Setup the context holding the permissible address ranges for flash operations through TEE
*
* @param tee_boot_part Active TEE partition
*
* @return esp_err_t ESP_OK: no error; ESP_FAIL otherwise.
*/
esp_err_t esp_tee_flash_setup_prot_ctx(uint8_t tee_boot_part);
/**
* @brief Get the partition table entry of a partition to be found,
* given its type, subtype and label
* If label is NULL, the first partition with matching type and
* subtype is returned.
*
* @param type Partition type
* @param subtype Partition subtype
* @param label Partition label
* @param dest_ptr Pointer to where the the partition table entry of
* the requested partition is to be copied
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_flash_find_partition(uint8_t type, uint8_t subtype, const char *label, esp_partition_info_t *dest_ptr);
/**
* @brief Get the partition table entry of a partition to be found,
* given an address present within its range
*
* @param paddr Physical address
* @param part_info Pointer to where the the partition table entry of
* the found partition is to be copied
*
* @return esp_err_t ESP_OK: no error;
* ESP_ERR_NO_INVALID_ARG: part_info is NULL;
* ESP_ERR_NOT_FOUND: No partition found for the given paddr;
* ESP_FAIL otherwise.
*/
esp_err_t esp_tee_flash_get_part_info_for_addr(uint32_t paddr, esp_partition_info_t *part_info);
/**
* @brief Set the running REE (user) app partition as per the argument given by the bootloader
*
* @param paddr Physical address within the running REE (user) app partition's range
*
* @return esp_err_t ESP_OK: no error; otherwise refer esp_tee_flash_get_part_info_for_addr() error codes
*/
esp_err_t esp_tee_flash_set_running_ree_partition(uint32_t paddr);
/**
* @brief Fetch the running REE (user) app partition
*
* @return esp_partition_info_t* Partition table entry for the running REE (user) app partition
*/
esp_partition_info_t *esp_tee_flash_get_running_ree_partition(void);

View File

@ -0,0 +1,17 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
set(srcs)
set(priv_requires)
set(include_dirs "include")
if(esp_tee_build)
list(APPEND srcs "esp_tee_ota_ops.c")
list(APPEND priv_requires bootloader_support esp_tee log spi_flash tee_flash_mgr)
else()
list(APPEND srcs "esp_tee_ota_ops_wrapper.c")
list(APPEND priv_requires esp_tee)
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include_dirs}
PRIV_REQUIRES ${priv_requires})

View File

@ -0,0 +1,143 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "esp_log.h"
#include "esp_err.h"
#include "hal/efuse_hal.h"
#include "spi_flash_mmap.h"
#include "esp_image_format.h"
#include "bootloader_utility_tee.h"
#include "esp_tee.h"
#include "esp_tee_flash.h"
#include "sdkconfig.h"
typedef enum {
ESP_TEE_OTA_UNDEFINED = 0,
ESP_TEE_OTA_BEGIN,
ESP_TEE_OTA_IN_PROGRESS,
ESP_TEE_OTA_END,
} esp_tee_ota_state_t;
typedef struct {
esp_tee_ota_state_t tee_ota_state;
esp_partition_pos_t tee_ota_data;
esp_partition_info_t tee_next;
} esp_tee_ota_handle_t;
/* Global handle for managing TEE OTA */
static esp_tee_ota_handle_t ota_handle = {};
static const char *TAG = "esp_tee_ota_ops";
static esp_err_t get_tee_otadata_part_pos(esp_partition_pos_t *tee_ota_pos)
{
if (tee_ota_pos == NULL) {
return ESP_ERR_INVALID_ARG;
}
esp_partition_info_t tee_ota_info = {};
esp_err_t err = esp_tee_flash_find_partition(PART_TYPE_DATA, PART_SUBTYPE_DATA_TEE_OTA, NULL, &tee_ota_info);
if (err != ESP_OK) {
return err;
}
memcpy(tee_ota_pos, &tee_ota_info.pos, sizeof(esp_partition_pos_t));
return ESP_OK;
}
esp_err_t esp_tee_ota_begin(void)
{
/* Initialising the TEE OTA handle */
memset(&ota_handle, 0x00, sizeof(esp_tee_ota_handle_t));
ota_handle.tee_ota_state = ESP_TEE_OTA_UNDEFINED;
esp_err_t err = get_tee_otadata_part_pos(&ota_handle.tee_ota_data);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch TEE OTA data partition!");
return err;
}
ota_handle.tee_ota_state = ESP_TEE_OTA_BEGIN;
int tee_boot_part = bootloader_utility_tee_get_boot_partition(&ota_handle.tee_ota_data);
int tee_next_boot_part = bootloader_utility_tee_get_next_update_partition(&ota_handle.tee_ota_data);
esp_partition_info_t tee_next = {};
err = esp_tee_flash_find_partition(PART_TYPE_APP, (uint8_t)tee_next_boot_part, NULL, &tee_next);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to find partition!");
return err;
}
ESP_LOGI(TAG, "Running partition - Subtype: 0x%x", (uint8_t)tee_boot_part);
ESP_LOGI(TAG, "Next partition - Subtype: 0x%x (Offset: 0x%" PRIx32 ")", (uint8_t)tee_next_boot_part, tee_next.pos.offset);
const uint32_t aligned_erase_size = (tee_next.pos.size + SPI_FLASH_SEC_SIZE - 1) & ~(SPI_FLASH_SEC_SIZE - 1);
int ret = esp_tee_flash_erase_range(tee_next.pos.offset, aligned_erase_size);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to erase partition!");
return ESP_ERR_FLASH_OP_FAIL;
}
memcpy(&ota_handle.tee_next, &tee_next, sizeof(esp_partition_info_t));
ota_handle.tee_ota_state = ESP_TEE_OTA_IN_PROGRESS;
return ESP_OK;
}
esp_err_t esp_tee_ota_write(uint32_t rel_offset, const void *data, size_t size)
{
if (data == NULL || size == 0) {
ESP_LOGE(TAG, "Data cannot be NULL!");
return ESP_ERR_INVALID_ARG;
}
if (ota_handle.tee_ota_state != ESP_TEE_OTA_IN_PROGRESS) {
ESP_LOGE(TAG, "TEE OTA found to be in an invalid state!");
return ESP_ERR_INVALID_STATE;
}
if (rel_offset + size > ota_handle.tee_next.pos.size) {
ESP_LOGE(TAG, "Out of region write not allowed!");
return ESP_FAIL;
}
ESP_LOGD(TAG, "Writing at offset: 0x%"PRIx32" | size: 0x%"PRIx32, rel_offset, size);
bool write_encrypted = efuse_hal_flash_encryption_enabled();
int ret = esp_tee_flash_write(ota_handle.tee_next.pos.offset + rel_offset, (void *)data, size, write_encrypted);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to write partition!");
return ESP_ERR_FLASH_OP_FAIL;
}
return ESP_OK;
}
esp_err_t esp_tee_ota_end(void)
{
if (ota_handle.tee_ota_state != ESP_TEE_OTA_IN_PROGRESS) {
ESP_LOGE(TAG, "TEE OTA found to be in an invalid state!");
return ESP_ERR_INVALID_STATE;
}
esp_err_t err = bootloader_utility_tee_set_boot_partition(&ota_handle.tee_ota_data, &ota_handle.tee_next);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set TEE boot partition (0x%"PRIx32")", err);
return err;
}
ota_handle.tee_ota_state = ESP_TEE_OTA_END;
return ESP_OK;
}

View File

@ -0,0 +1,35 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_tee.h"
#include "secure_service_num.h"
#include "sdkconfig.h"
static __attribute__((unused)) const char *TAG = "esp_tee_ota_ops_wrapper";
esp_err_t esp_tee_ota_begin(void)
{
return (esp_err_t)esp_tee_service_call_with_noniram_intr_disabled(1, SS_ESP_TEE_OTA_BEGIN);
}
esp_err_t esp_tee_ota_write(uint32_t rel_offset, const void *data, size_t size)
{
return (esp_err_t)esp_tee_service_call_with_noniram_intr_disabled(4, SS_ESP_TEE_OTA_WRITE, rel_offset, data, size);
}
esp_err_t esp_tee_ota_end(void)
{
return (esp_err_t)esp_tee_service_call_with_noniram_intr_disabled(1, SS_ESP_TEE_OTA_END);
}

View File

@ -0,0 +1,60 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Commence the TEE OTA update
*
* This function does the following:
* - Initialize the internal TEE OTA state machine
* - Set the passive TEE partition as the destination for the new image and erase it
*
* @return
* - `ESP_OK` on success
* - `ESP_ERR_NOT_FOUND` in case the `tee_otadata` or the passive TEE partition is not found
* - `ESP_ERR_FLASH_OP_FAIL` in case erasing the passive TEE partition fails
*/
esp_err_t esp_tee_ota_begin(void);
/**
* @brief Write TEE OTA update data to the partition.
* This function can be called multiple times as data is received during the OTA operation
* and can write data in non-contiguous manner.
*
* @param rel_offset Address offset at which the given data should be written at -
* relative to the start address of the passive TEE partition
* @param data Data buffer to write
* @param size Size of data buffer in bytes
*
* @return
* - `ESP_OK` on success
* - `ESP_ERR_INVALID_ARG` in case the `tee_otadata` or the passive TEE partition is not found
* - `ESP_ERR_INVALID_STATE` in case the TEE OTA state machine is in an invalid state
* - `ESP_ERR_FLASH_OP_FAIL` in case writing the new TEE image fails
* - `ESP_FAIL` for other errors
*
*/
esp_err_t esp_tee_ota_write(uint32_t rel_offset, const void *data, size_t size);
/**
* @brief Finish the TEE OTA update and validate newly written TEE image
*
* @return
* - `ESP_OK` on success
* - `ESP_ERR_INVALID_STATE` in case the TEE OTA state machine is in an invalid state
* - `ESP_ERR_IMAGE_INVALID` in case the new TEE OTA image verification fails
* - `ESP_FAIL` for other errors
*/
esp_err_t esp_tee_ota_end(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,17 @@
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
set(srcs)
set(priv_requires efuse mbedtls spi_flash)
if(esp_tee_build)
list(APPEND srcs "tee_sec_storage.c")
list(APPEND priv_requires log tee_flash_mgr)
else()
list(APPEND srcs "tee_sec_storage_wrapper.c")
set(priv_requires esp_tee)
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS include
PRIV_REQUIRES ${priv_requires}
)

View File

@ -0,0 +1,146 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include <stdbool.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 */
#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 */
/**
* @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,
ESP_SEC_STG_MAX,
} esp_tee_sec_storage_type_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;
/**
* @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;
/**
* @brief Initialize the TEE secure storage partition
*
* @return esp_err_t ESP_OK on success, appropriate error code otherwise.
*/
esp_err_t esp_tee_sec_storage_init(void);
/**
* @brief Generate a unique key and store it in the given secure storage slot
*
* @param slot_id secure storage slot ID
* @param key_type secure storage key type to generate
*
* @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);
/**
* @brief Generate and return the signature for the specified message digest using
* the key pair located in the given secure storage slot.
*
* @param[in] slot_id secure storage slot ID
* @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, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *out_sign);
/**
* @brief Return the public key for the given secure storage slot
*
* @param[in] slot_id secure storage slot ID
* @param[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_pubkey_t *pubkey);
/**
* @brief Check whether the given slot in the secure storage is empty or not
*
* @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[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);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,669 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_cpu.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 "esp_random.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_GCM_IV_LEN 12
#define AES256_GCM_TAG_LEN 16
#define AES256_GCM_AAD_LEN 16
#define ECDSA_SECP256R1_KEY_LEN 32
/* 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;
/* Structure to hold ECDSA SECP256R1 key pair */
typedef struct {
uint8_t priv_key[ECDSA_SECP256R1_KEY_LEN]; /* Private key for ECDSA SECP256R1 */
uint8_t pub_key[2 * ECDSA_SECP256R1_KEY_LEN]; /* Public key for ECDSA SECP256R1 (X and Y coordinates) */
} __attribute__((aligned(4))) __attribute__((__packed__)) sec_stg_ecdsa_secp256r1_t;
/* Structure to hold AES-256 GCM 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;
/* Union to hold different types of cryptographic keys */
typedef union {
sec_stg_ecdsa_secp256r1_t ecdsa_secp256r1; /* ECDSA SECP256R1 key pair */
sec_stg_aes256_gcm_t aes256_gcm; /* AES-256 GCM key and IV */
} __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");
// 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 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)"
#endif
static int buffer_hexdump(const char *label, const void *buffer, size_t length)
{
#if CONFIG_SECURE_TEE_LOG_LEVEL >= 4
if (label == NULL || buffer == NULL || length == 0) {
return -1;
}
const uint8_t *bytes = (const uint8_t *)buffer;
const size_t max_bytes_per_line = 16;
char hexbuf[max_bytes_per_line * 3];
ESP_LOGD(TAG, "%s -", label);
for (size_t i = 0; i < length; i += max_bytes_per_line) {
size_t chunk_len = MIN(max_bytes_per_line, length - i);
size_t hexbuf_idx = 0;
for (size_t j = 0; j < chunk_len; j++) {
uint8_t byte = bytes[i + j];
hexbuf[hexbuf_idx++] = (byte >> 4) < 10 ? '0' + (byte >> 4) : 'a' + (byte >> 4) - 10;
hexbuf[hexbuf_idx++] = (byte & 0x0F) < 10 ? '0' + (byte & 0x0F) : 'a' + (byte & 0x0F) - 10;
hexbuf[hexbuf_idx++] = ' ';
if ((j + 1) % 8 == 0 && j + 1 < chunk_len) {
hexbuf[hexbuf_idx++] = ' ';
}
}
hexbuf[hexbuf_idx] = '\0';
esp_rom_printf("%s\n", hexbuf);
}
#else
(void) label;
(void) buffer;
(void) length;
#endif
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)
{
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};
buffer_hexdump("Plaintext data", data, len);
mbedtls_gcm_context gcm;
mbedtls_gcm_init(&gcm);
esp_err_t err = get_sec_stg_encr_key(key, sizeof(key));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch key from eFuse!");
goto exit;
}
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);
return err;
}
static esp_err_t secure_storage_read(uint16_t slot_id, uint8_t *data, size_t len, uint8_t type)
{
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;
}
/* ---------------------------------------------- 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);
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;
}
return ESP_OK;
}
static int generate_ecdsa_secp256r1_key(sec_stg_key_t *keyctx)
{
if (keyctx == NULL) {
return -1;
}
ESP_LOGI(TAG, "Generating ECDSA-SECP256R1 private key...");
mbedtls_ecdsa_context ctxECDSA;
mbedtls_ecdsa_init(&ctxECDSA);
int ret = mbedtls_ecdsa_genkey(&ctxECDSA, MBEDTLS_ECP_DP_SECP256R1, rand_func, NULL);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to generate ECDSA key");
goto exit;
}
ret = mbedtls_mpi_write_binary(&ctxECDSA.MBEDTLS_PRIVATE(d), keyctx->ecdsa_secp256r1.priv_key, ECDSA_SECP256R1_KEY_LEN);
if (ret != 0) {
goto exit;
}
ret = mbedtls_mpi_write_binary(&(ctxECDSA.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X)), &keyctx->ecdsa_secp256r1.pub_key[0], ECDSA_SECP256R1_KEY_LEN);
if (ret != 0) {
goto exit;
}
ret = mbedtls_mpi_write_binary(&(ctxECDSA.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y)), &keyctx->ecdsa_secp256r1.pub_key[ECDSA_SECP256R1_KEY_LEN], ECDSA_SECP256R1_KEY_LEN);
if (ret != 0) {
goto exit;
}
buffer_hexdump("Private key", keyctx->ecdsa_secp256r1.priv_key, sizeof(keyctx->ecdsa_secp256r1.priv_key));
exit:
mbedtls_ecdsa_free(&ctxECDSA);
return ret;
}
static int generate_aes256_gcm_key(sec_stg_key_t *keyctx)
{
if (keyctx == NULL) {
return -1;
}
ESP_LOGI(TAG, "Generating AES-256-GCM key...");
esp_fill_random(&keyctx->aes256_gcm.key, AES256_GCM_KEY_LEN);
esp_fill_random(&keyctx->aes256_gcm.iv, AES256_GCM_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)
{
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)) {
ESP_LOGE(TAG, "Slot already occupied - clear before reuse");
return ESP_ERR_INVALID_STATE;
}
int ret = -1;
sec_stg_key_t keyctx;
switch (key_type) {
case ESP_SEC_STG_KEY_ECDSA_SECP256R1:
if (generate_ecdsa_secp256r1_key(&keyctx) != 0) {
ESP_LOGE(TAG, "Failed to generate ECDSA keypair (%d)", ret);
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);
return ESP_FAIL;
}
break;
default:
ESP_LOGE(TAG, "Unsupported key-type!");
return ESP_ERR_NOT_SUPPORTED;
}
return secure_storage_write(slot_id, (uint8_t *)&keyctx, sizeof(keyctx), key_type);
}
esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *out_sign)
{
if (slot_id > MAX_SEC_STG_SLOT_ID || hash == NULL || out_sign == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (hlen == 0) {
return ESP_ERR_INVALID_SIZE;
}
sec_stg_key_t keyctx;
esp_err_t err = secure_storage_read(slot_id, (uint8_t *)&keyctx, sizeof(sec_stg_key_t), ESP_SEC_STG_KEY_ECDSA_SECP256R1);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch key from slot");
return err;
}
mbedtls_mpi r, s;
mbedtls_ecp_keypair priv_key;
mbedtls_ecdsa_context sign_ctx;
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
mbedtls_ecp_keypair_init(&priv_key);
mbedtls_ecdsa_init(&sign_ctx);
int ret = mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, &priv_key, keyctx.ecdsa_secp256r1.priv_key, sizeof(keyctx.ecdsa_secp256r1.priv_key));
if (ret != 0) {
err = ESP_FAIL;
goto exit;
}
ret = mbedtls_ecdsa_from_keypair(&sign_ctx, &priv_key);
if (ret != 0) {
err = ESP_FAIL;
goto exit;
}
ESP_LOGI(TAG, "Generating ECDSA-SECP256R1 signature...");
ret = mbedtls_ecdsa_sign(&sign_ctx.MBEDTLS_PRIVATE(grp), &r, &s, &sign_ctx.MBEDTLS_PRIVATE(d), hash, hlen,
rand_func, NULL);
if (ret != 0) {
ESP_LOGE(TAG, "Error generating signature: %d", ret);
err = ESP_FAIL;
goto exit;
}
ret = mbedtls_mpi_write_binary(&r, out_sign->sign_r, ECDSA_SECP256R1_KEY_LEN);
if (ret != 0) {
err = ESP_FAIL;
goto exit;
}
ret = mbedtls_mpi_write_binary(&s, out_sign->sign_s, ECDSA_SECP256R1_KEY_LEN);
if (ret != 0) {
err = ESP_FAIL;
goto exit;
}
err = ESP_OK;
exit:
mbedtls_ecdsa_free(&sign_ctx);
mbedtls_ecp_keypair_free(&priv_key);
mbedtls_mpi_free(&s);
mbedtls_mpi_free(&r);
return err;
}
esp_err_t esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_pubkey_t *pubkey)
{
if (slot_id > MAX_SEC_STG_SLOT_ID || pubkey == NULL) {
return ESP_ERR_INVALID_ARG;
}
sec_stg_key_t keyctx;
esp_err_t err = secure_storage_read(slot_id, (uint8_t *)&keyctx, sizeof(sec_stg_key_t), ESP_SEC_STG_KEY_ECDSA_SECP256R1);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch key from slot");
return err;
}
memcpy(pubkey->pub_x, &keyctx.ecdsa_secp256r1.pub_key[0], ECDSA_SECP256R1_KEY_LEN);
memcpy(pubkey->pub_y, &keyctx.ecdsa_secp256r1.pub_key[ECDSA_SECP256R1_KEY_LEN], ECDSA_SECP256R1_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,
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");
return ESP_ERR_INVALID_ARG;
}
if (len == 0 || tag_len == 0) {
ESP_LOGE(TAG, "Invalid length/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), 1);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch key from slot");
return err;
}
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);
if (ret != 0) {
ESP_LOGE(TAG, "Error in setting key: %d", ret);
err = ESP_FAIL;
goto exit;
}
if (is_encrypt) {
ret = mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, len, keyctx.aes256_gcm.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);
err = ESP_FAIL;
}
} else {
ret = mbedtls_gcm_auth_decrypt(&gcm, len, keyctx.aes256_gcm.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);
err = ESP_FAIL;
}
}
err = ESP_OK;
exit:
mbedtls_gcm_free(&gcm);
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)
{
return tee_sec_storage_crypt_common(slot_id, input, len, aad, 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)
{
return tee_sec_storage_crypt_common(slot_id, input, len, aad, aad_len, tag, tag_len, output, false);
}

View File

@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "secure_service_num.h"
#include "esp_tee.h"
#include "esp_err.h"
#include "esp_tee_sec_storage.h"
esp_err_t esp_tee_sec_storage_init(void)
{
return esp_tee_service_call_with_noniram_intr_disabled(1, SS_ESP_TEE_SEC_STORAGE_INIT);
}
esp_err_t esp_tee_sec_storage_gen_key(uint16_t slot_id, esp_tee_sec_storage_type_t key_type)
{
return esp_tee_service_call_with_noniram_intr_disabled(3, SS_ESP_TEE_SEC_STORAGE_GEN_KEY, slot_id, key_type);
}
esp_err_t esp_tee_sec_storage_get_signature(uint16_t slot_id, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *sign)
{
return esp_tee_service_call_with_noniram_intr_disabled(5, SS_ESP_TEE_SEC_STORAGE_GET_SIGNATURE, slot_id, hash, hlen, sign);
}
esp_err_t esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_pubkey_t *pubkey)
{
return esp_tee_service_call_with_noniram_intr_disabled(3, SS_ESP_TEE_SEC_STORAGE_GET_PUBKEY, slot_id, pubkey);
}
bool esp_tee_sec_storage_is_slot_empty(uint16_t slot_id)
{
return esp_tee_service_call_with_noniram_intr_disabled(2, SS_ESP_TEE_SEC_STORAGE_IS_SLOT_EMPTY, slot_id);
}
esp_err_t esp_tee_sec_storage_clear_slot(uint16_t slot_id)
{
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);
}

View File

@ -0,0 +1,92 @@
idf_build_get_property(target IDF_TARGET)
idf_build_get_property(arch IDF_TARGET_ARCH)
idf_build_get_property(idf_path IDF_PATH)
idf_component_get_property(efuse_dir efuse COMPONENT_DIR)
idf_component_get_property(esp_hw_support_dir esp_hw_support COMPONENT_DIR)
idf_component_get_property(hal_dir hal COMPONENT_DIR)
idf_component_get_property(heap_dir heap COMPONENT_DIR)
idf_component_get_property(mbedtls_dir mbedtls COMPONENT_DIR)
set(srcs)
set(include)
# Common core implementation for TEE
set(srcs "core/esp_tee_init.c"
"core/esp_tee_intr.c"
"core/esp_secure_services.c"
"core/esp_secure_service_table.c")
# Arch specific implementation for TEE
list(APPEND srcs "arch/${arch}/esp_tee_vectors.S"
"arch/${arch}/esp_tee_vector_table.S"
"arch/${arch}/esp_tee_secure_entry.S")
# SoC specific implementation for TEE
list(APPEND srcs "soc/${target}/esp_tee_secure_sys_cfg.c"
"soc/${target}/esp_tee_pmp_pma_prot_cfg.c"
"soc/${target}/esp_tee_apm_prot_cfg.c"
"soc/${target}/esp_tee_apm_intr.c"
"soc/${target}/esp_tee_aes_intr.c")
# Common module implementation for TEE
# Panic handler implementation for TEE
list(APPEND srcs "common/panic/esp_tee_panic.c"
"common/panic/panic_helper_riscv.c")
# Brownout detector
list(APPEND srcs "common/brownout.c")
list(APPEND include "include"
"common"
"soc/${target}/include")
# Heap
list(APPEND srcs "common/multi_heap.c")
# Sources and headers shared with IDF
list(APPEND include "${efuse_dir}/private_include"
"${efuse_dir}/${target}/private_include")
list(APPEND srcs "${hal_dir}/apm_hal.c"
"${hal_dir}/brownout_hal.c"
"${hal_dir}/wdt_hal_iram.c")
# TLSF implementation for heap
list(APPEND include "${heap_dir}/include"
"${heap_dir}/tlsf"
"${heap_dir}/tlsf/include")
list(APPEND srcs "${heap_dir}/tlsf/tlsf.c")
# Crypto
# AES
list(APPEND include "${mbedtls_dir}/port/include"
"${mbedtls_dir}/port/aes/include"
"${mbedtls_dir}/port/aes/dma/include")
# SHA
list(APPEND include "${mbedtls_dir}/port/sha/dma/include")
# esp_app_desc_t configuration structure for TEE
list(APPEND srcs "common/esp_app_desc_tee.c")
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include})
set_source_files_properties("core/esp_secure_services.c" PROPERTIES COMPILE_FLAGS -Wno-deprecated)
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")
# 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)
string(SUBSTRING "${project_ver}" 0 31 PROJECT_VER_CUT)
string(SUBSTRING "${project_name}" 0 31 PROJECT_NAME_CUT)
message(STATUS "App \"${PROJECT_NAME_CUT}\" version: ${PROJECT_VER_CUT}")
set_source_files_properties(
SOURCE "common/esp_app_desc_tee.c"
PROPERTIES COMPILE_DEFINITIONS
"PROJECT_VER=\"${PROJECT_VER_CUT}\"; PROJECT_NAME=\"${PROJECT_NAME_CUT}\"")

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/tee_reg.h"
#include "soc/plic_reg.h"
.global esp_tee_service_dispatcher
/* Entry point to the secure world (i.e. M-mode) - responsible for
* setting up the execution environment for the secure world */
.section .text
.align 4
.global _sec_world_entry
.type _sec_world_entry, @function
_sec_world_entry:
/* Setup the APM for HP CPU in TEE mode */
li t0, TEE_M0_MODE_CTRL_REG
sw zero, 0(t0) /* APM_LL_SECURE_MODE_TEE = 0 */
/* Disable the U-mode delegation of all interrupts */
csrwi mideleg, 0
/* Jump to the secure service dispatcher */
jal esp_tee_service_dispatcher
/* Setup the APM for HP CPU in REE mode */
li t0, TEE_M0_MODE_CTRL_REG
li t1, 0x1 /* APM_LL_SECURE_MODE_REE = 1 */
sw t1, 0(t0)
/* Enable the U-mode delegation of all interrupts (except the TEE secure interrupt) */
li t0, 0xffffbfff
csrw mideleg, t0
/* Fire an M-ecall */
mv a1, zero
ecall
fence
ret

View File

@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include "soc/soc.h"
#if ETS_INT_WDT_INUM != 24
#error "ETS_INT_WDT_INUM expected to be 24"
#endif
/* Handlers defined in the `esp_tee_vectors.S` file */
.global _panic_handler
.global _tee_ns_intr_handler
.global _tee_s_intr_handler
.section .exception_vectors_table.text, "ax"
/* This is the vector table. MTVEC points here.
*
* Use 4-byte instructions here. 1 instruction = 1 entry of the table.
* The CPU jumps to MTVEC (i.e. the first entry) in case of an exception,
* and (MTVEC & 0xfffffffc) + (mcause & 0x7fffffff) * 4, in case of an interrupt.
*
* Note: for our CPU, we need to place this on a 256-byte boundary, as CPU
* only uses the 24 MSBs of the MTVEC, i.e. (MTVEC & 0xffffff00).
*/
.balign 0x100
/* Since each entry must take 4-byte, let's temporarily disable the compressed
* instruction set that could potentially generate 2-byte instructions. */
.option push
.option norvc
.global _vector_table
.type _vector_table, @function
_vector_table:
j _panic_handler /* 0: Exception entry */
/* NOTE: All of the free interrupts are used by the REE */
j _tee_ns_intr_handler /* 1: Free interrupt number */
j _tee_ns_intr_handler /* 2: Free interrupt number */
j _tee_ns_intr_handler /* 3: Free interrupt number */
j _tee_ns_intr_handler /* 4: Free interrupt number */
j _tee_ns_intr_handler /* 5: Free interrupt number */
j _tee_ns_intr_handler /* 6: Free interrupt number */
j _tee_ns_intr_handler /* 7: Free interrupt number */
j _tee_ns_intr_handler /* 8: Free interrupt number */
j _tee_ns_intr_handler /* 9: Free interrupt number */
j _tee_ns_intr_handler /* 10: Free interrupt number */
j _tee_ns_intr_handler /* 11: Free interrupt number */
j _tee_ns_intr_handler /* 12: Free interrupt number */
j _tee_ns_intr_handler /* 13: Free interrupt number */
j _tee_s_intr_handler /* 14: ESP-TEE: Secure interrupt handler entry */
j _tee_ns_intr_handler /* 15: Free interrupt number */
j _tee_ns_intr_handler /* 16: Free interrupt number */
j _tee_ns_intr_handler /* 17: Free interrupt number */
j _tee_ns_intr_handler /* 18: Free interrupt number */
j _tee_ns_intr_handler /* 19: Free interrupt number */
j _tee_ns_intr_handler /* 20: Free interrupt number */
j _tee_ns_intr_handler /* 21: Free interrupt number */
j _tee_ns_intr_handler /* 22: Free interrupt number */
j _tee_ns_intr_handler /* 23: Free interrupt number */
j _panic_handler /* 24: ETS_INT_WDT_INUM panic-interrupt (soc-level panic) */
j _panic_handler /* 25: ETS_CACHEERR_INUM panic-interrupt (soc-level panic) */
/* NOTE: Triggers panic irrespective of the Kconfig setting with ESP-TEE */
j _panic_handler /* 26: ETS_MEMPROT_ERR_INUM handler (soc-level panic) */
/* TODO: [IDF-10770] Not supported yet with ESP-TEE */
j _panic_handler /* 27: ETS_ASSIST_DEBUG_INUM handler (soc-level panic) */
j _tee_ns_intr_handler /* 28: Free interrupt number */
j _tee_ns_intr_handler /* 29: Free interrupt number */
j _tee_ns_intr_handler /* 30: Free interrupt number */
j _tee_ns_intr_handler /* 31: Free interrupt number */
j _panic_handler /* exception handler, entry 0 */
.size _vector_table, .-_vector_table
/* Re-enable the compressed instruction set it is was enabled before */
.option pop

View File

@ -0,0 +1,534 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "soc/interrupt_reg.h"
#include "soc/interrupt_matrix_reg.h"
#include "riscv/encoding.h"
#include "riscv/rvruntime-frames.h"
#include "esp_tee.h"
#include "sdkconfig.h"
.equ SAVE_REGS, 32
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
.equ panic_from_exception, tee_panic_from_exc
.equ panic_from_isr, tee_panic_from_isr
.equ MAGIC, 0x1f
.equ RTNVAL, 0xc0de
.equ ECALL_U_MODE, 0x8
.equ ECALL_M_MODE, 0xb
.equ TEE_APM_INTR_MASK_0, 0x00300000
.equ TEE_APM_INTR_MASK_1, 0x000000F8
.global esp_tee_global_interrupt_handler
.section .data
.align 4
.global _ns_sp
_ns_sp:
.word 0
.section .data
.align 4
.global _s_sp
_s_sp:
.word 0
/* Macro which first allocates space on the stack to save general
* purpose registers, and then save them. GP register is excluded.
* The default size allocated on the stack is CONTEXT_SIZE, but it
* can be overridden. */
.macro save_general_regs cxt_size=CONTEXT_SIZE
addi sp, sp, -\cxt_size
sw ra, RV_STK_RA(sp)
sw tp, RV_STK_TP(sp)
sw t0, RV_STK_T0(sp)
sw t1, RV_STK_T1(sp)
sw t2, RV_STK_T2(sp)
sw s0, RV_STK_S0(sp)
sw s1, RV_STK_S1(sp)
sw a0, RV_STK_A0(sp)
sw a1, RV_STK_A1(sp)
sw a2, RV_STK_A2(sp)
sw a3, RV_STK_A3(sp)
sw a4, RV_STK_A4(sp)
sw a5, RV_STK_A5(sp)
sw a6, RV_STK_A6(sp)
sw a7, RV_STK_A7(sp)
sw s2, RV_STK_S2(sp)
sw s3, RV_STK_S3(sp)
sw s4, RV_STK_S4(sp)
sw s5, RV_STK_S5(sp)
sw s6, RV_STK_S6(sp)
sw s7, RV_STK_S7(sp)
sw s8, RV_STK_S8(sp)
sw s9, RV_STK_S9(sp)
sw s10, RV_STK_S10(sp)
sw s11, RV_STK_S11(sp)
sw t3, RV_STK_T3(sp)
sw t4, RV_STK_T4(sp)
sw t5, RV_STK_T5(sp)
sw t6, RV_STK_T6(sp)
.endm
.macro save_mepc
csrr t0, mepc
sw t0, RV_STK_MEPC(sp)
.endm
.macro save_mcsr
csrr t0, mstatus
sw t0, RV_STK_MSTATUS(sp)
csrr t0, mtvec
sw t0, RV_STK_MTVEC(sp)
csrr t0, mtval
sw t0, RV_STK_MTVAL(sp)
csrr t0, mhartid
sw t0, RV_STK_MHARTID(sp)
.endm
/* Restore the general purpose registers (excluding gp) from the context on
* the stack. The context is then deallocated. The default size is CONTEXT_SIZE
* but it can be overridden. */
.macro restore_general_regs cxt_size=CONTEXT_SIZE
lw ra, RV_STK_RA(sp)
lw tp, RV_STK_TP(sp)
lw t0, RV_STK_T0(sp)
lw t1, RV_STK_T1(sp)
lw t2, RV_STK_T2(sp)
lw s0, RV_STK_S0(sp)
lw s1, RV_STK_S1(sp)
lw a0, RV_STK_A0(sp)
lw a1, RV_STK_A1(sp)
lw a2, RV_STK_A2(sp)
lw a3, RV_STK_A3(sp)
lw a4, RV_STK_A4(sp)
lw a5, RV_STK_A5(sp)
lw a6, RV_STK_A6(sp)
lw a7, RV_STK_A7(sp)
lw s2, RV_STK_S2(sp)
lw s3, RV_STK_S3(sp)
lw s4, RV_STK_S4(sp)
lw s5, RV_STK_S5(sp)
lw s6, RV_STK_S6(sp)
lw s7, RV_STK_S7(sp)
lw s8, RV_STK_S8(sp)
lw s9, RV_STK_S9(sp)
lw s10, RV_STK_S10(sp)
lw s11, RV_STK_S11(sp)
lw t3, RV_STK_T3(sp)
lw t4, RV_STK_T4(sp)
lw t5, RV_STK_T5(sp)
lw t6, RV_STK_T6(sp)
addi sp,sp, \cxt_size
.endm
.macro restore_mepc
lw t0, RV_STK_MEPC(sp)
csrw mepc, t0
.endm
.macro store_magic_general_regs
lui ra, MAGIC
lui tp, MAGIC
lui t0, MAGIC
lui t1, MAGIC
lui t2, MAGIC
lui s0, MAGIC
lui s1, MAGIC
lui a0, MAGIC
lui a1, MAGIC
lui a2, MAGIC
lui a3, MAGIC
lui a4, MAGIC
lui a5, MAGIC
lui a6, MAGIC
lui a7, MAGIC
lui s2, MAGIC
lui s3, MAGIC
lui s4, MAGIC
lui s5, MAGIC
lui s6, MAGIC
lui s7, MAGIC
lui s8, MAGIC
lui s9, MAGIC
lui s10, MAGIC
lui s11, MAGIC
lui t3, MAGIC
lui t4, MAGIC
lui t5, MAGIC
lui t6, MAGIC
.endm
.section .exception_vectors.text, "ax"
/* Exception handler. */
.global _panic_handler
.type _panic_handler, @function
_panic_handler:
/* Backup t0 on the stack before using it */
addi sp, sp, -16
sw t0, 0(sp)
/* Check whether the exception is an M-mode ecall */
csrr t0, mcause
xori t0, t0, ECALL_M_MODE
beqz t0, _machine_ecall
/* Check whether the exception is an U-mode ecall */
csrr t0, mcause
xori t0, t0, ECALL_U_MODE
beqz t0, _user_ecall
/* Restore t0 from the stack */
lw t0, 0(sp)
addi sp, sp, 16
/* Not an ecall, proceed to the panic handler */
/* Allocate space on the stack and store general purpose registers */
save_general_regs RV_STK_FRMSZ
/* As gp register is not saved by the macro, save it here */
sw gp, RV_STK_GP(sp)
/* Same goes for the SP value before trapping */
addi t0, sp, RV_STK_FRMSZ /* restore sp with the value when trap happened */
/* Save CSRs */
sw t0, RV_STK_SP(sp)
save_mepc
save_mcsr
/* Call panic_from_exception(sp) or panic_from_isr(sp)
* depending on whether we have a pseudo excause or not.
* If mcause's highest bit is 1, then an interrupt called this routine,
* so we have a pseudo excause. Else, it is due to a exception, we don't
* have an pseudo excause */
mv a0, sp
csrr a1, mcause
/* Branches instructions don't accept immediates values, so use t1 to
* store our comparator */
li t0, 0x80000000
bgeu a1, t0, _call_panic_handler
sw a1, RV_STK_MCAUSE(sp)
/* exception_from_panic never returns */
jal panic_from_exception
/* We arrive here if the exception handler has returned. */
j _return_from_exception
_call_panic_handler:
/* Remove highest bit from mcause (a1) register and save it in the
* structure */
not t0, t0
and a1, a1, t0
sw a1, RV_STK_MCAUSE(sp)
jal panic_from_isr
/* We arrive here if the exception handler has returned. This means that
* the exception was handled, and the execution flow should resume.
* Restore the registers and return from the exception.
*/
_return_from_exception:
restore_mepc
/* MTVEC and SP are assumed to be unmodified.
* MSTATUS, MHARTID, MTVAL are read-only and not restored. */
lw gp, RV_STK_GP(sp)
restore_general_regs RV_STK_FRMSZ
mret
.size _panic_handler, .-_panic_handler
/* ECALL handler. */
.type _ecall_handler, @function
_ecall_handler:
/* M-mode ecall handler */
_machine_ecall:
/* Check whether this is the first M-mode ecall (see esp_tee_init) and skip context restoration */
lui t0, ESP_TEE_M2U_SWITCH_MAGIC
beq a1, t0, _skip_ctx_restore
/* Switching back to the saved REE stack */
la t0, _ns_sp
lw sp, 0(t0)
fence
/* Backup the A0 register
* This point is reached after an ecall is triggered after executing the secure service.
* The A0 register contains the return value of the corresponding service.
* After restoring the entire register context, we assign A0 the value back to the return value. */
csrw mscratch, a0
restore_general_regs RV_STK_FRMSZ
csrrw a0, mscratch, zero
/* This point is reached only after the first M-mode ecall, never again (see esp_tee_init) */
_skip_ctx_restore:
/* Copy the ra register to mepc which contains the user app entry point (i.e. call_start_cpu0) */
csrw mepc, ra
/* Set the privilege mode to transition to after mret to U-mode */
li t3, MSTATUS_MPP
csrc mstatus, t3
/* Jump to the REE */
mret
/* U-mode ecall handler */
_user_ecall:
/* Check whether we are returning after servicing an U-mode interrupt */
lui t0, RTNVAL
csrr t1, mscratch
beq t0, t1, _rtn_from_ns_int
csrwi mscratch, 0
/* Restore t0 from the stack */
lw t0, 0(sp)
addi sp, sp, 16
/* This point is reached after a secure service call is issued from the REE */
/* Save register context and the mepc */
save_general_regs RV_STK_FRMSZ
save_mepc
/* Saving the U-mode (i.e. REE) stack pointer */
la t0, _ns_sp
sw sp, 0(t0)
/* Switching to the M-mode (i.e. TEE) stack */
la sp, _tee_stack
/* Load the TEE entry point (see sec_world_entry) in the mepc */
la t2, esp_tee_app_config
lw t2, ESP_TEE_CFG_OFFS_S_ENTRY_ADDR(t2)
csrw mepc, t2
/* Set the privilege mode to transition to after mret to M-mode */
li t3, MSTATUS_MPP
csrs mstatus, t3
mret
/* This point is reached after servicing a U-mode interrupt occurred
* while executing a secure service */
_rtn_from_ns_int:
/* Disable the U-mode interrupt delegation */
csrwi mideleg, 0
/* Restore the secure stack pointer */
la t0, _s_sp
lw sp, 0(t0)
/* Clear the flag set marking the completion of interrupt service */
csrwi mscratch, 0
/* Set the privilege mode to transition to after mret to M-mode */
li t0, MSTATUS_MPP
csrs mstatus, t0
/* Restore register context and resume the secure service */
restore_mepc
restore_general_regs
mret
.size _ecall_handler, .-_ecall_handler
/* This is the interrupt handler for the U-mode interrupts.
* It saves the registers on the stack, re-enables the interrupt delegation,
* then jumps to the U-mode global interrupt handler, */
.global _tee_ns_intr_handler
.type _tee_ns_intr_handler, @function
_tee_ns_intr_handler:
/* Start by saving the general purpose registers and the PC value before
* the interrupt happened. */
save_general_regs
save_mepc
/* Though it is not necessary we save GP and SP here.
* SP is necessary to help GDB to properly unwind
* the backtrace of threads preempted by interrupts (OS tick etc.).
* GP is saved just to have its proper value in GDB. */
/* As gp register is not saved by the macro, save it here */
sw gp, RV_STK_GP(sp)
/* Same goes for the SP value before trapping */
addi t0, sp, CONTEXT_SIZE /* restore sp with the value when interrupt happened */
/* Save SP */
sw t0, RV_STK_SP(sp)
/* For U-mode interrupts, we use mret to switch to U-mode after executing the below steps - */
/* Disable the U-mode global interrupts */
csrci ustatus, USTATUS_UIE
/* Pass the interrupt ID to be serviced to U-mode */
csrr t0, mcause
csrw ucause, t0
/* Configure `uepc` with the U-mode ecall handler (see u2m_switch) so that we can
* return to M-mode after handling the interrupt */
la t0, esp_tee_app_config
lw t1, ESP_TEE_CFG_OFFS_NS_ENTRY_ADDR(t0)
csrw uepc, t1
/* Set the program counter to the U-mode global interrupt handler (see _interrupt_handler) */
lw t1, ESP_TEE_CFG_OFFS_NS_INTR_HANDLER(t0)
csrw mepc, t1
/* Set the privilege mode to transition to after mret to U-mode */
li t1, MSTATUS_MPP
csrc mstatus, t1
/* Save the current secure stack pointer and switch to the U-mode interrupt stack
* saved while entering the secure service call routine (see `sec_world_entry`) */
la t0, _s_sp
sw sp, 0(t0)
la t1, _ns_sp
lw sp, 0(t1)
/* Set a flag to identify the next U2M switch would be after handling a U-mode interrupt */
lui t0, RTNVAL
csrw mscratch, t0
/* Enable the U-mode interrupt delegation (except for the TEE secure interrupt) */
li t0, 0xffffbfff
csrw mideleg, t0
/* Place magic bytes in all the general registers */
store_magic_general_regs
mret
.size _tee_ns_intr_handler, .-_tee_ns_intr_handler
/* This is the interrupt handler for the M-mode interrupts.
* It saves the registers on the stack, prepares for interrupt nesting,
* re-enables the interrupts, then jumps to the C dispatcher in esp_tee_intr.c. */
.global _tee_s_intr_handler
.type _tee_s_intr_handler, @function
_tee_s_intr_handler:
/* Start by saving the general purpose registers and the PC value before
* the interrupt happened. */
save_general_regs
save_mepc
/* Though it is not necessary we save GP and SP here.
* SP is necessary to help GDB to properly unwind
* the backtrace of threads preempted by interrupts (OS tick etc.).
* GP is saved just to have its proper value in GDB. */
/* As gp register is not saved by the macro, save it here */
sw gp, RV_STK_GP(sp)
/* Same goes for the SP value before trapping */
addi t0, sp, CONTEXT_SIZE /* restore sp with the value when interrupt happened */
/* Save SP */
sw t0, RV_STK_SP(sp)
/* Check if the interrupt source is related to an APM exception */
/* Define the addresses of the registers */
li t0, INTMTX_CORE0_INT_STATUS_REG_0_REG
/* Load the values from the registers */
lw t1, 0(t0)
/* Define the masks */
li t2, TEE_APM_INTR_MASK_0
/* Apply the masks */
and t1, t1, t2
/* Check if any of the masked bits are set */
bnez t1, _save_reg_ctx
/* Repeat for the other status register */
li t0, INTMTX_CORE0_INT_STATUS_REG_1_REG
lw t1, 0(t0)
li t2, TEE_APM_INTR_MASK_1
and t1, t1, t2
bnez t1, _save_reg_ctx
/* Continue normal execution */
j _continue
_save_reg_ctx:
/* Save CSR context here */
save_mcsr
j _intr_hdlr_exec
_continue:
/* Before doing anything preserve the stack pointer */
mv s11, sp
/* Switching to the TEE interrupt stack */
la sp, _tee_intr_stack
/* If this is a non-nested interrupt, SP now points to the interrupt stack */
/* Before dispatch c handler, restore interrupt to enable nested intr */
csrr s1, mcause
csrr s2, mstatus
/* TODO: [IDF-9972] Nested interrupts are not supported yet */
# /* Save the interrupt threshold level */
# li t0, PLIC_MXINT_THRESH_REG /*INTERRUPT_CORE0_CPU_INT_THRESH_REG*/
# lw s3, 0(t0)
# /* Increase interrupt threshold level */
# li t2, 0x7fffffff
# and t1, s1, t2 /* t1 = mcause & mask */
# slli t1, t1, 2 /* t1 = mcause * 4 */
# li t2, PLIC_MXINT0_PRI_REG /*INTC_INT_PRIO_REG(0)*/
# add t1, t2, t1 /* t1 = INTC_INT_PRIO_REG + 4 * mcause */
# lw t2, 0(t1) /* t2 = INTC_INT_PRIO_REG[mcause] */
# addi t2, t2, 1 /* t2 = t2 +1 */
# sw t2, 0(t0) /* INTERRUPT_CORE0_CPU_INT_THRESH_REG = t2 */
# fence
# csrsi mstatus, MSTATUS_MIE
# /* MIE set. Nested interrupts can now occur */
#ifdef CONFIG_PM_TRACE
li a0, 0 /* = ESP_PM_TRACE_IDLE */
#if SOC_CPU_CORES_NUM == 1
li a1, 0 /* No need to check core ID on single core hardware */
#else
csrr a1, mhartid
#endif
la t0, esp_pm_trace_exit
jalr t0 /* absolute jump, avoid the 1 MiB range constraint */
#endif
#ifdef CONFIG_PM_ENABLE
la t0, esp_pm_impl_isr_hook
jalr t0 /* absolute jump, avoid the 1 MiB range constraint */
#endif
_intr_hdlr_exec:
/* call the C dispatcher */
mv a0, sp /* argument 1, stack pointer */
mv a1, s1 /* argument 2, interrupt number (mcause) */
/* mask off the interrupt flag of mcause */
li t0, 0x7fffffff
and a1, a1, t0
jal esp_tee_global_interrupt_handler
/* TODO: [IDF-9972] Nested interrupts are not supported yet */
# csrci mstatus, MSTATUS_MIE
# /* MIE cleared. Nested interrupts are disabled */
# /* restore the interrupt threshold level */
# li t0, PLIC_MXINT_THRESH_REG /*INTERRUPT_CORE0_CPU_INT_THRESH_REG*/
# sw s3, 0(t0)
# fence
/* restore the rest of the registers */
csrw mcause, s1
csrw mstatus, s2
/* Restoring the stack pointer */
mv sp, s11
restore_mepc
restore_general_regs
/* exit, this will also re-enable the interrupts */
mret
.size _tee_s_intr_handler, .-_tee_s_intr_handler

View File

@ -0,0 +1,125 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "esp_macros.h"
#include "esp_log.h"
#include "esp_cpu.h"
#include "soc/soc.h"
#include "esp_attr.h"
#include "bootloader_flash.h"
#include "hal/brownout_hal.h"
#include "hal/brownout_ll.h"
#include "esp_rom_sys.h"
#include "esp_rom_uart.h"
#include "sdkconfig.h"
#include "esp_tee_intr.h"
/*
* NOTE: Brownout threshold levels
*
* +-----------------+-------------------+
* | Threshold Level | Voltage (Approx.) |
* +-----------------+-------------------+
* | 7 | 2.51V |
* | 6 | 2.64V |
* | 5 | 2.76V |
* | 4 | 2.92V |
* | 3 | 3.10V |
* | 2 | 3.27V |
* +-----------------+-------------------+
*/
#if defined(CONFIG_ESP_BROWNOUT_DET_LVL)
#define BROWNOUT_DET_LVL CONFIG_ESP_BROWNOUT_DET_LVL
#else
#define BROWNOUT_DET_LVL 0
#endif
static __attribute__((unused)) DRAM_ATTR const char *TAG = "BOD";
#if CONFIG_ESP_SYSTEM_BROWNOUT_INTR
IRAM_ATTR static void rtc_brownout_isr_handler(void *arg)
{
/* Normally RTC ISR clears the interrupt flag after the application-supplied
* handler returns. Since restart is called here, the flag needs to be
* cleared manually.
*/
brownout_ll_intr_clear();
// Stop the other core.
#if !CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
const uint32_t core_id = esp_cpu_get_core_id();
const uint32_t other_core_id = (core_id == 0) ? 1 : 0;
esp_cpu_stall(other_core_id);
#endif
// TODO: Support for resetting the flash during brownout for stability
ESP_DRAM_LOGI(TAG, "Brownout detector was triggered\r\n\r\n");
// Flush any data left in UART FIFOs
for (int i = 0; i < SOC_UART_HP_NUM; ++i) {
if (uart_ll_is_enabled(i)) {
esp_rom_output_tx_wait_idle(i);
}
}
// generate core reset
esp_rom_software_reset_system();
ESP_INFINITE_LOOP();
}
#endif // CONFIG_ESP_SYSTEM_BROWNOUT_INTR
void esp_tee_brownout_init(void)
{
#if CONFIG_ESP_SYSTEM_BROWNOUT_INTR
brownout_hal_config_t cfg = {
.threshold = BROWNOUT_DET_LVL,
.enabled = true,
.reset_enabled = false,
.flash_power_down = true,
.rf_power_down = true,
};
brownout_hal_config(&cfg);
brownout_ll_intr_clear();
// TODO IDF-6606: LP_RTC_TIMER interrupt source is shared by lp_timer and brownout detector, but lp_timer interrupt
// is not used now. An interrupt allocator is needed when lp_timer intr gets supported.
struct vector_desc_t bod_vd = { 0, NULL, NULL, NULL };
bod_vd.source = ETS_LP_RTC_TIMER_INTR_SOURCE;
bod_vd.isr = rtc_brownout_isr_handler;
esp_tee_intr_register((void *)&bod_vd);
brownout_ll_intr_enable(true);
#else // brownout without interrupt
brownout_hal_config_t cfg = {
.threshold = BROWNOUT_DET_LVL,
.enabled = true,
.reset_enabled = true,
.flash_power_down = true,
.rf_power_down = true,
};
brownout_hal_config(&cfg);
#endif
}
void esp_tee_brownout_disable(void)
{
brownout_hal_config_t cfg = {
.enabled = false,
};
brownout_hal_config(&cfg);
#if CONFIG_ESP_SYSTEM_BROWNOUT_INTR
brownout_ll_intr_enable(false);
#endif // CONFIG_ESP_SYSTEM_BROWNOUT_INTR
}

View File

@ -0,0 +1,43 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <assert.h>
#include <sys/param.h>
#include "esp_app_desc.h"
#include "esp_attr.h"
#include "sdkconfig.h"
// Application version info
const esp_app_desc_t __attribute__((section(".rodata_desc"))) esp_app_desc = {
.magic_word = ESP_APP_DESC_MAGIC_WORD,
.version = PROJECT_VER,
.project_name = PROJECT_NAME,
.idf_ver = IDF_VER,
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
.secure_version = CONFIG_BOOTLOADER_APP_SECURE_VERSION,
#else
.secure_version = 0,
#endif
#ifdef CONFIG_APP_COMPILE_TIME_DATE
.time = __TIME__,
.date = __DATE__,
#else
.time = "",
.date = "",
#endif
.min_efuse_blk_rev_full = CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL,
.max_efuse_blk_rev_full = CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL,
.mmu_page_size = 31 - __builtin_clz(CONFIG_MMU_PAGE_SIZE),
};
void esp_app_desc_tee_include_impl(void)
{
// Linker hook, exists for no other purpose
}
_Static_assert(sizeof(PROJECT_VER) <= sizeof(esp_app_desc.version), "[esp_tee] PROJECT_VER is longer than version field in structure");
_Static_assert(sizeof(IDF_VER) <= sizeof(esp_app_desc.idf_ver), "[esp_tee] IDF_VER is longer than idf_ver field in structure");
_Static_assert(sizeof(PROJECT_NAME) <= sizeof(esp_app_desc.project_name), "[esp_tee] PROJECT_NAME is longer than project_name field in structure");

View File

@ -0,0 +1,196 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "tlsf.h"
#include "tlsf_block_functions.h"
#include "multi_heap.h"
/* Handle to a registered TEE heap */
static multi_heap_handle_t tee_heap;
inline static void multi_heap_assert(bool condition, const char *format, int line, intptr_t address)
{
/* Can't use libc assert() here as it calls printf() which can cause another malloc() for a newlib lock.
Also, it's useful to be able to print the memory address where corruption was detected.
*/
(void) condition;
}
#define MULTI_HEAP_ASSERT(CONDITION, ADDRESS) \
multi_heap_assert((CONDITION), "CORRUPT HEAP: multi_heap.c:%d detected at 0x%08x\n", \
__LINE__, (intptr_t)(ADDRESS))
/* Check a block is valid for this heap. Used to verify parameters. */
static void assert_valid_block(const heap_t *heap, const block_header_t *block)
{
pool_t pool = tlsf_get_pool(heap->heap_data);
void *ptr = block_to_ptr(block);
MULTI_HEAP_ASSERT((ptr >= pool) &&
(ptr < pool + heap->pool_size),
(uintptr_t)ptr);
}
int tee_heap_register(void *start_ptr, size_t size)
{
assert(start_ptr);
if (size < (sizeof(heap_t))) {
//Region too small to be a heap.
return -1;
}
heap_t *result = (heap_t *)start_ptr;
size -= sizeof(heap_t);
/* Do not specify any maximum size for the allocations so that the default configuration is used */
const size_t max_bytes = 0;
result->heap_data = tlsf_create_with_pool(start_ptr + sizeof(heap_t), size, max_bytes);
if (result->heap_data == NULL) {
return -1;
}
result->lock = NULL;
result->free_bytes = size - tlsf_size(result->heap_data);
result->pool_size = size;
result->minimum_free_bytes = result->free_bytes;
tee_heap = (multi_heap_handle_t)result;
return 0;
}
void *tee_heap_malloc(size_t size)
{
if (tee_heap == NULL || size == 0) {
return NULL;
}
void *result = tlsf_malloc(tee_heap->heap_data, size);
if (result) {
tee_heap->free_bytes -= tlsf_block_size(result);
tee_heap->free_bytes -= tlsf_alloc_overhead();
if (tee_heap->free_bytes < tee_heap->minimum_free_bytes) {
tee_heap->minimum_free_bytes = tee_heap->free_bytes;
}
}
return result;
}
void *tee_heap_calloc(size_t n, size_t size)
{
size_t reg_size = n * size;
void *ptr = tee_heap_malloc(reg_size);
if (ptr != NULL) {
memset(ptr, 0x00, reg_size);
}
return ptr;
}
void *tee_heap_aligned_alloc(size_t size, size_t alignment)
{
if (tee_heap == NULL || size == 0) {
return NULL;
}
// Alignment must be a power of two
if (((alignment & (alignment - 1)) != 0) || (!alignment)) {
return NULL;
}
void *result = tlsf_memalign_offs(tee_heap->heap_data, alignment, size, 0x00);
if (result) {
tee_heap->free_bytes -= tlsf_block_size(result);
tee_heap->free_bytes -= tlsf_alloc_overhead();
if (tee_heap->free_bytes < tee_heap->minimum_free_bytes) {
tee_heap->minimum_free_bytes = tee_heap->free_bytes;
}
}
return result;
}
void tee_heap_free(void *p)
{
if (tee_heap == NULL || p == NULL) {
return;
}
assert_valid_block(tee_heap, block_from_ptr(p));
tee_heap->free_bytes += tlsf_block_size(p);
tee_heap->free_bytes += tlsf_alloc_overhead();
tlsf_free(tee_heap->heap_data, p);
}
void *malloc(size_t size)
{
return tee_heap_malloc(size);
}
void *calloc(size_t n, size_t size)
{
return tee_heap_calloc(n, size);
}
void free(void *ptr)
{
tee_heap_free(ptr);
}
void tee_heap_dump_free_size(void)
{
if (tee_heap == NULL) {
return;
}
printf("Free: %uB | Minimum free: %uB\n", tee_heap->free_bytes, tee_heap->minimum_free_bytes);
}
static bool tee_heap_dump_tlsf(void* ptr, size_t size, int used, void* user)
{
(void)user;
printf("Block %p data, size: %d bytes, Free: %s\n",
(void *)ptr,
size,
used ? "No" : "Yes");
return true;
}
void tee_heap_dump_info(void)
{
if (tee_heap == NULL) {
return;
}
printf("Showing data for TEE heap: %p\n", (void *)tee_heap);
tee_heap_dump_free_size();
tlsf_walk_pool(tlsf_get_pool(tee_heap->heap_data), tee_heap_dump_tlsf, NULL);
}
/* Definitions for functions from the heap component, used in files shared with ESP-IDF */
void *heap_caps_malloc(size_t alignment, size_t size, uint32_t caps)
{
(void) caps;
return tee_heap_malloc(size);
}
void *heap_caps_aligned_alloc(size_t alignment, size_t size, uint32_t caps)
{
(void) caps;
return tee_heap_aligned_alloc(size, alignment);
}
void *heap_caps_aligned_calloc(size_t alignment, size_t n, size_t size, uint32_t caps)
{
(void) caps;
uint32_t reg_size = n * size;
void *ptr = tee_heap_aligned_alloc(reg_size, alignment);
if (ptr != NULL) {
memset(ptr, 0x00, reg_size);
}
return ptr;
}

View File

@ -0,0 +1,123 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_attr.h"
#include "esp_macros.h"
#include "esp_rom_sys.h"
#include "esp_rom_uart.h"
#include "hal/apm_hal.h"
#include "riscv/rvruntime-frames.h"
#include "esp_tee.h"
#include "panic_helper.h"
#include "esp_tee_apm_intr.h"
#define RV_FUNC_STK_SZ (32)
#define tee_panic_print(format, ...) esp_rom_printf(DRAM_STR(format), ##__VA_ARGS__)
static void tee_panic_end(void)
{
// make sure all the panic handler output is sent from UART FIFO
if (CONFIG_ESP_CONSOLE_UART_NUM >= 0) {
esp_rom_output_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
}
// generate core reset
esp_rom_software_reset_system();
}
void __assert_func(const char *file, int line, const char *func, const char *expr)
{
tee_panic_print("Assert failed in %s, %s:%d (%s)\r\n", func, file, line, expr);
tee_panic_print("\n\n");
tee_panic_end();
ESP_INFINITE_LOOP();
}
void abort(void)
{
#if !CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT
tee_panic_print("abort() was called at PC 0x%08x\r\n\n", (intptr_t)__builtin_return_address(0) - 3);
tee_panic_print("\n\n");
#endif
tee_panic_end();
ESP_INFINITE_LOOP();
}
static void panic_handler(void *frame, bool pseudo_exccause)
{
int fault_core = esp_cpu_get_core_id();
tee_panic_print("\n=================================================\n");
tee_panic_print("Secure exception occurred on Core %d\n", fault_core);
if (pseudo_exccause) {
panic_print_isrcause((const void *)frame, fault_core);
} else {
panic_print_exccause((const void *)frame, fault_core);
}
tee_panic_print("=================================================\n");
panic_print_registers((const void *)frame, fault_core);
tee_panic_print("\n");
panic_print_backtrace((const void *)frame, 100);
tee_panic_print("\n");
tee_panic_print("Rebooting...\r\n\n");
tee_panic_end();
ESP_INFINITE_LOOP();
}
void tee_panic_from_exc(void *frame)
{
panic_handler(frame, false);
}
void tee_panic_from_isr(void *frame)
{
panic_handler(frame, true);
}
void tee_apm_violation_isr(void *arg)
{
intptr_t exc_sp = RV_READ_CSR(mscratch);
RvExcFrame *frame = (RvExcFrame *)exc_sp;
apm_ctrl_path_t *apm_excp_type = NULL;
apm_ctrl_exception_info_t excp_info;
apm_excp_type = (apm_ctrl_path_t *)arg;
excp_info.apm_path.apm_ctrl = apm_excp_type->apm_ctrl;
excp_info.apm_path.apm_m_path = apm_excp_type->apm_m_path;
apm_hal_apm_ctrl_get_exception_info(&excp_info);
/* Clear APM M path interrupt. */
apm_hal_apm_ctrl_exception_clear(apm_excp_type);
int fault_core = esp_cpu_get_core_id();
tee_panic_print("\n=================================================\n");
tee_panic_print("APM permission violation occurred on Core %d\n", fault_core);
tee_panic_print("Guru Meditation Error: Core %d panic'ed (%s). ", fault_core, esp_tee_apm_excp_type_to_str(excp_info.excp_type));
tee_panic_print("Exception was unhandled.\n");
tee_panic_print("Fault addr: 0x%x | Mode: %s\n", excp_info.excp_addr, esp_tee_apm_excp_mode_to_str(excp_info.excp_mode));
tee_panic_print("Module: %s | Path: 0x%02x\n", esp_tee_apm_excp_ctrl_to_str(excp_info.apm_path.apm_ctrl), excp_info.apm_path.apm_m_path);
tee_panic_print("Master: %s | Region: 0x%02x\n", esp_tee_apm_excp_mid_to_str(excp_info.excp_id), excp_info.excp_regn);
tee_panic_print("=================================================\n");
panic_print_registers((const void *)frame, fault_core);
tee_panic_print("\n");
panic_print_backtrace((const void *)frame, 100);
tee_panic_print("\n");
tee_panic_print("Rebooting...\r\n\n");
tee_panic_end();
ESP_INFINITE_LOOP();
}

View File

@ -0,0 +1,13 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
void panic_print_backtrace(const void *f, int depth);
void panic_print_registers(const void *f, int core);
void panic_print_exccause(const void *f, int core);
void panic_print_isrcause(const void *f, int core);

View File

@ -0,0 +1,152 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_tee.h"
#include "esp_attr.h"
#include "esp_private/panic_reason.h"
#include "riscv/csr.h"
#include "riscv/encoding.h"
#include "riscv/rvruntime-frames.h"
#define tee_panic_print(format, ...) esp_rom_printf(DRAM_STR(format), ##__VA_ARGS__)
void panic_print_backtrace(const void *f, int depth)
{
// Basic backtrace
tee_panic_print("\r\nStack memory\r\n");
uint32_t sp = (uint32_t)((RvExcFrame *)f)->sp;
const int per_line = 8;
for (int x = 0; x < depth; x += per_line * sizeof(uint32_t)) {
uint32_t *spp = (uint32_t *)(sp + x);
tee_panic_print("0x%08x: ", sp + x);
for (int y = 0; y < per_line; y++) {
tee_panic_print("0x%08x%s", spp[y], y == per_line - 1 ? "\r\n" : " ");
}
}
}
void panic_print_registers(const void *f, int core)
{
uint32_t *regs = (uint32_t *)f;
// only print ABI name
const char *desc[] = {
"MEPC ", "RA ", "SP ", "GP ", "TP ", "T0 ", "T1 ", "T2 ",
"S0/FP ", "S1 ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ",
"A6 ", "A7 ", "S2 ", "S3 ", "S4 ", "S5 ", "S6 ", "S7 ",
"S8 ", "S9 ", "S10 ", "S11 ", "T3 ", "T4 ", "T5 ", "T6 ",
"MSTATUS ", "MTVEC ", "MCAUSE ", "MTVAL ", "MHARTID "
};
tee_panic_print("\nCore %d register dump:", ((RvExcFrame *)f)->mhartid);
for (int x = 0; x < sizeof(desc) / sizeof(desc[0]); x += 4) {
tee_panic_print("\r\n");
for (int y = 0; y < 4 && x + y < sizeof(desc) / sizeof(desc[0]); y++) {
if (desc[x + y][0] != 0) {
tee_panic_print("%s: 0x%08x ", desc[x + y], regs[x + y]);
}
}
}
tee_panic_print("MIE : 0x%08x ", RV_READ_CSR(mie));
tee_panic_print("MIP : 0x%08x ", RV_READ_CSR(mip));
tee_panic_print("MSCRATCH: 0x%08x\n", RV_READ_CSR(mscratch));
tee_panic_print("UEPC : 0x%08x ", RV_READ_CSR(uepc));
tee_panic_print("USTATUS : 0x%08x ", RV_READ_CSR(ustatus));
tee_panic_print("UTVEC : 0x%08x ", RV_READ_CSR(utvec));
tee_panic_print("UCAUSE : 0x%08x\n", RV_READ_CSR(ucause));
tee_panic_print("UTVAL : 0x%08x ", RV_READ_CSR(utval));
tee_panic_print("UIE : 0x%08x ", RV_READ_CSR(uie));
tee_panic_print("UIP : 0x%08x\n", RV_READ_CSR(uip));
}
void panic_print_exccause(const void *f, int core)
{
RvExcFrame *regs = (RvExcFrame *) f;
//Please keep in sync with PANIC_RSN_* defines
static const char *reason[] = {
"Instruction address misaligned",
"Instruction access fault",
"Illegal instruction",
"Breakpoint",
"Load address misaligned",
"Load access fault",
"Store address misaligned",
"Store access fault",
"Environment call from U-mode",
"Environment call from S-mode",
NULL,
"Environment call from M-mode",
"Instruction page fault",
"Load page fault",
NULL,
"Store page fault",
};
const char *rsn = NULL;
if (regs->mcause < (sizeof(reason) / sizeof(reason[0]))) {
if (reason[regs->mcause] != NULL) {
rsn = (reason[regs->mcause]);
}
}
const char *desc = "Exception was unhandled.";
const void *addr = (void *) regs->mepc;
tee_panic_print("Guru Meditation Error: Core %d panic'ed (%s). %s\n", core, rsn, desc);
const char *exc_origin = "U-mode";
if (regs->mstatus & MSTATUS_MPP) {
exc_origin = "M-mode";
}
tee_panic_print("Fault addr: %p | Exception origin: %s\n", addr, exc_origin);
}
void panic_print_isrcause(const void *f, int core)
{
RvExcFrame *regs = (RvExcFrame *) f;
/* Please keep in sync with PANIC_RSN_* defines */
static const char *pseudo_reason[] = {
"Unknown reason",
"Interrupt wdt timeout on CPU0",
#if SOC_CPU_NUM > 1
"Interrupt wdt timeout on CPU1",
#endif
"Cache error",
};
const void *addr = (void *) regs->mepc;
const char *rsn = pseudo_reason[0];
/* The mcause has been set by the CPU when the panic occurred.
* All SoC-level panic will call this function, thus, this register
* lets us know which error was triggered. */
if (regs->mcause == ETS_CACHEERR_INUM) {
/* Panic due to a cache error, multiple cache error are possible,
* assign function print_cache_err_details to our structure's
* details field. As its name states, it will give more details
* about why the error happened. */
rsn = pseudo_reason[PANIC_RSN_CACHEERR];
} else if (regs->mcause == ETS_INT_WDT_INUM) {
/* Watchdog interrupt occurred, get the core on which it happened
* and update the reason/message accordingly. */
#if SOC_CPU_NUM > 1
_Static_assert(PANIC_RSN_INTWDT_CPU0 + 1 == PANIC_RSN_INTWDT_CPU1,
"PANIC_RSN_INTWDT_CPU1 must be equal to PANIC_RSN_INTWDT_CPU0 + 1");
#endif
rsn = pseudo_reason[PANIC_RSN_INTWDT_CPU0 + core];
}
const char *desc = "Exception was unhandled.";
tee_panic_print("Guru Meditation Error: Core %d panic'ed (%s). %s\n", core, rsn, desc);
const char *exc_origin = "U-mode";
if (regs->mstatus & MSTATUS_MPP) {
exc_origin = "M-mode";
}
tee_panic_print("Fault addr: %p | Exception origin: %s\n", addr, exc_origin);
}

View File

@ -0,0 +1,25 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "stddef.h"
#include "secure_service_num.h"
#include "secure_service_dec.h"
typedef void (*secure_service_t)(void);
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Woverride-init"
#endif
const secure_service_t tee_secure_service_table[] = {
[0 ... MAX_SECURE_SERVICES - 1] = (secure_service_t)NULL,
#define __SECURE_SERVICE(nr, symbol, nargs) [nr] = (secure_service_t)_ss_##symbol,
#include "secure_service.h"
};
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

View File

@ -0,0 +1,410 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include "esp_cpu.h"
#include "esp_efuse.h"
#include "hal/efuse_hal.h"
#include "hal/wdt_hal.h"
#include "esp_rom_efuse.h"
#include "esp_flash_encrypt.h"
#include "hal/sha_hal.h"
#include "soc/soc_caps.h"
#include "esp_tee.h"
#include "secure_service_num.h"
#include "esp_tee_intr.h"
#include "esp_tee_aes_intr.h"
#include "esp_tee_rv_utils.h"
#include "aes/esp_aes.h"
#include "sha/sha_dma.h"
#include "esp_tee_flash.h"
#include "esp_tee_sec_storage.h"
#include "esp_tee_ota_ops.h"
#include "esp_attestation.h"
#define ESP_TEE_MAX_INPUT_ARG 10
static const char *TAG = "esp_tee_sec_srv";
typedef void (*secure_service_t)(void);
extern const secure_service_t tee_secure_service_table[];
void _ss_invalid_secure_service(void)
{
assert(0);
}
/* ---------------------------------------------- Interrupts ------------------------------------------------- */
void _ss_esp_rom_route_intr_matrix(int cpu_no, uint32_t model_num, uint32_t intr_num)
{
return esp_tee_route_intr_matrix(cpu_no, model_num, intr_num);
}
void _ss_rv_utils_intr_enable(uint32_t intr_mask)
{
rv_utils_tee_intr_enable(intr_mask);
}
void _ss_rv_utils_intr_disable(uint32_t intr_mask)
{
rv_utils_tee_intr_disable(intr_mask);
}
void _ss_rv_utils_intr_set_priority(int rv_int_num, int priority)
{
rv_utils_tee_intr_set_priority(rv_int_num, priority);
}
void _ss_rv_utils_intr_set_type(int intr_num, enum intr_type type)
{
rv_utils_tee_intr_set_type(intr_num, type);
}
void _ss_rv_utils_intr_set_threshold(int priority_threshold)
{
rv_utils_tee_intr_set_threshold(priority_threshold);
}
void _ss_rv_utils_intr_edge_ack(uint32_t intr_num)
{
rv_utils_tee_intr_edge_ack(intr_num);
}
void _ss_rv_utils_intr_global_enable(void)
{
rv_utils_tee_intr_global_enable();
}
/* ---------------------------------------------- eFuse ------------------------------------------------- */
uint32_t _ss_efuse_hal_chip_revision(void)
{
return efuse_hal_chip_revision();
}
uint32_t _ss_efuse_hal_get_chip_ver_pkg(void)
{
return efuse_hal_get_chip_ver_pkg();
}
bool _ss_efuse_hal_get_disable_wafer_version_major(void)
{
return efuse_hal_get_disable_wafer_version_major();
}
void _ss_efuse_hal_get_mac(uint8_t *mac)
{
efuse_hal_get_mac(mac);
}
bool _ss_esp_efuse_check_secure_version(uint32_t secure_version)
{
return esp_efuse_check_secure_version(secure_version);
}
esp_err_t _ss_esp_efuse_read_field_blob(const esp_efuse_desc_t *field[], void *dst, size_t dst_size_bits)
{
if ((field != NULL) && (field[0]->efuse_block >= EFUSE_BLK4)) {
return ESP_ERR_INVALID_ARG;
}
return esp_efuse_read_field_blob(field, dst, dst_size_bits);
}
bool _ss_esp_flash_encryption_enabled(void)
{
uint32_t flash_crypt_cnt = 0;
#ifndef CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH
flash_crypt_cnt = efuse_ll_get_flash_crypt_cnt();
#else
esp_efuse_read_field_blob(ESP_EFUSE_SPI_BOOT_CRYPT_CNT, &flash_crypt_cnt, ESP_EFUSE_SPI_BOOT_CRYPT_CNT[0]->bit_count) ;
#endif
/* __builtin_parity is in flash, so we calculate parity inline */
bool enabled = false;
while (flash_crypt_cnt) {
if (flash_crypt_cnt & 1) {
enabled = !enabled;
}
flash_crypt_cnt >>= 1;
}
return enabled;
}
/* ---------------------------------------------- RTC_WDT ------------------------------------------------- */
void _ss_wdt_hal_init(wdt_hal_context_t *hal, wdt_inst_t wdt_inst, uint32_t prescaler, bool enable_intr)
{
wdt_hal_init(hal, wdt_inst, prescaler, enable_intr);
}
void _ss_wdt_hal_deinit(wdt_hal_context_t *hal)
{
wdt_hal_deinit(hal);
}
/* ---------------------------------------------- AES ------------------------------------------------- */
void _ss_esp_aes_intr_alloc(void)
{
esp_tee_aes_intr_alloc();
}
int _ss_esp_aes_crypt_cbc(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output)
{
return esp_aes_crypt_cbc(ctx, mode, length, iv, input, output);
}
int _ss_esp_aes_crypt_cfb128(esp_aes_context *ctx,
int mode,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output)
{
return esp_aes_crypt_cfb128(ctx, mode, length, iv_off, iv, input, output);
}
int _ss_esp_aes_crypt_cfb8(esp_aes_context *ctx,
int mode,
size_t length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output)
{
return esp_aes_crypt_cfb8(ctx, mode, length, iv, input, output);
}
int _ss_esp_aes_crypt_ctr(esp_aes_context *ctx,
size_t length,
size_t *nc_off,
unsigned char nonce_counter[16],
unsigned char stream_block[16],
const unsigned char *input,
unsigned char *output)
{
return esp_aes_crypt_ctr(ctx, length, nc_off, nonce_counter, stream_block, input, output);
}
int _ss_esp_aes_crypt_ecb(esp_aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16])
{
return esp_aes_crypt_ecb(ctx, mode, input, output);
}
int _ss_esp_aes_crypt_ofb(esp_aes_context *ctx,
size_t length,
size_t *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output)
{
return esp_aes_crypt_ofb(ctx, length, iv_off, iv, input, output);
}
/* ---------------------------------------------- SHA ------------------------------------------------- */
void _ss_esp_sha(esp_sha_type sha_type, const unsigned char *input, size_t ilen, unsigned char *output)
{
esp_sha(sha_type, input, ilen, output);
}
int _ss_esp_sha_dma(esp_sha_type sha_type, const void *input, uint32_t ilen,
const void *buf, uint32_t buf_len, bool is_first_block)
{
return esp_sha_dma(sha_type, input, ilen, buf, buf_len, is_first_block);
}
void _ss_esp_sha_read_digest_state(esp_sha_type sha_type, void *digest_state)
{
sha_hal_read_digest(sha_type, digest_state);
}
void _ss_esp_sha_write_digest_state(esp_sha_type sha_type, void *digest_state)
{
sha_hal_write_digest(sha_type, digest_state);
}
/* ---------------------------------------------- OTA ------------------------------------------------- */
int _ss_esp_tee_ota_begin(void)
{
return esp_tee_ota_begin();
}
int _ss_esp_tee_ota_write(uint32_t rel_offset, void *data, size_t size)
{
return esp_tee_ota_write(rel_offset, data, size);
}
int _ss_esp_tee_ota_end(void)
{
return esp_tee_ota_end();
}
/* ---------------------------------------------- Secure Storage ------------------------------------------------- */
esp_err_t _ss_esp_tee_sec_storage_init(void)
{
return esp_tee_sec_storage_init();
}
esp_err_t _ss_esp_tee_sec_storage_gen_key(uint16_t slot_id, uint8_t key_type)
{
return esp_tee_sec_storage_gen_key(slot_id, key_type);
}
esp_err_t _ss_esp_tee_sec_storage_get_signature(uint16_t slot_id, uint8_t *hash, size_t hlen, esp_tee_sec_storage_sign_t *out_sign)
{
return esp_tee_sec_storage_get_signature(slot_id, hash, hlen, out_sign);
}
esp_err_t _ss_esp_tee_sec_storage_get_pubkey(uint16_t slot_id, esp_tee_sec_storage_pubkey_t *pubkey)
{
return esp_tee_sec_storage_get_pubkey(slot_id, 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)
{
return esp_tee_sec_storage_encrypt(slot_id, input, len, aad, aad_len, 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)
{
return esp_tee_sec_storage_decrypt(slot_id, input, len, aad, aad_len, tag, tag_len, output);
}
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);
}
/* ---------------------------------------------- Attestation ------------------------------------------------- */
esp_err_t _ss_esp_tee_att_generate_token(const uint32_t nonce, const uint32_t client_id, const char *psa_cert_ref,
uint8_t *token_buf, const size_t token_buf_size, uint32_t *token_len)
{
return esp_att_generate_token(nonce, client_id, psa_cert_ref, token_buf, token_buf_size, token_len);
}
/* ---------------------------------------------- Secure Service Dispatcher ------------------------------------------------- */
/**
* @brief Entry point to the TEE binary during secure service call. It decipher the call and dispatch it
* to corresponding Secure Service API in secure world.
* TODO: Fix the assembly routine here for compatibility with all levels of compiler optimizations
*/
#pragma GCC push_options
#pragma GCC optimize ("Og")
int esp_tee_service_dispatcher(int argc, va_list ap)
{
if (argc > ESP_TEE_MAX_INPUT_ARG) {
ESP_LOGE(TAG, "Input arguments overflow! Received %d, Permitted %d",
argc, ESP_TEE_MAX_INPUT_ARG);
return -1;
}
int ret = -1;
void *fp_secure_service;
uint32_t argv[ESP_TEE_MAX_INPUT_ARG], *argp;
uint32_t sid = va_arg(ap, uint32_t);
argc--;
if (sid >= MAX_SECURE_SERVICES) {
ESP_LOGE(TAG, "Invalid Service ID!");
va_end(ap);
return -1;
}
fp_secure_service = (void *)tee_secure_service_table[sid];
for (int i = 0; i < argc; i++) {
argv[i] = va_arg(ap, uint32_t);
}
argp = &argv[0];
va_end(ap);
asm volatile(
"mv t0, %1 \n"
"beqz t0, service_call \n"
"lw a0, 0(%3) \n"
"addi t0, t0, -1 \n"
"beqz t0, service_call \n"
"lw a1, 4(%3) \n"
"addi t0, t0, -1 \n"
"beqz t0, service_call \n"
"lw a2, 8(%3) \n"
"addi t0, t0, -1 \n"
"beqz t0, service_call \n"
"lw a3, 12(%3) \n"
"addi t0, t0, -1 \n"
"beqz t0, service_call \n"
"lw a4, 16(%3) \n"
"addi t0, t0, -1 \n"
"beqz t0, service_call \n"
"lw a5, 20(%3) \n"
"addi t0, t0, -1 \n"
"beqz t0, service_call \n"
"lw a6, 24(%3) \n"
"addi t0, t0, -1 \n"
"beqz t0, service_call \n"
"lw a7, 28(%3) \n"
"addi t0, t0, -1 \n"
"beqz t0, service_call \n"
"addi %3, %3, 32 \n"
"mv t2, sp \n"
"loop: \n"
"lw t1, 0(%3) \n"
"sw t1, 0(t2) \n"
"addi t0, t0, -1 \n"
"addi t2, t2, 4 \n"
"addi %3, %3, 4 \n"
"bnez t0, loop \n"
"service_call: \n"
"mv t1, %2 \n"
"jalr 0(t1) \n"
"mv %0, a0 \n"
: "=r"(ret)
: "r"(argc), "r"(fp_secure_service), "r"(argp)
: "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "t0", "t1", "t2"
);
return ret;
}
#pragma GCC pop_options

View File

@ -0,0 +1,186 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_macros.h"
#include "esp_log.h"
#include "esp_rom_sys.h"
#include "riscv/rv_utils.h"
#include "esp_tee.h"
#include "multi_heap.h"
#include "esp_tee_brownout.h"
#include "esp_tee_flash.h"
#include "bootloader_utility_tee.h"
#if __has_include("esp_app_desc.h")
#define WITH_APP_IMAGE_INFO
#include "esp_app_desc.h"
#endif
/* TEE symbols */
extern uint32_t _tee_stack;
extern uint32_t _tee_intr_stack_bottom;
extern uint32_t _tee_heap_start;
extern uint32_t _tee_heap_end;
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;
#define TEE_HEAP_SIZE (((uint32_t)&_tee_heap_end - (uint32_t)&_tee_heap_start))
static const char *TAG = "esp_tee_init";
/* Initializes the TEE configuration structure with fields required for
* the REE-TEE interface from the TEE binary
*/
static void tee_init_app_config(void)
{
/* TODO: Integrate these compatibility checks into the bootloader
* so it can provide fallback behavior
*/
if (esp_tee_app_config.magic_word != ESP_TEE_APP_CFG_MAGIC) {
ESP_LOGE(TAG, "Configuration structure missing from the TEE app!");
ESP_INFINITE_LOOP(); // WDT will reset us
}
if (esp_tee_app_config.api_major_version != ESP_TEE_API_MAJOR_VER) {
ESP_LOGE(TAG, "TEE API version mismatch: app v%d != binary v%d",
esp_tee_app_config.api_major_version, ESP_TEE_API_MAJOR_VER);
ESP_INFINITE_LOOP();
}
/* Set the TEE API minor version */
esp_tee_app_config.api_minor_version = ESP_TEE_API_MINOR_VER;
/* Set the TEE-related fields (from the TEE binary) that the REE will use to interface with TEE */
esp_tee_app_config.s_entry_addr = &_sec_world_entry;
esp_tee_app_config.s_int_handler = &_tee_s_intr_handler;
}
/* Print the TEE application info */
static void tee_print_app_info(void)
{
#ifdef WITH_APP_IMAGE_INFO
const esp_app_desc_t *app_desc = esp_app_get_description();
ESP_LOGI(TAG, "TEE information:");
ESP_LOGI(TAG, "Project name: %s", app_desc->project_name);
ESP_LOGI(TAG, "App version: %s", app_desc->version);
#ifdef CONFIG_BOOTLOADER_APP_SECURE_VERSION
ESP_LOGI(TAG, "Secure version: %d", app_desc->secure_version);
#endif
ESP_LOGI(TAG, "Compile time: %s %s", app_desc->date, app_desc->time);
char buf[17];
esp_app_get_elf_sha256(buf, sizeof(buf));
ESP_LOGI(TAG, "ELF file SHA256: %s...", buf);
ESP_LOGI(TAG, "ESP-IDF: %s", app_desc->idf_ver);
#endif
}
/* Mark the current TEE image as valid and cancel rollback */
static void tee_mark_app_and_valid_cancel_rollback(void)
{
esp_partition_info_t tee_ota_info;
esp_err_t err = esp_tee_flash_find_partition(PART_TYPE_DATA, PART_SUBTYPE_DATA_TEE_OTA, NULL, &tee_ota_info);
if (err != ESP_OK) {
ESP_LOGD(TAG, "No TEE OTA data partition found");
return;
}
const esp_partition_pos_t tee_ota_pos = tee_ota_info.pos;
err = bootloader_utility_tee_mark_app_valid_and_cancel_rollback(&tee_ota_pos);
if (err != ESP_OK) {
if (err == ESP_ERR_INVALID_STATE) {
/* NOTE: App already marked valid */
return;
}
ESP_LOGE(TAG, "Failed to cancel rollback (0x%08x)", err);
esp_rom_software_reset_system();
}
ESP_LOGW(TAG, "Rollback succeeded, erasing the passive TEE partition...");
uint8_t tee_next_part = bootloader_utility_tee_get_next_update_partition(&tee_ota_pos);
esp_partition_info_t tee_next_part_info;
int ret = esp_tee_flash_find_partition(PART_TYPE_APP, tee_next_part, NULL, &tee_next_part_info);
ret |= esp_tee_flash_erase_range(tee_next_part_info.pos.offset, tee_next_part_info.pos.size);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to find/erase the passive TEE partition!");
return;
}
}
void __attribute__((noreturn)) esp_tee_init(uint32_t ree_entry_addr, uint32_t ree_drom_addr, uint8_t tee_boot_part)
{
/* Clear BSS */
memset(&_tee_bss_start, 0, (&_tee_bss_end - &_tee_bss_start) * sizeof(_tee_bss_start));
static uint32_t btld_sp;
/* Take backup of bootloader stack. */
asm volatile("mv %0, sp" : "=r"(btld_sp));
/* Switch to secure world stack. */
asm volatile("mv sp, %0" :: "r"((uint32_t)&_tee_stack));
/* TEE compatibility check and App config data initialization. */
tee_init_app_config();
/* TEE Secure World heap initialization. */
assert(tee_heap_register(((void *)&_tee_heap_start), TEE_HEAP_SIZE) == 0);
/* SoC specific secure initialization. */
esp_tee_soc_secure_sys_init();
/* Brownout detection initialization */
esp_tee_brownout_init();
/* Switch back to bootloader stack. */
asm volatile("mv sp, %0" :: "r"(btld_sp));
ESP_LOGI(TAG, "Initializing. RAM available for dynamic allocation:");
ESP_LOGI(TAG, "At %08X len %08X (%d KiB): %s",
((void *)&_tee_heap_start), TEE_HEAP_SIZE, TEE_HEAP_SIZE / 1024, "RAM");
/* Setting up the permissible flash operation address range */
assert(esp_tee_flash_setup_prot_ctx(tee_boot_part) == ESP_OK);
/* Setting up the running non-secure app partition as per the address provided by the bootloader */
assert(esp_tee_flash_set_running_ree_partition(ree_drom_addr) == ESP_OK);
tee_print_app_info();
/* NOTE: As image rollback is mandatorily enabled for TEE OTA,
* the most optimum checkpoint to mark the current app valid and
* cancel rollback is right before the TEE ends and is about to
* pass control to the non-secure app (see below).
*/
tee_mark_app_and_valid_cancel_rollback();
/* Switch to the REE and launch app */
esp_tee_switch_to_ree(ree_entry_addr);
/* 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

@ -0,0 +1,185 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_tee.h"
#include "esp_tee_intr.h"
#include "esp_tee_rv_utils.h"
#include "soc/periph_defs.h"
#include "soc/interrupt_reg.h"
#include "hal/cpu_ll.h"
#include "esp_cpu.h"
#include "esp_log.h"
/* For ESP32-C6 */
#define INTR_MAX_SOURCE (77)
#define INTR_SET_SIZE (32)
#define INTR_SET_COUNT ((((INTR_MAX_SOURCE) + ((INTR_SET_SIZE) - 1)) & ~((INTR_SET_SIZE) - 1)) / INTR_SET_SIZE)
#define INTR_DISABLED_INUM (6)
static const char *TAG = "esp_tee_intr";
typedef struct tee_handler_table_entry {
intr_handler_t handler;
void *arg;
} tee_handler_table_entry;
static tee_handler_table_entry tee_interrupt_table[INTR_MAX_SOURCE * portNUM_PROCESSORS];
static uint32_t protected_sources[INTR_SET_COUNT];
bool esp_tee_is_intr_src_protected(int source)
{
uint32_t base = source / INTR_SET_SIZE;
uint32_t offset = source % INTR_SET_SIZE;
return (protected_sources[base] & (1 << offset));
}
void esp_tee_protect_intr_src(int source)
{
uint32_t base = source / INTR_SET_SIZE;
uint32_t offset = source % INTR_SET_SIZE;
protected_sources[base] |= (1 << offset);
}
/* Default handler for unhandled interrupts */
void tee_unhandled_interrupt(void *arg)
{
ESP_LOGE(TAG, "Unhandled interrupt %d on cpu %d!", (int)arg, esp_cpu_get_core_id());
}
/* Interrupt Matrix configuration API to call from non-secure world */
void esp_tee_route_intr_matrix(int cpu_no, uint32_t model_num, uint32_t intr_num)
{
if (esp_tee_is_intr_src_protected(model_num) || intr_num == TEE_SECURE_INUM) {
return;
}
if (intr_num != ETS_INVALID_INUM) {
if (intr_num == INTR_DISABLED_INUM) {
rv_utils_tee_intr_disable(BIT(intr_num));
} else {
rv_utils_tee_intr_enable(BIT(intr_num));
}
}
esp_rom_route_intr_matrix(cpu_no, model_num, intr_num);
}
/**
* This function registers a handler for the specified interrupt. The "arg"
* parameter specifies the argument to be passed to the handler when it is
* invoked. The function returns the address of the previous handler.
* On error, it returns 0.
*/
static intr_handler_t tee_set_interrupt_handler(void *arg)
{
tee_handler_table_entry *entry;
intr_handler_t old;
vector_desc_t *vd = (vector_desc_t *)arg;
int source = vd->source;
if (source < 0 || source >= ETS_MAX_INTR_SOURCE) {
return 0; /* invalid interrupt source */
}
/* Convert exception number to _xt_exception_table name */
source = source * portNUM_PROCESSORS + esp_cpu_get_core_id();
entry = tee_interrupt_table + source;
old = entry->handler;
if (vd->isr) {
entry->handler = vd->isr;
entry->arg = vd->arg;
} else {
entry->handler = &tee_unhandled_interrupt;
entry->arg = (void *)source;
}
return ((old == &tee_unhandled_interrupt) ? 0 : old);
}
int esp_tee_intr_register(void *arg)
{
int cpu = esp_cpu_get_core_id();
struct vector_desc_t *vd = (struct vector_desc_t *)arg;
tee_set_interrupt_handler(vd);
esp_rom_route_intr_matrix(cpu, vd->source, TEE_SECURE_INUM);
return 0;
}
int esp_tee_intr_deregister(void *arg)
{
int cpu = esp_cpu_get_core_id();
struct vector_desc_t *vd = (struct vector_desc_t *)arg;
vd->isr = NULL;
vd->arg = (void *)((int)vd->source);
tee_set_interrupt_handler(vd);
// Setting back the default value for interrupt pin.
esp_rom_route_intr_matrix(cpu, vd->source, INTR_DISABLED_INUM);
return 0;
}
#define FIND_MSB_SET(n) (31 - __builtin_clz((uint32_t)(n)))
// LP_APM_M0_INTR, LP_APM_M1_INTR
#define TEE_SECURE_INT_APM_MASK_0 (0x00300000)
// EFUSE_INTR, LP_RTC_TIMER_INTR
#define TEE_SECURE_INT_MASK_0 (TEE_SECURE_INT_APM_MASK_0 | 0x0000C000)
// HP_APM_M0_INTR, HP_APM_M1_INTR, HP_APM_M2_INTR, HP_APM_M3_INTR, LP_APM0_INTR
#define TEE_SECURE_INT_APM_MASK_1 (0x000000F8)
#if !CONFIG_SECURE_TEE_TEST_MODE
#define TEE_SECURE_INT_MASK_1 (TEE_SECURE_INT_APM_MASK_1)
#else
// + TG0_T0_INTR (only for test mode)
#define TEE_SECURE_INT_MASK_1 (TEE_SECURE_INT_APM_MASK_1 | 0x00080000)
#endif
// AES_INTR, SHA_INTR, RSA_INTR, ECC_INTR
#define TEE_SECURE_INT_MASK_2 (0x00001E00)
/* called from esp_tee_vectors.S */
void esp_tee_global_interrupt_handler(intptr_t sp, int mcause)
{
uint32_t status, source;
while (1) {
if ((status = TEE_SECURE_INT_MASK_0 &
REG_READ(INTMTX_CORE0_INT_STATUS_REG_0_REG))) {
source = FIND_MSB_SET(status);
/* NOTE: With ESP-TEE, since APM violations trigger a panic, it's safe to use the mscratch
* register to pass on the stack pointer to the APM violation handler */
if (status & TEE_SECURE_INT_APM_MASK_0) {
RV_WRITE_CSR(mscratch, sp);
}
} else if ((status = TEE_SECURE_INT_MASK_1 &
REG_READ(INTMTX_CORE0_INT_STATUS_REG_1_REG))) {
source = FIND_MSB_SET(status) + 32;
if (status & TEE_SECURE_INT_APM_MASK_1) {
RV_WRITE_CSR(mscratch, sp);
}
} else if ((status = TEE_SECURE_INT_MASK_2 &
REG_READ(INTMTX_CORE0_INT_STATUS_REG_2_REG))) {
source = FIND_MSB_SET(status) + 64;
}
if (!status) {
break;
}
ESP_LOGV(TAG, "Found intr src: %d", source);
tee_handler_table_entry ih = tee_interrupt_table[source];
if (ih.handler) {
(*ih.handler)(ih.arg);
}
}
}

View File

@ -0,0 +1,6 @@
## IDF Component Manager Manifest File
dependencies:
espressif/json_generator:
version: "^1.1.2"
rules:
- if: "target in [esp32c6]"

View File

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void esp_tee_brownout_init(void);
void esp_tee_brownout_disable(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,78 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifndef __ASSEMBLER__
#include <stdlib.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Function prototype for interrupt handler function */
typedef void (*intr_handler_t)(void *arg);
typedef struct vector_desc_t vector_desc_t;
struct vector_desc_t {
int source: 8;
intr_handler_t isr;
void *arg;
vector_desc_t *next;
};
/**
* @brief Route peripheral interrupt sources to CPU's interrupt port via interrupt matrix
*
* Since the interrupt matrix controls the secure (TEE) interrupt source mapping to the
* TEE-reserved interrupt pin, this hardware module is controlled by the TEE.
* This API is provided as a Secure Service to the REE for the configuration of
* non-secure (REE) interrupts.
*
* @param cpu_no CPU core number to route the interrupt to
* @param model_num Peripheral interrupt source number
* @param intr_num CPU external interrupt number to assign
*/
void esp_tee_route_intr_matrix(int cpu_no, uint32_t model_num, uint32_t intr_num);
/**
* @brief Check if an interrupt source is protected from REE access
*
* @param source Peripheral interrupt source number
* @return true if the interrupt source is protected, false otherwise
*/
bool esp_tee_is_intr_src_protected(int source);
/**
* @brief Protect an interrupt source from REE access
*
* @param source Peripheral interrupt source number
*/
void esp_tee_protect_intr_src(int source);
/**
* @brief Register an interrupt handler
*
* @param arg Pointer to the interrupt descriptor
* @return 0 on success, non-zero on failure
*/
int esp_tee_intr_register(void *arg);
/**
* @brief Deregister an interrupt handler
*
* @param arg Pointer to the interrupt descriptor
* @return 0 on success, non-zero on failure
*/
int esp_tee_intr_deregister(void *arg);
#ifdef __cplusplus
}
#endif
#endif //__ASSEMBLER__

View File

@ -0,0 +1,100 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <string.h>
#include "esp_tee.h"
#include "tlsf.h"
/* multi_heap is a heap implementation for handling multiple
heterogeneous heaps in a single program.
Any contiguous block of memory can be registered as a heap.
*/
#ifdef __cplusplus
extern "C" {
#endif
struct multi_heap_info {
void *lock;
size_t free_bytes;
size_t minimum_free_bytes;
size_t pool_size;
void *heap_data;
};
typedef struct multi_heap_info heap_t;
/** @brief Opaque handle to a registered heap */
typedef struct multi_heap_info *multi_heap_handle_t;
/** @brief malloc() a buffer in a given heap
*
* Semantics are the same as standard malloc(), only the returned buffer will be allocated in the TEE heap.
*
* @param size Size of desired buffer.
*
* @return Pointer to new memory, or NULL if allocation fails.
*/
void *tee_heap_malloc(size_t size);
/** @brief calloc() a buffer in a given heap
*
* Semantics are the same as standard calloc(), only the returned buffer will be allocated in the TEE heap.
*
* @param size Size of desired buffer.
*
* @return Pointer to new memory, or NULL if allocation fails.
*/
void *tee_heap_calloc(size_t n, size_t size);
/**
* @brief allocate a chunk of memory with specific alignment
*
* @param heap Handle to a registered heap.
* @param size size in bytes of memory chunk
* @param alignment how the memory must be aligned
*
* @return pointer to the memory allocated, NULL on failure
*/
void *tee_heap_aligned_alloc(size_t size, size_t alignment);
/** @brief free() a buffer in a given heap.
*
* Semantics are the same as standard free(), only the argument 'p' must be NULL or have been allocated in the TEE heap.
*
* @param p NULL, or a pointer previously returned from multi_heap_malloc() or multi_heap_realloc() for the same heap.
*/
void tee_heap_free(void *p);
/** @brief Register a new heap for use
*
* This function initialises a heap at the specified address, and returns a handle for future heap operations.
*
* There is no equivalent function for deregistering a heap - if all blocks in the heap are free, you can immediately start using the memory for other purposes.
*
* @param start Start address of the memory to use for a new heap.
* @param size Size (in bytes) of the new heap.
*
* @return Handle of a new heap ready for use, or NULL if the heap region was too small to be initialised.
*/
int tee_heap_register(void *start, size_t size);
/**
* @brief Dump free and minimum free TEE heap information to stdout
*
*/
void tee_heap_dump_free_size(void);
/** @brief Dump TEE heap information to stdout
*
* For debugging purposes, this function dumps information about every block in the heap to stdout.
*
*/
void tee_heap_dump_info(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,243 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#define SRAM_IRAM_START (0x40800000)
#define SRAM_IRAM_TEE_ORG (SRAM_IRAM_START)
#define SRAM_DRAM_TEE_ORG (SRAM_IRAM_START)
#define SRAM_IRAM_ORG (SRAM_IRAM_TEE_ORG + CONFIG_SECURE_TEE_IRAM_SIZE + CONFIG_SECURE_TEE_DRAM_SIZE)
#define SRAM_DRAM_TEE_SIZE (CONFIG_SECURE_TEE_DRAM_SIZE - CONFIG_SECURE_TEE_STACK_SIZE - CONFIG_SECURE_TEE_INTR_STACK_SIZE)
#define SRAM_DRAM_TEE_END (SRAM_DRAM_TEE_ORG + CONFIG_SECURE_TEE_IRAM_SIZE + SRAM_DRAM_TEE_SIZE)
#define I_D_SRAM_TEE_SIZE (CONFIG_SECURE_TEE_IRAM_SIZE + SRAM_DRAM_TEE_SIZE)
/* TEE interrupt stack is placed at the end of the TEE DRAM segment.
* The top of the TEE stack is before the end of interrupt stack
* and the bottom of the stack is at _heap_end.
*/
#define SRAM_STACK_TEE_ORG (SRAM_DRAM_TEE_END)
#define SRAM_INTR_STACK_TEE_ORG (SRAM_DRAM_TEE_END + CONFIG_SECURE_TEE_STACK_SIZE)
#define FLASH_IROM_TEE_ORG (0x42000000)
#define FLASH_DROM_TEE_ORG (0x42000000)
#define I_D_FLASH_TEE_SIZE (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE)
/**
* These values are the same in every app binary for the same chip target.
*
* Values that may change when the app is rebuilt, or in a new ESP-IDF version,
* should be stored via esp_app_tee_config structure
*/
#if CONFIG_ESP_DEBUG_INCLUDE_OCD_STUB_BINS
PROVIDE ( esp_tee_app_config = SRAM_IRAM_ORG + 0x22e0 );
#else
PROVIDE ( esp_tee_app_config = SRAM_IRAM_ORG + 0x2e0 );
#endif
PROVIDE ( GDMA = 0x60080000 );
/* Default entry point: */
ENTRY(esp_tee_init);
MEMORY
{
/* IRAM Configuration */
iram_tee_seg (RX) : org = SRAM_IRAM_TEE_ORG, len = I_D_SRAM_TEE_SIZE
/* DRAM Configuration */
dram_tee_seg (RW) : org = SRAM_DRAM_TEE_ORG, len = I_D_SRAM_TEE_SIZE
/* TEE Stack Configuration */
stack_tee_seg (RW) : org = SRAM_STACK_TEE_ORG, len = CONFIG_SECURE_TEE_STACK_SIZE
/* TEE Interrupt Stack Configuration */
intr_stack_tee_seg (RW) : org = SRAM_INTR_STACK_TEE_ORG, len = CONFIG_SECURE_TEE_INTR_STACK_SIZE
/* TEE flash data section */
flash_data_seg (R) : org = FLASH_DROM_TEE_ORG + 0x20, len = I_D_FLASH_TEE_SIZE - 0x20
/* TEE flash text section */
flash_text_seg (RX): org = FLASH_IROM_TEE_ORG + 0x20, len = I_D_FLASH_TEE_SIZE - 0x20
}
SECTIONS
{
/**
* This section is required to skip .iram.tee_text area because iram_tee_seg and
* dram_tee_seg reflect the same address space on different buses.
*/
.dram.tee_dummy (NOLOAD):
{
. = ORIGIN(dram_tee_seg) + CONFIG_SECURE_TEE_IRAM_SIZE;
} > dram_tee_seg
.dram.tee.bss (NOLOAD) :
{
. = ALIGN (8);
_tee_dram_start = ABSOLUTE(.);
_tee_bss_start = ABSOLUTE(.);
*(.bss .bss.*)
*(.sbss .sbss.*)
. = ALIGN (8);
_tee_bss_end = ABSOLUTE(.);
} > dram_tee_seg
.dram.tee.data :
{
_data_start = ABSOLUTE(.);
*(.data .data.*)
*(.sdata .sdata.*)
*(.dram1 .dram1.*)
. = ALIGN(4);
_data_end = ABSOLUTE(.);
} > dram_tee_seg
.dram.tee.rodata :
{
_rodata_start = ABSOLUTE(.);
*libtee_flash_mgr.a:*(.rodata .srodata .rodata.* .srodata.*)
*libbootloader_support.a:bootloader_flash.*(.rodata .srodata .rodata.* .srodata.*)
*libmain.a:panic_helper_riscv.*(.rodata .srodata .rodata.* .srodata.*)
_rodata_end = ABSOLUTE(.);
_tee_dram_end = ABSOLUTE(.);
} > dram_tee_seg
.dram.tee.heap :
{
. = ALIGN (8);
_tee_heap_start = ABSOLUTE(.);
. = ORIGIN(dram_tee_seg) + LENGTH(dram_tee_seg);
_tee_heap_end = ABSOLUTE(.);
} > dram_tee_seg
.dram.tee.stack :
{
. = ALIGN (8);
_tee_stack_bottom = ABSOLUTE(.);
. = ORIGIN(stack_tee_seg) + LENGTH(stack_tee_seg);
. = ALIGN (8);
_tee_stack = ABSOLUTE(.);
} > stack_tee_seg
.dram.tee.intr_stack :
{
. = ALIGN (8);
_tee_intr_stack_bottom = ABSOLUTE(.);
. = ORIGIN(intr_stack_tee_seg) + LENGTH(intr_stack_tee_seg);
. = ALIGN (8);
_tee_intr_stack = ABSOLUTE(.);
} > intr_stack_tee_seg
.flash.rodata :
{
_tee_xip_data_start = ABSOLUTE(.);
*(.rodata_desc .rodata_desc.*) /* Should be the first. TEE App version info. DO NOT PUT ANYTHING BEFORE IT! */
*(.rodata .rodata.*)
*(.srodata .srodata.*)
_tee_xip_data_end = ABSOLUTE(.);
} > flash_data_seg
.flash.text_dummy (NOLOAD):
{
. = ALIGN(ALIGNOF(.flash.rodata));
/* Create an empty gap as big as .flash.rodata section */
. = . + SIZEOF(.flash.rodata);
/* Prepare the alignment of the section above. Few bytes (0x20) must be
* added for the mapping header. */
. = ALIGN(CONFIG_MMU_PAGE_SIZE) + 0x20;
} > flash_text_seg
.flash.text :
{
_tee_xip_text_start = ABSOLUTE(.);
/* Mbedtls for TEE */
*libmbedtls.a:*(.literal .text .literal.* .text.*)
*libmbedcrypto.a:*(.literal .text .literal.* .text.*)
/* TEE attestation module */
*libattestation.a:*(.literal .text .literal.* .text.*)
*json_generator.a:*(.literal .text .literal.* .text.*)
/* TEE test module */
*libtest_sec_srv.a:*(.literal .text .literal.* .text.*)
_tee_xip_text_end = ABSOLUTE(.);
_tee_xip_end = ABSOLUTE(.);
} > flash_text_seg
.iram.tee.text :
{
. = ALIGN(4);
/* Vectors go to start of IRAM */
ASSERT(ABSOLUTE(.) % 0x100 == 0, "vector address must be 256 byte aligned");
_tee_vec_start = ABSOLUTE(.);
KEEP(*(.exception_vectors_table.text));
KEEP(*(.exception_vectors.text));
. = ALIGN(4);
_invalid_pc_placeholder = ABSOLUTE(.);
_tee_vec_end = ABSOLUTE(.);
_tee_iram_start = ABSOLUTE(.);
*(.literal .text .iram1 .literal.* .text.* .iram1.*)
*(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
_tee_iram_end = ABSOLUTE(.);
} > iram_tee_seg
.riscv.attributes 0: { *(.riscv.attributes) }
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
.debug_pubtypes 0 : { *(.debug_pubtypes) }
/* DWARF 3 */
.debug_ranges 0 : { *(.debug_ranges) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* GNU DWARF 2 extensions */
.debug_gnu_pubnames 0 : { *(.debug_gnu_pubnames) }
.debug_gnu_pubtypes 0 : { *(.debug_gnu_pubtypes) }
/* DWARF 4 */
.debug_types 0 : { *(.debug_types) }
/* DWARF 5 */
.debug_addr 0 : { *(.debug_addr) }
.debug_line_str 0 : { *(.debug_line_str) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_macro 0 : { *(.debug_macro) }
.debug_names 0 : { *(.debug_names) }
.debug_rnglists 0 : { *(.debug_rnglists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.comment 0 : { *(.comment) }
.note.GNU-stack 0: { *(.note.GNU-stack) }
/**
* Discarding .rela.* sections results in the following mapping:
* .rela.text.* -> .text.*
* .rela.data.* -> .data.*
* And so forth...
*/
/DISCARD/ : { *(.rela.*) }
}
ASSERT ((_tee_iram_end < _tee_dram_start), "Error: TEE IRAM segment overflowed into the DRAM segment!");

View File

@ -0,0 +1,29 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
#
idf_build_get_property(target IDF_TARGET)
idf_build_get_property(sdkconfig_header SDKCONFIG_HEADER)
idf_build_get_property(config_dir CONFIG_DIR)
# -------------------------------- esp_tee.ld --------------------------------
set(ld_input "${CMAKE_CURRENT_LIST_DIR}/${target}/esp_tee.ld.in")
set(ld_output "${CMAKE_CURRENT_BINARY_DIR}/ld/esp_tee.ld")
target_linker_script(${COMPONENT_LIB} INTERFACE "${ld_output}")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/ld")
# Preprocess esp_tee.ld.in linker script to include configuration, becomes esp_tee.ld
add_custom_command(
OUTPUT ${ld_output}
COMMAND "${CMAKE_C_COMPILER}" -C -P -x c -E -o ${ld_output} -I ${config_dir}
-I "${CMAKE_CURRENT_LIST_DIR}" ${ld_input}
MAIN_DEPENDENCY ${ld_input}
DEPENDS ${sdkconfig_header}
COMMENT "Generating esp_tee.ld linker script..."
VERBATIM)
add_custom_target(esp_tee_ld DEPENDS ${ld_output})
add_dependencies(${COMPONENT_LIB} esp_tee_ld)

View File

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <assert.h>
#include "hal/aes_hal.h"
#include "soc/interrupts.h"
#include "esp_attr.h"
#include "esp_tee_intr.h"
#include "esp_tee_aes_intr.h"
volatile DRAM_ATTR bool intr_flag;
static IRAM_ATTR void esp_tee_aes_complete_isr(void *arg)
{
aes_hal_interrupt_clear();
intr_flag = false;
}
void esp_tee_aes_intr_alloc(void)
{
struct vector_desc_t aes_vd = { 0, NULL, NULL, NULL};
aes_vd.source = ETS_AES_INTR_SOURCE;
aes_vd.isr = esp_tee_aes_complete_isr;
esp_tee_intr_register((void *)&aes_vd);
intr_flag = false;
}

View File

@ -0,0 +1,126 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <assert.h>
#include "hal/apm_ll.h"
#include "hal/apm_hal.h"
#include "esp_tee.h"
#include "esp_tee_apm_intr.h"
const char *esp_tee_apm_excp_mid_to_str(uint8_t mid)
{
char *excp_mid = NULL;
switch (mid) {
case APM_LL_MASTER_HPCORE:
excp_mid = "HPCORE";
break;
case APM_LL_MASTER_LPCORE:
excp_mid = "LPCORE";
break;
case APM_LL_MASTER_REGDMA:
excp_mid = "REGDMA";
break;
case APM_LL_MASTER_SDIOSLV:
excp_mid = "SDIOSLV";
break;
case APM_LL_MASTER_MODEM:
excp_mid = "MODEM";
break;
case APM_LL_MASTER_MEM_MONITOR:
excp_mid = "MEM_MONITOR";
break;
case APM_LL_MASTER_TRACE:
excp_mid = "TRACE";
break;
case APM_LL_MASTER_GDMA_SPI2:
excp_mid = "GDMA_SPI2";
break;
case APM_LL_MASTER_GDMA_UHCI0:
excp_mid = "GDMA_UHCI0";
break;
case APM_LL_MASTER_GDMA_I2S0:
excp_mid = "GDMA_I2S0";
break;
case APM_LL_MASTER_GDMA_AES:
excp_mid = "GDMA_AES";
break;
case APM_LL_MASTER_GDMA_SHA:
excp_mid = "GDMA_SHA";
break;
case APM_LL_MASTER_GDMA_ADC:
excp_mid = "GDMA_ADC";
break;
case APM_LL_MASTER_GDMA_PARLIO:
excp_mid = "GDMA_PARLIO";
break;
default:
excp_mid = "Unknown";
break;
}
return excp_mid;
}
const char *esp_tee_apm_excp_type_to_str(uint8_t type)
{
char *excp_type = "Unknown exception";
if (type & 0x01) {
excp_type = "Authority exception";
} else if (type & 0x02) {
excp_type = "Space exception";
}
return excp_type;
}
const char *esp_tee_apm_excp_ctrl_to_str(apm_ll_apm_ctrl_t apm_ctrl)
{
char *excp_ctrl = NULL;
switch (apm_ctrl) {
case LP_APM0_CTRL:
excp_ctrl = "LP_APM0";
break;
case HP_APM_CTRL:
excp_ctrl = "HP_APM";
break;
case LP_APM_CTRL:
excp_ctrl = "LP_APM";
break;
default:
excp_ctrl = "Unknown";
break;
}
return excp_ctrl;
}
const char *esp_tee_apm_excp_mode_to_str(uint8_t mode)
{
char *excp_mode = NULL;
switch (mode) {
case APM_LL_SECURE_MODE_TEE:
case APM_LL_SECURE_MODE_REE0:
excp_mode = "REE0";
break;
case APM_LL_SECURE_MODE_REE1:
excp_mode = "REE1";
break;
case APM_LL_SECURE_MODE_REE2:
excp_mode = "REE2";
break;
default:
excp_mode = "Unknown";
break;
}
return excp_mode;
}

View File

@ -0,0 +1,306 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_log.h"
#include "esp_tee.h"
#include "esp_tee_intr.h"
#include "hal/apm_hal.h"
#include "soc/soc.h"
#include "soc/spi_mem_reg.h"
#include "soc/efuse_reg.h"
extern void tee_apm_violation_isr(void *arg);
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)"
#endif
#define LP_APM_EFUSE_REG_START \
(EFUSE_RD_KEY0_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 0x20))
#define LP_APM_EFUSE_REG_END \
(EFUSE_RD_KEY1_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 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
#define LP_APM_EFUSE_REG_END EFUSE_RD_SYS_PART2_DATA0_REG
#else
#define LP_APM_EFUSE_REG_END LP_APM_EFUSE_REG_START
#endif
#endif
/*----------------HP APM Configuration-----------------------*/
/* HP APM Range and Filter configuration. */
apm_ctrl_region_config_data_t hp_apm_pms_data[] = {
/* Region0: LP memory region access. (RWX)*/
{
.regn_num = 0,
.regn_start_addr = SOC_RTC_IRAM_LOW,
.regn_end_addr = SOC_RTC_IRAM_HIGH,
.regn_pms = 0x7,
.filter_enable = 1,
},
/* Peripherals region access.(RW)*/
/* Region1: Next 2 entries for MMU peripheral protection. */
{
.regn_num = 1,
.regn_start_addr = SOC_PERIPHERAL_LOW,
.regn_end_addr = (DR_REG_INTERRUPT_MATRIX_BASE - 0x4),
.regn_pms = 0x6,
.filter_enable = 1,
},
/* Region4: Interrupt Matrix protection. */
{
.regn_num = 2,
.regn_start_addr = DR_REG_ATOMIC_BASE,
.regn_end_addr = (DR_REG_AES_BASE - 0x4),
.regn_pms = 0x6,
.filter_enable = 1,
},
/* Region5: Peripherals region access. (RW)*/
{
.regn_num = 3,
.regn_start_addr = DR_REG_RSA_BASE,
.regn_end_addr = (DR_REG_TEE_BASE - 0x4),
.regn_pms = 0x6,
.filter_enable = 1,
},
/* Region6: Peripherals region access. (RW)*/
{
.regn_num = 4,
.regn_start_addr = DR_REG_MISC_BASE,
.regn_end_addr = (DR_REG_PMU_BASE - 0x04),
.regn_pms = 0x6,
.filter_enable = 1,
},
/* Region7: Peripherals region access. (RW)*/
{
.regn_num = 5,
.regn_start_addr = DR_REG_OPT_DEBUG_BASE,
.regn_end_addr = 0x600D0000, //PWDET_CONF_REG,
.regn_pms = 0x6,
.filter_enable = 1,
},
/* Region8: IRAM region access. (RW)*/
{
.regn_num = 6,
.regn_start_addr = SOC_NS_IRAM_START,
.regn_end_addr = SOC_IRAM_HIGH,
.regn_pms = 0x6,
.filter_enable = 1,
},
};
/* NOTE: Following are the master IDs for setting the security mode and access through APM:
* +---------+-------------+
* | Bit | Source |
* +---------+-------------+
* | 0 | HP CPU |
* | 1 | LP CPU |
* | 2 | reserved |
* | 3 | SDIO_SLV |
* | 4 | reserved |
* | 5 | MEM_MONITOR |
* | 6 | TRACE |
* | 7~15 | reserved |
* | 16 | SPI2 |
* | 17 | Dummy-1 |
* | 18 | UHCI |
* | 19 | I2S |
* | 20 | Dummy-4 |
* | 21 | Dummy-5 |
* | 22 | AES |
* | 23 | SHA |
* | 24 | ADC |
* | 25 | PARLIO |
* | 26~31 | Dummy-10~15 |
* +---------+-------------+
*/
/* HP APM configuration for the Masters. */
apm_ctrl_secure_mode_config_t hp_apm_sec_mode_data = {
.apm_ctrl = HP_APM_CTRL,
.apm_m_cnt = HP_APM_MAX_ACCESS_PATH,
.sec_mode = APM_LL_SECURE_MODE_REE0,
/* Except crypto DMA (AES and SHA) and HP CPU, all other masters will be switch to REE0 mode - refer above note */
.master_ids = 0xFF3FFFFE,
.pms_data = hp_apm_pms_data,
};
/*----------------HP APM TEE Configuration-----------------------*/
/* HP APM Range and Filter configuration. */
apm_ctrl_region_config_data_t hp_apm_pms_data_tee[] = {
/* Region9: TEE All access enable. (RW)*/
{
.regn_num = 7,
.regn_start_addr = 0x0,
.regn_end_addr = ~0x0,
.regn_pms = 0x7,
.filter_enable = 1,
},
};
apm_ctrl_secure_mode_config_t hp_apm_sec_mode_data_tee = {
.apm_ctrl = HP_APM_CTRL,
.apm_m_cnt = HP_APM_MAX_ACCESS_PATH,
.sec_mode = APM_LL_SECURE_MODE_TEE,
/* Crypto DMA will be switch to TEE mode - refer above note*/
.master_ids = 0xC00001,
.pms_data = hp_apm_pms_data_tee,
};
/*----------------LP APM0 Configuration-----------------------*/
/* LP APM0 Range and Filter configuration. */
apm_ctrl_region_config_data_t lp_apm0_pms_data[] = {
/* Region0: LP memory. (RWX) */
{
.regn_num = 0,
.regn_start_addr = SOC_RTC_IRAM_LOW,
.regn_end_addr = SOC_RTC_IRAM_HIGH,
.regn_pms = 0x7,
.filter_enable = 1,
},
};
/* LP APM0 configuration for the Masters. */
apm_ctrl_secure_mode_config_t lp_apm0_sec_mode_data = {
.apm_ctrl = LP_APM0_CTRL,
.apm_m_cnt = LP_APM0_MAX_ACCESS_PATH,
.sec_mode = APM_LL_SECURE_MODE_REE0,
.master_ids = 0x2, //Only LP_CPU here.
.pms_data = lp_apm0_pms_data,
};
/*----------------LP APM Configuration-----------------------*/
/* LP APM Range and Filter configuration. */
apm_ctrl_region_config_data_t lp_apm_pms_data[] = {
/* Region0: LP memory. (RWX) */
{
.regn_num = 0,
.regn_start_addr = SOC_RTC_IRAM_LOW,
.regn_end_addr = SOC_RTC_IRAM_HIGH,
.regn_pms = 0x7,
.filter_enable = 1,
},
/* Region1: LP Peri 1. (RW) */
{
.regn_num = 1,
.regn_start_addr = DR_REG_PMU_BASE,
.regn_end_addr = (LP_APM_EFUSE_REG_START - 0x04),
.regn_pms = 0x6,
.filter_enable = 1,
},
/* Region2: LP Peri 2. (RW) */
{
.regn_num = 2,
.regn_start_addr = LP_APM_EFUSE_REG_END,
.regn_end_addr = (DR_REG_TRACE_BASE - 0x04),
.regn_pms = 0x6,
.filter_enable = 1,
},
};
/* LP APM configuration for the Masters. */
apm_ctrl_secure_mode_config_t lp_apm_sec_mode_data = {
.apm_ctrl = LP_APM_CTRL,
.apm_m_cnt = LP_APM_MAX_ACCESS_PATH,
.sec_mode = APM_LL_SECURE_MODE_REE0,
.master_ids = 0x3, //Only HP_CPU & LP_CPU here.
.pms_data = lp_apm_pms_data,
};
/* LP APM Range and Filter configuration. */
apm_ctrl_region_config_data_t lp_apm_pms_data_tee[] = {
/* Region3: TEE All access enable. (RW)*/
{
.regn_num = 3,
.regn_start_addr = 0x0,
.regn_end_addr = ~0x0,
.regn_pms = 0x7,
.filter_enable = 1,
},
};
apm_ctrl_secure_mode_config_t lp_apm_sec_mode_data_tee = {
.apm_ctrl = LP_APM_CTRL,
.apm_m_cnt = LP_APM_MAX_ACCESS_PATH,
.sec_mode = APM_LL_SECURE_MODE_TEE,
/* HP CPU and LP CPU will be switch to TEE mode */
.master_ids = 0x00003,
.pms_data = lp_apm_pms_data_tee,
};
/*---------------- TEE APM API-----------------------*/
void esp_tee_apm_int_enable(apm_ctrl_secure_mode_config_t *sec_mode_data)
{
for (int i = 0; i < sec_mode_data->apm_m_cnt; i++) {
apm_ctrl_path_t *apm_excp_data = calloc(1, sizeof(apm_ctrl_path_t));
assert(apm_excp_data != NULL);
apm_excp_data->apm_ctrl = sec_mode_data->apm_ctrl;
apm_excp_data->apm_m_path = i;
int intr_src_num = apm_hal_apm_ctrl_get_int_src_num(apm_excp_data);
struct vector_desc_t apm_vd = {0};
apm_vd.source = intr_src_num;
apm_vd.isr = tee_apm_violation_isr;
apm_vd.arg = (void *)apm_excp_data;
/* Register interrupt handler with TEE. */
esp_tee_intr_register((void *)&apm_vd);
/* Enable APM Ctrl intewrrupt for access path(M[0:n]) */
apm_hal_apm_ctrl_exception_clear(apm_excp_data);
apm_hal_apm_ctrl_interrupt_enable(apm_excp_data, true);
}
}
void esp_tee_configure_apm_protection(void)
{
/* Disable all control filter first to have full access of address rage. */
apm_hal_apm_ctrl_filter_enable_all(false);
/* LP APM0 configuration. */
lp_apm0_sec_mode_data.regn_count = sizeof(lp_apm0_pms_data) / sizeof(apm_ctrl_region_config_data_t);
apm_hal_apm_ctrl_master_sec_mode_config(&lp_apm0_sec_mode_data);
/* LP APM0 interrupt configuration. */
esp_tee_apm_int_enable(&lp_apm0_sec_mode_data);
ESP_LOGD(TAG, "[REE0] LP_APM0 configured");
/* LP APM TEE configuration. */
lp_apm_sec_mode_data_tee.regn_count = sizeof(lp_apm_pms_data_tee) / sizeof(apm_ctrl_region_config_data_t);
apm_hal_apm_ctrl_master_sec_mode_config(&lp_apm_sec_mode_data_tee);
ESP_LOGD(TAG, "[TEE] LP_APM configured");
/* LP APM configuration. */
lp_apm_sec_mode_data.regn_count = sizeof(lp_apm_pms_data) / sizeof(apm_ctrl_region_config_data_t);
apm_hal_apm_ctrl_master_sec_mode_config(&lp_apm_sec_mode_data);
/* LP APM interrupt configuration. */
esp_tee_apm_int_enable(&lp_apm_sec_mode_data);
ESP_LOGD(TAG, "[REE0] LP_APM configured");
/* HP APM TEE configuration. */
hp_apm_sec_mode_data_tee.regn_count = sizeof(hp_apm_pms_data_tee) / sizeof(apm_ctrl_region_config_data_t);
apm_hal_apm_ctrl_master_sec_mode_config(&hp_apm_sec_mode_data_tee);
ESP_LOGD(TAG, "[TEE] HP_APM configured");
/* HP APM configuration. */
hp_apm_sec_mode_data.regn_count = sizeof(hp_apm_pms_data) / sizeof(apm_ctrl_region_config_data_t);
apm_hal_apm_ctrl_master_sec_mode_config(&hp_apm_sec_mode_data);
/* HP APM interrupt configuration. */
esp_tee_apm_int_enable(&hp_apm_sec_mode_data);
ESP_LOGD(TAG, "[REE0] HP_APM configured");
}

View File

@ -0,0 +1,179 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "sdkconfig.h"
#include "soc/soc.h"
#include "soc/ext_mem_defs.h"
#include "soc/lp_analog_peri_reg.h"
#include "soc/lp_wdt_reg.h"
#include "riscv/csr.h"
#include "esp_cpu.h"
#include "esp_fault.h"
#include "esp_tee.h"
#define CONDITIONAL_NONE 0x0
#define CONDITIONAL_R PMP_R
#define CONDITIONAL_RX PMP_R | PMP_X
#define CONDITIONAL_RW PMP_R | PMP_W
#define CONDITIONAL_RWX PMP_R | PMP_W | PMP_X
#define IS_PMA_ENTRY_UNLOCKED(ENTRY) \
((RV_READ_CSR((CSR_PMACFG0) + (ENTRY)) & PMA_L) == 0)
#define SWD_PROT_REG_START (LP_WDT_SWD_CONFIG_REG)
#define SWD_PROT_REG_END (LP_WDT_INT_CLR_REG)
#define BOD_PROT_REG_START (DR_REG_LP_ANALOG_PERI_BASE)
#define BOD_PROT_REG_END (DR_REG_LP_ANALOG_PERI_BASE + 0x40)
static void esp_tee_configure_invalid_regions(void)
{
const unsigned PMA_NONE = PMA_L | PMA_EN;
__attribute__((unused)) const unsigned PMA_RW = PMA_L | PMA_EN | PMA_R | PMA_W;
__attribute__((unused)) const unsigned PMA_RX = PMA_L | PMA_EN | PMA_R | PMA_X;
__attribute__((unused)) const unsigned PMA_RWX = PMA_L | PMA_EN | PMA_R | PMA_W | PMA_X;
// 1. Gap at bottom of address space
PMA_RESET_AND_ENTRY_SET_TOR(0, SOC_CPU_SUBSYSTEM_LOW, PMA_NONE);
// 2. Gap between CPU subsystem region & IROM
PMA_RESET_AND_ENTRY_SET_TOR(1, SOC_CPU_SUBSYSTEM_HIGH, PMA_NONE);
PMA_RESET_AND_ENTRY_SET_TOR(2, SOC_IROM_MASK_LOW, PMA_TOR | PMA_NONE);
// 3. Gap between ROM & RAM
PMA_RESET_AND_ENTRY_SET_TOR(3, SOC_DROM_MASK_HIGH, PMA_NONE);
PMA_RESET_AND_ENTRY_SET_TOR(4, SOC_IRAM_LOW, PMA_TOR | PMA_NONE);
// 4. Gap between DRAM and I_Cache
PMA_RESET_AND_ENTRY_SET_TOR(5, SOC_IRAM_HIGH, PMA_NONE);
PMA_RESET_AND_ENTRY_SET_TOR(6, SOC_IROM_LOW, PMA_TOR | PMA_NONE);
// 5. Gap between D_Cache & LP_RAM
PMA_RESET_AND_ENTRY_SET_TOR(7, SOC_DROM_HIGH, PMA_NONE);
PMA_RESET_AND_ENTRY_SET_TOR(8, SOC_RTC_IRAM_LOW, PMA_TOR | PMA_NONE);
// 6. Gap between LP memory & peripheral addresses
PMA_RESET_AND_ENTRY_SET_TOR(9, SOC_RTC_IRAM_HIGH, PMA_NONE);
PMA_RESET_AND_ENTRY_SET_TOR(10, SOC_PERIPHERAL_LOW, PMA_TOR | PMA_NONE);
// 7. End of address space
PMA_RESET_AND_ENTRY_SET_TOR(11, SOC_PERIPHERAL_HIGH, PMA_NONE);
PMA_RESET_AND_ENTRY_SET_TOR(12, UINT32_MAX, PMA_TOR | PMA_NONE);
// 8. Using PMA to configure the TEE text and data section access attribute. */
assert(IS_PMA_ENTRY_UNLOCKED(13));
assert(IS_PMA_ENTRY_UNLOCKED(14));
assert(IS_PMA_ENTRY_UNLOCKED(15));
PMA_RESET_AND_ENTRY_SET_TOR(13, SOC_S_IRAM_START, PMA_NONE);
PMA_RESET_AND_ENTRY_SET_TOR(14, SOC_S_IRAM_END, PMA_TOR | PMA_RX);
PMA_RESET_AND_ENTRY_SET_TOR(15, SOC_S_DRAM_END, PMA_TOR | PMA_RW);
}
void esp_tee_configure_region_protection(void)
{
/* Notes on implementation:
*
* 1) Note: ESP32-C6 CPU doesn't support overlapping PMP regions
*
* 2) ESP32-C6 supports 16 PMA regions so we use this feature to block all the invalid address ranges
*
* 3) We use combination of NAPOT (Naturally Aligned Power Of Two) and TOR (top of range)
* entries to map all the valid address space, bottom to top. This leaves us with some extra PMP entries
* which can be used to provide more granular access
*
* 4) Entries are grouped in order with some static asserts to try and verify everything is
* correct.
*/
const unsigned NONE = PMP_L;
const unsigned R = PMP_L | PMP_R;
const unsigned RW = PMP_L | PMP_R | PMP_W;
const unsigned RX = PMP_L | PMP_R | PMP_X;
const unsigned RWX = PMP_L | PMP_R | PMP_W | PMP_X;
//
// Configure all the invalid address regions using PMA
//
// We lock the PMA entries since they mark the invalid regions and is applicable to both the privilege modes
//
esp_tee_configure_invalid_regions();
//
// Configure all the valid address regions using PMP
//
// We are not locking the PMP entries so these permission configurations do not apply to M mode
//
// 1.1 I/D-ROM
PMP_ENTRY_SET(0, SOC_IROM_MASK_LOW, NONE);
PMP_ENTRY_SET(1, SOC_IROM_MASK_HIGH, PMP_TOR | RX);
_Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid I/D-ROM region");
/* TODO: Check whether changes are required here */
if (esp_cpu_dbgr_is_attached()) {
// Anti-FI check that cpu is really in ocd mode
ESP_FAULT_ASSERT(esp_cpu_dbgr_is_attached());
// 2. IRAM and DRAM
const uint32_t pmpaddr2 = PMPADDR_NAPOT(SOC_IRAM_LOW, SOC_IRAM_HIGH);
PMP_ENTRY_SET(2, pmpaddr2, PMP_NAPOT | RWX);
_Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid RAM region");
} else {
// 2. IRAM and DRAM
// Splitting the REE SRAM region into IRAM and DRAM
PMP_ENTRY_SET(2, (int)SOC_NS_IRAM_START, NONE);
PMP_ENTRY_SET(3, (int)esp_tee_app_config.ns_iram_end, PMP_TOR | RX);
PMP_ENTRY_SET(4, SOC_DRAM_HIGH, PMP_TOR | RW);
}
const uint32_t s_irom_resv_end = SOC_IROM_LOW + CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE;
const uint32_t ns_irom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)esp_tee_app_config.ns_irom_end);
const uint32_t ns_drom_resv_end = ALIGN_UP_TO_MMU_PAGE_SIZE((uint32_t)esp_tee_app_config.ns_drom_end);
const uint32_t ns_drom_mmap_end = (uint32_t)(SOC_S_MMU_MMAP_RESV_START_VADDR);
// 4. I_Cache / D_Cache (flash) - REE
PMP_ENTRY_CFG_RESET(5);
PMP_ENTRY_CFG_RESET(6);
PMP_ENTRY_CFG_RESET(7);
PMP_ENTRY_CFG_RESET(8);
PMP_ENTRY_SET(5, s_irom_resv_end, NONE);
PMP_ENTRY_SET(6, ns_irom_resv_end, PMP_TOR | RX);
PMP_ENTRY_SET(7, ns_drom_resv_end, PMP_TOR | R);
PMP_ENTRY_SET(8, ns_drom_mmap_end, PMP_TOR | RX);
// 5. LP memory
/* Reset the corresponding PMP config because PMP_ENTRY_SET only sets the given bits
* Bootloader might have given extra permissions and those won't be cleared
*/
const uint32_t pmpaddr9 = PMPADDR_NAPOT(SOC_RTC_IRAM_LOW, SOC_RTC_IRAM_HIGH);
PMP_ENTRY_SET(9, pmpaddr9, PMP_NAPOT | RWX);
_Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region");
// 6. Super WDT and Brownout detector
PMP_ENTRY_SET(10, SWD_PROT_REG_START, CONDITIONAL_NONE);
PMP_ENTRY_SET(11, SWD_PROT_REG_END, PMP_TOR | CONDITIONAL_NONE);
_Static_assert(SWD_PROT_REG_START < SWD_PROT_REG_END, "Invalid peripheral region");
/* NOTE: Due to the limited number of PMP entries, NAPOT address matching had to be
* utilized here. To meet the requirements of NAPOT, the adjacent 20 bytes have also
* been protected along with the intended region.
*/
const uint32_t pmpaddr12 = PMPADDR_NAPOT(BOD_PROT_REG_START, BOD_PROT_REG_END);
PMP_ENTRY_SET(12, pmpaddr12, PMP_NAPOT | CONDITIONAL_NONE);
_Static_assert(BOD_PROT_REG_START < BOD_PROT_REG_END, "Invalid peripheral region");
// 7. Peripheral addresses
const uint32_t pmpaddr13 = PMPADDR_NAPOT(SOC_PERIPHERAL_LOW, SOC_PERIPHERAL_HIGH);
PMP_ENTRY_SET(13, pmpaddr13, PMP_NAPOT | RW);
_Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region");
// 8. User-mode interrupt controller registers
const uint32_t pmpaddr14 = PMPADDR_NAPOT(DR_REG_PLIC_UX_BASE, DR_REG_CLINT_M_BASE);
PMP_ENTRY_SET(14, pmpaddr14, PMP_NAPOT | RW);
_Static_assert(DR_REG_PLIC_UX_BASE < DR_REG_CLINT_M_BASE, "Invalid User mode PLIC region");
const uint32_t pmpaddr15 = PMPADDR_NAPOT(DR_REG_CLINT_U_BASE, DR_REG_CLINT_U_END);
PMP_ENTRY_SET(15, pmpaddr15, PMP_NAPOT | RW);
_Static_assert(DR_REG_CLINT_U_BASE < DR_REG_CLINT_U_END, "Invalid User mode CLINT region");
}

View File

@ -0,0 +1,105 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include <stdint.h>
#include "riscv/rv_utils.h"
#include "riscv/encoding.h"
#include "esp_cpu.h"
#include "esp_log.h"
#include "hal/apm_hal.h"
#include "esp_tee.h"
#include "esp_tee_intr.h"
#define _m2u_switch(arg0, arg1) \
({ \
register uintptr_t ra asm("ra") = (uintptr_t)(arg0); \
register uintptr_t a1 asm("a1") = (uintptr_t)(arg1); \
asm volatile("ecall" : :"r"(ra), "r"(a1) : ); \
})
#define SET_BIT(t, n) (t |= (1UL << (n)))
#define CLR_BIT(t, n) (t &= ~(1UL << (n)))
static const char *TAG = "esp_tee_secure_sys_cfg";
extern uint32_t _vector_table;
void esp_tee_soc_secure_sys_init(void)
{
ESP_LOGI(TAG, "Current privilege level - %d", esp_cpu_get_curr_privilege_level());
/* NOTE: M/U-mode PLIC Special Configuration Register
* Bit 0: Use the external PLIC registers (legacy) from the SoC (default)
* Bit 1: Use the internal PLIC registers as per the new SoC address map
*/
REG_SET_BIT(PLIC_MXINT_CONF_REG, BIT(0));
REG_SET_BIT(PLIC_UXINT_CONF_REG, BIT(0));
/* Setting the M-mode vector table */
rv_utils_set_mtvec((uint32_t)&_vector_table);
/* Disable global interrupts */
RV_CLEAR_CSR(mstatus, MSTATUS_UIE);
RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
/* Clear all interrupts */
RV_WRITE_CSR(mie, 0x00);
RV_WRITE_CSR(uie, 0x00);
/* All interrupts except the TEE secure interrupt are delegated to the U-mode */
uint32_t mideleg_val = UINT32_MAX;
CLR_BIT(mideleg_val, TEE_SECURE_INUM);
RV_WRITE_CSR(mideleg, mideleg_val);
/* TODO: IDF-8958
* The values for the secure interrupt number and priority and
* the interrupt priority threshold (for both M and U mode) need
* to be investigated further
*/
#ifdef SOC_CPU_HAS_FLEXIBLE_INTC
/* TODO: Currently, we do not allow interrupts to be set up with a priority greater than 7, see intr_alloc.c */
esprv_int_set_priority(TEE_SECURE_INUM, 7);
esprv_int_set_type(TEE_SECURE_INUM, ESP_CPU_INTR_TYPE_LEVEL);
esprv_int_set_threshold(1);
esprv_int_enable(BIT(TEE_SECURE_INUM));
#endif
ESP_LOGD(TAG, "Initial interrupt config -");
ESP_LOGD(TAG, "mideleg: 0x%08x", RV_READ_CSR(mideleg));
ESP_LOGD(TAG, "mie: 0x%08x | uie: 0x%08x", RV_READ_CSR(mie), RV_READ_CSR(uie));
ESP_LOGD(TAG, "mstatus: 0x%08x | ustatus: 0x%08x", RV_READ_CSR(mstatus), RV_READ_CSR(ustatus));
ESP_LOGD(TAG, "[PLIC] MX: 0x%08x | UX: 0x%08x", REG_READ(PLIC_MXINT_ENABLE_REG), REG_READ(PLIC_UXINT_ENABLE_REG));
/* PMP, PMA and APM configuration to isolate the resources between TEE and REE. */
esp_tee_configure_region_protection();
esp_tee_configure_apm_protection();
/* Protect secure interrupt sources */
esp_tee_protect_intr_src(ETS_LP_APM_M0_INTR_SOURCE); // LP_APM_M0
esp_tee_protect_intr_src(ETS_LP_APM_M1_INTR_SOURCE); // LP_APM_M1
esp_tee_protect_intr_src(ETS_HP_APM_M0_INTR_SOURCE); // HP_APM_M0
esp_tee_protect_intr_src(ETS_HP_APM_M1_INTR_SOURCE); // HP_APM_M1
esp_tee_protect_intr_src(ETS_HP_APM_M2_INTR_SOURCE); // HP_APM_M2
esp_tee_protect_intr_src(ETS_HP_APM_M3_INTR_SOURCE); // HP_APM_M3
esp_tee_protect_intr_src(ETS_LP_APM0_INTR_SOURCE); // LP_APM0
esp_tee_protect_intr_src(ETS_EFUSE_INTR_SOURCE); // eFuse
esp_tee_protect_intr_src(ETS_AES_INTR_SOURCE); // AES
esp_tee_protect_intr_src(ETS_SHA_INTR_SOURCE); // SHA
}
IRAM_ATTR inline void esp_tee_switch_to_ree(uint32_t ree_entry_addr)
{
/* Switch HP_CPU to REE0 mode. */
apm_tee_hal_set_master_secure_mode(HP_APM_CTRL, APM_LL_MASTER_HPCORE, APM_LL_SECURE_MODE_REE0);
/* 2nd argument is used as magic value to detect very first M2U switch */
/* TBD: clean this up and use proper temporary register instead of a1 */
/* Switch to non-secure world and launch App. */
_m2u_switch(ree_entry_addr, ESP_TEE_M2U_SWITCH_MAGIC << 12);
}

View File

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void esp_tee_aes_intr_alloc(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "hal/apm_hal.h"
#ifdef __cplusplus
extern "C" {
#endif
const char *esp_tee_apm_excp_mid_to_str(uint8_t mid);
const char *esp_tee_apm_excp_type_to_str(uint8_t type);
const char *esp_tee_apm_excp_ctrl_to_str(apm_ll_apm_ctrl_t apm_ctrl);
const char *esp_tee_apm_excp_mode_to_str(uint8_t mode);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,128 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <assert.h>
#include "riscv/csr.h"
#include "riscv/interrupt.h"
#include "soc/interrupt_reg.h"
#include "soc/plic_reg.h"
#include "esp_attr.h"
#include "esp_tee.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SET_BIT(t, n) (t |= (1UL << (n)))
#define CLR_BIT(t, n) (t &= ~(1UL << (n)))
FORCE_INLINE_ATTR void rv_utils_tee_intr_global_enable(void)
{
/*
* Set the the U-mode previous enable global interrupts state
*
* NOTE: The TICK interrupt is setup before this service call and thus,
* it occurs in the return path of this call.
*
* Before entering the U-mode interrupt handler routine, USTATUS:UIE is
* cleared to disable U-mode interrupts temporarily.
*
* While exiting the above routine, URET is executed, setting USTATUS:UIE
* to the value of USTATUS:UPIE. However, since no interrupts were enabled
* previously, USTATUS:UPIE and thus, USTATUS:UIE is cleared.
*
* The service call completes and returns to U-mode with USTATUS:UIE disabled,
* preventing any further interrupts in U-mode.
*
*/
RV_SET_CSR(ustatus, USTATUS_UPIE);
/* Enabling the global M-mode and U-mode interrupts */
RV_SET_CSR(ustatus, USTATUS_UIE);
RV_SET_CSR(mstatus, MSTATUS_UIE);
RV_SET_CSR(mstatus, MSTATUS_MIE);
}
FORCE_INLINE_ATTR void rv_utils_tee_intr_enable(uint32_t intr_mask)
{
unsigned old_xstatus;
// Machine mode
// Disable all interrupts to make updating of the interrupt mask atomic.
old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
REG_SET_BIT(PLIC_MXINT_ENABLE_REG, intr_mask);
RV_SET_CSR(mie, intr_mask);
RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE);
// User mode
// Disable all interrupts to make updating of the interrupt mask atomic.
old_xstatus = RV_CLEAR_CSR(ustatus, USTATUS_UIE);
REG_SET_BIT(PLIC_UXINT_ENABLE_REG, intr_mask);
RV_SET_CSR(uie, intr_mask);
RV_SET_CSR(ustatus, old_xstatus & USTATUS_UIE);
}
FORCE_INLINE_ATTR void rv_utils_tee_intr_disable(uint32_t intr_mask)
{
unsigned old_xstatus;
// Machine mode
// Disable all interrupts to make updating of the interrupt mask atomic.
old_xstatus = RV_CLEAR_CSR(mstatus, MSTATUS_MIE);
REG_CLR_BIT(PLIC_MXINT_ENABLE_REG, intr_mask);
RV_CLEAR_CSR(mie, intr_mask);
RV_SET_CSR(mstatus, old_xstatus & MSTATUS_MIE);
// User mode
// Disable all interrupts to make updating of the interrupt mask atomic.
old_xstatus = RV_CLEAR_CSR(ustatus, USTATUS_UIE);
REG_CLR_BIT(PLIC_UXINT_ENABLE_REG, intr_mask);
RV_CLEAR_CSR(uie, intr_mask);
RV_SET_CSR(ustatus, old_xstatus & USTATUS_UIE);
}
FORCE_INLINE_ATTR void rv_utils_tee_intr_set_type(int intr_num, enum intr_type type)
{
assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
if (type == INTR_TYPE_LEVEL) {
REG_CLR_BIT(PLIC_MXINT_TYPE_REG, BIT(intr_num));
REG_CLR_BIT(PLIC_UXINT_TYPE_REG, BIT(intr_num));
} else {
REG_SET_BIT(PLIC_MXINT_TYPE_REG, BIT(intr_num));
REG_SET_BIT(PLIC_UXINT_TYPE_REG, BIT(intr_num));
}
}
FORCE_INLINE_ATTR void rv_utils_tee_intr_set_priority(int rv_int_num, int priority)
{
assert(rv_int_num >= 0 && rv_int_num < SOC_CPU_INTR_NUM);
REG_WRITE(PLIC_MXINT_PRI_REG(rv_int_num), priority);
REG_WRITE(PLIC_UXINT_PRI_REG(rv_int_num), priority);
}
FORCE_INLINE_ATTR void rv_utils_tee_intr_set_threshold(int priority_threshold)
{
REG_WRITE(PLIC_MXINT_THRESH_REG, priority_threshold);
REG_WRITE(PLIC_UXINT_THRESH_REG, priority_threshold);
}
FORCE_INLINE_ATTR void rv_utils_tee_intr_edge_ack(int intr_num)
{
assert(intr_num >= 0 && intr_num < SOC_CPU_INTR_NUM);
REG_SET_BIT(PLIC_MXINT_CLEAR_REG, intr_num);
REG_SET_BIT(PLIC_UXINT_CLEAR_REG, intr_num);
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,11 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
components/esp_tee/test_apps/tee_cli_app:
disable:
- if: IDF_TARGET not in ["esp32c6"]
reason: only supported with esp32c6 for now
components/esp_tee/test_apps/tee_test_fw:
disable:
- if: IDF_TARGET not in ["esp32c6"]
reason: only supported with esp32c6 for now

View File

@ -0,0 +1,16 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
# (Not part of the boilerplate)
# This example uses extra components for the following -
# 1. common functions such as Wi-Fi and Ethernet connection.
# 2. managing TEE OTA updates
# 3. dumping TEE attestation info
# 4. TEE Secure storage
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_ota_ops
$ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_attestation
$ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_sec_storage)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tee_cli)

View File

@ -0,0 +1,292 @@
| Supported Targets | ESP32-C6 |
| ----------------- | -------- |
# TEE CLI Application: Secure Services Demonstration
## Example Usage
### Hardware Required
This example can be executed on any development board with a Espressif SOC chip supporting the TEE framework (see Supported Targets table above).
### Configure the project
- Before the project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
- 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 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`.
- Snippet for burning the secure storage key in eFuse is given below.
```shell
# Programming user 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
```
### Build and Flash
- Build the project and flash it to the board, then run the monitor tool to view the serial output:
```shell
# Replace PORT with the name of the serial port to use
idf.py -p <PORT> flash monitor
```
(To exit the serial monitor, type `Ctrl-]`.)
Refer the _Getting Started_ guide for full steps to configure and use ESP-IDF to build projects.
### Example Output
Enter the `help` command get a full list of all available commands provided by the example.
```log
I (627) app_start: Starting scheduler on CPU0
I (632) main_task: Started on CPU0
I (632) main_task: Calling app_main()
I (632) example: ESP-TEE: Secure services demonstration
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 (702) main_task: Returned from app_main()
esp32c6> help
wifi_connect [--timeout=<t>] <ssid> [<pass>]
Join WiFi AP as a station
--timeout=<t> Connection timeout, ms
<ssid> SSID of AP
<pass> PSK of AP
tee_ota <url>
Initiate TEE app OTA
<url> URL for fetching the update
user_ota <url>
Initiate User app OTA
<url> URL for fetching the update
tee_att_info
Dump the TEE-generated entity attestation token
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_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
<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
<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
<ciphertext> Ciphertext to be decrypted
<tag> AES-GCM authentication tag
help
Print the list of registered commands
```
## Secure Services
### 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.
<details>
<summary><b>Sample output:</b> <i>tee_att_info</i></summary>
```log
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"}}'
```
</details>
### 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
- 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> get_msg_sha256 "hello world"
I (3984) tee_sec_stg: Message digest (SHA256) -
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
esp32c6> tee_sec_stg_sign 7 b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
I (5384) tee_sec_stg: Generated signature -
944684f6ddcf4c268ac6b65e34ccb8d95bd2849567a87867101bc1f09208f0885d935d7b3ba9d46014f28e4c7c988d68c775431fcb2cb2d4ca5c6862db771088
I (6404) tee_sec_stg: Public key (Uncompressed) -
04a515bf1c43766cc34980dd6934b9ff54fd3d5d70fe7a694b1fea7a0bbc74434d008c7c3117ce0a5216ffdb2b807f2668cce9c973d524c038ab47b4344064dbbf
I (6444) tee_sec_stg: Signature verified successfully!
```
</details>
<details>
<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
I (3084) tee_sec_stg: Ciphertext -
58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1
I (3594) tee_sec_stg: Tag -
caeedb43e08dc3b4e35a58b2412908cc
esp32c6> tee_sec_stg_decrypt 8 58054310a96d48c2dccdf2e34005aa63b40817723d3ec3d597ab362efea084c1 caeedb43e08dc3b4e35a58b2412908cc
I (4314) tee_sec_stg: Decrypted plaintext -
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
```
</details>
### Over-the-Air updates
- The TEE/REE OTA service demonstrates the following workflow:
1. Connect to a WiFi network (`wifi_connect` command)
2. Initiate the TEE/REE OTA update, fetching the _new_ application image from the given URL (`tee_ota` and `user_ota` commands)
<details>
<summary><b>Sample output:</b> <i>wifi_connect</i></summary>
```log
esp32c6> wifi_connect myssid mypassword
I (498) connect: Connecting to 'myssid'
I (498) pp: pp rom version: 5b8dcfa
I (508) net80211: net80211 rom version: 5b8dcfa
I (518) wifi_init: rx ba win: 6
I (518) wifi_init: tcpip mbox: 32
I (518) wifi_init: udp mbox: 6
I (518) wifi_init: tcp mbox: 6
I (528) wifi_init: tcp tx win: 5760
I (528) wifi_init: tcp rx win: 5760
I (538) wifi_init: tcp mss: 1440
I (538) wifi_init: WiFi IRAM OP enabled
I (538) wifi_init: WiFi RX IRAM OP enabled
I (548) phy_init: phy_version 290,81efd96,May 8 2024,10:42:13
W (598) wifi:(bf)761:0x600a7cac:0x01b4b4b0
W (598) wifi:(agc)0x600a7128:0xd2173800, min.avgNF:0xce->0xd2(dB), RCalCount:0x173, min.RRssi:0x800(-128.00)
W (608) wifi:(TB)WDEV_PWR_TB_MCS0:19
W (608) wifi:(TB)WDEV_PWR_TB_MCS1:19
W (608) wifi:(TB)WDEV_PWR_TB_MCS2:19
W (608) wifi:(TB)WDEV_PWR_TB_MCS3:19
W (618) wifi:(TB)WDEV_PWR_TB_MCS4:19
W (618) wifi:(TB)WDEV_PWR_TB_MCS5:19
W (618) wifi:(TB)WDEV_PWR_TB_MCS6:18
W (628) wifi:(TB)WDEV_PWR_TB_MCS7:18
W (628) wifi:(TB)WDEV_PWR_TB_MCS8:17
W (628) wifi:(TB)WDEV_PWR_TB_MCS9:15
W (648) wifi:(TB)WDEV_PWR_TB_MCS10:15
W (648) wifi:(TB)WDEV_PWR_TB_MCS11:15
W (1328) wifi:<ba-add>idx:0, ifx:0, tid:0, TAHI:0x1008fe0, TALO:0xc7e45510, (ssn:0, win:64, cur_ssn:0), CONF:0xc0000005
I (6358) esp_netif_handlers: sta ip: 192.168.1.30, mask: 255.255.255.0, gw: 192.168.1.1
I (6358) event_handler: got ip:192.168.1.30
I (6368) connect: Connected
```
</details>
<details>
<summary><b>Sample output:</b> <i>tee_ota</i></summary>
```log
esp32c6> tee_ota https://192.168.1.1:4443/esp_tee/esp_tee.bin
I (5884) ota_with_tee: Starting TEE OTA...
esp32c6> I (1066394) esp-x509-crt-bundle: Certificate validated
I (7424) ota_with_tee: esp_tee_ota_begin succeeded
I (7904) ota_with_tee: Connection closed
I (7904) ota_with_tee: esp_tee_ota_write succeeded
I (7904) ota_with_tee: Total binary data written: 118784
I (8064) ota_with_tee: esp_tee_ota_end succeeded
I (8064) ota_with_tee: Prepare to restart system!
```
</details>
<details>
<summary><b>Sample output:</b> <i>user_ota</i></summary>
```log
esp32c6> user_ota https://192.168.1.1:4443/tee_cli.bin
I (2388) ota_with_tee: Starting User OTA task...
I (2388) ota_with_tee: Attempting to download update from https://192.168.1.1:4443/tee_cli.bin
I (2438) esp-x509-crt-bundle: Certificate validated
esp32c6> I (62888) esp_https_ota: Starting OTA...
I (2888) esp_https_ota: Writing to partition subtype 17 at offset 0x1f0000
I (37338) esp_image: segment 0: paddr=001f0020 vaddr=420e0020 size=2ecc0h (191680) map
I (37368) esp_image: segment 1: paddr=0021ece8 vaddr=40811000 size=01330h ( 4912)
I (37378) esp_image: segment 2: paddr=00220020 vaddr=42020020 size=b88b0h (755888) map
I (37518) esp_image: segment 3: paddr=002d88d8 vaddr=40812330 size=14488h ( 83080)
I (37538) esp_image: segment 4: paddr=002ecd68 vaddr=408267c0 size=032c0h ( 12992)
I (37538) esp_image: segment 5: paddr=002f0030 vaddr=00000000 size=0ffa0h ( 65440)
I (37568) esp_image: Verifying image signature...
I (37568) secure_boot_v2: Take trusted digest key(s) from eFuse block(s)
I (37568) secure_boot_v2: #0 app key digest == #0 trusted key digest
I (37578) secure_boot_v2: Verifying with RSA-PSS...
I (37638) secure_boot_v2_rsa: Signature verified successfully!
I (37648) esp_image: segment 0: paddr=001f0020 vaddr=420e0020 size=2ecc0h (191680) map
I (37678) esp_image: segment 1: paddr=0021ece8 vaddr=40811000 size=01330h ( 4912)
I (37678) esp_image: segment 2: paddr=00220020 vaddr=42020020 size=b88b0h (755888) map
I (37828) esp_image: segment 3: paddr=002d88d8 vaddr=40812330 size=14488h ( 83080)
I (37848) esp_image: segment 4: paddr=002ecd68 vaddr=408267c0 size=032c0h ( 12992)
I (37848) esp_image: segment 5: paddr=002f0030 vaddr=00000000 size=0ffa0h ( 65440)
I (37868) esp_image: Verifying image signature...
I (37878) secure_boot_v2: Take trusted digest key(s) from eFuse block(s)
I (37878) secure_boot_v2: #0 app key digest == #0 trusted key digest
I (37888) secure_boot_v2: Verifying with RSA-PSS...
I (37948) secure_boot_v2_rsa: Signature verified successfully!
I (37998) ota_with_tee: OTA Succeed, Rebooting...
```
</details>
#### Local HTTPS server for hosting OTA images
- The script `https_server.py` is a helper script for the OTA service. Executing it with the `python https_server.py` command starts a local HTTPS server with the test certificates from the `test_certs` directory and serves files from the `build` directory that holds the generated binaries for the TEE and REE. Following is the script help -
```
python https_server.py --help
usage: https_server.py [-h] [--certfile CERTFILE] [--keyfile KEYFILE] [--port PORT] [--path PATH]
Start a local HTTPS server.
options:
-h, --help show this help message and exit
--certfile CERTFILE Path to the SSL certificate file (default: test_certs/server_cert.pem)
--keyfile KEYFILE Path to the SSL key file (default: test_certs/server_key.pem)
--port PORT Port number to bind the server to (default: 4443)
--path PATH Path to the directory to serve files from (default: build directory)
E.g., python https_server.py --certfile test_certs/server_cert.pem --keyfile test_certs/server_key.pem --port 8070 --path build
```

View File

@ -0,0 +1,86 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import argparse
import http.server
import os
import socket
import ssl
import sys
# python https_server.py --help
# usage: https_server.py [-h] [--certfile CERTFILE] [--keyfile KEYFILE] [--port PORT] [--path PATH]
#
# Start a local HTTPS server.
#
# options:
# -h, --help show this help message and exit
# --certfile CERTFILE Path to the SSL certificate file (default: test_certs/server_cert.pem)
# --keyfile KEYFILE Path to the SSL key file (default: test_certs/server_key.pem)
# --port PORT Port number to bind the server to (default: 4443)
# --path PATH Path to the directory to serve files from (default: current directory)
#
# E.g., python https_server.py --certfile test_certs/server_cert.pem --keyfile test_certs/server_key.pem --port 8070 --path build
# Get the local machine's network IP address
def get_local_addr() -> str:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
local_addr = ''
try:
s.connect(('8.8.8.8', 80)) # Connect to a remote server to get the network IP
local_addr = s.getsockname()[0]
finally:
s.close()
return local_addr
def main() -> None:
# Parse command-line arguments
parser = argparse.ArgumentParser(description='Start a local HTTPS server')
parser.add_argument('--certfile', default='test_certs/server_cert.pem', help='Path to the SSL certificate file (default: test_certs/server_cert.pem)')
parser.add_argument('--keyfile', default='test_certs/server_key.pem', help='Path to the SSL key file (default: test_certs/server_key.pem)')
parser.add_argument('--port', type=int, default=4443, help='Port number to bind the server to (default: 4443)')
parser.add_argument('--path', default='build', help='Path to the directory to serve files from (default: build directory)')
args = parser.parse_args()
# Resolve the absolute paths for the cert and key files before changing the working directory
certfile_path = os.path.abspath(args.certfile)
keyfile_path = os.path.abspath(args.keyfile)
# Change to the specified directory
os.chdir(args.path)
# Get the network IP address
local_ip = get_local_addr()
# Define the server address and port
server_address = (local_ip, args.port)
# Create a simple request handler
Handler = http.server.SimpleHTTPRequestHandler
# Create the HTTP server
httpd = http.server.HTTPServer(server_address, Handler)
# Create an SSL context
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile=certfile_path, keyfile=keyfile_path)
# Wrap the server's socket with the SSL context
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
# Print the server address for clients to use
print(f'Server is running at: https://{local_ip}:{args.port}')
print('Press [Ctrl+C] to shut down the server')
# Start the server
try:
httpd.serve_forever()
except KeyboardInterrupt:
print('\nShutting down the server...')
httpd.shutdown()
sys.exit(0)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,6 @@
idf_component_register(SRCS "tee_srv_att.c"
"tee_srv_ota.c"
"tee_srv_sec_str.c"
"tee_cmd_wifi.c"
"app_main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,15 @@
menu "Example Configuration"
config EXAMPLE_SKIP_COMMON_NAME_CHECK
bool "Skip server certificate CN fieldcheck"
default n
help
This allows you to skip the validation of OTA server certificate CN field.
config EXAMPLE_OTA_RECV_TIMEOUT
int "OTA Receive Timeout"
default 5000
help
Maximum time for reception
endmenu

View File

@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <inttypes.h>
#include <errno.h>
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_console.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "example_tee_srv.h"
#define PROMPT_STR CONFIG_IDF_TARGET
static const char *TAG = "example";
static void setup_console(void)
{
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
repl_config.prompt = PROMPT_STR ">";
repl_config.max_cmdline_length = 128;
/* Register help command */
ESP_ERROR_CHECK(esp_console_register_help_command());
/* Register custom commands */
register_cmd_wifi();
register_srv_tee_ota();
register_srv_user_ota();
register_srv_attestation();
register_cmd_msg_sha256();
register_srv_sec_stg_gen_key();
register_srv_sec_stg_sign();
register_srv_sec_stg_encrypt();
register_srv_sec_stg_decrypt();
#if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) || defined(CONFIG_ESP_CONSOLE_UART_CUSTOM)
esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
#elif defined(CONFIG_ESP_CONSOLE_USB_CDC)
esp_console_dev_usb_cdc_config_t hw_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&hw_config, &repl_config, &repl));
#elif defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG)
esp_console_dev_usb_serial_jtag_config_t hw_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl));
#else
#error Unsupported console type
#endif
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}
void app_main(void)
{
ESP_LOGI(TAG, "ESP-TEE: Secure services demonstration");
// Initialize NVS
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
setup_console();
}

View File

@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void register_cmd_wifi(void);
void register_srv_attestation(void);
void register_srv_tee_ota(void);
void register_srv_user_ota(void);
void register_cmd_msg_sha256(void);
void register_srv_sec_stg_gen_key(void);
void register_srv_sec_stg_sign(void);
void register_srv_sec_stg_encrypt(void);
void register_srv_sec_stg_decrypt(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,134 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_log.h"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_netif.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "example_tee_srv.h"
#define JOIN_TIMEOUT_MS (15000)
static EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
esp_wifi_connect();
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(__func__, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
}
}
static void initialise_wifi(void)
{
esp_log_level_set("wifi", ESP_LOG_WARN);
static bool initialized = false;
if (initialized) {
return;
}
ESP_ERROR_CHECK(esp_netif_init());
wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap();
assert(ap_netif);
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
assert(sta_netif);
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
ESP_ERROR_CHECK(esp_wifi_start());
initialized = true;
}
static bool wifi_join(const char *ssid, const char *pass, int timeout_ms)
{
initialise_wifi();
wifi_config_t wifi_config = { 0 };
strlcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
if (pass) {
strlcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
esp_wifi_connect();
int bits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
pdFALSE, pdTRUE, timeout_ms / portTICK_PERIOD_MS);
return (bits & CONNECTED_BIT) != 0;
}
/** Arguments used by 'join' function */
static struct {
struct arg_int *timeout;
struct arg_str *ssid;
struct arg_str *password;
struct arg_end *end;
} join_args;
static int connect(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &join_args);
if (nerrors != 0) {
arg_print_errors(stderr, join_args.end, argv[0]);
return ESP_ERR_INVALID_ARG;
}
ESP_LOGI(__func__, "Connecting to '%s'",
join_args.ssid->sval[0]);
/* set default value*/
if (join_args.timeout->count == 0) {
join_args.timeout->ival[0] = JOIN_TIMEOUT_MS;
}
bool connected = wifi_join(join_args.ssid->sval[0],
join_args.password->sval[0],
join_args.timeout->ival[0]);
if (!connected) {
ESP_LOGW(__func__, "Connection timed out");
return ESP_ERR_TIMEOUT;
}
ESP_LOGI(__func__, "Connected");
return 0;
}
void register_cmd_wifi(void)
{
join_args.timeout = arg_int0(NULL, "timeout", "<t>", "Connection timeout, ms");
join_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
join_args.password = arg_str0(NULL, NULL, "<pass>", "PSK of AP");
join_args.end = arg_end(2);
const esp_console_cmd_t join_cmd = {
.command = "wifi_connect",
.help = "Join WiFi AP as a station",
.hint = NULL,
.func = &connect,
.argtable = &join_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&join_cmd));
}

View File

@ -0,0 +1,57 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_tee_attestation.h"
#include "example_tee_srv.h"
static const char *TAG = "tee_attest";
#define ESP_ATT_TK_BUF_SIZE (1792)
#define ESP_ATT_TK_PSA_CERT_REF ("0716053550477-10100")
static uint8_t token_buf[ESP_ATT_TK_BUF_SIZE] = {0};
static int tee_dump_att_token(int argc, char **argv)
{
if (argc != 1) {
ESP_LOGE(TAG, "Incorrect number of arguments given!");
return ESP_ERR_INVALID_ARG;
}
uint32_t token_len = 0;
esp_err_t err = esp_tee_att_generate_token(0xA1B2C3D4, 0x0FACADE0, (const char *)ESP_ATT_TK_PSA_CERT_REF,
token_buf, sizeof(token_buf), &token_len);
if (err != ESP_OK) {
return err;
}
ESP_LOGI(TAG, "Attestation token - Length: %lu", token_len);
ESP_LOGI(TAG, "Attestation token - Data:\n'%.*s'", (int)token_len, token_buf);
return ESP_OK;
}
void register_srv_attestation(void)
{
const esp_console_cmd_t tee_att_cmd = {
.command = "tee_att_info",
.help = "Dump the TEE-generated entity attestation token",
.hint = NULL,
.func = &tee_dump_att_token,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&tee_att_cmd));
}

View File

@ -0,0 +1,300 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <inttypes.h>
#include <errno.h>
#include "esp_system.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_app_format.h"
#include "esp_image_format.h"
#include "esp_flash_partitions.h"
#include "esp_partition.h"
#include "esp_crt_bundle.h"
#include "esp_http_client.h"
#include "esp_https_ota.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "esp_tee_ota_ops.h"
#include "example_tee_srv.h"
#define BUF_SIZE 256
static const char *TAG = "ota_with_tee";
/* Semaphore governing the TEE and User app OTA processes; only one should be active at a time */
static SemaphoreHandle_t s_ota_mgmt;
static void http_cleanup(esp_http_client_handle_t client)
{
esp_http_client_close(client);
esp_http_client_cleanup(client);
}
static void task_fatal_error(void)
{
ESP_LOGE(TAG, "Exiting task due to fatal error...");
(void)vTaskDelete(NULL);
}
static esp_err_t setup_task_conn(esp_http_client_config_t *config, const char *url)
{
if (config == NULL || url == NULL) {
ESP_LOGE(TAG, "Invalid arguments!");
return ESP_ERR_INVALID_ARG;
}
/* Wait for the semaphore to be free for the taking */
if (xSemaphoreTake(s_ota_mgmt, pdMS_TO_TICKS(1000)) != pdTRUE) {
ESP_LOGE(TAG, "Other OTA already in progress!");
return ESP_FAIL;
}
config->url = url;
config->timeout_ms = CONFIG_EXAMPLE_OTA_RECV_TIMEOUT;
config->keep_alive_enable = true;
config->crt_bundle_attach = esp_crt_bundle_attach;
#ifdef CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK
config->skip_cert_common_name_check = true;
#endif
return ESP_OK;
}
static void tee_ota_task(void *pvParameter)
{
ESP_LOGI(TAG, "Starting TEE OTA...");
esp_http_client_config_t config = {};
if (setup_task_conn(&config, (const char *)pvParameter) != ESP_OK) {
ESP_LOGE(TAG, "Failed to setup OTA task");
task_fatal_error();
}
esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
ESP_LOGE(TAG, "Failed to initialise HTTP connection");
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
}
esp_err_t err = esp_http_client_open(client, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
esp_http_client_cleanup(client);
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
}
esp_http_client_fetch_headers(client);
uint32_t curr_write_offset = 0;
bool image_header_was_checked = false; /* deal with all receive packet */
char ota_write_data[BUF_SIZE + 1] = {0}; /* an ota data write buffer ready to write to the flash */
while (1) {
int data_read = esp_http_client_read(client, ota_write_data, BUF_SIZE);
if (data_read < 0) {
ESP_LOGE(TAG, "Error: SSL data read error");
http_cleanup(client);
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
} else if (data_read > 0) {
if (image_header_was_checked == false) {
/* TODO: TEE image header is missing the `esp_app_desc_t` configuration structure */
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)) {
esp_image_header_t img_hdr;
memcpy(&img_hdr, ota_write_data, sizeof(esp_image_header_t));
if (img_hdr.chip_id != CONFIG_IDF_FIRMWARE_CHIP_ID) {
ESP_LOGE(TAG, "Mismatch chip id, expected %d, found %d", CONFIG_IDF_FIRMWARE_CHIP_ID, img_hdr.chip_id);
http_cleanup(client);
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
}
image_header_was_checked = true;
err = esp_tee_ota_begin();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_tee_ota_begin failed (%s)", esp_err_to_name(err));
http_cleanup(client);
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
}
ESP_LOGI(TAG, "esp_tee_ota_begin succeeded");
} else {
ESP_LOGE(TAG, "received package is not fit len");
http_cleanup(client);
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
}
}
err = esp_tee_ota_write(curr_write_offset, (const void *)ota_write_data, data_read);
if (err != ESP_OK) {
http_cleanup(client);
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
}
curr_write_offset += data_read;
memset(ota_write_data, 0x00, sizeof(ota_write_data));
ESP_LOGD(TAG, "Written image length: %lu", curr_write_offset);
} else if (data_read == 0) {
/*
* As esp_http_client_read never returns negative error code, we rely on
* `errno` to check for underlying transport connectivity closure if any
*/
if (errno == ECONNRESET || errno == ENOTCONN) {
ESP_LOGE(TAG, "Connection closed, errno = %d", errno);
break;
}
if (esp_http_client_is_complete_data_received(client) == true) {
ESP_LOGI(TAG, "Connection closed");
break;
}
}
}
ESP_LOGI(TAG, "esp_tee_ota_write succeeded");
ESP_LOGI(TAG, "Total binary data written: %lu", curr_write_offset);
if (esp_http_client_is_complete_data_received(client) != true) {
ESP_LOGE(TAG, "Error in receiving complete file");
http_cleanup(client);
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
}
err = esp_tee_ota_end();
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
http_cleanup(client);
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
}
ESP_LOGI(TAG, "esp_tee_ota_end succeeded");
/* Ending connection, freeing the semaphore */
http_cleanup(client);
xSemaphoreGive(s_ota_mgmt);
ESP_LOGI(TAG, "Prepare to restart system!");
esp_restart();
return;
}
static void user_ota_task(void *pvParameter)
{
ESP_LOGI(TAG, "Starting User OTA task...");
esp_http_client_config_t config = {};
if (setup_task_conn(&config, (const char *)pvParameter) != 0) {
ESP_LOGE(TAG, "Failed to setup OTA task");
task_fatal_error();
}
esp_https_ota_config_t ota_config = {
.http_config = &config,
};
ESP_LOGI(TAG, "Attempting to download update from %s", config.url);
esp_err_t ret = esp_https_ota(&ota_config);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "OTA Succeed, Rebooting...");
esp_restart();
} else {
ESP_LOGE(TAG, "Firmware upgrade failed");
}
xSemaphoreGive(s_ota_mgmt);
task_fatal_error();
}
static void init_ota_sem(void)
{
static bool first_call = true;
if (first_call) {
s_ota_mgmt = xSemaphoreCreateBinary();
xSemaphoreGive(s_ota_mgmt);
first_call = false;
}
}
static int create_ota_task(const char *url, const char *task_name, void (*ota_task)(void *))
{
init_ota_sem();
if (xTaskCreate(ota_task, task_name, configMINIMAL_STACK_SIZE * 3, (void *)url, 5, NULL) != pdPASS) {
ESP_LOGE(TAG, "Task creation failed for %s", task_name);
return ESP_FAIL;
}
return ESP_OK;
}
static struct {
struct arg_str *url;
struct arg_end *end;
} tee_ota_args;
static struct {
struct arg_str *url;
struct arg_end *end;
} user_ota_args;
static int tee_app_ota_task(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &tee_ota_args);
if (nerrors != 0) {
arg_print_errors(stderr, tee_ota_args.end, argv[0]);
return ESP_ERR_INVALID_ARG;
}
return create_ota_task(tee_ota_args.url->sval[0], "tee_ota_task", &tee_ota_task);
}
static int user_app_ota_task(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &user_ota_args);
if (nerrors != 0) {
arg_print_errors(stderr, user_ota_args.end, argv[0]);
return ESP_ERR_INVALID_ARG;
}
return create_ota_task(user_ota_args.url->sval[0], "user_ota_task", &user_ota_task);
}
void register_srv_tee_ota(void)
{
tee_ota_args.url = arg_str1(NULL, NULL, "<url>", "URL for fetching the update");
tee_ota_args.end = arg_end(2);
const esp_console_cmd_t tee_ota_cmd = {
.command = "tee_ota",
.help = "Initiate TEE app OTA",
.hint = NULL,
.func = &tee_app_ota_task,
.argtable = &tee_ota_args,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&tee_ota_cmd));
}
void register_srv_user_ota(void)
{
user_ota_args.url = arg_str1(NULL, NULL, "<url>", "URL for fetching the update");
user_ota_args.end = arg_end(2);
const esp_console_cmd_t user_ota_cmd = {
.command = "user_ota",
.help = "Initiate User app OTA",
.hint = NULL,
.func = &user_app_ota_task,
.argtable = &user_ota_args,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&user_ota_cmd));
}

View File

@ -0,0 +1,547 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "mbedtls/ecp.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/sha256.h"
#include "esp_tee_sec_storage.h"
#include "example_tee_srv.h"
#define SHA256_DIGEST_SZ (32)
#define ECDSA_SECP256R1_KEY_LEN (32)
#define AES256_GCM_TAG_LEN (16)
#define MAX_AES_PLAINTEXT_LEN (256)
static const char *TAG = "tee_sec_stg";
static esp_err_t hexstr_to_hexbuf(const char *hexstr, size_t hexstr_len, void *hexbuf, size_t hexbuf_sz)
{
if (hexstr == NULL || hexbuf == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (hexstr_len == 0 || hexbuf_sz < (hexstr_len / 2)) {
return ESP_ERR_INVALID_SIZE;
}
const uint8_t *const_bytes = (const uint8_t *)hexstr;
uint8_t *bytes = (uint8_t *)hexbuf;
for (size_t i = 0; i < hexstr_len; i += 2) {
uint8_t upper_nibble = (const_bytes[i] >= 'a') ? (const_bytes[i] - 'a' + 10) : (const_bytes[i] - '0');
uint8_t lower_nibble = (const_bytes[i + 1] >= 'a') ? (const_bytes[i + 1] - 'a' + 10) : (const_bytes[i + 1] - '0');
bytes[i / 2] = (upper_nibble << 4) | (lower_nibble);
}
return ESP_OK;
}
static esp_err_t hexbuf_to_hexstr(const void *hexbuf, size_t hexbuf_sz, char *hexstr, size_t hexstr_len)
{
if (hexbuf == NULL || hexstr == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (hexbuf_sz == 0 || hexstr_len < (hexbuf_sz * 2 + 1)) {
return ESP_ERR_INVALID_SIZE;
}
const uint8_t *bytes = (const uint8_t *)hexbuf;
for (size_t i = 0; i < hexbuf_sz; i++) {
for (int shift = 0; shift < 2; shift++) {
uint8_t nibble = (bytes[i] >> (shift ? 0 : 4)) & 0x0F;
if (nibble < 10) {
hexstr[i * 2 + shift] = '0' + nibble;
} else {
hexstr[i * 2 + shift] = 'a' + nibble - 10;
}
}
}
hexstr[hexbuf_sz * 2] = '\0';
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)
{
if (pubkey == NULL || digest == NULL || sign == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (len == 0) {
return ESP_ERR_INVALID_SIZE;
}
esp_err_t err = ESP_FAIL;
mbedtls_mpi r, s;
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
mbedtls_ecdsa_context ecdsa_context;
mbedtls_ecdsa_init(&ecdsa_context);
int ret = mbedtls_ecp_group_load(&ecdsa_context.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1);
if (ret != 0) {
goto exit;
}
size_t plen = mbedtls_mpi_size(&ecdsa_context.MBEDTLS_PRIVATE(grp).P);
ret = mbedtls_mpi_read_binary(&r, sign->sign_r, plen);
if (ret != 0) {
goto exit;
}
ret = mbedtls_mpi_read_binary(&s, sign->sign_s, plen);
if (ret != 0) {
goto exit;
}
ret = mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), pubkey->pub_x, plen);
if (ret != 0) {
goto exit;
}
ret = mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), pubkey->pub_y, plen);
if (ret != 0) {
goto exit;
}
ret = mbedtls_mpi_lset(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 1);
if (ret != 0) {
goto exit;
}
ret = mbedtls_ecdsa_verify(&ecdsa_context.MBEDTLS_PRIVATE(grp), digest, len, &ecdsa_context.MBEDTLS_PRIVATE(Q), &r, &s);
if (ret != 0) {
goto exit;
}
err = ESP_OK;
exit:
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);
mbedtls_ecdsa_free(&ecdsa_context);
return err;
}
static struct {
struct arg_str *msg;
struct arg_end *end;
} cmd_get_msg_sha256_args;
static int get_msg_sha256(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &cmd_get_msg_sha256_args);
if (nerrors != 0) {
arg_print_errors(stderr, cmd_get_msg_sha256_args.end, argv[0]);
return ESP_ERR_INVALID_ARG;
}
const char *msg = (const char *)cmd_get_msg_sha256_args.msg->sval[0];
uint8_t msg_digest[SHA256_DIGEST_SZ];
int ret = mbedtls_sha256((const unsigned char *)msg, strlen(msg), msg_digest, false);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to calculate message hash!");
return ESP_FAIL;
}
size_t digest_hexstr_len = SHA256_DIGEST_SZ * 2 + 1;
char *digest_hexstr = calloc(digest_hexstr_len, sizeof(char));
if (digest_hexstr == NULL) {
return ESP_ERR_NO_MEM;
}
hexbuf_to_hexstr(msg_digest, sizeof(msg_digest), digest_hexstr, digest_hexstr_len);
ESP_LOGI(TAG, "Message digest (SHA256) -\n%s", digest_hexstr);
free(digest_hexstr);
return ESP_OK;
}
void register_cmd_msg_sha256(void)
{
cmd_get_msg_sha256_args.msg = arg_str1(NULL, NULL, "\"<msg>\"", "Message for SHA256 digest calculation");
cmd_get_msg_sha256_args.end = arg_end(2);
const esp_console_cmd_t cmd_get_msg_sha256 = {
.command = "get_msg_sha256",
.help = "Get the SHA256 digest for the given message",
.hint = NULL,
.func = &get_msg_sha256,
.argtable = &cmd_get_msg_sha256_args,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_get_msg_sha256));
}
static struct {
struct arg_int *slot_id;
struct arg_int *key_type;
struct arg_end *end;
} tee_sec_stg_gen_key_args;
static int tee_sec_stg_gen_key(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&tee_sec_stg_gen_key_args);
if (nerrors != 0) {
arg_print_errors(stderr, tee_sec_stg_gen_key_args.end, argv[0]);
return 1;
}
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) {
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);
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);
exit:
return err;
}
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.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",
.hint = NULL,
.func = &tee_sec_stg_gen_key,
.argtable = &tee_sec_stg_gen_key_args,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&tee_sec_stg));
}
static struct {
struct arg_int *slot_id;
struct arg_str *msg_sha256;
struct arg_end *end;
} tee_sec_stg_sign_args;
static int tee_sec_stg_sign(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &tee_sec_stg_sign_args);
if (nerrors != 0) {
arg_print_errors(stderr, tee_sec_stg_sign_args.end, argv[0]);
return ESP_ERR_INVALID_ARG;
}
const char *msg_sha256 = (const char *)tee_sec_stg_sign_args.msg_sha256->sval[0];
size_t msg_sha256_len = strnlen(msg_sha256, SHA256_DIGEST_SZ * 2 + 1);
if (msg_sha256_len != SHA256_DIGEST_SZ * 2) {
ESP_LOGE(TAG, "Invalid input digest!");
return ESP_ERR_INVALID_ARG;
}
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];
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, digest, sizeof(digest), &sign);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to generate signature!");
goto exit;
}
size_t sign_hexstr_len = (ECDSA_SECP256R1_KEY_LEN * 2) * 2 + 1;
char *sign_hexstr = calloc(sign_hexstr_len, sizeof(char));
if (sign_hexstr == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
hexbuf_to_hexstr(&sign, sizeof(sign), sign_hexstr, sign_hexstr_len);
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, &pubkey);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to fetch public-key!");
goto exit;
}
size_t pubkey_hexstr_len = (ECDSA_SECP256R1_KEY_LEN * 2) * 2 + 1;
char *pubkey_hexstr = calloc(pubkey_hexstr_len, sizeof(char));
if (pubkey_hexstr == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
hexbuf_to_hexstr(&pubkey, sizeof(pubkey), pubkey_hexstr, pubkey_hexstr_len);
ESP_LOGI(TAG, "Public key (Uncompressed) -\n04%s", pubkey_hexstr);
free(pubkey_hexstr);
err = verify_ecdsa_secp256r1_sign(digest, sizeof(digest), &pubkey, &sign);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to verify signature!");
goto exit;
}
ESP_LOGI(TAG, "Signature verified successfully!");
exit:
return err;
}
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.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",
.hint = NULL,
.func = &tee_sec_stg_sign,
.argtable = &tee_sec_stg_sign_args,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&tee_sec_stg));
}
static struct {
struct arg_int *slot_id;
struct arg_str *plaintext;
struct arg_end *end;
} tee_sec_stg_encrypt_args;
static int tee_sec_stg_encrypt(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&tee_sec_stg_encrypt_args);
if (nerrors != 0) {
arg_print_errors(stderr, tee_sec_stg_encrypt_args.end, argv[0]);
return 1;
}
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 *plaintext = tee_sec_stg_encrypt_args.plaintext->sval[0];
size_t plaintext_len = strnlen(plaintext, MAX_AES_PLAINTEXT_LEN);
if (plaintext_len == MAX_AES_PLAINTEXT_LEN && plaintext[MAX_AES_PLAINTEXT_LEN] != '\0') {
ESP_LOGE(TAG, "Plaintext too long (max - %d bytes)", MAX_AES_PLAINTEXT_LEN);
err = ESP_ERR_INVALID_ARG;
goto exit;
}
if (plaintext_len % 2 != 0) {
ESP_LOGE(TAG, "Invalid plaintext - should be a hex string");
err = ESP_ERR_INVALID_ARG;
goto exit;
}
size_t plaintext_buf_len = plaintext_len / 2;
uint8_t *plaintext_buf = calloc(plaintext_buf_len, sizeof(uint8_t));
if (plaintext_buf == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
hexstr_to_hexbuf(plaintext, plaintext_len, plaintext_buf, plaintext_buf_len);
uint8_t *ciphertext_buf = calloc(plaintext_buf_len, sizeof(uint8_t));
if (ciphertext_buf == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
goto exit;
}
err = esp_tee_sec_storage_encrypt(slot_id, (uint8_t *)plaintext_buf, plaintext_buf_len,
NULL, 0, tag, sizeof(tag), ciphertext_buf);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt data: %s", esp_err_to_name(err));
goto exit;
}
char *ciphertext = calloc(plaintext_len + 1, sizeof(char));
if (ciphertext == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
hexbuf_to_hexstr(ciphertext_buf, plaintext_buf_len, ciphertext, plaintext_len + 1);
char tag_hexstr[AES256_GCM_TAG_LEN * 2 + 1];
hexbuf_to_hexstr(tag, sizeof(tag), tag_hexstr, sizeof(tag_hexstr));
ESP_LOGI(TAG, "Ciphertext -\n%s", ciphertext);
ESP_LOGI(TAG, "Tag -\n%s", tag_hexstr);
free(plaintext_buf);
free(ciphertext_buf);
free(ciphertext);
exit:
return err;
}
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.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",
.hint = NULL,
.func = &tee_sec_stg_encrypt,
.argtable = &tee_sec_stg_encrypt_args,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&tee_sec_stg));
}
static struct {
struct arg_int *slot_id;
struct arg_str *ciphertext;
struct arg_str *tag;
struct arg_end *end;
} tee_sec_stg_decrypt_args;
static int tee_sec_stg_decrypt(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **)&tee_sec_stg_decrypt_args);
if (nerrors != 0) {
arg_print_errors(stderr, tee_sec_stg_decrypt_args.end, argv[0]);
return 1;
}
esp_err_t err = ESP_FAIL;
uint16_t slot_id = (uint16_t)tee_sec_stg_decrypt_args.slot_id->ival[0];
const char *tag_hexstr = tee_sec_stg_decrypt_args.tag->sval[0];
uint8_t tag[AES256_GCM_TAG_LEN];
hexstr_to_hexbuf(tag_hexstr, strlen(tag_hexstr), tag, sizeof(tag));
const char *ciphertext = tee_sec_stg_decrypt_args.ciphertext->sval[0];
size_t ciphertext_len = strnlen(ciphertext, MAX_AES_PLAINTEXT_LEN);
if (ciphertext_len == MAX_AES_PLAINTEXT_LEN && ciphertext[MAX_AES_PLAINTEXT_LEN] != '\0') {
ESP_LOGE(TAG, "Ciphertext too long (max - %d bytes)", MAX_AES_PLAINTEXT_LEN);
err = ESP_ERR_INVALID_ARG;
goto exit;
}
if (ciphertext_len % 2 != 0) {
ESP_LOGE(TAG, "Invalid plaintext - should be a hex string");
err = ESP_ERR_INVALID_ARG;
goto exit;
}
size_t ciphertext_buf_len = ciphertext_len / 2;
uint8_t *ciphertext_buf = calloc(ciphertext_buf_len, sizeof(uint8_t));
if (ciphertext_buf == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
hexstr_to_hexbuf(ciphertext, ciphertext_len, ciphertext_buf, ciphertext_buf_len);
uint8_t *plaintext_buf = calloc(ciphertext_buf_len, sizeof(uint8_t));
if (plaintext_buf == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
err = esp_tee_sec_storage_init();
if (err != ESP_OK) {
goto exit;
}
err = esp_tee_sec_storage_decrypt(slot_id, ciphertext_buf, ciphertext_buf_len,
NULL, 0, tag, sizeof(tag), plaintext_buf);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to decrypt data: %s", esp_err_to_name(err));
goto exit;
}
char *plaintext = calloc(ciphertext_len + 1, sizeof(char));
if (plaintext == NULL) {
err = ESP_ERR_NO_MEM;
goto exit;
}
hexbuf_to_hexstr(plaintext_buf, ciphertext_buf_len, plaintext, ciphertext_len + 1);
ESP_LOGI(TAG, "Decrypted plaintext -\n%s", plaintext);
free(ciphertext_buf);
free(plaintext_buf);
free(plaintext);
exit:
return err;
}
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.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",
.hint = NULL,
.func = &tee_sec_stg_decrypt,
.argtable = &tee_sec_stg_decrypt_args,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&tee_sec_stg));
}

View File

@ -0,0 +1,242 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import hashlib
import http.server
import json
import logging
import multiprocessing
import os
import socket
import ssl
import time
from typing import Any
from typing import Callable
import pexpect
import pytest
from common_test_methods import get_env_config_variable
from common_test_methods import get_host_ip4_by_dest_ip
from cryptography.hazmat.primitives.asymmetric import utils
from ecdsa.curves import NIST256p
from ecdsa.keys import VerifyingKey
from ecdsa.util import sigdecode_der
from pytest_embedded import Dut
from RangeHTTPServer import RangeRequestHandler
TEST_MSG = 'hello world'
server_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_certs/server_cert.pem')
key_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_certs/server_key.pem')
###########################
# ESP-TEE: Secure Storage #
###########################
@pytest.mark.esp32c6
@pytest.mark.generic
def test_tee_cli_secure_storage(dut: Dut) -> None:
# Dumping the REE binary size
binary_file = os.path.join(dut.app.binary_path, 'tee_cli.bin')
bin_size = os.path.getsize(binary_file)
logging.info('tee_cli_bin_size : {}KB'.format(bin_size // 1024))
# Starting the test
dut.expect('ESP-TEE: Secure services demonstration', timeout=30)
time.sleep(1)
# Get the SHA256 digest of the test message
dut.write(f'get_msg_sha256 "{TEST_MSG}"')
test_msg_hash = dut.expect(r'Message digest \(SHA256\) -\s*([0-9a-fA-F]{64})', timeout=30)[1].decode()
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)
dut.write(f'tee_sec_stg_sign {sec_stg_slots.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)
vk = VerifyingKey.from_string(bytes.fromhex(test_msg_pubkey), curve=NIST256p, hashfunc=hashlib.sha256)
assert vk.verify_digest(bytes.fromhex(test_msg_sign), bytes.fromhex(test_msg_hash))
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_encrypt {sec_stg_slots.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}')
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)
time.sleep(1)
########################
# ESP-TEE: Attestation #
########################
def verify_att_token_signature(att_tk: str) -> Any:
# Parsing the token
tk_info = json.loads(att_tk)
# Fetching the data to be verified
tk_hdr_val = json.dumps(tk_info['header'], separators=(',', ':')).encode('latin-1')
tk_eat_val = json.dumps(tk_info['eat'], separators=(',', ':')).encode('latin-1')
tk_pubkey_val = json.dumps(tk_info['public_key'], separators=(',', ':')).encode('latin-1')
# Pre-hashing the data
ctx = hashlib.new('sha256')
ctx.update(tk_hdr_val)
ctx.update(tk_eat_val)
ctx.update(tk_pubkey_val)
digest = ctx.digest()
# Fetching the public key
tk_pubkey_c = bytes.fromhex(tk_info['public_key']['compressed'])
# Fetching the appended signature
tk_sign_r = bytes.fromhex(tk_info['sign']['r'])
tk_sign_s = bytes.fromhex(tk_info['sign']['s'])
# Construct the signature using the R and S components
signature = utils.encode_dss_signature(int.from_bytes(tk_sign_r, 'big'), int.from_bytes(tk_sign_s, 'big'))
# Uncompress the public key and verify the signature
vk = VerifyingKey.from_string(tk_pubkey_c, NIST256p, hashfunc=hashlib.sha256)
return vk.verify_digest(signature, digest, sigdecode=sigdecode_der)
@pytest.mark.esp32c6
@pytest.mark.generic
def test_tee_cli_attestation(dut: Dut) -> None:
# Dumping the REE binary size
binary_file = os.path.join(dut.app.binary_path, 'tee_cli.bin')
bin_size = os.path.getsize(binary_file)
logging.info('tee_cli_bin_size : {}KB'.format(bin_size // 1024))
# Starting the test
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)
# Get the Entity Attestation token from TEE and verify its signature
dut.write('tee_att_info')
dut.expect(r'Attestation token - Length: (\d+)', timeout=30)
att_tk = dut.expect(r"'(.*?)'", timeout=30)[1].decode()
assert verify_att_token_signature(att_tk)
#######################################
# ESP-TEE: Over-the-Air (OTA) updates #
#######################################
def https_request_handler() -> Callable[...,http.server.BaseHTTPRequestHandler]:
"""
Returns a request handler class that handles broken pipe exception
"""
class RequestHandler(RangeRequestHandler):
def finish(self) -> None:
try:
if not self.wfile.closed:
self.wfile.flush()
self.wfile.close()
except socket.error:
pass
self.rfile.close()
def handle(self) -> None:
try:
RangeRequestHandler.handle(self)
except socket.error:
pass
return RequestHandler
def start_https_server(ota_image_dir: str, server_ip: str, server_port: int) -> None:
os.chdir(ota_image_dir)
requestHandler = https_request_handler()
httpd = http.server.HTTPServer((server_ip, server_port), requestHandler)
httpd.socket = ssl.wrap_socket(httpd.socket,
keyfile=key_file,
certfile=server_file, server_side=True)
httpd.serve_forever()
@pytest.mark.esp32c6
@pytest.mark.wifi_high_traffic
def test_tee_cli_secure_ota_wifi(dut: Dut) -> None:
"""
This is a positive test case, which downloads complete binary file multiple number of times.
Number of iterations can be specified in variable iterations.
steps:
1. join AP
2. Fetch TEE/REE OTA image over HTTPS
3. Reboot with the new TEE OTA image
"""
# Number of iterations to validate OTA
iterations = 4
server_port = 8001
tee_bin = 'esp_tee/esp_tee.bin'
user_bin = 'tee_cli.bin'
# Start server
thread1 = multiprocessing.Process(target=start_https_server, args=(dut.app.binary_path, '0.0.0.0', server_port))
thread1.daemon = True
thread1.start()
time.sleep(1)
try:
# start test
for i in range(iterations):
# Boot up sequence checks
dut.expect('Loaded TEE app from partition at offset', timeout=30)
dut.expect('Loaded app from partition at offset', timeout=30)
# Starting the test
dut.expect('ESP-TEE: Secure services demonstration', timeout=30)
time.sleep(1)
# Connecting to Wi-Fi
env_name = 'wifi_high_traffic'
ap_ssid = get_env_config_variable(env_name, 'ap_ssid')
ap_password = get_env_config_variable(env_name, 'ap_password')
dut.write(f'wifi_connect {ap_ssid} {ap_password}')
# Fetch the DUT IP address
try:
ip_address = dut.expect(r'got ip:(\d+\.\d+\.\d+\.\d+)[^\d]', timeout=30)[1].decode()
print('Connected to AP/Ethernet with IP: {}'.format(ip_address))
except pexpect.exceptions.TIMEOUT:
raise ValueError('ENV_TEST_FAILURE: Cannot connect to AP')
host_ip = get_host_ip4_by_dest_ip(ip_address)
# User OTA for last iteration
if i == (iterations - 1):
dut.write(f'user_ota https://{host_ip}:{str(server_port)}/{user_bin}')
dut.expect('OTA Succeed, Rebooting', timeout=150)
else:
dut.write(f'tee_ota https://{host_ip}:{str(server_port)}/{tee_bin}')
dut.expect('esp_tee_ota_end succeeded', timeout=150)
dut.expect('Prepare to restart system!', timeout=150)
finally:
thread1.terminate()

View File

@ -0,0 +1,15 @@
# Security features - build-only configuration
CONFIG_PARTITION_TABLE_OFFSET=0xf000
# Secure Boot
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y
CONFIG_SECURE_BOOT_SIGNING_KEY="test_keys/secure_boot_signing_key.pem"
# Flash Encryption
CONFIG_SECURE_FLASH_ENC_ENABLED=y
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

View File

@ -0,0 +1,20 @@
# Enabling TEE
CONFIG_SECURE_ENABLE_TEE=y
CONFIG_SECURE_TEE_IRAM_SIZE=0x9000
# Custom partition table
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
CONFIG_PARTITION_TABLE_TWO_OTA_TEE=y
# TEE: Flash operations' limitation
CONFIG_FREERTOS_UNICORE=y
# TEE: OTA-related
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y
CONFIG_EXAMPLE_SKIP_COMMON_NAME_CHECK=y
CONFIG_EXAMPLE_OTA_RECV_TIMEOUT=45000
# Custom certificates for testing
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN=y
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE=y
CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE_PATH="test_certs/server_cert.pem"

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDWDCCAkACCQCbF4+gVh/MLjANBgkqhkiG9w0BAQsFADBuMQswCQYDVQQGEwJJ
TjELMAkGA1UECAwCTUgxDDAKBgNVBAcMA1BVTjEMMAoGA1UECgwDRVNQMQwwCgYD
VQQLDANFU1AxDDAKBgNVBAMMA0VTUDEaMBgGCSqGSIb3DQEJARYLZXNwQGVzcC5j
b20wHhcNMjEwNzEyMTIzNjI3WhcNNDEwNzA3MTIzNjI3WjBuMQswCQYDVQQGEwJJ
TjELMAkGA1UECAwCTUgxDDAKBgNVBAcMA1BVTjEMMAoGA1UECgwDRVNQMQwwCgYD
VQQLDANFU1AxDDAKBgNVBAMMA0VTUDEaMBgGCSqGSIb3DQEJARYLZXNwQGVzcC5j
b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDhxF/y7bygndxPwiWL
SwS9LY3uBMaJgup0ufNKVhx+FhGQOu44SghuJAaH3KkPUnt6SOM8jC97/yQuc32W
ukI7eBZoA12kargSnzdv5m5rZZpd+NznSSpoDArOAONKVlzr25A1+aZbix2mKRbQ
S5w9o1N2BriQuSzd8gL0Y0zEk3VkOWXEL+0yFUT144HnErnD+xnJtHe11yPO2fEz
YaGiilh0ddL26PXTugXMZN/8fRVHP50P2OG0SvFpC7vghlLp4VFM1/r3UJnvL6Oz
3ALc6dhxZEKQucqlpj8l1UegszQToopemtIj0qXTHw2+uUnkUyWIPjPC+wdOAoap
rFTRAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAItw24y565k3C/zENZlxyzto44ud
IYPQXN8Fa2pBlLe1zlSIyuaA/rWQ+i1daS8nPotkCbWZyf5N8DYaTE4B0OfvoUPk
B5uGDmbuk6akvlB5BGiYLfQjWHRsK9/4xjtIqN1H58yf3QNROuKsPAeywWS3Fn32
3//OpbWaClQePx6udRYMqAitKR+QxL7/BKZQsX+UyShuq8hjphvXvk0BW8ONzuw9
RcoORxM0FzySYjeQvm4LhzC/P3ZBhEq0xs55aL2a76SJhq5hJy7T/Xz6NFByvlrN
lFJJey33KFrAf5vnV9qcyWFIo7PYy2VsaaEjFeefr7q3sTFSMlJeadexW2Y=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDhxF/y7bygndxP
wiWLSwS9LY3uBMaJgup0ufNKVhx+FhGQOu44SghuJAaH3KkPUnt6SOM8jC97/yQu
c32WukI7eBZoA12kargSnzdv5m5rZZpd+NznSSpoDArOAONKVlzr25A1+aZbix2m
KRbQS5w9o1N2BriQuSzd8gL0Y0zEk3VkOWXEL+0yFUT144HnErnD+xnJtHe11yPO
2fEzYaGiilh0ddL26PXTugXMZN/8fRVHP50P2OG0SvFpC7vghlLp4VFM1/r3UJnv
L6Oz3ALc6dhxZEKQucqlpj8l1UegszQToopemtIj0qXTHw2+uUnkUyWIPjPC+wdO
AoaprFTRAgMBAAECggEAE0HCxV/N1Q1h+1OeDDGL5+74yjKSFKyb/vTVcaPCrmaH
fPvp0ddOvMZJ4FDMAsiQS6/n4gQ7EKKEnYmwTqj4eUYW8yxGUn3f0YbPHbZT+Mkj
z5woi3nMKi/MxCGDQZX4Ow3xUQlITUqibsfWcFHis8c4mTqdh4qj7xJzehD2PVYF
gNHZsvVj6MltjBDAVwV1IlGoHjuElm6vuzkfX7phxcA1B4ZqdYY17yCXUnvui46z
Xn2kUTOOUCEgfgvGa9E+l4OtdXi5IxjaSraU+dlg2KsE4TpCuN2MEVkeR5Ms3Y7Q
jgJl8vlNFJDQpbFukLcYwG7rO5N5dQ6WWfVia/5XgQKBgQD74at/bXAPrh9NxPmz
i1oqCHMDoM9sz8xIMZLF9YVu3Jf8ux4xVpRSnNy5RU1gl7ZXbpdgeIQ4v04zy5aw
8T4tu9K3XnR3UXOy25AK0q+cnnxZg3kFQm+PhtOCKEFjPHrgo2MUfnj+EDddod7N
JQr9q5rEFbqHupFPpWlqCa3QmQKBgQDldWUGokNaEpmgHDMnHxiibXV5LQhzf8Rq
gJIQXb7R9EsTSXEvsDyqTBb7PHp2Ko7rZ5YQfyf8OogGGjGElnPoU/a+Jij1gVFv
kZ064uXAAISBkwHdcuobqc5EbG3ceyH46F+FBFhqM8KcbxJxx08objmh58+83InN
P9Qr25Xw+QKBgEGXMHuMWgQbSZeM1aFFhoMvlBO7yogBTKb4Ecpu9wI5e3Kan3Al
pZYltuyf+VhP6XG3IMBEYdoNJyYhu+nzyEdMg8CwXg+8LC7FMis/Ve+o7aS5scgG
1to/N9DK/swCsdTRdzmc/ZDbVC+TuVsebFBGYZTyO5KgqLpezqaIQrTxAoGALFCU
10glO9MVyl9H3clap5v+MQ3qcOv/EhaMnw6L2N6WVT481tnxjW4ujgzrFcE4YuxZ
hgwYu9TOCmeqopGwBvGYWLbj+C4mfSahOAs0FfXDoYazuIIGBpuv03UhbpB1Si4O
rJDfRnuCnVWyOTkl54gKJ2OusinhjztBjcrV1XkCgYEA3qNi4uBsPdyz9BZGb/3G
rOMSw0CaT4pEMTLZqURmDP/0hxvTk1polP7O/FYwxVuJnBb6mzDa0xpLFPTpIAnJ
YXB8xpXU69QVh+EBbemdJWOd+zp5UCfXvb2shAeG3Tn/Dz4cBBMEUutbzP+or0nG
vSXnRLaxQhooWm+IuX9SuBQ=
-----END PRIVATE KEY-----

View File

@ -0,0 +1,39 @@
-----BEGIN RSA PRIVATE KEY-----
MIIG5AIBAAKCAYEAvB+DrHYuY3yM7fiBPpxE7ZZqde/ULn5eG7vlHF+/2sblb2LC
o/sdWRcImSOzbbVG8wYPCqtCG3FeQzhsdAKapd8ha7+YQcIvqPgJMfOSIfsYdFua
qNhAzAj/2zBxajzHfdtFPVjdWhW9tIztv7uTlkXoIsy2ty0De6uwOCVBTbvpJ9JU
g03IIV5h20J3qAf6ODRA/bi2P8r1muxgs+hhjP4V90h6149Kwv82B3A0eV5a9hG/
4Ut4uX9jNYn/KU+0zEGif0EQMjcKOc3F0ruuiaHdAJLJ6yK4MhM8Rgmfdy7okdfF
Q7SrliFW/1Crys4iCxShRffCYNGgKgwMpaK6qrqwVf56Geu+4uk8/TOhlOg7Tyvo
5tpafvdcJo4y+mX6Bb/5ErskCaRWCsb0733rbWaBkrEDNZ/dJmkvByu7JWqGGHmN
rbiiop60+pz7N8jcBwPtAlUGH3aKEMvX3RTbjrug4cFTQvsIM3kJaklhmv0UDFhg
zeGu6Pm9w+f/JlV5AgMBAAECggGAAV72OReGlwSdWLUEgoEZCRidH5oaOoyCzkKJ
FOZA7uAFymrQO4NLX3U8UGfJZijgCRb0nRtKZaQAQw6/Afyfq+QuyV47Rp9np+kt
Oy2PO96Px9x/1m/2nB+kqn0PFofpQqpXNgjW9B62CmhmHMBjmI2Oqrin/2wjwY73
jDEy6W7pI0Unhuo8mLtBwBtOS+2WJ6EZ21kXp7dbutlMixyybH8D7sPODiwZKC3V
jJBT9/gPQz1pEvc91fw7rA3Py9LgENDbSlPlu0qx9vKvcr3QaGVS5Mghi+g4vpZH
Txyz96dSHoQOJ+1NFTZQn+P3TEpXH9YabBodyAJzeWFswYJLVgJw4S48BwsKDNje
Kx8z0dUe/8mK1LLO+c8GArAe36gAGr9PO/o3WZC7ZNdDuTnl0l6Ccg4+JzUwaL2y
s5eNt7dGSyl40xnG0J2+eVmVdECVdLHRsGVqlmp4wqtUSirBwUx9F+M1iIELPvmp
O6AexJLeX8uSBVYgBkRMVMqq6hzRAoHBAO7Aft5ctKbTwr0eff4pQb3O1DB1WUsM
U5L13cob9fpVSNOiydK2armJeKZI2TSryY+dkY0TF1aIvQ+lD0leWVON8TgYwTGU
He0hwhb23+ZQC6VKCzGTIwqCw/1hY6cJXaTAtZl7JCx3Nfj1s2MQTZND+aX65Uba
FQ+skuszLYzTS/5q0ooi7VLLUGtAqnkjFuskLlawgj3PY51HEZaivftsdCapLmoc
eWefu7X2Qok/VjvdQakaPe+jWoHaGiwL3wKBwQDJtq8AGDY+ZuTWYvROGz4oYH53
BC213d6dEMZeVX+zsMta1/+9LwZ7k3QbZOF1GF4tMT21mlT/AtbCA6WYz0pzYagp
8zm5Ppm7LuwcY+q4z3xc1b4PvHTfcbCLFohDPd1fKsw3uNsac8st+voaWmbSPRv9
frA8vWklC2GHQFH0QNEGB3LKto65A3p8dkGcG6KMLqedHSB8vlGUD2jZnC5E67Hj
q1J0bsWsfP9madoWcEMIeG7YX15TNvp5DMP4SacCgcEAz46NPKZ/bmC99Udi5Ofc
/TOniBEcdX/bz/DqxV+VcfFehI4KwqJPGckHGBhQq9eEPNQQywID0fjB0639Ih1h
rp/BSrFKkt1fPt2QLAKnF0QTO7ipDooJdoPD+q0FwxsDS800kp9ZDUb9pteDAeYU
aqg3ijIZzISetqeiedJeEQVIFX+sVOaURtv1p6fqC7GTpQwpNLfyIeX/haVZBwmn
+8GHIG0HqrviV1GoEEJsCYEEaLqq8GWd3oy2jBidlBklAoHAOSca1JMHo4yx4BCv
mgXcCcK1cnFrpjEUJzqeihH9meNI6xdybZ8KXi88YZqmyu+5l2JxUqhE1vBt+xDf
dbmeJa0Q4bn5rAQRuNilq7Gfyvd3DDK/pGPIVZs342DiAPNhatqMfGlTI16VvhRz
ks30fjM0YBHqS3t7dDSSUKknz2YiE2w+2TL6o29Z4DP9iQbHeqKpeTnMf6vJH4Ny
ON4oufxyLcjYFwdf2OydzN3HHj52r8q31XTIHBnixDOavxnXAoHBAIMS2ipFhYpk
aPlcnygqV4tlcmsy376OMqOv2Ggv/ZhNHnKZkmywEtw463o19ruIjZ2F2ik/FhRH
V94/PeLRRWn54mI6z6N7eZ1m96sgfPH0shH5a4yLFtCPpmVlBlUv0wj6FXlfqDaL
DtTzbY3i2dDskcx1ollMAoz/RJErhbxNGCQE1mtjld6lIb4gNd3DmZhkdGQS38rG
fvBa0fgPJBzVu1kfVcJemFJxOhpS/+MxbEfFw7AgIFIKGh0mviUBWA==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1 @@
1.0.0

View File

@ -0,0 +1,18 @@
#This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16)
# This example uses extra components for the following -
# 1. Test framework related.
# 2. Managing TEE OTA updates
# 3. TEE Secure Storage
# 4. TEE Entity Attestation
list(APPEND EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/tools/unit-test-app/components
$ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_ota_ops
$ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_sec_storage
$ENV{IDF_PATH}/components/esp_tee/subproject/components/tee_attestation)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/components/test_sec_srv/test_tee_project.cmake)
project(esp_tee_test)

View File

@ -0,0 +1,28 @@
| Supported Targets | ESP32-C6 |
| ----------------- | -------- |
## ESP-TEE: Test Suite
- ESP-TEE utilizes the `pytest` framework in ESP-IDF for executing the dedicated unit tests on the target. The test application includes cases spanning the following modules -
- Secure service call interface
- Interrupts and exception handling
- Privilege violation
- Cryptographic operations
- TEE OTA updates
- Secure storage
- Attestation
- For executing the test locally, ESP-IDF needs to be installed with the additional Python requirements.
```bash
cd $IDF_PATH
bash install.sh --enable-ci --enable-pytest
. ./export.sh
```
- For example, to execute the TEE test suite for ESP32-C6 with all the available `sdkconfig` files, run the following steps. The required test applications will be built and flashed automatically onto the DUT by the `pytest` framework.
```bash
python $IDF_PATH/tools/ci/ci_build_apps.py . --target esp32c6 -v --pytest-apps
pytest --target esp32c6
```

View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.16)
idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
set(srcs)
set(priv_requires)
if(esp_tee_build)
list(APPEND srcs "src/test_dummy_srv.c"
"src/test_interrupt.c"
"src/test_panic.c"
"src/test_sec_srv.c")
list(APPEND priv_requires main)
else()
list(APPEND srcs "src/test_dummy_srv_wrapper.c")
list(APPEND priv_requires esp_tee)
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS include
PRIV_REQUIRES ${priv_requires})

View File

@ -0,0 +1,42 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define TEE_TEST_INT_COUNT 3
uint32_t __attribute__((__noinline__)) esp_tee_service_add(uint32_t a, uint32_t b);
uint32_t __attribute__((__noinline__)) esp_tee_service_sub(uint32_t a, uint32_t b);
uint32_t __attribute__((__noinline__)) esp_tee_service_mul(uint32_t a, uint32_t b);
uint32_t __attribute__((__noinline__)) esp_tee_service_div(uint32_t a, uint32_t b);
int esp_tee_secure_int_test(void);
int esp_tee_non_secure_int_test(volatile uint32_t* volatile ns_int_count);
int esp_tee_test_int_count(uint32_t *secure_int_count);
int esp_tee_test_store_prohibited(uint32_t type);
int esp_tee_test_illegal_instr(void);
int esp_tee_test_instr_fetch_prohibited(uint32_t type);
void dummy_secure_service(void);
uint32_t add_in_loop(uint32_t a, uint32_t b, uint32_t iter);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "secure_service_num.h"
#include "esp_tee.h"
#include "esp_err.h"
#include "esp_rom_sys.h"
void _ss_dummy_secure_service(void)
{
esp_rom_printf("Dummy secure service\n");
}

View File

@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include "secure_service_num.h"
#include "esp_tee.h"
#include "esp_err.h"
void dummy_secure_service(void)
{
esp_tee_service_call(1, SS_DUMMY_SECURE_SERVICE);
}

View File

@ -0,0 +1,174 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/timer_group_reg.h"
#include "esp_tee.h"
#include "esp_log.h"
#include "esp_tee_test.h"
#include "riscv/csr.h"
#include "soc/interrupt_matrix_reg.h"
#include "esp_tee_intr.h"
#include "hal/timer_ll.h"
#include "hal/clk_gate_ll.h"
#include "soc/timer_group_reg.h"
#define TIMER_DIVIDER 80 // Hardware timer clock divider
#define TIMER_RESOLUTION_HZ 1000000 // 1MHz resolution
#define TIMER_ALARM_PERIOD_S 0.10 // sample test interval for the first timer
/* TEE uses Group0 Timer0 */
#define TEE_SECURE_TIMER 0
static const char *TAG = "esp_tee_intr_test";
static timg_dev_t *timg_hw = (&TIMERG0);
uint32_t *psecure_int_count = NULL;
static void IRAM_ATTR timer_group0_isr(void *arg)
{
ESP_LOGI(TAG, "Timer ISR Handler from World %d!", esp_cpu_get_curr_privilege_level());
/* For interrupt test. */
*psecure_int_count = *psecure_int_count + 1;
/* Clear interrupt */
timer_ll_clear_intr_status(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER));
/* Re-enable the alarm. */
timer_ll_enable_alarm(timg_hw, TEE_SECURE_TIMER, true);
}
static void tee_timer_enable(void)
{
struct vector_desc_t timer_vd = { 0, NULL, NULL, NULL };
// init timer_vc
timer_vd.source = ETS_TG0_T0_LEVEL_INTR_SOURCE;
timer_vd.isr = timer_group0_isr;
ESP_LOGI(TAG, "Enabling test timer from secure world");
/* Enable TG0 peripheral module */
periph_ll_enable_clk_clear_rst(PERIPH_TIMG0_MODULE);
/* Stop counter, alarm, auto-reload at first place */
timer_ll_enable_clock(timg_hw, TEE_SECURE_TIMER, true);
timer_ll_enable_counter(timg_hw, TEE_SECURE_TIMER, false);
timer_ll_enable_auto_reload(timg_hw, TEE_SECURE_TIMER, false);
timer_ll_enable_alarm(timg_hw, TEE_SECURE_TIMER, false);
// Set clock source
timer_ll_set_clock_source(timg_hw, TEE_SECURE_TIMER, GPTIMER_CLK_SRC_DEFAULT);
timer_ll_set_clock_prescale(timg_hw, TEE_SECURE_TIMER, TIMER_DIVIDER);
// Initialize counter value to zero
timer_ll_set_reload_value(timg_hw, TEE_SECURE_TIMER, 0);
timer_ll_trigger_soft_reload(timg_hw, TEE_SECURE_TIMER);
// set counting direction
timer_ll_set_count_direction(timg_hw, TEE_SECURE_TIMER, GPTIMER_COUNT_UP);
// disable interrupt
timer_ll_enable_intr(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER), false);
// clear pending interrupt event
timer_ll_clear_intr_status(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER));
esp_tee_intr_register((void *)&timer_vd);
timer_ll_enable_intr(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER), true);
timer_ll_set_reload_value(timg_hw, TEE_SECURE_TIMER, 0);
// enable timer interrupt
timer_ll_enable_intr(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER), true);
// set timer alarm
uint64_t alarm_value = (TIMER_ALARM_PERIOD_S * TIMER_RESOLUTION_HZ);
timer_ll_set_alarm_value(timg_hw, TEE_SECURE_TIMER, alarm_value);
timer_ll_enable_auto_reload(timg_hw, TEE_SECURE_TIMER, true);
timer_ll_enable_alarm(timg_hw, TEE_SECURE_TIMER, true);
timer_ll_enable_counter(timg_hw, TEE_SECURE_TIMER, true);
}
static void tee_timer_disable(void)
{
ESP_LOGI(TAG, "Disabling test timer from secure world");
/* Init timer interrupt vector descriptor */
struct vector_desc_t timer_vd = { 0, NULL, NULL, NULL };
timer_vd.source = ETS_TG0_T0_LEVEL_INTR_SOURCE;
timer_vd.isr = timer_group0_isr;
esp_tee_intr_deregister((void *)&timer_vd);
/* Disable timer */
timer_ll_enable_counter(timg_hw, TEE_SECURE_TIMER, false);
timer_ll_enable_auto_reload(timg_hw, TEE_SECURE_TIMER, false);
timer_ll_enable_alarm(timg_hw, TEE_SECURE_TIMER, false);
/* Disable and clear interrupt */
timer_ll_enable_intr(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER), false);
timer_ll_clear_intr_status(timg_hw, TIMER_LL_EVENT_ALARM(TEE_SECURE_TIMER));
/* Disable TG0 peripheral module */
// periph_ll_disable_clk_set_rst(PERIPH_TIMG0_MODULE);
}
void _ss_esp_tee_test_timer_init(bool enable)
{
if (enable) {
tee_timer_enable();
} else {
tee_timer_disable();
}
}
/**
* Secure interrupt in secure world test.
*/
int _ss_esp_tee_secure_int_test(void)
{
ESP_LOGD(TAG, "In WORLD %d", esp_cpu_get_curr_privilege_level());
volatile uint32_t secure_int_count = 0;
psecure_int_count = (uint32_t *)&secure_int_count;
_ss_esp_tee_test_timer_init(true);
while (secure_int_count < TEE_TEST_INT_COUNT);
_ss_esp_tee_test_timer_init(false);
ESP_LOGD(TAG, "Exiting WORLD %d", esp_cpu_get_curr_privilege_level());
return secure_int_count;
}
/**
* Non-Secure interrupt in secure world test.
*/
int _ss_esp_tee_non_secure_int_test(volatile uint32_t *volatile ns_int_count)
{
ESP_LOGD(TAG, "In WORLD %d", esp_cpu_get_curr_privilege_level());
uint32_t count = 0;
count = *ns_int_count;
while ((*ns_int_count < TEE_TEST_INT_COUNT)) {
if (*ns_int_count > count) {
count = *ns_int_count;
ESP_LOGI(TAG, "Interrupt count %d", count);
}
}
ESP_LOGD(TAG, "Exiting WORLD %d", esp_cpu_get_curr_privilege_level());
return 0;
}
int _ss_esp_tee_test_int_count(uint32_t *secure_int_count)
{
psecure_int_count = secure_int_count;
return (*psecure_int_count);
}

View File

@ -0,0 +1,82 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_tee_test.h"
#include "esp_log.h"
#define RND_VAL (0xA5A5A5A5)
extern int _tee_vec_start;
extern int _tee_vec_end;
extern int _tee_iram_start;
extern int _tee_iram_end;
extern int _tee_dram_start;
typedef void (*func_ptr)(void);
__attribute__((unused)) static const char *TAG = "esp_tee_test_panic";
#pragma GCC push_options
#pragma GCC optimize ("Og")
void _ss_esp_tee_test_resv_reg1_write_violation(void)
{
uint32_t *test_addr = (uint32_t *)((uint32_t)(0x1));
*test_addr = RND_VAL;
}
void _ss_esp_tee_test_resv_reg1_exec_violation(void)
{
volatile func_ptr fptr = (void(*)(void))0x1;
fptr();
}
void _ss_esp_tee_test_iram_reg1_write_violation(void)
{
uint32_t *test_addr = (uint32_t *)((uint32_t)(&_tee_vec_start) - 0x100);
*test_addr = RND_VAL;
}
void _ss_esp_tee_test_iram_reg2_write_violation(void)
{
uint32_t *test_addr = (uint32_t *)((uint32_t)(&_tee_iram_start) - 0x04);
*test_addr = RND_VAL;
}
#pragma GCC pop_options
static void foo_d(void)
{
for (int i = 0; i < 16; i++) {
__asm__ __volatile__("NOP");
}
}
void _ss_esp_tee_test_dram_reg1_exec_violation(void)
{
uint8_t s_dram_buf[256];
memcpy(&s_dram_buf, &foo_d, sizeof(s_dram_buf));
volatile func_ptr fptr = (void(*)(void))&s_dram_buf;
fptr();
}
void _ss_esp_tee_test_dram_reg2_exec_violation(void)
{
uint8_t *instr = calloc(256, sizeof(uint8_t));
assert(instr);
memcpy(instr, &foo_d, 256);
volatile func_ptr fptr = (void(*)(void))instr;
fptr();
}
void _ss_esp_tee_test_illegal_instruction(void)
{
#if CONFIG_IDF_TARGET_ARCH_XTENSA
__asm__ __volatile__("ill");
#elif CONFIG_IDF_TARGET_ARCH_RISCV
__asm__ __volatile__("unimp");
#endif
}

View File

@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "esp_log.h"
#include "esp_tee.h"
#include "esp_tee_test.h"
static const char *TAG = "test_sec_srv";
/* Sample Trusted App */
uint32_t __attribute__((__noinline__)) _ss_esp_tee_service_add(uint32_t a, uint32_t b)
{
ESP_LOGD(TAG, "SS: %s", __func__);
return (a + b);
}
uint32_t __attribute__((__noinline__)) _ss_esp_tee_service_sub(uint32_t a, uint32_t b)
{
ESP_LOGD(TAG, "SS: %s", __func__);
return (a - b);
}
uint32_t __attribute__((__noinline__)) _ss_esp_tee_service_mul(uint32_t a, uint32_t b)
{
ESP_LOGD(TAG, "SS: %s", __func__);
return (a * b);
}
uint32_t __attribute__((__noinline__)) _ss_esp_tee_service_div(uint32_t a, uint32_t b)
{
ESP_LOGD(TAG, "SS: %s", __func__);
return (a / b);
}
uint32_t _ss_add_in_loop(uint32_t a, uint32_t b, uint32_t iter)
{
ESP_LOGD(TAG, "SS: %s", __func__);
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);
}
return a;
}

View File

@ -0,0 +1,18 @@
# SS no. API type Function Args
101 custom esp_tee_service_add 6
102 custom esp_tee_service_sub 6
103 custom esp_tee_service_mul 6
104 custom esp_tee_service_div 6
105 custom esp_tee_test_timer_init 6
106 custom esp_tee_secure_int_test 6
107 custom esp_tee_non_secure_int_test 6
108 custom esp_tee_test_int_count 6
109 custom esp_tee_test_resv_reg1_write_violation 0
110 custom esp_tee_test_resv_reg1_exec_violation 0
111 custom esp_tee_test_iram_reg1_write_violation 0
112 custom esp_tee_test_iram_reg2_write_violation 0
113 custom esp_tee_test_dram_reg1_exec_violation 0
114 custom esp_tee_test_dram_reg2_exec_violation 0
115 custom esp_tee_test_illegal_instruction 0
201 custom dummy_secure_service 6
202 custom add_in_loop 6

View File

@ -0,0 +1,15 @@
# tee_project.cmake file must be manually included in the project's top level CMakeLists.txt before project()
# This ensures that the variables are set before TEE starts building
get_filename_component(directory "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE DIRECTORY)
get_filename_component(name ${CMAKE_CURRENT_LIST_DIR} NAME)
# Append secure service table consisting of secure services
idf_build_set_property(CUSTOM_SECURE_SERVICE_TBL ${CMAKE_CURRENT_LIST_DIR}/test.tbl APPEND)
# Append the directory of this component which is used by esp_tee component as
# EXTRA_COMPONENT_DIRS
idf_build_set_property(CUSTOM_SECURE_SERVICE_COMPONENT_DIR ${directory} APPEND)
# Append the name of the component so that esp_tee can include it in its COMPONENTS list
idf_build_set_property(CUSTOM_SECURE_SERVICE_COMPONENT ${name} APPEND)

View File

@ -0,0 +1,178 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
# pylint: disable=W0621 # redefined-outer-name
import os
import tempfile
from typing import Any
import espsecure
import esptool
import pytest
from _pytest.fixtures import FixtureRequest
from _pytest.monkeypatch import MonkeyPatch
from pytest_embedded_idf.serial import IdfSerial
from pytest_embedded_serial_esp.serial import EspSerial
esp_tee_empty_bin = {
'esp32c6': [
0xE9, 0x04, 0x02, 0x10, 0x00, 0x00, 0x80, 0x40, 0xEE, 0x00, 0x00, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
# DROM segment
0x20, 0x00, 0x00, 0x42, 0x00, 0x02, 0x00, 0x00,
# esp_app_desc structure
0x32, 0x54, 0xCD, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x76, 0x35, 0x2E, 0x35, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x65, 0x73, 0x70, 0x5F, 0x74, 0x65, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x30, 0x3A, 0x30, 0x30, 0x3A, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x4E, 0x6F, 0x76, 0x20, 0x31, 0x31, 0x20, 0x32,
0x30, 0x32, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x35, 0x2E, 0x35,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x2D, 0x63, 0x66, 0x8B, 0x75, 0xFA, 0x59, 0x05,
0x53, 0x34, 0x91, 0x71, 0x51, 0x33, 0x91, 0xDD, 0xF8, 0xB1, 0xFE, 0x83,
0x06, 0xEB, 0x03, 0x80, 0x45, 0xC9, 0x18, 0x20, 0x83, 0x7E, 0x2E, 0x43,
*([0x00] * 0x58),
# Padding
*([0x00] * 0x100),
# IRAM segment
0x00, 0x00, 0x80, 0x40, 0x20, 0x00, 0x00, 0x00,
*([0x00] * 0x20),
# PADDING segment
0x00, 0x00, 0x00, 0x00, 0xC8, 0x7D, 0x00, 0x00,
*([0x00] * 0x7DC8),
# IROM segment
0x20, 0x80, 0x00, 0x42, 0x00, 0x01, 0x00, 0x00,
*([0x00] * 0x100),
# Padding
*([0x00] * 0x0F),
# CRC8 checksum
0x56,
# Image SHA256
0xF4, 0xA4, 0xCF, 0x06, 0xAE, 0x94, 0x75, 0x47, 0xBC, 0x88, 0xA2, 0xCA,
0x52, 0x97, 0x7A, 0x5C, 0x55, 0x43, 0xD9, 0xF5, 0xD3, 0x45, 0xD1, 0x34,
0xFC, 0x74, 0xB2, 0xB9, 0x34, 0x72, 0xC3, 0x00
]
}
# This is a custom IdfSerial class to support custom functionality
# which is required only for this test
class TEESerial(IdfSerial):
def __init__(self, *args, **kwargs) -> None: # type: ignore
super().__init__(*args, **kwargs)
def _get_flash_size(self) -> Any:
return self.app.sdkconfig.get('ESPTOOLPY_FLASHSIZE', '')
@EspSerial.use_esptool()
def bootloader_force_flash_if_req(self) -> None:
# Forcefully flash the bootloader only if security features are enabled
if any((
self.app.sdkconfig.get('SECURE_BOOT', True),
self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED', True),
)):
offs = int(self.app.sdkconfig.get('BOOTLOADER_OFFSET_IN_FLASH', 0))
bootloader_path = os.path.join(self.app.binary_path, 'bootloader', 'bootloader.bin')
encrypt = '--encrypt' if self.app.sdkconfig.get('SECURE_FLASH_ENC_ENABLED') else ''
flash_size = self._get_flash_size()
esptool.main(
f'--no-stub write_flash {offs} {bootloader_path} --force {encrypt} --flash_size {flash_size}'.split(),
esp=self.esp
)
@EspSerial.use_esptool()
def custom_erase_partition(self, partition: str) -> None:
if self.app.sdkconfig.get('SECURE_ENABLE_SECURE_ROM_DL_MODE'):
with tempfile.NamedTemporaryFile(delete=True) as temp_file:
offs = self.app.partition_table[partition]['offset']
size = self.app.partition_table[partition]['size']
flash_size = self._get_flash_size()
binstr = b'\xff' * int(size)
temp_file.write(binstr)
temp_file.flush()
esptool.main(
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:
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 = os.path.join(self.app.binary_path, 'esp_tee', 'esp_tee.bin')
if is_rollback:
datafile = 'esp_tee_empty.bin'
datafile_signed = 'esp_tee_empty_signed.bin'
flash_file = datafile
with open(datafile, 'wb') as data_file:
bin_data = esp_tee_empty_bin.get(self.app.sdkconfig.get('IDF_TARGET'), None)
if bin_data is not None:
data_file.write(bytes(bin_data))
data_file.flush()
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()
)
flash_file = datafile_signed
esptool.main(
f'{no_stub} write_flash {offs} {flash_file} {encrypt} --flash_size {flash_size}'.split(),
esp=self.esp
)
if is_rollback:
if os.path.exists(datafile):
os.remove(datafile)
if os.path.exists(datafile_signed):
os.remove(datafile_signed)
@EspSerial.use_esptool()
def custom_flash(self) -> None:
self.bootloader_force_flash_if_req()
self.flash()
@EspSerial.use_esptool()
def custom_flash_w_test_tee_img_gen(self) -> None:
self.bootloader_force_flash_if_req()
self.flash()
self.copy_test_tee_img('ota_1', False)
@EspSerial.use_esptool()
def custom_flash_w_test_tee_img_rb(self) -> None:
self.bootloader_force_flash_if_req()
self.flash()
self.copy_test_tee_img('ota_1', True)
@EspSerial.use_esptool()
def custom_flash_with_empty_sec_stg(self) -> None:
self.bootloader_force_flash_if_req()
self.flash()
self.custom_erase_partition('secure_storage')
@pytest.fixture(scope='module')
def monkeypatch_module(request: FixtureRequest) -> MonkeyPatch:
mp = MonkeyPatch()
request.addfinalizer(mp.undo)
return mp
@pytest.fixture(scope='module', autouse=True)
def replace_dut_class(monkeypatch_module: MonkeyPatch) -> None:
monkeypatch_module.setattr('pytest_embedded_idf.IdfSerial', TEESerial)

View File

@ -0,0 +1,37 @@
idf_build_get_property(idf_path IDF_PATH)
set(priv_requires bootloader_support driver esp_tee esp_timer mbedtls spi_flash)
# Test FW related
list(APPEND priv_requires cmock json test_utils unity)
# TEE related
list(APPEND priv_requires tee_sec_storage tee_attestation tee_ota_ops test_sec_srv)
set(srcs "app_main.c")
list(APPEND srcs "test_esp_tee_ctx_switch.c"
"test_esp_tee_interrupt.c"
"test_esp_tee_panic.c"
"test_esp_tee_sec_stg.c"
"test_esp_tee_ota.c"
"test_esp_tee_att.c")
set(mbedtls_test_srcs_dir "${idf_path}/components/mbedtls/test_apps/main")
# AES
list(APPEND srcs "${mbedtls_test_srcs_dir}/test_aes.c"
"${mbedtls_test_srcs_dir}/test_aes_gcm.c"
"${mbedtls_test_srcs_dir}/test_aes_perf.c")
# SHA
list(APPEND srcs "${mbedtls_test_srcs_dir}/test_mbedtls_sha.c"
"${mbedtls_test_srcs_dir}/test_sha.c"
"${mbedtls_test_srcs_dir}/test_sha_perf.c")
# Mixed
list(APPEND srcs "${mbedtls_test_srcs_dir}/test_aes_sha_parallel.c")
# Utility
list(APPEND srcs "${mbedtls_test_srcs_dir}/test_apb_dport_access.c"
"${mbedtls_test_srcs_dir}/test_mbedtls_utils.c")
idf_component_register(SRCS ${srcs}
PRIV_REQUIRES ${priv_requires}
WHOLE_ARCHIVE)

View File

@ -0,0 +1,46 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "memory_checks.h"
/* setUp runs before every test */
void setUp(void)
{
test_utils_record_free_mem();
test_utils_set_leak_level(CONFIG_UNITY_CRITICAL_LEAK_LEVEL_GENERAL, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_GENERAL);
test_utils_set_leak_level(CONFIG_UNITY_WARN_LEAK_LEVEL_GENERAL, ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_GENERAL);
test_utils_set_leak_level(0, ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_LWIP);
}
/* tearDown runs after every test */
void tearDown(void)
{
/* some FreeRTOS stuff is cleaned up by idle task */
vTaskDelay(5);
/* clean up some of the newlib's lazy allocations */
esp_reent_cleanup();
/* check if unit test has caused heap corruption in any heap */
TEST_ASSERT_MESSAGE(heap_caps_check_integrity(MALLOC_CAP_INVALID, true), "The test has corrupted the heap");
test_utils_finish_and_evaluate_leaks(test_utils_get_leak_level(ESP_LEAK_TYPE_WARNING, ESP_COMP_LEAK_ALL),
test_utils_get_leak_level(ESP_LEAK_TYPE_CRITICAL, ESP_COMP_LEAK_ALL));
}
static void test_task(void *pvParameters)
{
vTaskDelay(10); /* Delay a bit to let the main task be deleted */
unity_run_menu();
}
void app_main(void)
{
xTaskCreatePinnedToCore(test_task, "testTask", CONFIG_UNITY_FREERTOS_STACK_SIZE, NULL, CONFIG_UNITY_FREERTOS_PRIORITY, NULL, CONFIG_UNITY_FREERTOS_CPU);
}

View File

@ -0,0 +1,2 @@
dependencies:
ccomp_timer: "^1.0.0"

View File

@ -0,0 +1,301 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "mbedtls/ecp.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/sha256.h"
#include "esp_tee.h"
#include "esp_tee_attestation.h"
#include "secure_service_num.h"
#include "esp_tee_sec_storage.h"
#include "cJSON.h"
#include "unity.h"
/* Note: negative value here so that assert message prints a grep-able
error hex value (mbedTLS uses -N for error codes) */
#define TEST_ASSERT_MBEDTLS_OK(X) TEST_ASSERT_EQUAL_HEX32(0, -(X))
#define SHA256_DIGEST_SZ (32)
#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_NONCE (0xABCD1234)
#define ESP_ATT_TK_CLIENT_ID (0x0FACADE0)
static const char *TAG = "test_esp_tee_att";
extern int 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 uint8_t hexchar_to_byte(char hex)
{
if (hex >= '0' && hex <= '9') {
return hex - '0';
} else if (hex >= 'a' && hex <= 'f') {
return hex - 'a' + 10;
} else if (hex >= 'A' && hex <= 'F') {
return hex - 'A' + 10;
} else {
// Handle invalid hex characters
return UINT8_MAX;
}
}
static void hexstr_to_bytes(const char *hex_str, uint8_t **hex_buf, size_t *buf_sz)
{
size_t hex_len = strlen(hex_str);
// Check if the hex string has an even number of characters
TEST_ASSERT_EQUAL(0, hex_len % 2);
// Calculate the size of the byte buffer
*buf_sz = hex_len / 2;
// Allocate memory for the byte buffer
*hex_buf = calloc(*buf_sz, sizeof(uint8_t));
TEST_ASSERT_NOT_NULL(hex_buf);
// Convert each pair of hex characters to a byte
for (size_t i = 0; i < *buf_sz; ++i) {
(*hex_buf)[i] = (hexchar_to_byte(hex_str[2 * i]) << 4) | hexchar_to_byte(hex_str[2 * i + 1]);
}
}
static int decompress_ecdsa_pubkey(const mbedtls_ecp_group *grp, const unsigned char *input, size_t ilen, esp_tee_sec_storage_pubkey_t *pubkey)
{
int ret = -1;
mbedtls_mpi r, x, n;
size_t plen = mbedtls_mpi_size(&grp->P);
if (ilen != plen + 1) {
return (MBEDTLS_ERR_ECP_BAD_INPUT_DATA);
}
if (input[0] != 0x02 && input[0] != 0x03) {
return (MBEDTLS_ERR_ECP_BAD_INPUT_DATA);
}
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&x);
mbedtls_mpi_init(&n);
// x <= input
MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&x, input + 1, plen));
// r = x^2
MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &x, &x));
// r = x^2 + a
if (grp->A.MBEDTLS_PRIVATE(p) == NULL) {
// Special case where a is -3
MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&r, &r, 3));
} else {
MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->A));
}
// r = x^3 + ax
MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &r, &x));
// r = x^3 + ax + b
MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->B));
// Calculate square root of r over finite field P:
// r = sqrt(x^3 + ax + b) = (x^3 + ax + b) ^ ((P + 1) / 4) (mod P)
// n = P + 1
MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&n, &grp->P, 1));
// n = (P + 1) / 4
MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&n, 2));
// r ^ ((P + 1) / 4) (mod p)
MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&r, &r, &n, &grp->P, NULL));
// Select solution that has the correct "sign" (equals odd/even solution in finite group)
if ((input[0] == 0x03) != mbedtls_mpi_get_bit(&r, 0)) {
// r = p - r
MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&r, &grp->P, &r));
}
// y => output
memcpy(pubkey->pub_x, input + 1, ilen - 1);
ret = mbedtls_mpi_write_binary(&r, pubkey->pub_y, plen);
cleanup:
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&x);
mbedtls_mpi_free(&n);
return (ret);
}
static void prehash_token_data(const char *token_json, uint8_t *digest, size_t len)
{
TEST_ASSERT_NOT_NULL(token_json);
TEST_ASSERT_NOT_NULL(digest);
TEST_ASSERT_NOT_EQUAL(0, len);
// Parse JSON string
cJSON *root = cJSON_Parse(token_json);
TEST_ASSERT_NOT_NULL(root);
// Fetching the data to be verified
cJSON *header = cJSON_GetObjectItemCaseSensitive(root, "header");
TEST_ASSERT_NOT_NULL(header);
cJSON *eat = cJSON_GetObjectItemCaseSensitive(root, "eat");
TEST_ASSERT_NOT_NULL(eat);
cJSON *public_key = cJSON_GetObjectItemCaseSensitive(root, "public_key");
TEST_ASSERT_NOT_NULL(public_key);
char *header_str = cJSON_PrintUnformatted(header);
char *eat_str = cJSON_PrintUnformatted(eat);
char *public_key_str = cJSON_PrintUnformatted(public_key);
mbedtls_sha256_context sha256_ctx;
mbedtls_sha256_init(&sha256_ctx);
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_starts(&sha256_ctx, false));
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_update(&sha256_ctx, (const unsigned char *)header_str, strlen(header_str)));
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_update(&sha256_ctx, (const unsigned char *)eat_str, strlen(eat_str)));
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_update(&sha256_ctx, (const unsigned char *)public_key_str, strlen(public_key_str)));
TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256_finish(&sha256_ctx, digest));
mbedtls_sha256_free(&sha256_ctx);
free(public_key_str);
free(eat_str);
free(header_str);
cJSON_Delete(root);
}
static void fetch_pubkey(const char *token_json, esp_tee_sec_storage_pubkey_t *pubkey_ctx)
{
TEST_ASSERT_NOT_NULL(token_json);
TEST_ASSERT_NOT_NULL(pubkey_ctx);
// Parse JSON string
cJSON *root = cJSON_Parse(token_json);
TEST_ASSERT_NOT_NULL(root);
cJSON *public_key = cJSON_GetObjectItemCaseSensitive(root, "public_key");
TEST_ASSERT_NOT_NULL(token_json);
cJSON *compressed = cJSON_GetObjectItemCaseSensitive(public_key, "compressed");
TEST_ASSERT_NOT_NULL(compressed);
uint8_t *pubkey_buf = NULL;
size_t pubkey_buf_sz = 0;
hexstr_to_bytes(compressed->valuestring, &pubkey_buf, &pubkey_buf_sz);
mbedtls_ecp_keypair keypair;
mbedtls_ecp_keypair_init(&keypair);
mbedtls_ecp_point_init(&keypair.MBEDTLS_PRIVATE(Q));
mbedtls_ecp_group_init(&keypair.MBEDTLS_PRIVATE(grp));
TEST_ASSERT_MBEDTLS_OK(mbedtls_ecp_group_load(&keypair.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1));
TEST_ASSERT_EQUAL(0, decompress_ecdsa_pubkey(&keypair.MBEDTLS_PRIVATE(grp), pubkey_buf, pubkey_buf_sz, pubkey_ctx));
mbedtls_ecp_keypair_free(&keypair);
free(pubkey_buf);
cJSON_Delete(root);
}
static void fetch_signature(const char *token_json, esp_tee_sec_storage_sign_t *sign_ctx)
{
TEST_ASSERT_NOT_NULL(token_json);
TEST_ASSERT_NOT_NULL(sign_ctx);
// Parse JSON string
cJSON *root = cJSON_Parse(token_json);
TEST_ASSERT_NOT_NULL(root);
cJSON *sign = cJSON_GetObjectItemCaseSensitive(root, "sign");
TEST_ASSERT_NOT_NULL(sign);
cJSON *sign_r = cJSON_GetObjectItemCaseSensitive(sign, "r");
TEST_ASSERT_NOT_NULL(sign_r);
cJSON *sign_s = cJSON_GetObjectItemCaseSensitive(sign, "s");
TEST_ASSERT_NOT_NULL(sign_s);
uint8_t *sign_r_buf = NULL;
size_t sign_r_buf_sz = 0;
hexstr_to_bytes(sign_r->valuestring, &sign_r_buf, &sign_r_buf_sz);
memcpy(sign_ctx->sign_r, sign_r_buf, sign_r_buf_sz);
free(sign_r_buf);
uint8_t *sign_s_buf = NULL;
size_t sign_s_buf_sz = 0;
hexstr_to_bytes(sign_s->valuestring, &sign_s_buf, &sign_s_buf_sz);
memcpy(sign_ctx->sign_s, sign_s_buf, sign_s_buf_sz);
free(sign_s_buf);
cJSON_Delete(root);
}
TEST_CASE("Test TEE Attestation - Generate and verify the EAT", "[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);
ESP_LOGI(TAG, "Generating EAT for all active firmwares (Bootloader, TEE and non-secure app)...");
// Generating the attestation token
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));
ESP_LOGI(TAG, "EAT generated - length: %"PRIu32"", token_len);
// Pre-hashing the data
uint8_t digest[SHA256_DIGEST_SZ] = {};
prehash_token_data((const char *)token_buf, digest, sizeof(digest));
// Fetching and decompressing the public key
esp_tee_sec_storage_pubkey_t pubkey_ctx = {};
fetch_pubkey((const char *)token_buf, &pubkey_ctx);
// Fetching the signature components
esp_tee_sec_storage_sign_t sign_ctx = {};
fetch_signature((const char *)token_buf, &sign_ctx);
ESP_LOGI(TAG, "Verifying the generated EAT...");
// Verifying the generated token
TEST_ASSERT_EQUAL(0, verify_ecdsa_secp256r1_sign(digest, sizeof(digest), &pubkey_ctx, &sign_ctx));
free(token_buf);
}
TEST_CASE("Test TEE Attestation - Invalid token buffer", "[attestation]")
{
esp_err_t err;
uint32_t token_len = 0;
uint8_t *token_buf = heap_caps_calloc(4, sizeof(uint8_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
TEST_ASSERT_NOT_NULL(token_buf);
err = esp_tee_att_generate_token(ESP_ATT_TK_NONCE, ESP_ATT_TK_CLIENT_ID, (const char *)ESP_ATT_TK_PSA_CERT_REF,
token_buf, 0, &token_len);
TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, err);
err = esp_tee_att_generate_token(ESP_ATT_TK_NONCE, ESP_ATT_TK_CLIENT_ID, (const char *)ESP_ATT_TK_PSA_CERT_REF,
NULL, 0, &token_len);
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, err);
free(token_buf);
}

Some files were not shown because too many files have changed in this diff Show More