diff --git a/components/bootloader/CMakeLists.txt b/components/bootloader/CMakeLists.txt index 5fddb9c89..afe1ad639 100644 --- a/components/bootloader/CMakeLists.txt +++ b/components/bootloader/CMakeLists.txt @@ -1,15 +1,20 @@ -idf_component_register() +idf_component_register(PRIV_REQUIRES partition_table) # Do not generate flash file when building bootloader or is in early expansion of the build if(BOOTLOADER_BUILD) return() endif() +# When secure boot is enabled, do not flash bootloader along with invocation of `idf.py flash` +if(NOT CONFIG_SECURE_BOOT_ENABLED) + set(flash_bootloader FLASH_IN_PROJECT) +endif() + # Set values used in flash_bootloader_args.in and generate flash file # for bootloader esptool_py_flash_project_args(bootloader 0x1000 ${BOOTLOADER_BUILD_DIR}/bootloader.bin - FLASH_IN_PROJECT + ${flash_bootloader} FLASH_FILE_TEMPLATE flash_bootloader_args.in) esptool_py_custom_target(bootloader-flash bootloader "bootloader") diff --git a/components/bootloader/project_include.cmake b/components/bootloader/project_include.cmake index 49e51b9cb..019d1df71 100644 --- a/components/bootloader/project_include.cmake +++ b/components/bootloader/project_include.cmake @@ -1,32 +1,10 @@ -# Do not generate flash file when building bootloader or is in early expansion of the build +set(BOOTLOADER_OFFSET 0x1000) + +# Do not generate flash file when building bootloader if(BOOTLOADER_BUILD) return() endif() -idf_build_get_property(project_dir PROJECT_DIR) - -# This is for tracking the top level project path -if(BOOTLOADER_BUILD) - set(main_project_path "${CMAKE_BINARY_DIR}/../..") -else() - set(main_project_path "${project_dir}") -endif() - -get_filename_component(secure_boot_signing_key - "${CONFIG_SECURE_BOOT_SIGNING_KEY}" - ABSOLUTE BASE_DIR "${main_project_path}") -if(NOT EXISTS ${secure_boot_signing_key}) - # If the signing key is not found, create a phony gen_secure_boot_signing_key target that - # fails the build. fail_at_build_time also touches CMakeCache.txt to cause a cmake run next time - # (to pick up a new signing key if one exists, etc.) - fail_at_build_time(gen_secure_boot_signing_key - "Secure Boot Signing Key ${CONFIG_SECURE_BOOT_SIGNING_KEY} does not exist. Generate using:" - "\tespsecure.py generate_signing_key ${CONFIG_SECURE_BOOT_SIGNING_KEY}") -else() - add_custom_target(gen_secure_boot_signing_key) -endif() - - # Glue to build the bootloader subproject binary as an external # cmake project under this one # @@ -39,41 +17,104 @@ set(bootloader_binary_files "${BOOTLOADER_BUILD_DIR}/bootloader.map" ) -# These additional files may get generated -if(CONFIG_SECURE_BOOTLOADER_REFLASHABLE) - set(bootloader_binary_files - ${bootloader_binary_files} - "${BOOTLOADER_BUILD_DIR}/bootloader-reflash-digest.bin" - "${BOOTLOADER_BUILD_DIR}/secure-bootloader-key-192.bin" - "${BOOTLOADER_BUILD_DIR}/secure-bootloader-key-256.bin" - ) +idf_build_get_property(project_dir PROJECT_DIR) + +# There are some additional processing when CONFIG_CONFIG_SECURE_SIGNED_APPS. This happens +# when either CONFIG_SECURE_BOOT_ENABLED or SECURE_BOOT_BUILD_SIGNED_BINARIES. +# For both cases, the user either sets binaries to be signed during build or not +# using CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES. +# +# Regardless, pass the main project's keys (signing/verification) to the bootloader subproject +# via config. +if(CONFIG_SECURE_SIGNED_APPS) + add_custom_target(gen_secure_boot_keys) + + if(CONFIG_SECURE_BOOT_ENABLED) + # Check that the configuration is sane + if((CONFIG_SECURE_BOOTLOADER_REFLASHABLE AND CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH) OR + (NOT CONFIG_SECURE_BOOTLOADER_REFLASHABLE AND NOT CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH)) + fail_at_build_time(bootloader "Invalid bootloader target: bad sdkconfig?") + endif() + + if(CONFIG_SECURE_BOOTLOADER_REFLASHABLE) + set(bootloader_binary_files + ${bootloader_binary_files} + "${BOOTLOADER_BUILD_DIR}/bootloader-reflash-digest.bin" + "${BOOTLOADER_BUILD_DIR}/secure-bootloader-key-192.bin" + "${BOOTLOADER_BUILD_DIR}/secure-bootloader-key-256.bin" + ) + endif() + endif() + + # Since keys are usually given relative to main project dir, get the absolute paths to the keys + # for use by the bootloader subproject. Replace the values in config with these absolute paths, + # so that bootloader subproject does not need to assume main project dir to obtain path to the keys. + if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) + get_filename_component(secure_boot_signing_key + "${CONFIG_SECURE_BOOT_SIGNING_KEY}" + ABSOLUTE BASE_DIR "${project_dir}") + + if(NOT EXISTS ${secure_boot_signing_key}) + # If the signing key is not found, create a phony gen_secure_boot_signing_key target that + # fails the build. fail_at_build_time causes a cmake run next time + # (to pick up a new signing key if one exists, etc.) + fail_at_build_time(gen_secure_boot_signing_key + "Secure Boot Signing Key ${CONFIG_SECURE_BOOT_SIGNING_KEY} does not exist. Generate using:" + "\tespsecure.py generate_signing_key ${CONFIG_SECURE_BOOT_SIGNING_KEY}") + else() + add_custom_target(gen_secure_boot_signing_key) + endif() + + set(SECURE_BOOT_SIGNING_KEY ${secure_boot_signing_key}) # needed by some other components + set(sign_key_arg "-DSECURE_BOOT_SIGNING_KEY=${secure_boot_signing_key}") + + add_dependencies(gen_secure_boot_keys gen_secure_boot_signing_key) + else() + + get_filename_component(secure_boot_verification_key + ${CONFIG_SECURE_BOOT_VERIFICATION_KEY} + ABSOLUTE BASE_DIR "${project_dir}") + + if(NOT EXISTS ${secure_boot_verification_key}) + # If the verification key is not found, create a phony gen_secure_boot_verification_key target that + # fails the build. fail_at_build_time causes a cmake run next time + # (to pick up a new verification key if one exists, etc.) + fail_at_build_time(gen_secure_boot_verification_key + "Secure Boot Verification Public Key ${CONFIG_SECURE_BOOT_VERIFICATION_KEY} does not exist." + "\tThis can be extracted from the private signing key." + "\tSee docs/security/secure-boot.rst for details.") + else() + add_custom_target(gen_secure_boot_verification_key) + endif() + + set(ver_key_arg "-DSECURE_BOOT_VERIFICATION_KEY=${secure_boot_verification_key}") + + add_dependencies(gen_secure_boot_keys gen_secure_boot_verification_key) + endif() endif() -if((NOT CONFIG_SECURE_BOOT_ENABLED) OR - CONFIG_SECURE_BOOTLOADER_REFLASHABLE OR - CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH) - idf_build_get_property(idf_path IDF_PATH) - idf_build_get_property(sdkconfig SDKCONFIG) - idf_build_get_property(idf_target IDF_TARGET) - externalproject_add(bootloader - # TODO: support overriding the bootloader in COMPONENT_PATHS - SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/subproject" - BINARY_DIR "${BOOTLOADER_BUILD_DIR}" - CMAKE_ARGS -DSDKCONFIG=${sdkconfig} -DIDF_PATH=${idf_path} -DIDF_TARGET=${idf_target} - -DSECURE_BOOT_SIGNING_KEY=${secure_boot_signing_key} - -DPYTHON_DEPS_CHECKED=1 - -DEXTRA_COMPONENT_DIRS=${CMAKE_CURRENT_LIST_DIR} - # LEGACY_INCLUDE_COMMON_HEADERS has to be passed in via cache variable since - # the bootloader common component requirements depends on this and - # config variables are not available before project() call. - -DLEGACY_INCLUDE_COMMON_HEADERS=${CONFIG_LEGACY_INCLUDE_COMMON_HEADERS} - INSTALL_COMMAND "" - BUILD_ALWAYS 1 # no easy way around this... - BUILD_BYPRODUCTS ${bootloader_binary_files} - DEPENDS gen_secure_boot_signing_key - ) -else() - fail_at_build_time(bootloader "Invalid bootloader target: bad sdkconfig?") +idf_build_get_property(idf_path IDF_PATH) +idf_build_get_property(idf_target IDF_TARGET) +idf_build_get_property(sdkconfig SDKCONFIG) + +externalproject_add(bootloader + SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/subproject" + BINARY_DIR "${BOOTLOADER_BUILD_DIR}" + CMAKE_ARGS -DSDKCONFIG=${sdkconfig} -DIDF_PATH=${idf_path} -DIDF_TARGET=${idf_target} + -DPYTHON_DEPS_CHECKED=1 + -DEXTRA_COMPONENT_DIRS=${CMAKE_CURRENT_LIST_DIR} + ${sign_key_arg} ${ver_key_arg} + # LEGACY_INCLUDE_COMMON_HEADERS has to be passed in via cache variable since + # the bootloader common component requirements depends on this and + # config variables are not available before project() call. + -DLEGACY_INCLUDE_COMMON_HEADERS=${CONFIG_LEGACY_INCLUDE_COMMON_HEADERS} + INSTALL_COMMAND "" + BUILD_ALWAYS 1 # no easy way around this... + BUILD_BYPRODUCTS ${bootloader_binary_files} + ) + +if(CONFIG_SECURE_SIGNED_APPS) + add_dependencies(bootloader gen_secure_boot_keys) endif() # this is a hack due to an (annoying) shortcoming in cmake, it can't diff --git a/components/bootloader/subproject/CMakeLists.txt b/components/bootloader/subproject/CMakeLists.txt index d6334ae15..c125cc558 100644 --- a/components/bootloader/subproject/CMakeLists.txt +++ b/components/bootloader/subproject/CMakeLists.txt @@ -29,8 +29,6 @@ project(bootloader) idf_build_set_property(COMPILE_DEFINITIONS "-DBOOTLOADER_BUILD=1" APPEND) idf_build_set_property(COMPILE_OPTIONS "-fno-stack-protector" APPEND) -set(secure_boot_signing_key ${SECURE_BOOT_SIGNING_KEY}) - string(REPLACE ";" " " espsecurepy "${ESPSECUREPY}") string(REPLACE ";" " " espefusepy "${ESPEFUSEPY}") set(esptoolpy_write_flash "${ESPTOOLPY_WRITE_FLASH_STR}") @@ -53,7 +51,7 @@ if(CONFIG_SECURE_BOOTLOADER_REFLASHABLE) add_custom_command(OUTPUT "${secure_bootloader_key}" COMMAND ${ESPSECUREPY} digest_private_key --keylen "${key_digest_len}" - --keyfile "${secure_boot_signing_key}" + --keyfile "${SECURE_BOOT_SIGNING_KEY}" "${secure_bootloader_key}" VERBATIM) @@ -67,7 +65,7 @@ if(CONFIG_SECURE_BOOTLOADER_REFLASHABLE) "\nTo generate one, you can use this command:" "\n\t${espsecurepy} generate_flash_encryption_key ${secure_bootloader_key}" "\nIf a signing key is present, then instead use:" - "\n\t${ESPSECUREPY} digest_private_key " + "\n\t${espsecurepy} digest_private_key " "--keylen (192/256) --keyfile KEYFILE " "${secure_bootloader_key}") endif() @@ -78,14 +76,14 @@ if(CONFIG_SECURE_BOOTLOADER_REFLASHABLE) COMMAND ${CMAKE_COMMAND} -E echo "DIGEST ${bootloader_digest_bin}" COMMAND ${ESPSECUREPY} digest_secure_bootloader --keyfile "${secure_bootloader_key}" -o "${bootloader_digest_bin}" "${CMAKE_BINARY_DIR}/bootloader.bin" - DEPENDS gen_secure_bootloader_key "${CMAKE_BINARY_DIR}/bootloader.bin" + DEPENDS gen_secure_bootloader_key gen_project_binary VERBATIM) add_custom_target (gen_bootloader_digest_bin ALL DEPENDS "${bootloader_digest_bin}") endif() if(CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH) - add_custom_command(TARGET bootloader POST_BUILD + add_custom_command(TARGET bootloader.elf POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "==============================================================================" COMMAND ${CMAKE_COMMAND} -E echo @@ -97,9 +95,8 @@ if(CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH) COMMAND ${CMAKE_COMMAND} -E echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" VERBATIM) - elseif(CONFIG_SECURE_BOOTLOADER_REFLASHABLE) - add_custom_command(TARGET bootloader POST_BUILD + add_custom_command(TARGET bootloader.elf POST_BUILD COMMAND ${CMAKE_COMMAND} -E echo "==============================================================================" COMMAND ${CMAKE_COMMAND} -E echo diff --git a/components/bootloader_support/CMakeLists.txt b/components/bootloader_support/CMakeLists.txt index 2b8c62ad0..a49f2699c 100644 --- a/components/bootloader_support/CMakeLists.txt +++ b/components/bootloader_support/CMakeLists.txt @@ -36,33 +36,28 @@ idf_component_register(SRCS "${srcs}" PRIV_REQUIRES "${priv_requires}") if(BOOTLOADER_BUILD AND CONFIG_SECURE_SIGNED_APPS) - get_filename_component(secure_boot_verification_key - "signature_verification_key.bin" - ABSOLUTE BASE_DIR "${CMAKE_BINARY_DIR}") + # Whether CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES or not, we need verification key to embed + # in the library. if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) + # We generate the key from the signing key. The signing key is passed from the main project. + get_filename_component(secure_boot_signing_key + "${SECURE_BOOT_SIGNING_KEY}" + ABSOLUTE BASE_DIR "${project_dir}") + get_filename_component(secure_boot_verification_key + "signature_verification_key.bin" + ABSOLUTE BASE_DIR "${CMAKE_CURRENT_BINARY_DIR}") add_custom_command(OUTPUT "${secure_boot_verification_key}" COMMAND ${ESPSECUREPY} extract_public_key --keyfile "${secure_boot_signing_key}" "${secure_boot_verification_key}" - DEPENDS gen_secure_boot_signing_key VERBATIM) else() - get_filename_component(orig_secure_boot_verification_key - "${CONFIG_SECURE_BOOT_VERIFICATION_KEY}" - ABSOLUTE BASE_DIR "${main_project_path}") - if(NOT EXISTS ${orig_secure_boot_verification_key}) - message(FATAL_ERROR - "Secure Boot Verification Public Key ${CONFIG_SECURE_BOOT_VERIFICATION_KEY} does not exist." - "\nThis can be extracted from the private signing key." - "\nSee docs/security/secure-boot.rst for details.") - endif() - - add_custom_command(OUTPUT "${secure_boot_verification_key}" - COMMAND ${CMAKE_COMMAND} -E copy "${orig_secure_boot_verification_key}" - "${secure_boot_verification_key}" - DEPENDS "${orig_secure_boot_verification_key}" - VERBATIM) + # We expect to 'inherit' the verification key passed from main project. + get_filename_component(secure_boot_verification_key + ${SECURE_BOOT_VERIFICATION_KEY} + ABSOLUTE BASE_DIR "${project_dir}") endif() + target_add_binary_data(${COMPONENT_LIB} "${secure_boot_verification_key}" "BINARY") set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES diff --git a/components/esptool_py/project_include.cmake b/components/esptool_py/project_include.cmake index ce4eac842..d4f8c3f93 100644 --- a/components/esptool_py/project_include.cmake +++ b/components/esptool_py/project_include.cmake @@ -91,40 +91,37 @@ set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" "${build_dir}/${unsigned_project_binary}" ) -if(NOT BOOTLOADER_BUILD AND - CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) - - # for locally signed secure boot image, add a signing step to get from unsigned app to signed app - add_custom_command(OUTPUT "${build_dir}/.signed_bin_timestamp" - COMMAND ${ESPSECUREPY} sign_data --keyfile ${secure_boot_signing_key} - -o "${build_dir}/${PROJECT_BIN}" "${build_dir}/${unsigned_project_binary}" - COMMAND ${CMAKE_COMMAND} -E echo "Generated signed binary image ${build_dir}/${PROJECT_BIN}" - "from ${build_dir}/${unsigned_project_binary}" - COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${PROJECT_BIN}" > "${build_dir}/.signed_bin_timestamp" - DEPENDS "${build_dir}/.bin_timestamp" - VERBATIM - COMMENT "Generating signed binary image" - ) - add_custom_target(gen_signed_project_binary DEPENDS "${build_dir}/.signed_bin_timestamp") - add_dependencies(gen_project_binary gen_signed_project_binary) - - set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" - APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES - "${build_dir}/${PROJECT_BIN}" - ) -endif() - add_custom_target(app ALL DEPENDS gen_project_binary) -if(NOT BOOTLOADER_BUILD AND - CONFIG_SECURE_BOOT_ENABLED AND - NOT CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) - add_custom_command(TARGET app POST_BUILD - COMMAND ${CMAKE_COMMAND} -E echo - "App built but not signed. Sign app before flashing" - COMMAND ${CMAKE_COMMAND} -E echo - "\t${ESPSECUREPY} sign_data --keyfile KEYFILE ${build_dir}/${elf_bin}" - VERBATIM) +if(NOT BOOTLOADER_BUILD AND CONFIG_SECURE_SIGNED_APPS) + if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) + # for locally signed secure boot image, add a signing step to get from unsigned app to signed app + add_custom_command(OUTPUT "${build_dir}/.signed_bin_timestamp" + COMMAND ${ESPSECUREPY} sign_data --keyfile ${secure_boot_signing_key} + -o "${build_dir}/${PROJECT_BIN}" "${build_dir}/${unsigned_project_binary}" + COMMAND ${CMAKE_COMMAND} -E echo "Generated signed binary image ${build_dir}/${PROJECT_BIN}" + "from ${build_dir}/${unsigned_project_binary}" + COMMAND ${CMAKE_COMMAND} -E md5sum "${build_dir}/${PROJECT_BIN}" > "${build_dir}/.signed_bin_timestamp" + DEPENDS "${build_dir}/.bin_timestamp" + VERBATIM + COMMENT "Generating signed binary image" + ) + add_custom_target(gen_signed_project_binary DEPENDS "${build_dir}/.signed_bin_timestamp") + add_dependencies(gen_project_binary gen_signed_project_binary) + + set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES + "${build_dir}/${PROJECT_BIN}" + ) + else() + string(REPLACE ";" " " espsecurepy "${ESPSECUREPY}") + add_custom_command(TARGET app POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo + "App built but not signed. Sign app before flashing" + COMMAND ${CMAKE_COMMAND} -E echo + "\t${espsecurepy} sign_data --keyfile KEYFILE ${build_dir}/${PROJECT_BIN}" + VERBATIM) + endif() endif() # diff --git a/components/partition_table/CMakeLists.txt b/components/partition_table/CMakeLists.txt index 34c60c8e5..233e03d77 100644 --- a/components/partition_table/CMakeLists.txt +++ b/components/partition_table/CMakeLists.txt @@ -40,19 +40,6 @@ add_custom_command(OUTPUT "${build_dir}/partition_table/${unsigned_partition_bin DEPENDS ${partition_csv} "${CMAKE_CURRENT_SOURCE_DIR}/gen_esp32part.py" VERBATIM) -# Add signing steps -if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) - add_custom_target(gen_unsigned_partition_bin ALL DEPENDS - "${build_dir}/partition_table/${unsigned_partition_bin}") - - add_custom_command(OUTPUT "${build_dir}/partition_table/${final_partition_bin}" - COMMAND ${ESPSECUREPY} sign_data --keyfile "${secure_boot_signing_key}" - -o "${build_dir}/partition_table/${final_partition_bin}" - "${build_dir}/partition_table/${unsigned_partition_bin}" - DEPENDS "${build_dir}/partition_table/${unsigned_partition_bin}" - VERBATIM) -endif() - if(EXISTS ${partition_csv}) add_custom_target(partition_table ALL DEPENDS "${build_dir}/partition_table/${final_partition_bin}") else() @@ -64,14 +51,27 @@ else() "Either change partition table in menuconfig or create this input file.") endif() -if(CONFIG_SECURE_BOOT_ENABLED AND - NOT CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) - add_custom_command(TARGET partition_table POST_BUILD - COMMAND ${CMAKE_COMMAND} -E echo - "Partition table built but not signed. Sign partition data before flashing:" - COMMAND ${CMAKE_COMMAND} -E echo - "\t${ESPSECUREPY} sign_data --keyfile KEYFILE ${CMAKE_CURRENT_BINARY_DIR}/${final_partition_bin}" - VERBATIM) +# Add signing steps +if(CONFIG_SECURE_SIGNED_APPS) + if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES) + add_custom_target(gen_unsigned_partition_bin ALL DEPENDS + "${build_dir}/partition_table/${unsigned_partition_bin}") + + add_custom_command(OUTPUT "${build_dir}/partition_table/${final_partition_bin}" + COMMAND ${ESPSECUREPY} sign_data --keyfile "${SECURE_BOOT_SIGNING_KEY}" + -o "${build_dir}/partition_table/${final_partition_bin}" + "${build_dir}/partition_table/${unsigned_partition_bin}" + DEPENDS "${build_dir}/partition_table/${unsigned_partition_bin}" + VERBATIM) + else() + string(REPLACE ";" " " espsecurepy "${ESPSECUREPY}") + add_custom_command(TARGET partition_table POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo + "Partition table built but not signed. Sign partition data before flashing:" + COMMAND ${CMAKE_COMMAND} -E echo + "\t${espsecurepy} sign_data --keyfile KEYFILE ${build_dir}/partition_table/${final_partition_bin}" + VERBATIM) + endif() endif() # Use global properties ESPTOOL_WRITE_FLASH_ARGS to pass this info to build