From a6ea9bcd411d72bc48dc884e6e1b5a4eb93aa3fa Mon Sep 17 00:00:00 2001 From: "harshal.patil" Date: Thu, 27 Feb 2025 16:18:47 +0530 Subject: [PATCH] fix(secure_boot): Fix SB verification failure when sig block and key digest mismatch - Secure boot V2 verification failed when multiple keys are used to sign the bootloader and the application is signed with a key other than the first key that is used to sign the bootloader. - The issue was introduced as a regression from the commit `ff16ce43`. - Added a QEMU test for recreating the issue. - Made SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT independent of SECURE_BOOT_BUILD_SIGNED_BINARIES. --- components/bootloader/Kconfig.projbuild | 2 +- .../secure_boot_signatures_bootloader.c | 3 +- .../test_apps/security/.build-test-rules.yml | 2 +- .../security/secure_boot/main/CMakeLists.txt | 35 ++++++++++++++++ .../secure_boot/main/Kconfig.projbuild | 9 ++++ .../secure_boot/main/secure_boot_main.c | 9 ++++ .../secure_boot/pytest_secure_boot.py | 32 +++++++++++++- .../security/secure_boot/sdkconfig.ci.qemu | 7 ++++ .../secure_boot/test/esp32c3_efuses.bin | Bin 0 -> 1024 bytes .../test/secure_boot_signing_key0.pem | 39 ++++++++++++++++++ .../test/secure_boot_signing_key1.pem | 39 ++++++++++++++++++ .../test/secure_boot_signing_key2.pem | 39 ++++++++++++++++++ 12 files changed, 211 insertions(+), 5 deletions(-) create mode 100644 tools/test_apps/security/secure_boot/main/Kconfig.projbuild create mode 100644 tools/test_apps/security/secure_boot/sdkconfig.ci.qemu create mode 100644 tools/test_apps/security/secure_boot/test/esp32c3_efuses.bin create mode 100644 tools/test_apps/security/secure_boot/test/secure_boot_signing_key0.pem create mode 100644 tools/test_apps/security/secure_boot/test/secure_boot_signing_key1.pem create mode 100644 tools/test_apps/security/secure_boot/test/secure_boot_signing_key2.pem diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 222e2ad5d4..214ad0f48e 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -764,7 +764,7 @@ menu "Security features" config SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT bool "Flash bootloader along with other artifacts when using the default flash command" - depends on SECURE_BOOT_V2_ENABLED && SECURE_BOOT_BUILD_SIGNED_BINARIES + depends on SECURE_BOOT_V2_ENABLED default n help When Secure Boot V2 is enabled, by default the bootloader is not flashed along with other artifacts diff --git a/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_bootloader.c b/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_bootloader.c index a653707845..45332f9925 100644 --- a/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_bootloader.c +++ b/components/bootloader_support/src/secure_boot_v2/secure_boot_signatures_bootloader.c @@ -154,13 +154,12 @@ esp_err_t esp_secure_boot_verify_sbv2_signature_block(const ets_secure_boot_sign ets_secure_boot_key_digests_t trusted_key_digests = {0}; bool valid_sig_blk = false; for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) { + trusted_key_digests.key_digests[i] = &trusted.key_digests[i]; if (sig_block->block[i].version != ESP_SECURE_BOOT_SCHEME) { ESP_LOGD(TAG, "%s signing scheme selected but signature block %d generated for %s scheme", esp_secure_boot_get_scheme_name(ESP_SECURE_BOOT_SCHEME), i, esp_secure_boot_get_scheme_name(sig_block->block[i].version)); - continue; } else { valid_sig_blk = true; } - trusted_key_digests.key_digests[i] = &trusted.key_digests[i]; } if (valid_sig_blk != true) { ESP_LOGE(TAG, "No signature block generated for valid scheme"); diff --git a/tools/test_apps/security/.build-test-rules.yml b/tools/test_apps/security/.build-test-rules.yml index c015f7ea42..45e3922604 100644 --- a/tools/test_apps/security/.build-test-rules.yml +++ b/tools/test_apps/security/.build-test-rules.yml @@ -2,7 +2,7 @@ tools/test_apps/security/secure_boot: disable: - - if: IDF_ENV_FPGA != 1 + - if: IDF_ENV_FPGA != 1 and CONFIG_NAME != "qemu" reason: the test can only run on an FPGA as efuses need to be reset during the test. tools/test_apps/security/signed_app_no_secure_boot: diff --git a/tools/test_apps/security/secure_boot/main/CMakeLists.txt b/tools/test_apps/security/secure_boot/main/CMakeLists.txt index 609328ff5e..5201d97810 100644 --- a/tools/test_apps/security/secure_boot/main/CMakeLists.txt +++ b/tools/test_apps/security/secure_boot/main/CMakeLists.txt @@ -7,3 +7,38 @@ endif() idf_component_register(SRCS "${main_src}" INCLUDE_DIRS ".") target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") + +if(CONFIG_EXAMPLE_TARGET_QEMU) + set(bootloader_unsigned_bin "bootloader-unsigned.bin") + set(app_unsigned_bin "${PROJECT_BIN}-unsigned.bin") + + add_custom_target(sign_bootloader ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/bootloader/bootloader.bin" + "${CMAKE_BINARY_DIR}/bootloader/${bootloader_unsigned_bin}" + COMMAND ${ESPSECUREPY} sign_data --version 2 --keyfile + ${PROJECT_DIR}/test/secure_boot_signing_key0.pem + ${PROJECT_DIR}/test/secure_boot_signing_key1.pem + ${PROJECT_DIR}/test/secure_boot_signing_key2.pem + -o "${CMAKE_BINARY_DIR}/bootloader/bootloader.bin" + "${CMAKE_BINARY_DIR}/bootloader/${bootloader_unsigned_bin}" + COMMAND ${CMAKE_COMMAND} -E echo "Generated signed binary image ${CMAKE_BINARY_DIR}/bootloader/bootloader.bin" + "from ${CMAKE_BINARY_DIR}/bootloader/${bootloader_unsigned_bin}" + VERBATIM + COMMENT "Generated the test-specific signed bootloader") + + add_dependencies(sign_bootloader bootloader) + + add_custom_target(sign_app ALL + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/${PROJECT_BIN}" + "${CMAKE_BINARY_DIR}/${app_unsigned_bin}" + COMMAND ${ESPSECUREPY} sign_data --version 2 --keyfile + ${PROJECT_DIR}/test/secure_boot_signing_key1.pem + -o "${CMAKE_BINARY_DIR}/${PROJECT_BIN}" + "${CMAKE_BINARY_DIR}/${app_unsigned_bin}" + COMMAND ${CMAKE_COMMAND} -E echo "Generated signed binary image ${CMAKE_BINARY_DIR}/${PROJECT_BIN}" + "from ${CMAKE_BINARY_DIR}/${app_unsigned_bin}" + VERBATIM + COMMENT "Generated the test-specific signed application") + + add_dependencies(sign_app app) +endif() diff --git a/tools/test_apps/security/secure_boot/main/Kconfig.projbuild b/tools/test_apps/security/secure_boot/main/Kconfig.projbuild new file mode 100644 index 0000000000..471d37419a --- /dev/null +++ b/tools/test_apps/security/secure_boot/main/Kconfig.projbuild @@ -0,0 +1,9 @@ +menu "Example Configuration" + + config EXAMPLE_TARGET_QEMU + bool "Run the example tests for target QEMU" + default n + help + Run the example tests for target QEMU + +endmenu diff --git a/tools/test_apps/security/secure_boot/main/secure_boot_main.c b/tools/test_apps/security/secure_boot/main/secure_boot_main.c index 011b599cdc..82611c1f39 100644 --- a/tools/test_apps/security/secure_boot/main/secure_boot_main.c +++ b/tools/test_apps/security/secure_boot/main/secure_boot_main.c @@ -76,4 +76,13 @@ static void example_secure_boot_status(void) } else { ESP_LOGI(TAG, "Secure Boot not enabled. Enable Secure Boot in menuconfig, build & flash again."); } + +#if CONFIG_EXAMPLE_TARGET_QEMU + for (int i = 5; i >= 0; i--) { + ESP_LOGI(TAG, "Restarting in %d seconds...", i); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } + ESP_LOGI(TAG, "Restarting now."); + esp_restart(); +#endif /* CONFIG_EXAMPLE_TARGET_QEMU */ } diff --git a/tools/test_apps/security/secure_boot/pytest_secure_boot.py b/tools/test_apps/security/secure_boot/pytest_secure_boot.py index 6bd5ae69cd..1ebb0fcee4 100644 --- a/tools/test_apps/security/secure_boot/pytest_secure_boot.py +++ b/tools/test_apps/security/secure_boot/pytest_secure_boot.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Unlicense OR CC0-1.0 import os import struct @@ -91,6 +91,36 @@ def test_examples_security_secure_boot(dut: Dut) -> None: dut.burn_wafer_version() +# Test secure boot flow. +# Correctly signed bootloader + correctly signed app should work +@pytest.mark.host_test +@pytest.mark.qemu +@pytest.mark.esp32c3 +@pytest.mark.parametrize( + 'qemu_extra_args', + [ + f'-drive file={os.path.join(os.path.dirname(__file__), "test", "esp32c3_efuses.bin")},if=none,format=raw,id=efuse ' + '-global driver=nvram.esp32c3.efuse,property=drive,value=efuse ' + '-global driver=timer.esp32c3.timg,property=wdt_disable,value=true', + ], + indirect=True, +) +@pytest.mark.parametrize('config', ['qemu'], indirect=True) +def test_examples_security_secure_boot_qemu(dut: Dut) -> None: + try: + dut.expect('Secure Boot is enabled', timeout=10) + dut.expect('Restarting now.', timeout=10) + dut.expect('Secure Boot is enabled', timeout=10) + + finally: + # the above example test burns the efuses, and hence the efuses file which the + # qemu uses to emulate the efuses, "esp32c3_efuses.bin", gets modified. + # Thus, restore the efuses file values back to the default ESP32C3 efuses values. + with open(os.path.join(os.path.dirname(__file__), 'test', 'esp32c3_efuses.bin'), 'wb') as efuse_file: + esp32c3_efuses = '0' * 77 + 'c' + '0' * 1970 + efuse_file.write(bytearray.fromhex(esp32c3_efuses)) + + # Test efuse key index and key block combination. # Any key index can be written to any key block and should work @pytest.mark.esp32c3 diff --git a/tools/test_apps/security/secure_boot/sdkconfig.ci.qemu b/tools/test_apps/security/secure_boot/sdkconfig.ci.qemu new file mode 100644 index 0000000000..2f609c136b --- /dev/null +++ b/tools/test_apps/security/secure_boot/sdkconfig.ci.qemu @@ -0,0 +1,7 @@ +CONFIG_IDF_TARGET="esp32c3" + +CONFIG_SECURE_BOOT=y +CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=n +CONFIG_SECURE_BOOT_FLASH_BOOTLOADER_DEFAULT=y + +CONFIG_EXAMPLE_TARGET_QEMU=y diff --git a/tools/test_apps/security/secure_boot/test/esp32c3_efuses.bin b/tools/test_apps/security/secure_boot/test/esp32c3_efuses.bin new file mode 100644 index 0000000000000000000000000000000000000000..bcab7e0b284aa0d9f983e70d2f633f6ea4a91269 GIT binary patch literal 1024 XcmZP|3h)r6YE;o^2#kin&