mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 09:09:10 -04:00
feat(esp_tee): Support for ESP-TEE - the main
component
This commit is contained in:
parent
420810ee77
commit
373930655a
138
components/esp_tee/CMakeLists.txt
Normal file
138
components/esp_tee/CMakeLists.txt
Normal 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()
|
144
components/esp_tee/Kconfig.projbuild
Normal file
144
components/esp_tee/Kconfig.projbuild
Normal 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
|
90
components/esp_tee/include/esp_tee.h
Normal file
90
components/esp_tee/include/esp_tee.h
Normal 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
|
37
components/esp_tee/include/private/esp_tee_app.h
Normal file
37
components/esp_tee/include/private/esp_tee_app.h
Normal 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
|
100
components/esp_tee/include/private/esp_tee_binary.h
Normal file
100
components/esp_tee/include/private/esp_tee_binary.h
Normal 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
|
52
components/esp_tee/project_include.cmake
Normal file
52
components/esp_tee/project_include.cmake
Normal 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})
|
42
components/esp_tee/scripts/esp32c6/secure_service.tbl
Normal file
42
components/esp_tee/scripts/esp32c6/secure_service.tbl
Normal 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
|
76
components/esp_tee/scripts/secure_service_hdr.py
Normal file
76
components/esp_tee/scripts/secure_service_hdr.py
Normal 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()
|
50
components/esp_tee/scripts/secure_service_tbl.py
Normal file
50
components/esp_tee/scripts/secure_service_tbl.py
Normal 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()
|
24
components/esp_tee/scripts/secure_service_wrap.py
Normal file
24
components/esp_tee/scripts/secure_service_wrap.py
Normal 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()
|
218
components/esp_tee/src/esp_secure_service_wrapper.c
Normal file
218
components/esp_tee/src/esp_secure_service_wrapper.c
Normal 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);
|
||||
}
|
76
components/esp_tee/src/esp_tee.c
Normal file
76
components/esp_tee/src/esp_tee.c
Normal 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;
|
||||
}
|
34
components/esp_tee/src/esp_tee_config.c
Normal file
34
components/esp_tee/src/esp_tee_config.c
Normal 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,
|
||||
};
|
13
components/esp_tee/src/esp_tee_u2m_switch.S
Normal file
13
components/esp_tee/src/esp_tee_u2m_switch.S
Normal 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
|
87
components/esp_tee/subproject/CMakeLists.txt
Normal file
87
components/esp_tee/subproject/CMakeLists.txt
Normal 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()
|
@ -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})
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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
|
@ -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})
|
@ -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);
|
||||
}
|
@ -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
|
@ -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)
|
@ -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;
|
||||
}
|
@ -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);
|
@ -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})
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
@ -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}
|
||||
)
|
@ -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
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
92
components/esp_tee/subproject/main/CMakeLists.txt
Normal file
92
components/esp_tee/subproject/main/CMakeLists.txt
Normal 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}\"")
|
@ -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
|
@ -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
|
534
components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S
Normal file
534
components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S
Normal 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
|
125
components/esp_tee/subproject/main/common/brownout.c
Normal file
125
components/esp_tee/subproject/main/common/brownout.c
Normal 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
|
||||
}
|
43
components/esp_tee/subproject/main/common/esp_app_desc_tee.c
Normal file
43
components/esp_tee/subproject/main/common/esp_app_desc_tee.c
Normal 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");
|
196
components/esp_tee/subproject/main/common/multi_heap.c
Normal file
196
components/esp_tee/subproject/main/common/multi_heap.c
Normal 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;
|
||||
}
|
123
components/esp_tee/subproject/main/common/panic/esp_tee_panic.c
Normal file
123
components/esp_tee/subproject/main/common/panic/esp_tee_panic.c
Normal 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();
|
||||
}
|
@ -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);
|
@ -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);
|
||||
}
|
@ -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
|
410
components/esp_tee/subproject/main/core/esp_secure_services.c
Normal file
410
components/esp_tee/subproject/main/core/esp_secure_services.c
Normal 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
|
186
components/esp_tee/subproject/main/core/esp_tee_init.c
Normal file
186
components/esp_tee/subproject/main/core/esp_tee_init.c
Normal 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) {}
|
185
components/esp_tee/subproject/main/core/esp_tee_intr.c
Normal file
185
components/esp_tee/subproject/main/core/esp_tee_intr.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
6
components/esp_tee/subproject/main/idf_component.yml
Normal file
6
components/esp_tee/subproject/main/idf_component.yml
Normal file
@ -0,0 +1,6 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/json_generator:
|
||||
version: "^1.1.2"
|
||||
rules:
|
||||
- if: "target in [esp32c6]"
|
@ -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
|
78
components/esp_tee/subproject/main/include/esp_tee_intr.h
Normal file
78
components/esp_tee/subproject/main/include/esp_tee_intr.h
Normal 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__
|
100
components/esp_tee/subproject/main/include/multi_heap.h
Normal file
100
components/esp_tee/subproject/main/include/multi_heap.h
Normal 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
|
243
components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in
Normal file
243
components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in
Normal 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!");
|
29
components/esp_tee/subproject/main/ld/esp_tee_ld.cmake
Normal file
29
components/esp_tee/subproject/main/ld/esp_tee_ld.cmake
Normal 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)
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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");
|
||||
}
|
@ -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");
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
@ -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
|
@ -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
|
11
components/esp_tee/test_apps/.build-test-rules.yml
Normal file
11
components/esp_tee/test_apps/.build-test-rules.yml
Normal 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
|
16
components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt
Normal file
16
components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt
Normal 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)
|
292
components/esp_tee/test_apps/tee_cli_app/README.md
Normal file
292
components/esp_tee/test_apps/tee_cli_app/README.md
Normal 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
|
||||
```
|
86
components/esp_tee/test_apps/tee_cli_app/https_server.py
Normal file
86
components/esp_tee/test_apps/tee_cli_app/https_server.py
Normal 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()
|
@ -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 ".")
|
@ -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
|
80
components/esp_tee/test_apps/tee_cli_app/main/app_main.c
Normal file
80
components/esp_tee/test_apps/tee_cli_app/main/app_main.c
Normal 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();
|
||||
}
|
@ -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
|
134
components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c
Normal file
134
components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c
Normal 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));
|
||||
}
|
57
components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c
Normal file
57
components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c
Normal 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));
|
||||
}
|
300
components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c
Normal file
300
components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c
Normal 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));
|
||||
}
|
547
components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c
Normal file
547
components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c
Normal 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));
|
||||
}
|
242
components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py
Normal file
242
components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py
Normal 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()
|
15
components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe
Normal file
15
components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe
Normal 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
|
20
components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults
Normal file
20
components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults
Normal 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"
|
@ -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-----
|
@ -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-----
|
@ -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-----
|
1
components/esp_tee/test_apps/tee_cli_app/version.txt
Normal file
1
components/esp_tee/test_apps/tee_cli_app/version.txt
Normal file
@ -0,0 +1 @@
|
||||
1.0.0
|
18
components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt
Normal file
18
components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt
Normal 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)
|
28
components/esp_tee/test_apps/tee_test_fw/README.md
Normal file
28
components/esp_tee/test_apps/tee_test_fw/README.md
Normal 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
|
||||
```
|
@ -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})
|
@ -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
|
@ -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");
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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)
|
178
components/esp_tee/test_apps/tee_test_fw/conftest.py
Normal file
178
components/esp_tee/test_apps/tee_test_fw/conftest.py
Normal 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)
|
37
components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt
Normal file
37
components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt
Normal 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)
|
46
components/esp_tee/test_apps/tee_test_fw/main/app_main.c
Normal file
46
components/esp_tee/test_apps/tee_test_fw/main/app_main.c
Normal 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);
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
dependencies:
|
||||
ccomp_timer: "^1.0.0"
|
301
components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c
Normal file
301
components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c
Normal 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
Loading…
x
Reference in New Issue
Block a user