From 373930655ab26714ab0028fc01f85dacd48ae7ae Mon Sep 17 00:00:00 2001 From: Laukik Hase Date: Fri, 15 Nov 2024 10:45:29 +0530 Subject: [PATCH] feat(esp_tee): Support for ESP-TEE - the `main` component --- components/esp_tee/CMakeLists.txt | 138 ++++ components/esp_tee/Kconfig.projbuild | 144 ++++ components/esp_tee/include/esp_tee.h | 90 +++ .../esp_tee/include/private/esp_tee_app.h | 37 + .../esp_tee/include/private/esp_tee_binary.h | 100 +++ components/esp_tee/project_include.cmake | 52 ++ .../scripts/esp32c6/secure_service.tbl | 42 ++ .../esp_tee/scripts/secure_service_hdr.py | 76 ++ .../esp_tee/scripts/secure_service_tbl.py | 50 ++ .../esp_tee/scripts/secure_service_wrap.py | 24 + .../esp_tee/src/esp_secure_service_wrapper.c | 218 ++++++ components/esp_tee/src/esp_tee.c | 76 ++ components/esp_tee/src/esp_tee_config.c | 34 + components/esp_tee/src/esp_tee_u2m_switch.S | 13 + components/esp_tee/subproject/CMakeLists.txt | 87 +++ .../components/attestation/CMakeLists.txt | 21 + .../attestation/esp_att_utils_crypto.c | 341 +++++++++ .../attestation/esp_att_utils_json.c | 288 ++++++++ .../attestation/esp_att_utils_part_info.c | 545 ++++++++++++++ .../components/attestation/esp_attestation.c | 304 ++++++++ .../attestation/include/esp_attestation.h | 31 + .../private_include/esp_attestation_utils.h | 278 ++++++++ .../components/tee_attestation/CMakeLists.txt | 13 + .../tee_attestation/esp_tee_attestation.c | 27 + .../tee_attestation/esp_tee_attestation.h | 42 ++ .../components/tee_flash_mgr/CMakeLists.txt | 7 + .../components/tee_flash_mgr/esp_tee_flash.c | 151 ++++ .../tee_flash_mgr/include/esp_tee_flash.h | 78 ++ .../components/tee_ota_ops/CMakeLists.txt | 17 + .../components/tee_ota_ops/esp_tee_ota_ops.c | 143 ++++ .../tee_ota_ops/esp_tee_ota_ops_wrapper.c | 35 + .../tee_ota_ops/include/esp_tee_ota_ops.h | 60 ++ .../components/tee_sec_storage/CMakeLists.txt | 17 + .../include/esp_tee_sec_storage.h | 146 ++++ .../tee_sec_storage/tee_sec_storage.c | 669 ++++++++++++++++++ .../tee_sec_storage/tee_sec_storage_wrapper.c | 53 ++ .../esp_tee/subproject/main/CMakeLists.txt | 92 +++ .../main/arch/riscv/esp_tee_secure_entry.S | 42 ++ .../main/arch/riscv/esp_tee_vector_table.S | 79 +++ .../main/arch/riscv/esp_tee_vectors.S | 534 ++++++++++++++ .../esp_tee/subproject/main/common/brownout.c | 125 ++++ .../subproject/main/common/esp_app_desc_tee.c | 43 ++ .../subproject/main/common/multi_heap.c | 196 +++++ .../main/common/panic/esp_tee_panic.c | 123 ++++ .../main/common/panic/panic_helper.h | 13 + .../main/common/panic/panic_helper_riscv.c | 152 ++++ .../main/core/esp_secure_service_table.c | 25 + .../main/core/esp_secure_services.c | 410 +++++++++++ .../subproject/main/core/esp_tee_init.c | 186 +++++ .../subproject/main/core/esp_tee_intr.c | 185 +++++ .../esp_tee/subproject/main/idf_component.yml | 6 + .../main/include/esp_tee_brownout.h | 18 + .../subproject/main/include/esp_tee_intr.h | 78 ++ .../subproject/main/include/multi_heap.h | 100 +++ .../subproject/main/ld/esp32c6/esp_tee.ld.in | 243 +++++++ .../subproject/main/ld/esp_tee_ld.cmake | 29 + .../main/soc/esp32c6/esp_tee_aes_intr.c | 33 + .../main/soc/esp32c6/esp_tee_apm_intr.c | 126 ++++ .../main/soc/esp32c6/esp_tee_apm_prot_cfg.c | 306 ++++++++ .../soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c | 179 +++++ .../main/soc/esp32c6/esp_tee_secure_sys_cfg.c | 105 +++ .../soc/esp32c6/include/esp_tee_aes_intr.h | 16 + .../soc/esp32c6/include/esp_tee_apm_intr.h | 24 + .../soc/esp32c6/include/esp_tee_rv_utils.h | 128 ++++ .../esp_tee/test_apps/.build-test-rules.yml | 11 + .../test_apps/tee_cli_app/CMakeLists.txt | 16 + .../esp_tee/test_apps/tee_cli_app/README.md | 292 ++++++++ .../test_apps/tee_cli_app/https_server.py | 86 +++ .../test_apps/tee_cli_app/main/CMakeLists.txt | 6 + .../tee_cli_app/main/Kconfig.projbuild | 15 + .../test_apps/tee_cli_app/main/app_main.c | 80 +++ .../tee_cli_app/main/example_tee_srv.h | 33 + .../test_apps/tee_cli_app/main/tee_cmd_wifi.c | 134 ++++ .../test_apps/tee_cli_app/main/tee_srv_att.c | 57 ++ .../test_apps/tee_cli_app/main/tee_srv_ota.c | 300 ++++++++ .../tee_cli_app/main/tee_srv_sec_str.c | 547 ++++++++++++++ .../test_apps/tee_cli_app/pytest_tee_cli.py | 242 +++++++ .../tee_cli_app/sdkconfig.ci.default | 0 .../test_apps/tee_cli_app/sdkconfig.ci.sb_fe | 15 + .../test_apps/tee_cli_app/sdkconfig.defaults | 20 + .../tee_cli_app/test_certs/server_cert.pem | 20 + .../tee_cli_app/test_certs/server_key.pem | 28 + .../test_keys/secure_boot_signing_key.pem | 39 + .../esp_tee/test_apps/tee_cli_app/version.txt | 1 + .../test_apps/tee_test_fw/CMakeLists.txt | 18 + .../esp_tee/test_apps/tee_test_fw/README.md | 28 + .../components/test_sec_srv/CMakeLists.txt | 22 + .../test_sec_srv/include/esp_tee_test.h | 42 ++ .../test_sec_srv/src/test_dummy_srv.c | 14 + .../test_sec_srv/src/test_dummy_srv_wrapper.c | 14 + .../test_sec_srv/src/test_interrupt.c | 174 +++++ .../components/test_sec_srv/src/test_panic.c | 82 +++ .../test_sec_srv/src/test_sec_srv.c | 47 ++ .../components/test_sec_srv/test.tbl | 18 + .../test_sec_srv/test_tee_project.cmake | 15 + .../esp_tee/test_apps/tee_test_fw/conftest.py | 178 +++++ .../test_apps/tee_test_fw/main/CMakeLists.txt | 37 + .../test_apps/tee_test_fw/main/app_main.c | 46 ++ .../tee_test_fw/main/idf_component.yml | 2 + .../tee_test_fw/main/test_esp_tee_att.c | 301 ++++++++ .../main/test_esp_tee_ctx_switch.c | 95 +++ .../tee_test_fw/main/test_esp_tee_interrupt.c | 216 ++++++ .../tee_test_fw/main/test_esp_tee_ota.c | 173 +++++ .../tee_test_fw/main/test_esp_tee_panic.c | 165 +++++ .../tee_test_fw/main/test_esp_tee_sec_stg.c | 274 +++++++ .../test_apps/tee_test_fw/partitions.csv | 6 + .../tee_test_fw/partitions_tee_ota.csv | 10 + .../tee_test_fw/pytest_esp_tee_ut.py | 301 ++++++++ .../tee_test_fw/sdkconfig.ci.default | 0 .../test_apps/tee_test_fw/sdkconfig.ci.ota | 11 + .../test_apps/tee_test_fw/sdkconfig.defaults | 17 + 111 files changed, 12291 insertions(+) create mode 100644 components/esp_tee/CMakeLists.txt create mode 100644 components/esp_tee/Kconfig.projbuild create mode 100644 components/esp_tee/include/esp_tee.h create mode 100644 components/esp_tee/include/private/esp_tee_app.h create mode 100644 components/esp_tee/include/private/esp_tee_binary.h create mode 100644 components/esp_tee/project_include.cmake create mode 100644 components/esp_tee/scripts/esp32c6/secure_service.tbl create mode 100644 components/esp_tee/scripts/secure_service_hdr.py create mode 100644 components/esp_tee/scripts/secure_service_tbl.py create mode 100644 components/esp_tee/scripts/secure_service_wrap.py create mode 100644 components/esp_tee/src/esp_secure_service_wrapper.c create mode 100644 components/esp_tee/src/esp_tee.c create mode 100644 components/esp_tee/src/esp_tee_config.c create mode 100644 components/esp_tee/src/esp_tee_u2m_switch.S create mode 100644 components/esp_tee/subproject/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/attestation/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c create mode 100644 components/esp_tee/subproject/components/attestation/esp_att_utils_json.c create mode 100644 components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c create mode 100644 components/esp_tee/subproject/components/attestation/esp_attestation.c create mode 100644 components/esp_tee/subproject/components/attestation/include/esp_attestation.h create mode 100644 components/esp_tee/subproject/components/attestation/private_include/esp_attestation_utils.h create mode 100644 components/esp_tee/subproject/components/tee_attestation/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.c create mode 100644 components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.h create mode 100644 components/esp_tee/subproject/components/tee_flash_mgr/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/tee_flash_mgr/esp_tee_flash.c create mode 100644 components/esp_tee/subproject/components/tee_flash_mgr/include/esp_tee_flash.h create mode 100644 components/esp_tee/subproject/components/tee_ota_ops/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops.c create mode 100644 components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops_wrapper.c create mode 100644 components/esp_tee/subproject/components/tee_ota_ops/include/esp_tee_ota_ops.h create mode 100644 components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt create mode 100644 components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h create mode 100644 components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c create mode 100644 components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c create mode 100644 components/esp_tee/subproject/main/CMakeLists.txt create mode 100644 components/esp_tee/subproject/main/arch/riscv/esp_tee_secure_entry.S create mode 100644 components/esp_tee/subproject/main/arch/riscv/esp_tee_vector_table.S create mode 100644 components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S create mode 100644 components/esp_tee/subproject/main/common/brownout.c create mode 100644 components/esp_tee/subproject/main/common/esp_app_desc_tee.c create mode 100644 components/esp_tee/subproject/main/common/multi_heap.c create mode 100644 components/esp_tee/subproject/main/common/panic/esp_tee_panic.c create mode 100644 components/esp_tee/subproject/main/common/panic/panic_helper.h create mode 100644 components/esp_tee/subproject/main/common/panic/panic_helper_riscv.c create mode 100644 components/esp_tee/subproject/main/core/esp_secure_service_table.c create mode 100644 components/esp_tee/subproject/main/core/esp_secure_services.c create mode 100644 components/esp_tee/subproject/main/core/esp_tee_init.c create mode 100644 components/esp_tee/subproject/main/core/esp_tee_intr.c create mode 100644 components/esp_tee/subproject/main/idf_component.yml create mode 100644 components/esp_tee/subproject/main/include/esp_tee_brownout.h create mode 100644 components/esp_tee/subproject/main/include/esp_tee_intr.h create mode 100644 components/esp_tee/subproject/main/include/multi_heap.h create mode 100644 components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in create mode 100644 components/esp_tee/subproject/main/ld/esp_tee_ld.cmake create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_aes_intr.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_intr.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_prot_cfg.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/esp_tee_secure_sys_cfg.c create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_aes_intr.h create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_apm_intr.h create mode 100644 components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_rv_utils.h create mode 100644 components/esp_tee/test_apps/.build-test-rules.yml create mode 100644 components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_cli_app/README.md create mode 100644 components/esp_tee/test_apps/tee_cli_app/https_server.py create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/Kconfig.projbuild create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/app_main.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/example_tee_srv.h create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c create mode 100644 components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py create mode 100644 components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default create mode 100644 components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe create mode 100644 components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults create mode 100644 components/esp_tee/test_apps/tee_cli_app/test_certs/server_cert.pem create mode 100644 components/esp_tee/test_apps/tee_cli_app/test_certs/server_key.pem create mode 100644 components/esp_tee/test_apps/tee_cli_app/test_keys/secure_boot_signing_key.pem create mode 100644 components/esp_tee/test_apps/tee_cli_app/version.txt create mode 100644 components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_test_fw/README.md create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/include/esp_tee_test.h create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv_wrapper.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_interrupt.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_panic.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_sec_srv.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test.tbl create mode 100644 components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test_tee_project.cmake create mode 100644 components/esp_tee/test_apps/tee_test_fw/conftest.py create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/app_main.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/idf_component.yml create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ctx_switch.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ota.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_panic.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c create mode 100644 components/esp_tee/test_apps/tee_test_fw/partitions.csv create mode 100644 components/esp_tee/test_apps/tee_test_fw/partitions_tee_ota.csv create mode 100644 components/esp_tee/test_apps/tee_test_fw/pytest_esp_tee_ut.py create mode 100644 components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.default create mode 100644 components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.ota create mode 100644 components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults diff --git a/components/esp_tee/CMakeLists.txt b/components/esp_tee/CMakeLists.txt new file mode 100644 index 0000000000..d66d84e447 --- /dev/null +++ b/components/esp_tee/CMakeLists.txt @@ -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() diff --git a/components/esp_tee/Kconfig.projbuild b/components/esp_tee/Kconfig.projbuild new file mode 100644 index 0000000000..5037dcdd44 --- /dev/null +++ b/components/esp_tee/Kconfig.projbuild @@ -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 diff --git a/components/esp_tee/include/esp_tee.h b/components/esp_tee/include/esp_tee.h new file mode 100644 index 0000000000..3b4833c937 --- /dev/null +++ b/components/esp_tee/include/esp_tee.h @@ -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 +#include +#include +#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 diff --git a/components/esp_tee/include/private/esp_tee_app.h b/components/esp_tee/include/private/esp_tee_app.h new file mode 100644 index 0000000000..7aa652bd63 --- /dev/null +++ b/components/esp_tee/include/private/esp_tee_app.h @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#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 diff --git a/components/esp_tee/include/private/esp_tee_binary.h b/components/esp_tee/include/private/esp_tee_binary.h new file mode 100644 index 0000000000..f0975f451b --- /dev/null +++ b/components/esp_tee/include/private/esp_tee_binary.h @@ -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 +#include +#include +#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 diff --git a/components/esp_tee/project_include.cmake b/components/esp_tee/project_include.cmake new file mode 100644 index 0000000000..b3c0e91265 --- /dev/null +++ b/components/esp_tee/project_include.cmake @@ -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}) diff --git a/components/esp_tee/scripts/esp32c6/secure_service.tbl b/components/esp_tee/scripts/esp32c6/secure_service.tbl new file mode 100644 index 0000000000..ee014a8789 --- /dev/null +++ b/components/esp_tee/scripts/esp32c6/secure_service.tbl @@ -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 diff --git a/components/esp_tee/scripts/secure_service_hdr.py b/components/esp_tee/scripts/secure_service_hdr.py new file mode 100644 index 0000000000..3a2bb2be4c --- /dev/null +++ b/components/esp_tee/scripts/secure_service_hdr.py @@ -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() diff --git a/components/esp_tee/scripts/secure_service_tbl.py b/components/esp_tee/scripts/secure_service_tbl.py new file mode 100644 index 0000000000..f16b8d8703 --- /dev/null +++ b/components/esp_tee/scripts/secure_service_tbl.py @@ -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() diff --git a/components/esp_tee/scripts/secure_service_wrap.py b/components/esp_tee/scripts/secure_service_wrap.py new file mode 100644 index 0000000000..6deb5c9c3c --- /dev/null +++ b/components/esp_tee/scripts/secure_service_wrap.py @@ -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() diff --git a/components/esp_tee/src/esp_secure_service_wrapper.c b/components/esp_tee/src/esp_secure_service_wrapper.c new file mode 100644 index 0000000000..c1b02fbd83 --- /dev/null +++ b/components/esp_tee/src/esp_secure_service_wrapper.c @@ -0,0 +1,218 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#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); +} diff --git a/components/esp_tee/src/esp_tee.c b/components/esp_tee/src/esp_tee.c new file mode 100644 index 0000000000..c8fabb6749 --- /dev/null +++ b/components/esp_tee/src/esp_tee.c @@ -0,0 +1,76 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#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; +} diff --git a/components/esp_tee/src/esp_tee_config.c b/components/esp_tee/src/esp_tee_config.c new file mode 100644 index 0000000000..c506a0c6ee --- /dev/null +++ b/components/esp_tee/src/esp_tee_config.c @@ -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, +}; diff --git a/components/esp_tee/src/esp_tee_u2m_switch.S b/components/esp_tee/src/esp_tee_u2m_switch.S new file mode 100644 index 0000000000..ae53876c3d --- /dev/null +++ b/components/esp_tee/src/esp_tee_u2m_switch.S @@ -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 diff --git a/components/esp_tee/subproject/CMakeLists.txt b/components/esp_tee/subproject/CMakeLists.txt new file mode 100644 index 0000000000..a475a9ba9a --- /dev/null +++ b/components/esp_tee/subproject/CMakeLists.txt @@ -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() diff --git a/components/esp_tee/subproject/components/attestation/CMakeLists.txt b/components/esp_tee/subproject/components/attestation/CMakeLists.txt new file mode 100644 index 0000000000..411472021c --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/CMakeLists.txt @@ -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}) diff --git a/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c b/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c new file mode 100644 index 0000000000..886a3295d2 --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/esp_att_utils_crypto.c @@ -0,0 +1,341 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#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; +} diff --git a/components/esp_tee/subproject/components/attestation/esp_att_utils_json.c b/components/esp_tee/subproject/components/attestation/esp_att_utils_json.c new file mode 100644 index 0000000000..79bce76c7e --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/esp_att_utils_json.c @@ -0,0 +1,288 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#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; +} diff --git a/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c b/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c new file mode 100644 index 0000000000..5fb346b108 --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/esp_att_utils_part_info.c @@ -0,0 +1,545 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include +#include + +#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 +} diff --git a/components/esp_tee/subproject/components/attestation/esp_attestation.c b/components/esp_tee/subproject/components/attestation/esp_attestation.c new file mode 100644 index 0000000000..2fa56295f1 --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/esp_attestation.c @@ -0,0 +1,304 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include + +#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; +} diff --git a/components/esp_tee/subproject/components/attestation/include/esp_attestation.h b/components/esp_tee/subproject/components/attestation/include/esp_attestation.h new file mode 100644 index 0000000000..d83725d0f5 --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/include/esp_attestation.h @@ -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 diff --git a/components/esp_tee/subproject/components/attestation/private_include/esp_attestation_utils.h b/components/esp_tee/subproject/components/attestation/private_include/esp_attestation_utils.h new file mode 100644 index 0000000000..a5d2b2dac6 --- /dev/null +++ b/components/esp_tee/subproject/components/attestation/private_include/esp_attestation_utils.h @@ -0,0 +1,278 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#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 diff --git a/components/esp_tee/subproject/components/tee_attestation/CMakeLists.txt b/components/esp_tee/subproject/components/tee_attestation/CMakeLists.txt new file mode 100644 index 0000000000..ed3d3b4449 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_attestation/CMakeLists.txt @@ -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}) diff --git a/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.c b/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.c new file mode 100644 index 0000000000..1f1c27e97c --- /dev/null +++ b/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.c @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#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); +} diff --git a/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.h b/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.h new file mode 100644 index 0000000000..4de73aa69f --- /dev/null +++ b/components/esp_tee/subproject/components/tee_attestation/esp_tee_attestation.h @@ -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 diff --git a/components/esp_tee/subproject/components/tee_flash_mgr/CMakeLists.txt b/components/esp_tee/subproject/components/tee_flash_mgr/CMakeLists.txt new file mode 100644 index 0000000000..569101f7a4 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_flash_mgr/CMakeLists.txt @@ -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) diff --git a/components/esp_tee/subproject/components/tee_flash_mgr/esp_tee_flash.c b/components/esp_tee/subproject/components/tee_flash_mgr/esp_tee_flash.c new file mode 100644 index 0000000000..cdbddd16f6 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_flash_mgr/esp_tee_flash.c @@ -0,0 +1,151 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#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; +} diff --git a/components/esp_tee/subproject/components/tee_flash_mgr/include/esp_tee_flash.h b/components/esp_tee/subproject/components/tee_flash_mgr/include/esp_tee_flash.h new file mode 100644 index 0000000000..f0551cae74 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_flash_mgr/include/esp_tee_flash.h @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include + +#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); diff --git a/components/esp_tee/subproject/components/tee_ota_ops/CMakeLists.txt b/components/esp_tee/subproject/components/tee_ota_ops/CMakeLists.txt new file mode 100644 index 0000000000..e5ac22068a --- /dev/null +++ b/components/esp_tee/subproject/components/tee_ota_ops/CMakeLists.txt @@ -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}) diff --git a/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops.c b/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops.c new file mode 100644 index 0000000000..5f6efa00ad --- /dev/null +++ b/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops.c @@ -0,0 +1,143 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#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; +} diff --git a/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops_wrapper.c b/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops_wrapper.c new file mode 100644 index 0000000000..ccc4213d4e --- /dev/null +++ b/components/esp_tee/subproject/components/tee_ota_ops/esp_tee_ota_ops_wrapper.c @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +#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); +} diff --git a/components/esp_tee/subproject/components/tee_ota_ops/include/esp_tee_ota_ops.h b/components/esp_tee/subproject/components/tee_ota_ops/include/esp_tee_ota_ops.h new file mode 100644 index 0000000000..736230e653 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_ota_ops/include/esp_tee_ota_ops.h @@ -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 diff --git a/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt b/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt new file mode 100644 index 0000000000..dc7650dd56 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_sec_storage/CMakeLists.txt @@ -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} + ) diff --git a/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h b/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h new file mode 100644 index 0000000000..d1a48fd11b --- /dev/null +++ b/components/esp_tee/subproject/components/tee_sec_storage/include/esp_tee_sec_storage.h @@ -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 +#include +#include + +#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 diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c new file mode 100644 index 0000000000..4d39a6a6e5 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage.c @@ -0,0 +1,669 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#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); +} diff --git a/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c new file mode 100644 index 0000000000..94b69a99e7 --- /dev/null +++ b/components/esp_tee/subproject/components/tee_sec_storage/tee_sec_storage_wrapper.c @@ -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); +} diff --git a/components/esp_tee/subproject/main/CMakeLists.txt b/components/esp_tee/subproject/main/CMakeLists.txt new file mode 100644 index 0000000000..ebdf46b7c0 --- /dev/null +++ b/components/esp_tee/subproject/main/CMakeLists.txt @@ -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}\"") diff --git a/components/esp_tee/subproject/main/arch/riscv/esp_tee_secure_entry.S b/components/esp_tee/subproject/main/arch/riscv/esp_tee_secure_entry.S new file mode 100644 index 0000000000..dc5487d48d --- /dev/null +++ b/components/esp_tee/subproject/main/arch/riscv/esp_tee_secure_entry.S @@ -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 diff --git a/components/esp_tee/subproject/main/arch/riscv/esp_tee_vector_table.S b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vector_table.S new file mode 100644 index 0000000000..9826ea129c --- /dev/null +++ b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vector_table.S @@ -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 diff --git a/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S new file mode 100644 index 0000000000..aded4c17a5 --- /dev/null +++ b/components/esp_tee/subproject/main/arch/riscv/esp_tee_vectors.S @@ -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 diff --git a/components/esp_tee/subproject/main/common/brownout.c b/components/esp_tee/subproject/main/common/brownout.c new file mode 100644 index 0000000000..e0038abeb3 --- /dev/null +++ b/components/esp_tee/subproject/main/common/brownout.c @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#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 +} diff --git a/components/esp_tee/subproject/main/common/esp_app_desc_tee.c b/components/esp_tee/subproject/main/common/esp_app_desc_tee.c new file mode 100644 index 0000000000..9dbc004078 --- /dev/null +++ b/components/esp_tee/subproject/main/common/esp_app_desc_tee.c @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#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"); diff --git a/components/esp_tee/subproject/main/common/multi_heap.c b/components/esp_tee/subproject/main/common/multi_heap.c new file mode 100644 index 0000000000..5da57c4f4d --- /dev/null +++ b/components/esp_tee/subproject/main/common/multi_heap.c @@ -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; +} diff --git a/components/esp_tee/subproject/main/common/panic/esp_tee_panic.c b/components/esp_tee/subproject/main/common/panic/esp_tee_panic.c new file mode 100644 index 0000000000..8558a5e0e7 --- /dev/null +++ b/components/esp_tee/subproject/main/common/panic/esp_tee_panic.c @@ -0,0 +1,123 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#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(); +} diff --git a/components/esp_tee/subproject/main/common/panic/panic_helper.h b/components/esp_tee/subproject/main/common/panic/panic_helper.h new file mode 100644 index 0000000000..5a4b90da45 --- /dev/null +++ b/components/esp_tee/subproject/main/common/panic/panic_helper.h @@ -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); diff --git a/components/esp_tee/subproject/main/common/panic/panic_helper_riscv.c b/components/esp_tee/subproject/main/common/panic/panic_helper_riscv.c new file mode 100644 index 0000000000..de2eae60fd --- /dev/null +++ b/components/esp_tee/subproject/main/common/panic/panic_helper_riscv.c @@ -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); +} diff --git a/components/esp_tee/subproject/main/core/esp_secure_service_table.c b/components/esp_tee/subproject/main/core/esp_secure_service_table.c new file mode 100644 index 0000000000..e094c3a6a4 --- /dev/null +++ b/components/esp_tee/subproject/main/core/esp_secure_service_table.c @@ -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 diff --git a/components/esp_tee/subproject/main/core/esp_secure_services.c b/components/esp_tee/subproject/main/core/esp_secure_services.c new file mode 100644 index 0000000000..79bbed2b05 --- /dev/null +++ b/components/esp_tee/subproject/main/core/esp_secure_services.c @@ -0,0 +1,410 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#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 diff --git a/components/esp_tee/subproject/main/core/esp_tee_init.c b/components/esp_tee/subproject/main/core/esp_tee_init.c new file mode 100644 index 0000000000..88c7f804ba --- /dev/null +++ b/components/esp_tee/subproject/main/core/esp_tee_init.c @@ -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) {} diff --git a/components/esp_tee/subproject/main/core/esp_tee_intr.c b/components/esp_tee/subproject/main/core/esp_tee_intr.c new file mode 100644 index 0000000000..2b64871348 --- /dev/null +++ b/components/esp_tee/subproject/main/core/esp_tee_intr.c @@ -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); + } + } +} diff --git a/components/esp_tee/subproject/main/idf_component.yml b/components/esp_tee/subproject/main/idf_component.yml new file mode 100644 index 0000000000..d2a3d2d24d --- /dev/null +++ b/components/esp_tee/subproject/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/json_generator: + version: "^1.1.2" + rules: + - if: "target in [esp32c6]" diff --git a/components/esp_tee/subproject/main/include/esp_tee_brownout.h b/components/esp_tee/subproject/main/include/esp_tee_brownout.h new file mode 100644 index 0000000000..1453154915 --- /dev/null +++ b/components/esp_tee/subproject/main/include/esp_tee_brownout.h @@ -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 diff --git a/components/esp_tee/subproject/main/include/esp_tee_intr.h b/components/esp_tee/subproject/main/include/esp_tee_intr.h new file mode 100644 index 0000000000..9a7371dd75 --- /dev/null +++ b/components/esp_tee/subproject/main/include/esp_tee_intr.h @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#ifndef __ASSEMBLER__ + +#include +#include + +#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__ diff --git a/components/esp_tee/subproject/main/include/multi_heap.h b/components/esp_tee/subproject/main/include/multi_heap.h new file mode 100644 index 0000000000..b61a0b0bf3 --- /dev/null +++ b/components/esp_tee/subproject/main/include/multi_heap.h @@ -0,0 +1,100 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +#include +#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 diff --git a/components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in b/components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in new file mode 100644 index 0000000000..95646e3867 --- /dev/null +++ b/components/esp_tee/subproject/main/ld/esp32c6/esp_tee.ld.in @@ -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!"); diff --git a/components/esp_tee/subproject/main/ld/esp_tee_ld.cmake b/components/esp_tee/subproject/main/ld/esp_tee_ld.cmake new file mode 100644 index 0000000000..e7569ed498 --- /dev/null +++ b/components/esp_tee/subproject/main/ld/esp_tee_ld.cmake @@ -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) diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_aes_intr.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_aes_intr.c new file mode 100644 index 0000000000..9c23c53924 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_aes_intr.c @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#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; +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_intr.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_intr.c new file mode 100644 index 0000000000..95ddb92e32 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_intr.c @@ -0,0 +1,126 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#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; +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_prot_cfg.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_prot_cfg.c new file mode 100644 index 0000000000..c5c3e645c8 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_apm_prot_cfg.c @@ -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"); +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c new file mode 100644 index 0000000000..af6cf5e20b --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_pmp_pma_prot_cfg.c @@ -0,0 +1,179 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#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"); +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_secure_sys_cfg.c b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_secure_sys_cfg.c new file mode 100644 index 0000000000..9b558054cc --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/esp_tee_secure_sys_cfg.c @@ -0,0 +1,105 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include + +#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); +} diff --git a/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_aes_intr.h b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_aes_intr.h new file mode 100644 index 0000000000..ebdbebe451 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_aes_intr.h @@ -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 diff --git a/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_apm_intr.h b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_apm_intr.h new file mode 100644 index 0000000000..6019df64b3 --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_apm_intr.h @@ -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 diff --git a/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_rv_utils.h b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_rv_utils.h new file mode 100644 index 0000000000..60e588e02b --- /dev/null +++ b/components/esp_tee/subproject/main/soc/esp32c6/include/esp_tee_rv_utils.h @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#include +#include + +#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 diff --git a/components/esp_tee/test_apps/.build-test-rules.yml b/components/esp_tee/test_apps/.build-test-rules.yml new file mode 100644 index 0000000000..4f0f92041d --- /dev/null +++ b/components/esp_tee/test_apps/.build-test-rules.yml @@ -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 diff --git a/components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt b/components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt new file mode 100644 index 0000000000..09fa1b505e --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/CMakeLists.txt @@ -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) diff --git a/components/esp_tee/test_apps/tee_cli_app/README.md b/components/esp_tee/test_apps/tee_cli_app/README.md new file mode 100644 index 0000000000..b57c21af35 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/README.md @@ -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 `. + +- 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 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=] [] + Join WiFi AP as a station + --timeout= Connection timeout, ms + SSID of AP + PSK of AP + +tee_ota + Initiate TEE app OTA + URL for fetching the update + +user_ota + Initiate User app OTA + URL for fetching the update + +tee_att_info + Dump the TEE-generated entity attestation token + +get_msg_sha256 "" + Get the SHA256 digest for the given message + "" Message for SHA256 digest calculation + +tee_sec_stg_gen_key + Generate and store a new key of the specified type in the given TEE secure + storage slot + TEE Secure storage slot for storing the key + Key type (0: ECDSA_SECP256R1, 1: AES256) + +tee_sec_stg_sign + Sign a message using the ECDSA keypair stored in the given slot ID and verify + the signature + TEE Secure storage slot storing the ecdsa-secp256r1 keypair + SHA256 digest of the message to be signed and verified + +tee_sec_stg_encrypt + 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 +``` diff --git a/components/esp_tee/test_apps/tee_cli_app/https_server.py b/components/esp_tee/test_apps/tee_cli_app/https_server.py new file mode 100644 index 0000000000..352e935734 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/https_server.py @@ -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() diff --git a/components/esp_tee/test_apps/tee_cli_app/main/CMakeLists.txt b/components/esp_tee/test_apps/tee_cli_app/main/CMakeLists.txt new file mode 100644 index 0000000000..825c28ebe7 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/CMakeLists.txt @@ -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 ".") diff --git a/components/esp_tee/test_apps/tee_cli_app/main/Kconfig.projbuild b/components/esp_tee/test_apps/tee_cli_app/main/Kconfig.projbuild new file mode 100644 index 0000000000..f748858eea --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/Kconfig.projbuild @@ -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 diff --git a/components/esp_tee/test_apps/tee_cli_app/main/app_main.c b/components/esp_tee/test_apps/tee_cli_app/main/app_main.c new file mode 100644 index 0000000000..9f619a8ee7 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/app_main.c @@ -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(); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/main/example_tee_srv.h b/components/esp_tee/test_apps/tee_cli_app/main/example_tee_srv.h new file mode 100644 index 0000000000..87a58e9e65 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/example_tee_srv.h @@ -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 diff --git a/components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c b/components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c new file mode 100644 index 0000000000..e09b2168bb --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/tee_cmd_wifi.c @@ -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)); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c new file mode 100644 index 0000000000..985897d7e5 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_att.c @@ -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)); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c new file mode 100644 index 0000000000..42aacd3a37 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_ota.c @@ -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)); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c new file mode 100644 index 0000000000..fad89de36f --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/main/tee_srv_sec_str.c @@ -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)); +} diff --git a/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py b/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py new file mode 100644 index 0000000000..ef948dbc7b --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/pytest_tee_cli.py @@ -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() diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.default new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe new file mode 100644 index 0000000000..7ae1369488 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.ci.sb_fe @@ -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 diff --git a/components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults new file mode 100644 index 0000000000..f6cf613bd1 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/sdkconfig.defaults @@ -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" diff --git a/components/esp_tee/test_apps/tee_cli_app/test_certs/server_cert.pem b/components/esp_tee/test_apps/tee_cli_app/test_certs/server_cert.pem new file mode 100644 index 0000000000..b29ba7ab1f --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/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----- diff --git a/components/esp_tee/test_apps/tee_cli_app/test_certs/server_key.pem b/components/esp_tee/test_apps/tee_cli_app/test_certs/server_key.pem new file mode 100644 index 0000000000..20a4bdb624 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/test_certs/server_key.pem @@ -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----- diff --git a/components/esp_tee/test_apps/tee_cli_app/test_keys/secure_boot_signing_key.pem b/components/esp_tee/test_apps/tee_cli_app/test_keys/secure_boot_signing_key.pem new file mode 100644 index 0000000000..405f3a01e7 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/test_keys/secure_boot_signing_key.pem @@ -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----- diff --git a/components/esp_tee/test_apps/tee_cli_app/version.txt b/components/esp_tee/test_apps/tee_cli_app/version.txt new file mode 100644 index 0000000000..3eefcb9dd5 --- /dev/null +++ b/components/esp_tee/test_apps/tee_cli_app/version.txt @@ -0,0 +1 @@ +1.0.0 diff --git a/components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt b/components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt new file mode 100644 index 0000000000..ac89d5cec6 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/CMakeLists.txt @@ -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) diff --git a/components/esp_tee/test_apps/tee_test_fw/README.md b/components/esp_tee/test_apps/tee_test_fw/README.md new file mode 100644 index 0000000000..8259fa367e --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/README.md @@ -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 +``` diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/CMakeLists.txt b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/CMakeLists.txt new file mode 100644 index 0000000000..a93e6bc7ef --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/CMakeLists.txt @@ -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}) diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/include/esp_tee_test.h b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/include/esp_tee_test.h new file mode 100644 index 0000000000..0830bfb8be --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/include/esp_tee_test.h @@ -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 diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv.c new file mode 100644 index 0000000000..24faeb5e41 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv.c @@ -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"); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv_wrapper.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv_wrapper.c new file mode 100644 index 0000000000..ae40a94193 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_dummy_srv_wrapper.c @@ -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); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_interrupt.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_interrupt.c new file mode 100644 index 0000000000..c5877e009a --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_interrupt.c @@ -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); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_panic.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_panic.c new file mode 100644 index 0000000000..e159106cb9 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_panic.c @@ -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 +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_sec_srv.c b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_sec_srv.c new file mode 100644 index 0000000000..7d84a4e7a7 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/src/test_sec_srv.c @@ -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; +} diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test.tbl b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test.tbl new file mode 100644 index 0000000000..08f27b7b4f --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test.tbl @@ -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 diff --git a/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test_tee_project.cmake b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test_tee_project.cmake new file mode 100644 index 0000000000..8be3ba76b3 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/components/test_sec_srv/test_tee_project.cmake @@ -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) diff --git a/components/esp_tee/test_apps/tee_test_fw/conftest.py b/components/esp_tee/test_apps/tee_test_fw/conftest.py new file mode 100644 index 0000000000..67055572ee --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/conftest.py @@ -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) diff --git a/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt b/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt new file mode 100644 index 0000000000..9cb889dcdb --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/CMakeLists.txt @@ -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) diff --git a/components/esp_tee/test_apps/tee_test_fw/main/app_main.c b/components/esp_tee/test_apps/tee_test_fw/main/app_main.c new file mode 100644 index 0000000000..14c57a8afe --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/app_main.c @@ -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); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/idf_component.yml b/components/esp_tee/test_apps/tee_test_fw/main/idf_component.yml new file mode 100644 index 0000000000..2ae836a935 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/idf_component.yml @@ -0,0 +1,2 @@ +dependencies: + ccomp_timer: "^1.0.0" diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c new file mode 100644 index 0000000000..6dd98f1024 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_att.c @@ -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); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ctx_switch.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ctx_switch.c new file mode 100644 index 0000000000..298d3ade3e --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ctx_switch.c @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "esp_tee.h" +#include "secure_service_num.h" +#include "esp_tee_test.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "sdkconfig.h" + +static void test_op(int sec_srv_num, uint32_t a, uint32_t b) +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + uint32_t lval = 0; + switch (sec_srv_num) { + case SS_ESP_TEE_SERVICE_ADD: + lval = (a + b); + break; + case SS_ESP_TEE_SERVICE_SUB: + lval = (a - b); + break; + case SS_ESP_TEE_SERVICE_MUL: + lval = (a * b); + break; + case SS_ESP_TEE_SERVICE_DIV: + lval = (a / b); + break; + default: + lval = UINT32_MAX; + break; + } + + uint32_t val = esp_tee_service_call(3, sec_srv_num, a, b); + TEST_ASSERT_EQUAL_UINT32(lval, val); + + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Sample app world switch failed"); +} + +TEST_CASE("Test single calls to sample app (basic services)", "[basic]") +{ + const uint32_t a = 200, b = 100; + test_op(SS_ESP_TEE_SERVICE_ADD, a, b); + test_op(SS_ESP_TEE_SERVICE_SUB, a, b); + test_op(SS_ESP_TEE_SERVICE_MUL, a, b); + test_op(SS_ESP_TEE_SERVICE_DIV, a, b); +} + +TEST_CASE("Test multiple calls to sample app (basic services)", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + for (int i = 0; i < 1024; i++) { + uint32_t val = esp_tee_service_call(3, SS_ESP_TEE_SERVICE_ADD, i, i + 1); + TEST_ASSERT_EQUAL_UINT32((2 * i + 1), val); + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Sample app world switch failed"); + } +} + +TEST_CASE("Custom secure service call", "[basic]") +{ + dummy_secure_service(); +} + +void test_task(void *pvParameters) +{ + for (int i = 0; i < 8; i++) { + esp_rom_printf("[mode: %d] test_task - %d\n", esp_cpu_get_curr_privilege_level(), i); + vTaskDelay(pdMS_TO_TICKS(1000)); + } + vTaskDelete(NULL); +} + +TEST_CASE("Task switching during secure service calls", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + xTaskCreate(test_task, "test_task", 4096, NULL, CONFIG_UNITY_FREERTOS_PRIORITY + 3, NULL); + + const uint32_t a = 100, b = 200, iter = 8; + TEST_ASSERT_EQUAL_UINT32(a + b * iter, esp_tee_service_call(4, SS_ADD_IN_LOOP, a, b, iter)); + + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c new file mode 100644 index 0000000000..9b172c79e2 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_interrupt.c @@ -0,0 +1,216 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "esp_log.h" +#include "esp_intr_alloc.h" +#include "driver/gptimer.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" + +#include "esp_tee.h" +#include "esp_tee_test.h" +#include "secure_service_num.h" + +#include "unity.h" + +static const char *TAG = "test_esp_tee_intr"; + +/* ---------------------------------------------------- Utility functions ---------------------------------------------------- */ + +typedef struct { + uint64_t event_count; +} test_queue_element_t; + +static QueueHandle_t s_timer_queue; + +static gptimer_handle_t gptimer = NULL; + +static volatile uint32_t ns_int_count; + +static bool IRAM_ATTR test_timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_data) +{ + ESP_EARLY_LOGI(TAG, "Timer ISR Handler from World %d!", esp_cpu_get_curr_privilege_level()); + + BaseType_t high_task_awoken = pdFALSE; + QueueHandle_t queue = (QueueHandle_t)user_data; + // Retrieve count value and send to queue + test_queue_element_t ele = { + .event_count = edata->count_value + }; + ns_int_count += 1; + + xQueueSendFromISR(queue, &ele, &high_task_awoken); + // return whether we need to yield at the end of ISR + return (high_task_awoken == pdTRUE); +} + +static void IRAM_ATTR timer_evt_task(void *arg) +{ + int record = 3; + while (1) { + test_queue_element_t ele; + if (xQueueReceive(s_timer_queue, &ele, pdMS_TO_TICKS(2000))) { + ESP_LOGI(TAG, "Timer reloaded, count=%llu", ele.event_count); + record--; + } else { + ESP_LOGW(TAG, "Missed one count event"); + } + if (!record) { + break; + } + } +} + +static void test_timer_init(bool for_ns_world) +{ + s_timer_queue = xQueueCreate(10, sizeof(test_queue_element_t)); + if (!s_timer_queue) { + ESP_LOGE(TAG, "Creating queue failed"); + return; + } + + ns_int_count = 0; + + /* Select and initialize basic parameters of the timer */ + gptimer_config_t timer_config = { + .clk_src = GPTIMER_CLK_SRC_DEFAULT, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = 1000000, // 1MHz, 1 tick=1us + }; + ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &gptimer)); + + gptimer_event_callbacks_t cbs = { + .on_alarm = test_timer_on_alarm_cb, + }; + ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, s_timer_queue)); + + ESP_ERROR_CHECK(gptimer_enable(gptimer)); + + gptimer_alarm_config_t alarm_config2 = { + .reload_count = 0, + .alarm_count = 250000, // Alarm target = 250ms @ resolution 1MHz + .flags.auto_reload_on_alarm = true, + }; + ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config2)); + ESP_ERROR_CHECK(gptimer_start(gptimer)); + + if (for_ns_world) { + timer_evt_task(NULL); + } +} + +static void test_timer_deinit(void) +{ + ESP_ERROR_CHECK(gptimer_stop(gptimer)); + ESP_ERROR_CHECK(gptimer_disable(gptimer)); + ESP_ERROR_CHECK(gptimer_del_timer(gptimer)); + + if (s_timer_queue != NULL) { + vQueueDelete(s_timer_queue); + s_timer_queue = NULL; + } +} + +/* ---------------------------------------------------- Test cases ---------------------------------------------------- */ + +TEST_CASE("Test Secure interrupt in Non-Secure World", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + volatile uint32_t lsecure_int_count = 0; + + /* Pass the variable to secure world to record the interrupt count. */ + esp_tee_service_call(2, SS_ESP_TEE_TEST_INT_COUNT, &lsecure_int_count); + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); + + esp_tee_service_call(2, SS_ESP_TEE_TEST_TIMER_INIT, true); + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); + + /* Secure timer initialized. + * As secure timer interrupt will fire; CPU will switch to secure world. + * Secure world ISR handler will be called, Secure ISR log can be observed on console. + * After handling the secure interrupt, CPU will return to non-secure world + * and resume this loop and wait for the next secure timer interrupt. + * CPU will wait for TEE_TEST_INT_COUNT number of secure interrupts. + */ + while (lsecure_int_count < TEE_TEST_INT_COUNT); + + /* After waiting for TEE_TEST_INT_COUNT secure interrupt, + * disable the secure timer and assert the test status. + */ + esp_tee_service_call(2, SS_ESP_TEE_TEST_TIMER_INIT, false); + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); + + /* Assert the number of secure interrupt occurred again. */ + TEST_ASSERT_EQUAL_UINT32(TEE_TEST_INT_COUNT, lsecure_int_count); +} + +TEST_CASE("Test Secure interrupt in Secure World", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + uint32_t cnt = esp_tee_service_call(1, SS_ESP_TEE_SECURE_INT_TEST); + TEST_ASSERT_EQUAL_UINT32(TEE_TEST_INT_COUNT, cnt); + + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); +} + +static volatile uint32_t *get_ns_int_count(void) +{ + return &ns_int_count; +} + +TEST_CASE("Test Non-secure interrupt in Secure World", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not W1"); + + /* Non-secure world timer initialization. */ + ESP_LOGI(TAG, "Enabling test timer from non-secure world"); + test_timer_init(false); + + volatile uint32_t *volatile lns_int_count; + lns_int_count = get_ns_int_count(); + + /* After non-secure timer initialization, + * CPU will switch to secure world by using a service call to test API. + * CPU will wait in finite loop in secure world. + * And as non-secure timer interrupt fires, CPU will switch to non-secure world. + * Non-secure world ISR handler will be called, non-secure ISR log can be obsereved on console. + * After handling the interrupt in non-secure world, CPU will switch back to secure world + * and wait for the next timer interrupt. + * In secure world CPU will wait for TEE_TEST_INT_COUNT non-secure interrupts. + */ + uint32_t val = esp_tee_service_call(2, SS_ESP_TEE_NON_SECURE_INT_TEST, lns_int_count); + TEST_ASSERT_EQUAL_UINT32(0, val); + + world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "World switch failed"); + + ESP_LOGI(TAG, "Disabling test timer from non-secure world"); + test_timer_deinit(); +} + +TEST_CASE("Test Non-secure interrupt in Non-Secure World", "[basic]") +{ + esp_cpu_priv_mode_t world = esp_cpu_get_curr_privilege_level(); + TEST_ASSERT_MESSAGE((world == ESP_CPU_NS_MODE), "Current world is not NS"); + + ESP_LOGI(TAG, "Enabling test timer from non-secure world"); + test_timer_init(true); + + vTaskDelay(pdMS_TO_TICKS(1000)); + + ESP_LOGI(TAG, "Disabling test timer from non-secure world"); + test_timer_deinit(); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ota.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ota.c new file mode 100644 index 0000000000..6096e49ac3 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_ota.c @@ -0,0 +1,173 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include <string.h> + +#include "unity.h" +#include "esp_log.h" + +#include "esp_system.h" +#include "esp_partition.h" +#include "esp_image_format.h" +#include "esp_tee_ota_ops.h" + +#include "esp_tee.h" +#include "secure_service_num.h" + +#define TEE_IMG_SRC_PART_SUBTYPE (ESP_PARTITION_SUBTYPE_APP_OTA_1) +#define OTA_BUF_SIZE (512) + +#define FLASH_SECTOR_SIZE (4096) +#define ALIGN_UP(num, align) (((num) + ((align)-1)) & ~((align)-1)) +#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) + +static const char *TAG = "test_esp_tee_ota"; + +TEST_CASE("Test TEE OTA - API state check", "[ota_neg_1]") +{ + uint8_t buf[OTA_BUF_SIZE]; + memset(buf, 0xAA, sizeof(buf)); + + ESP_LOGI(TAG, "TEE OTA - write without begin"); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_ota_write(0x00, (const void *)buf, sizeof(buf))); + + ESP_LOGI(TAG, "TEE OTA - end without begin and write"); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_ota_end()); + + ESP_LOGI(TAG, "TEE OTA - end without write"); + TEST_ESP_OK(esp_tee_ota_begin()); + TEST_ESP_ERR(ESP_ERR_IMAGE_INVALID, esp_tee_ota_end()); +} + +TEST_CASE("Test TEE OTA - Write out of bounds", "[ota_neg_1]") +{ + uint8_t buf[OTA_BUF_SIZE]; + memset(buf, 0xAA, sizeof(buf)); + + const esp_partition_t *tee_next = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_TEE_1, NULL); + TEST_ASSERT_NOT_NULL(tee_next); + + TEST_ESP_OK(esp_tee_ota_begin()); + TEST_ESP_ERR(ESP_FAIL, esp_tee_ota_write(tee_next->size, (const void *)buf, sizeof(buf))); + TEST_ESP_ERR(ESP_FAIL, esp_tee_ota_write(tee_next->size - sizeof(buf) + 1, (const void *)buf, sizeof(buf))); +} + +TEST_CASE("Test TEE OTA - Invalid image", "[ota_neg_1]") +{ + uint8_t buf[OTA_BUF_SIZE]; + memset(buf, 0xAA, sizeof(buf)); + + TEST_ESP_OK(esp_tee_ota_begin()); + TEST_ESP_OK(esp_tee_ota_write(0x00, (const void *)buf, sizeof(buf))); + TEST_ESP_ERR(ESP_ERR_IMAGE_INVALID, esp_tee_ota_end()); +} + +static uint32_t copy_tee_update(void) +{ + /* NOTE: In a real-world scenario, we would get the non-secure app's next update partition */ + const esp_partition_t *tee_update = esp_partition_find_first(ESP_PARTITION_TYPE_APP, TEE_IMG_SRC_PART_SUBTYPE, NULL); + TEST_ASSERT_NOT_NULL(tee_update); + + const esp_partition_pos_t tee_next_pos = { + .offset = tee_update->address, + .size = tee_update->size, + }; + esp_image_metadata_t tee_next_metadata = {}; + + TEST_ESP_OK(esp_image_get_metadata(&tee_next_pos, &tee_next_metadata)); + + uint32_t tee_next_img_len = tee_next_metadata.image_len; + +#if CONFIG_SECURE_BOOT_V2_ENABLED + tee_next_img_len = ALIGN_UP(tee_next_img_len, FLASH_SECTOR_SIZE) + FLASH_SECTOR_SIZE; +#endif + + uint32_t curr_write_offset = 0; + bool image_header_was_checked = false; /* deal with all receive packet */ + + /* an ota data write buffer ready to write to the flash */ + uint8_t ota_write_data[OTA_BUF_SIZE]; + memset(ota_write_data, 0xFF, OTA_BUF_SIZE); + + while (curr_write_offset < tee_next_img_len) { + uint32_t bytes_to_copy = (curr_write_offset + OTA_BUF_SIZE <= tee_next_img_len) + ? OTA_BUF_SIZE + : (tee_next_img_len - curr_write_offset); + + TEST_ESP_OK(esp_partition_read(tee_update, curr_write_offset, ota_write_data, bytes_to_copy)); + + if (image_header_was_checked == false) { + esp_image_header_t img_hdr; + memcpy(&img_hdr, ota_write_data, sizeof(esp_image_header_t)); + TEST_ASSERT_EQUAL(img_hdr.chip_id, CONFIG_IDF_FIRMWARE_CHIP_ID); + image_header_was_checked = true; + + TEST_ESP_OK(esp_tee_ota_begin()); + } + + TEST_ESP_OK(esp_tee_ota_write(curr_write_offset, (const void *)ota_write_data, bytes_to_copy)); + + curr_write_offset += bytes_to_copy; + memset(ota_write_data, 0xFF, OTA_BUF_SIZE); + } + + ESP_LOGI(TAG, "Done copying the update!"); + return curr_write_offset; +} + +TEST_CASE("Test TEE OTA - Corrupted image", "[ota_neg_2]") +{ + ESP_LOGI(TAG, "Starting TEE OTA..."); + + uint32_t curr_write_offset = copy_tee_update(); + ESP_LOGI(TAG, "Total binary data written: %lu", curr_write_offset); + + /* Corrupting the image */ + ESP_LOGI(TAG, "Corrupting the image at some offset..."); + uint32_t corrupt[8] = {[0 ... 7] = 0x0BADC0DE}; + curr_write_offset -= (2 * FLASH_SECTOR_SIZE + sizeof(corrupt)); + TEST_ESP_OK(esp_tee_ota_write(curr_write_offset, (const void *)corrupt, sizeof(corrupt))); + + TEST_ESP_ERR(ESP_ERR_IMAGE_INVALID, esp_tee_ota_end()); +} + +TEST_CASE("Test TEE OTA - Reboot without ending OTA", "[ota_neg_2]") +{ + ESP_LOGI(TAG, "Starting TEE OTA..."); + + uint32_t bytes_wr = copy_tee_update(); + ESP_LOGI(TAG, "Total binary data written: %lu", bytes_wr); + + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); +} + +TEST_CASE("Test TEE OTA - Valid image", "[ota_valid_img]") +{ + ESP_LOGI(TAG, "Starting TEE OTA..."); + + uint32_t bytes_wr = copy_tee_update(); + ESP_LOGI(TAG, "Total binary data written: %lu", bytes_wr); + + TEST_ESP_OK(esp_tee_ota_end()); + ESP_LOGI(TAG, "TEE OTA update successful!"); + + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); +} + +TEST_CASE("Test TEE OTA - Rollback", "[ota_rollback]") +{ + ESP_LOGI(TAG, "Starting TEE OTA..."); + + uint32_t bytes_wr = copy_tee_update(); + ESP_LOGI(TAG, "Total binary data written: %lu", bytes_wr); + + TEST_ESP_OK(esp_tee_ota_end()); + + ESP_LOGI(TAG, "TEE OTA update successful!"); + ESP_LOGI(TAG, "Prepare to restart system!"); + esp_restart(); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_panic.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_panic.c new file mode 100644 index 0000000000..a9eb544d22 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_panic.c @@ -0,0 +1,165 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/aes_reg.h" +#include "soc/efuse_reg.h" +#include "soc/lp_analog_peri_reg.h" +#include "soc/lp_wdt_reg.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "unity.h" +#include "esp_tee.h" +#include "secure_service_num.h" + +#define ALIGN_DOWN_TO_MMU_PAGE_SIZE(addr) ((addr) & ~((SOC_MMU_PAGE_SIZE) - 1)) + +extern uint32_t _iram_start; +extern uint32_t _instruction_reserved_start; + +/* ---------------------------------------------------- Basic Exceptions for TEE tests ---------------------------------------------------- */ + +#if CONFIG_SECURE_TEE_SEC_STG_MODE_RELEASE +#define TEST_APM_EFUSE_PROT_REG \ + (EFUSE_RD_KEY0_DATA0_REG + (((CONFIG_SECURE_TEE_SEC_STG_KEY_EFUSE_BLK) - 4) * 0x20)) +#else +#define TEST_APM_EFUSE_PROT_REG EFUSE_RD_KEY5_DATA0_REG +#endif + +TEST_CASE("Test APM violation interrupt: AES", "[apm_violation]") +{ + uint32_t val = UINT32_MAX; + val = REG_READ(AES_DATE_REG); + TEST_ASSERT_EQUAL(0, val); + TEST_FAIL_MESSAGE("APM violation interrupt should have been generated"); +} + +TEST_CASE("Test APM violation interrupt: eFuse", "[apm_violation]") +{ + uint32_t val = UINT32_MAX; + val = REG_READ(TEST_APM_EFUSE_PROT_REG); + TEST_ASSERT_EQUAL(0, val); + TEST_FAIL_MESSAGE("APM violation interrupt should have been generated"); +} + +/* TEE IRAM: Reserved/Vector-table boundary */ +TEST_CASE("Test TEE-TEE violation: IRAM (W1)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_IRAM_REG1_WRITE_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* Illegal memory space: Write */ +TEST_CASE("Test TEE-TEE violation: Reserved (W1)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_RESV_REG1_WRITE_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* Illegal memory space: Execution */ +TEST_CASE("Test TEE-TEE violation: Reserved (X1)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_RESV_REG1_EXEC_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IRAM: Vector table region */ +TEST_CASE("Test TEE-TEE violation: IRAM (W2)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_IRAM_REG2_WRITE_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DRAM: Stack region */ +TEST_CASE("Test TEE-TEE violation: DRAM (X1)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_DRAM_REG1_EXEC_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DRAM: Heap region */ +TEST_CASE("Test TEE-TEE violation: DRAM (X2)", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_DRAM_REG2_EXEC_VIOLATION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* Illegal Instruction */ +TEST_CASE("Test TEE-TEE violation: Illegal Instruction", "[exception]") +{ + esp_tee_service_call(1, SS_ESP_TEE_TEST_ILLEGAL_INSTRUCTION); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DRAM -REE IRAM Boundary */ +TEST_CASE("Test REE-TEE isolation: DRAM (R1)", "[exception]") +{ + uint32_t* val = (uint32_t *)(&_iram_start - 0x04); + TEST_ASSERT_EQUAL(0, *val); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DRAM -REE IRAM Boundary */ +TEST_CASE("Test REE-TEE isolation: DRAM (W1)", "[exception]") +{ + *(uint32_t *)(&_iram_start - 0x04) = 0xbadc0de; + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IRAM region */ +TEST_CASE("Test REE-TEE isolation: IRAM (R1)", "[exception]") +{ + uint32_t *val = (uint32_t *)(&_iram_start - (CONFIG_SECURE_TEE_IRAM_SIZE + CONFIG_SECURE_TEE_DRAM_SIZE) + 0x04); + TEST_ASSERT_EQUAL(0, *val); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IRAM region */ +TEST_CASE("Test REE-TEE isolation: IRAM (W1)", "[exception]") +{ + *(uint32_t *)(&_iram_start - (CONFIG_SECURE_TEE_IRAM_SIZE + CONFIG_SECURE_TEE_DRAM_SIZE) + 0x04) = 0xbadc0de; + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IROM region */ +TEST_CASE("Test REE-TEE isolation: IROM (R1)", "[exception]") +{ + uint32_t *val = (uint32_t *)(SOC_IROM_LOW + 0x04); + TEST_ASSERT_EQUAL(0, *val); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE IROM region */ +TEST_CASE("Test REE-TEE isolation: IROM (W1)", "[exception]") +{ + *(uint32_t *)(SOC_IROM_LOW + 0x04) = 0xbadc0de; + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DROM - REE IROM boundary */ +TEST_CASE("Test REE-TEE isolation: DROM (R1)", "[exception]") +{ + const uint32_t test_addr = ALIGN_DOWN_TO_MMU_PAGE_SIZE((uint32_t)&_instruction_reserved_start); + uint32_t *val = (uint32_t *)(test_addr - 0x04); + TEST_ASSERT_EQUAL(0, *val); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* TEE DROM - REE IROM boundary */ +TEST_CASE("Test REE-TEE isolation: DROM (W1)", "[exception]") +{ + const uint32_t test_addr = ALIGN_DOWN_TO_MMU_PAGE_SIZE((uint32_t)&_instruction_reserved_start); + *(uint32_t *)(test_addr - 0x04) = 0xbadc0de; + TEST_FAIL_MESSAGE("Exception should have been generated"); +} + +/* SWDT/BOD Reset register */ +TEST_CASE("Test REE-TEE isolation: SWDT/BOD (W)", "[exception]") +{ + REG_WRITE(LP_ANALOG_PERI_LP_ANA_FIB_ENABLE_REG, 0); + TEST_FAIL_MESSAGE("Exception should have been generated"); +} diff --git a/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c new file mode 100644 index 0000000000..fa5e381b6e --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/main/test_esp_tee_sec_stg.c @@ -0,0 +1,274 @@ +/* + * 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 "esp_partition.h" + +#include "mbedtls/ecp.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/sha256.h" + +#include "esp_tee.h" +#include "esp_tee_sec_storage.h" +#include "secure_service_num.h" + +#include "esp_random.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) + +static const char *TAG = "test_esp_tee_sec_storage"; + +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) +{ + TEST_ASSERT_NOT_NULL(pubkey); + TEST_ASSERT_NOT_NULL(digest); + TEST_ASSERT_NOT_NULL(sign); + TEST_ASSERT_NOT_EQUAL(0, len); + + mbedtls_mpi r, s; + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + mbedtls_ecdsa_context ecdsa_context; + mbedtls_ecdsa_init(&ecdsa_context); + + TEST_ASSERT_MBEDTLS_OK(mbedtls_ecp_group_load(&ecdsa_context.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1)); + size_t plen = mbedtls_mpi_size(&ecdsa_context.MBEDTLS_PRIVATE(grp).P); + + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&r, sign->sign_r, plen)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&s, sign->sign_s, plen)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), pubkey->pub_x, plen)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), pubkey->pub_y, plen)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_lset(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 1)); + TEST_ASSERT_MBEDTLS_OK(mbedtls_ecdsa_verify(&ecdsa_context.MBEDTLS_PRIVATE(grp), digest, len, &ecdsa_context.MBEDTLS_PRIVATE(Q), &r, &s)); + + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&s); + mbedtls_ecdsa_free(&ecdsa_context); + + return 0; +} + +TEST_CASE("Test TEE Secure Storage - Sign-verify (ecdsa_secp256r1) with all key-slot IDs", "[secure_storage]") +{ + const size_t buf_sz = 16 * 1024 + 6; // NOTE: Not an exact multiple of SHA block size + unsigned char *message = heap_caps_malloc(buf_sz, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(message); + + esp_fill_random(message, buf_sz); + + uint8_t msg_digest[SHA256_DIGEST_SZ]; + TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, buf_sz, msg_digest, false)); + free(message); + + TEST_ESP_OK(esp_tee_sec_storage_init()); + + for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) { + ESP_LOGI(TAG, "Slot ID: %u", slot_id); + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1)); + + esp_tee_sec_storage_sign_t sign = {}; + TEST_ESP_OK(esp_tee_sec_storage_get_signature(slot_id, msg_digest, sizeof(msg_digest), &sign)); + + esp_tee_sec_storage_pubkey_t pubkey = {}; + TEST_ESP_OK(esp_tee_sec_storage_get_pubkey(slot_id, &pubkey)); + + ESP_LOGI(TAG, "Verifying generated signature..."); + TEST_ESP_OK(verify_ecdsa_secp256r1_sign(msg_digest, sizeof(msg_digest), &pubkey, &sign)); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id)); + } +} + +TEST_CASE("Test TEE Secure Storage - Encrypt-decrypt (aes256_gcm) with all key-slot IDs", "[secure_storage]") +{ + const uint8_t SZ = 100; + uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(plaintext); + esp_fill_random(plaintext, SZ); + + uint8_t *ciphertext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(ciphertext); + + uint8_t *decryptedtext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(decryptedtext); + + uint8_t tag[12]; + uint8_t aad[16]; + memset(aad, 0xA5, sizeof(aad)); + + TEST_ESP_OK(esp_tee_sec_storage_init()); + + for (uint16_t slot_id = 0; slot_id <= MAX_SEC_STG_SLOT_ID; slot_id++) { + ESP_LOGI(TAG, "Slot ID: %u", slot_id); + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256)); + + TEST_ESP_OK(esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad), + tag, sizeof(tag), ciphertext)); + TEST_ESP_OK(esp_tee_sec_storage_decrypt(slot_id, ciphertext, SZ, aad, sizeof(aad), + tag, sizeof(tag), decryptedtext)); + + TEST_ASSERT_EQUAL_HEX8_ARRAY(plaintext, decryptedtext, SZ); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id)); + } + + free(plaintext); + free(ciphertext); + free(decryptedtext); +} + +TEST_CASE("Test TEE Secure Storage - Operations with invalid/non-existent keys", "[secure_storage]") +{ + // Setup for ECDSA + const uint8_t SZ = 100; + unsigned char *message = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(message); + esp_fill_random(message, SZ); + uint8_t msg_digest[SHA256_DIGEST_SZ]; + TEST_ASSERT_MBEDTLS_OK(mbedtls_sha256(message, SZ, msg_digest, false)); + free(message); + + // Setup for AES + uint8_t *plaintext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(plaintext); + esp_fill_random(plaintext, SZ); + uint8_t *ciphertext = heap_caps_malloc(SZ, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + TEST_ASSERT_NOT_NULL(ciphertext); + uint8_t tag[12]; + uint8_t aad[16]; + memset(aad, 0xA5, sizeof(aad)); + + TEST_ESP_OK(esp_tee_sec_storage_init()); + + // Test ECDSA key with AES operation + const uint16_t slot_id = 7; + ESP_LOGI(TAG, "Slot ID: %u - Trying AES operation with ECDSA key...", slot_id); + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1)); + + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad), + tag, sizeof(tag), ciphertext)); + + // Test AES key with ECDSA operation + ESP_LOGI(TAG, "Slot ID: %u - Trying ECDSA operation with AES key...", slot_id); + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256)); + + esp_tee_sec_storage_sign_t sign = {}; + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_get_signature(slot_id, msg_digest, sizeof(msg_digest), &sign)); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ASSERT_TRUE(esp_tee_sec_storage_is_slot_empty(slot_id)); + + // Test with non-existent data + TEST_ESP_ERR(ESP_ERR_NOT_FOUND, esp_tee_sec_storage_encrypt(slot_id, plaintext, SZ, aad, sizeof(aad), + tag, sizeof(tag), ciphertext)); + + free(plaintext); + free(ciphertext); +} + +TEST_CASE("Test TEE Secure Storage - Invalid key-slot IDs", "[secure_storage]") +{ + TEST_ESP_OK(esp_tee_sec_storage_init()); + + uint16_t slot_id = MAX_SEC_STG_SLOT_ID + 1; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1)); + + slot_id = MIN_SEC_STG_SLOT_ID - 1; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256)); +} + +TEST_CASE("Test TEE Secure Storage - Exhaust all key-slots", "[secure_storage]") +{ + TEST_ESP_OK(esp_tee_sec_storage_init()); + + esp_err_t err = ESP_FAIL; + uint16_t slot_id = MIN_SEC_STG_SLOT_ID; + + while (1) { + esp_tee_sec_storage_clear_slot(slot_id); + err = esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256); + if (err != ESP_OK) { + break; + } + TEST_ASSERT_FALSE(esp_tee_sec_storage_is_slot_empty(slot_id)); + slot_id++; + } + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, err); + + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(MAX_SEC_STG_SLOT_ID, ESP_SEC_STG_KEY_AES256)); + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(MIN_SEC_STG_SLOT_ID, ESP_SEC_STG_KEY_AES256)); + + uint16_t mid_slot = (MIN_SEC_STG_SLOT_ID + MAX_SEC_STG_SLOT_ID) / 2; + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, esp_tee_sec_storage_gen_key(mid_slot, ESP_SEC_STG_KEY_AES256)); +} + +TEST_CASE("Test TEE Secure Storage - Null Pointer and Zero Length", "[secure_storage]") +{ + TEST_ESP_OK(esp_tee_sec_storage_init()); + + const uint16_t slot_id = 7; + uint8_t data[31], tag[12]; + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_AES256)); + + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_encrypt(slot_id, NULL, sizeof(data), NULL, 0, tag, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_encrypt(slot_id, data, 0, NULL, 0, tag, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_encrypt(slot_id, data, sizeof(data), NULL, 0, NULL, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_encrypt(slot_id, data, sizeof(data), NULL, 0, tag, 0, data)); + + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_decrypt(slot_id, NULL, sizeof(data), NULL, 0, tag, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_decrypt(slot_id, data, 0, NULL, 0, tag, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_decrypt(slot_id, data, sizeof(data), NULL, 0, NULL, sizeof(tag), data)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_decrypt(slot_id, data, sizeof(data), NULL, 0, tag, 0, data)); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); + TEST_ESP_OK(esp_tee_sec_storage_gen_key(slot_id, ESP_SEC_STG_KEY_ECDSA_SECP256R1)); + + esp_tee_sec_storage_sign_t sign = {}; + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_signature(slot_id, NULL, sizeof(data), &sign)); + TEST_ESP_ERR(ESP_ERR_INVALID_SIZE, esp_tee_sec_storage_get_signature(slot_id, data, 0, &sign)); + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_signature(slot_id, data, sizeof(data), NULL)); + + TEST_ESP_ERR(ESP_ERR_INVALID_ARG, esp_tee_sec_storage_get_pubkey(slot_id, NULL)); + + TEST_ESP_OK(esp_tee_sec_storage_clear_slot(slot_id)); +} + +#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32C6) +TEST_CASE("Test TEE Secure Storage - Corruption from non-secure world", "[secure_storage_neg]") +{ + const esp_partition_t *tee_sec_stg = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_TEE_SEC_STORAGE, NULL); + TEST_ASSERT_NOT_NULL(tee_sec_stg); + + uint8_t buf_w[128]; + memset(buf_w, 0xA5, sizeof(buf_w)); + TEST_ESP_OK(esp_partition_write(tee_sec_stg, 0x00, buf_w, sizeof(buf_w))); + + uint8_t buf_r[128]; + memset(buf_r, 0x00, sizeof(buf_r)); + TEST_ESP_OK(esp_partition_read(tee_sec_stg, 0x00, buf_r, sizeof(buf_r))); + ESP_LOG_BUFFER_HEXDUMP(TAG, buf_r, sizeof(buf_r), ESP_LOG_INFO); + + TEST_FAIL_MESSAGE("APM violation interrupt should have been generated"); +} +#endif diff --git a/components/esp_tee/test_apps/tee_test_fw/partitions.csv b/components/esp_tee/test_apps/tee_test_fw/partitions.csv new file mode 100644 index 0000000000..e5f7bb21fd --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/partitions.csv @@ -0,0 +1,6 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +tee, app, tee_0, , 192K, +secure_storage, data, tee_sec_stg, , 64K, +factory, app, factory, , 512K, +nvs, data, nvs, , 24K, diff --git a/components/esp_tee/test_apps/tee_test_fw/partitions_tee_ota.csv b/components/esp_tee/test_apps/tee_test_fw/partitions_tee_ota.csv new file mode 100644 index 0000000000..7856d6bb34 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/partitions_tee_ota.csv @@ -0,0 +1,10 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +tee_0, app, tee_0, , 192K, +tee_1, app, tee_1, , 192K, +tee_otadata, data, tee_ota, , 8K, +secure_storage, data, tee_sec_stg, , 56K, +ota_0, app, ota_0, , 512K, +ota_1, app, ota_1, , 512K, +otadata, data, ota, , 8K, +nvs, data, nvs, , 24K, diff --git a/components/esp_tee/test_apps/tee_test_fw/pytest_esp_tee_ut.py b/components/esp_tee/test_apps/tee_test_fw/pytest_esp_tee_ut.py new file mode 100644 index 0000000000..05ba445334 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/pytest_esp_tee_ut.py @@ -0,0 +1,301 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +from enum import Enum +from typing import Any +from typing import Dict + +import pytest +from pytest_embedded_idf import IdfDut + +CONFIGS_DEFAULT = [ + pytest.param('default', marks=[pytest.mark.esp32c6]) +] + +CONFIGS_OTA = [ + pytest.param('ota', marks=[pytest.mark.esp32c6]) +] + +TEE_VIOLATION_TEST_EXC_RSN: Dict[str, Any] = { + 'esp32c6': { + ('Reserved', 'W1'): 'Store access fault', + ('Reserved', 'X1'): 'Instruction access fault', + ('IRAM', 'W1'): 'Store access fault', + ('IRAM', 'W2'): 'Store access fault', + ('DRAM', 'X1'): 'Instruction access fault', + ('DRAM', 'X2'): 'Instruction access fault' + } +} + +REE_ISOLATION_TEST_EXC_RSN: Dict[str, Any] = { + 'esp32c6': { + ('DRAM', 'R1'): 'Load access fault', + ('DRAM', 'W1'): 'Store access fault', + ('IRAM', 'R1'): 'Load access fault', + ('IRAM', 'W1'): 'Store access fault', + ('IROM', 'R1'): 'Load access fault', + ('IROM', 'W1'): 'Store access fault', + ('DROM', 'R1'): 'Load access fault', + ('DROM', 'W1'): 'Store access fault', + ('SWDT/BOD', 'W'): 'Store access fault', + } +} + +TEE_APM_VIOLATION_EXC_CHK = ['AES', 'eFuse'] + +# ---------------- TEE default tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='basic') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_crypto_aes(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='aes') + dut.run_all_single_board_cases(group='aes-gcm') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_crypto_sha(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='mbedtls') + dut.run_all_single_board_cases(group='hw_crypto') + + +# NOTE: Stress testing the AES performance case for interrupt-related edge-cases +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_aes_perf(dut: IdfDut) -> None: + # start test + for i in range(24): + if not i: + dut.expect_exact('Press ENTER to see the list of tests') + else: + dut.expect_exact("Enter next test, or 'enter' to see menu") + dut.write('"mbedtls AES performance"') + dut.expect_unity_test_output(timeout=60) + +# ---------------- TEE Exceptions generation Tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_apm_violation(dut: IdfDut) -> None: + for check in TEE_APM_VIOLATION_EXC_CHK: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write(f'"Test APM violation interrupt: {check}"') + exc = dut.expect(r"Core ([01]) panic\'ed \(([^)]+)\)", timeout=30).group(2).decode() + if exc != 'Authority exception': + raise RuntimeError('Incorrect exception received!') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_illegal_instruction(dut: IdfDut) -> None: + dut.expect_exact('Press ENTER to see the list of tests') + dut.write(f'"Test TEE-TEE violation: Illegal Instruction"') + exc = dut.expect(r"Core ([01]) panic\'ed \(([^)]+)\)", timeout=30).group(2).decode() + if exc != 'Illegal instruction': + raise RuntimeError('Incorrect exception received!') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_violation_checks(dut: IdfDut) -> None: + checks_list = TEE_VIOLATION_TEST_EXC_RSN[dut.target] + for test in checks_list: + memory, access_type = test + expected_exc = checks_list[test] + if expected_exc is None: + continue + dut.expect_exact('Press ENTER to see the list of tests') + dut.write(f'"Test TEE-TEE violation: {memory} ({access_type})"') + actual_exc = dut.expect(r"Core ([01]) panic\'ed \(([^)]+)\)", timeout=30).group(2).decode() + if actual_exc != expected_exc: + raise RuntimeError('Incorrect exception received!') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_DEFAULT, indirect=True) +def test_esp_tee_isolation_checks(dut: IdfDut) -> None: + checks_list = REE_ISOLATION_TEST_EXC_RSN[dut.target] + for test in checks_list: + memory, access_type = test + expected_exc = checks_list[test] + if expected_exc is None: + continue + dut.expect_exact('Press ENTER to see the list of tests') + dut.write(f'"Test REE-TEE isolation: {memory} ({access_type})"') + actual_exc = dut.expect(r"Core ([01]) panic\'ed \(([^)]+)\)", timeout=30).group(2).decode() + if actual_exc != expected_exc: + raise RuntimeError('Incorrect exception received!') + dut.expect('Exception origin: U-mode') + +# ---------------- TEE Local OTA tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +def test_esp_tee_ota_negative(dut: IdfDut) -> None: + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('[ota_neg_1]') + dut.expect_unity_test_output(timeout=120) + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_ota_corrupted_img(dut: IdfDut) -> None: + # Flashing the TEE app to the non-secure app's passive partition + dut.serial.custom_flash_w_test_tee_img_gen() + + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"Test TEE OTA - Corrupted image"') + dut.expect_unity_test_output(timeout=120) + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + + +class TeeOtaStage(Enum): + PRE = 1 + BEGIN = 2 + REBOOT = 3 + + +def tee_ota_stage_checks(dut: IdfDut, stage: TeeOtaStage, offset: str) -> None: + if stage == TeeOtaStage.PRE: + dut.expect(f'Loaded TEE app from partition at offset {offset}', timeout=30) + dut.expect('Current image already has been marked VALID', timeout=30) + elif stage == TeeOtaStage.BEGIN: + dut.expect('Starting TEE OTA...', timeout=30) + dut.expect('Running partition - Subtype: 0x30', timeout=30) + dut.expect_exact(f'Next partition - Subtype: 0x31 (Offset: {offset})', timeout=30) + elif stage == TeeOtaStage.REBOOT: + dut.expect(f'Loaded TEE app from partition at offset {offset}', timeout=30) + dut.expect_exact('Press ENTER to see the list of tests') + else: + raise ValueError('Undefined stage!') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_ota_reboot_without_ota_end(dut: IdfDut) -> None: + # Flashing the TEE app to the non-secure app's passive partition + dut.serial.custom_flash_w_test_tee_img_gen() + + # pre-test checks + tee_ota_stage_checks(dut, TeeOtaStage.PRE, '0x10000') + + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('"Test TEE OTA - Reboot without ending OTA"') + + # OTA begin checks + tee_ota_stage_checks(dut, TeeOtaStage.BEGIN, '0x40000') + + # after reboot + tee_ota_stage_checks(dut, TeeOtaStage.REBOOT, '0x10000') + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_ota_valid_img(dut: IdfDut) -> None: + # Flashing the TEE app to the non-secure app's passive partition + dut.serial.custom_flash_w_test_tee_img_gen() + + # pre-test checks + tee_ota_stage_checks(dut, TeeOtaStage.PRE, '0x10000') + + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('[ota_valid_img]') + + # OTA begin checks + tee_ota_stage_checks(dut, TeeOtaStage.BEGIN, '0x40000') + dut.expect('TEE OTA update successful!', timeout=30) + + # after reboot 1 + tee_ota_stage_checks(dut, TeeOtaStage.REBOOT, '0x40000') + + # resetting device to check for image validity + dut.serial.hard_reset() + + # after reboot 2 + dut.expect('TEE otadata - Current image state: VALID', timeout=30) + tee_ota_stage_checks(dut, TeeOtaStage.REBOOT, '0x40000') + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_ota_rollback(dut: IdfDut) -> None: + # Flashing the TEE app to the non-secure app's passive partition + dut.serial.custom_flash_w_test_tee_img_rb() + + # pre-test checks + tee_ota_stage_checks(dut, TeeOtaStage.PRE, '0x10000') + + # start test + dut.expect_exact('Press ENTER to see the list of tests') + dut.write('[ota_rollback]') + + # OTA begin checks + tee_ota_stage_checks(dut, TeeOtaStage.BEGIN, '0x40000') + dut.expect('TEE OTA update successful!', timeout=30) + + # after reboot 1 + dut.expect('TEE otadata - Current image state: NEW', timeout=10) + dut.expect('Loaded TEE app from partition at offset 0x40000', timeout=10) + rst_rsn = dut.expect(r'rst:(0x[0-9A-Fa-f]+) \(([^)]+)\)', timeout=30).group(2).decode() + # NOTE: LP_WDT_SYS is for ESP32-C6 case as bootloader fails to load the dummy TEE app + if rst_rsn != 'LP_WDT_SYS': + raise RuntimeError('Incorrect reset reason observed after TEE image failure!') + + # after rollback + dut.expect('TEE otadata - Current image state: PENDING_VERIFY', timeout=30) + tee_ota_stage_checks(dut, TeeOtaStage.REBOOT, '0x10000') + + # erasing TEE otadata + dut.serial.erase_partition('tee_otadata') + +# ---------------- TEE Secure Storage tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_secure_storage(dut: IdfDut) -> None: + # Flash image and erase the secure_storage partition + dut.serial.custom_flash_with_empty_sec_stg() + + dut.run_all_single_board_cases(group='secure_storage') + +# ---------------- TEE Attestation tests ---------------- + + +@pytest.mark.generic +@pytest.mark.parametrize('config', CONFIGS_OTA, indirect=True) +@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True) +def test_esp_tee_attestation(dut: IdfDut) -> None: + # Flash image and erase the secure_storage partition + dut.serial.custom_flash_with_empty_sec_stg() + + # start test + dut.run_all_single_board_cases(group='attestation') diff --git a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.default b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.default new file mode 100644 index 0000000000..e69de29bb2 diff --git a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.ota b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.ota new file mode 100644 index 0000000000..f6984d573c --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.ci.ota @@ -0,0 +1,11 @@ +# Custom partition table +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_tee_ota.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_tee_ota.csv" + +# Increasing Bootloader log verbosity +CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG=y + +# secure storage key slot for attestation +CONFIG_SECURE_TEE_ATT_KEY_SLOT_ID=14 diff --git a/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults new file mode 100644 index 0000000000..9f36b65b80 --- /dev/null +++ b/components/esp_tee/test_apps/tee_test_fw/sdkconfig.defaults @@ -0,0 +1,17 @@ +# Test-app related +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT_INIT=n + +# Enabling TEE +CONFIG_SECURE_ENABLE_TEE=y +CONFIG_SECURE_TEE_DEBUG_MODE=y +CONFIG_SECURE_TEE_LOG_LEVEL_DEBUG=y +CONFIG_SECURE_TEE_TEST_MODE=y + +# Custom partition table +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" + +# TEE IRAM size +CONFIG_SECURE_TEE_IRAM_SIZE=0x9000