Merge branch 'feat/secure_boot_v2_v41' into 'master'
feat/secure_boot_v2: Adding secure boot v2 support to ESP32-ECO3 Closes IDF-799 See merge request espressif/esp-idf!6778
This commit is contained in:
commit
5f897fd33c
80 changed files with 2266 additions and 310 deletions
|
@ -10,15 +10,31 @@ unset(link_options)
|
|||
# Add the following build specifications here, since these seem to be dependent
|
||||
# on config values on the root Kconfig.
|
||||
|
||||
if(CONFIG_COMPILER_OPTIMIZATION_SIZE)
|
||||
list(APPEND compile_options "-Os")
|
||||
list(APPEND compile_options "-freorder-blocks")
|
||||
elseif(CONFIG_COMPILER_OPTIMIZATION_DEFAULT)
|
||||
list(APPEND compile_options "-Og")
|
||||
elseif(CONFIG_COMPILER_OPTIMIZATION_NONE)
|
||||
list(APPEND compile_options "-O0")
|
||||
elseif(CONFIG_COMPILER_OPTIMIZATION_PERF)
|
||||
list(APPEND compile_options "-O2")
|
||||
if(NOT BOOTLOADER_BUILD)
|
||||
|
||||
if(CONFIG_COMPILER_OPTIMIZATION_SIZE)
|
||||
list(APPEND compile_options "-Os")
|
||||
list(APPEND compile_options "-freorder-blocks")
|
||||
elseif(CONFIG_COMPILER_OPTIMIZATION_DEFAULT)
|
||||
list(APPEND compile_options "-Og")
|
||||
elseif(CONFIG_COMPILER_OPTIMIZATION_NONE)
|
||||
list(APPEND compile_options "-O0")
|
||||
elseif(CONFIG_COMPILER_OPTIMIZATION_PERF)
|
||||
list(APPEND compile_options "-O2")
|
||||
endif()
|
||||
|
||||
else() # BOOTLOADER_BUILD
|
||||
|
||||
if(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE)
|
||||
list(APPEND compile_options "-Os")
|
||||
list(APPEND compile_options "-freorder-blocks")
|
||||
elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG)
|
||||
list(APPEND compile_options "-Og")
|
||||
elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE)
|
||||
list(APPEND compile_options "-O0")
|
||||
elseif(CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF)
|
||||
list(APPEND compile_options "-O2")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
|
|
5
Kconfig
5
Kconfig
|
@ -170,7 +170,7 @@ mainmenu "Espressif IoT Development Framework Configuration"
|
|||
prompt "Optimization Level"
|
||||
default COMPILER_OPTIMIZATION_DEFAULT
|
||||
help
|
||||
This option sets compiler optimization level (gcc -O argument).
|
||||
This option sets compiler optimization level (gcc -O argument) for the app.
|
||||
|
||||
- The "Default" setting will add the -0g flag to CFLAGS.
|
||||
- The "Size" setting will add the -0s flag to CFLAGS.
|
||||
|
@ -188,6 +188,9 @@ mainmenu "Espressif IoT Development Framework Configuration"
|
|||
|
||||
Note that custom optimization levels may be unsupported.
|
||||
|
||||
Compiler optimization for the IDF bootloader is set separately,
|
||||
see the BOOTLOADER_COMPILER_OPTIMIZATION setting.
|
||||
|
||||
config COMPILER_OPTIMIZATION_DEFAULT
|
||||
bool "Debug (-Og)"
|
||||
config COMPILER_OPTIMIZATION_SIZE
|
||||
|
|
|
@ -6,11 +6,15 @@ if(BOOTLOADER_BUILD OR NOT CONFIG_APP_BUILD_BOOTLOADER)
|
|||
endif()
|
||||
|
||||
add_dependencies(bootloader partition_table)
|
||||
# When secure boot is enabled, do not flash bootloader along with invocation of `idf.py flash`
|
||||
if(NOT CONFIG_SECURE_BOOT)
|
||||
set(flash_bootloader FLASH_IN_PROJECT)
|
||||
endif()
|
||||
|
||||
esptool_py_custom_target(bootloader-flash bootloader "bootloader")
|
||||
esptool_py_flash_target_image(bootloader-flash bootloader "0x1000" "${BOOTLOADER_BUILD_DIR}/bootloader.bin")
|
||||
|
||||
# Also attach an image to the project flash target
|
||||
if(NOT CONFIG_SECURE_BOOT_ENABLED)
|
||||
if(NOT CONFIG_SECURE_BOOT)
|
||||
esptool_py_flash_target_image(flash bootloader "0x1000" "${BOOTLOADER_BUILD_DIR}/bootloader.bin")
|
||||
endif()
|
|
@ -1,4 +1,29 @@
|
|||
menu "Bootloader config"
|
||||
|
||||
choice BOOTLOADER_COMPILER_OPTIMIZATION
|
||||
prompt "Bootloader optimization Level"
|
||||
default BOOTLOADER_COMPILER_OPTIMIZATION_SIZE
|
||||
help
|
||||
This option sets compiler optimization level (gcc -O argument)
|
||||
for the bootloader.
|
||||
|
||||
- The default "Size" setting will add the -0s flag to CFLAGS.
|
||||
- The "Debug" setting will add the -Og flag to CFLAGS.
|
||||
- The "Performance" setting will add the -O2 flag to CFLAGS.
|
||||
- The "None" setting will add the -O0 flag to CFLAGS.
|
||||
|
||||
Note that custom optimization levels may be unsupported.
|
||||
|
||||
config BOOTLOADER_COMPILER_OPTIMIZATION_SIZE
|
||||
bool "Size (-Os)"
|
||||
config BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG
|
||||
bool "Debug (-Og)"
|
||||
config BOOTLOADER_COMPILER_OPTIMIZATION_PERF
|
||||
bool "Optimize for performance (-O2)"
|
||||
config BOOTLOADER_COMPILER_OPTIMIZATION_NONE
|
||||
bool "Debug without optimization (-O0)"
|
||||
endchoice
|
||||
|
||||
choice BOOTLOADER_LOG_LEVEL
|
||||
bool "Bootloader log verbosity"
|
||||
default BOOTLOADER_LOG_LEVEL_INFO
|
||||
|
@ -224,7 +249,7 @@ menu "Bootloader config"
|
|||
|
||||
config BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP
|
||||
bool "Skip image validation when exiting deep sleep"
|
||||
depends on (SECURE_BOOT_ENABLED && SECURE_BOOT_INSECURE) || !SECURE_BOOT_ENABLED
|
||||
depends on (SECURE_BOOT && SECURE_BOOT_INSECURE) || !SECURE_BOOT
|
||||
default n
|
||||
help
|
||||
This option disables the normal validation of an image coming out of
|
||||
|
@ -279,12 +304,12 @@ menu "Security features"
|
|||
config SECURE_SIGNED_ON_BOOT
|
||||
bool
|
||||
default y
|
||||
depends on SECURE_BOOT_ENABLED || SECURE_SIGNED_ON_BOOT_NO_SECURE_BOOT
|
||||
depends on SECURE_BOOT || SECURE_SIGNED_ON_BOOT_NO_SECURE_BOOT
|
||||
|
||||
config SECURE_SIGNED_ON_UPDATE
|
||||
bool
|
||||
default y
|
||||
depends on SECURE_BOOT_ENABLED || SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
|
||||
depends on SECURE_BOOT || SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
|
||||
|
||||
config SECURE_SIGNED_APPS
|
||||
bool
|
||||
|
@ -298,8 +323,7 @@ menu "Security features"
|
|||
|
||||
config SECURE_SIGNED_APPS_NO_SECURE_BOOT
|
||||
bool "Require signed app images"
|
||||
default n
|
||||
depends on !SECURE_BOOT_ENABLED
|
||||
depends on !SECURE_BOOT
|
||||
help
|
||||
Require apps to be signed to verify their integrity.
|
||||
|
||||
|
@ -308,6 +332,35 @@ menu "Security features"
|
|||
against remote network access, but not physical access. Compared to using hardware Secure Boot this option
|
||||
is much simpler to implement.
|
||||
|
||||
choice SECURE_SIGNED_APPS_SCHEME
|
||||
bool "App Signing Scheme"
|
||||
depends on SECURE_BOOT || SECURE_SIGNED_APPS_NO_SECURE_BOOT
|
||||
default SECURE_SIGNED_APPS_ECDSA_SCHEME if SECURE_BOOT_V1_ENABLED
|
||||
default SECURE_SIGNED_APPS_RSA_SCHEME if SECURE_BOOT_V2_ENABLED
|
||||
help
|
||||
Select the Secure App signing scheme. Depends on the Chip Revision.
|
||||
There are two options:
|
||||
1. ECDSA based secure boot scheme. (Only choice for Secure Boot V1)
|
||||
Supported in ESP32 and ESP32-ECO3.
|
||||
2. The RSA based secure boot scheme. (Only choice for Secure Boot V2)
|
||||
Supported in ESP32-ECO3. (ESP32 Chip Revision 3 onwards)
|
||||
|
||||
config SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
bool "ECDSA"
|
||||
depends on IDF_TARGET_ESP32 && (SECURE_SIGNED_APPS_NO_SECURE_BOOT || SECURE_BOOT_V1_ENABLED)
|
||||
help
|
||||
Embeds the ECDSA public key in the bootloader and signs the application with an ECDSA key.
|
||||
|
||||
Refer to the documentation before enabling.
|
||||
|
||||
config SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
bool "RSA"
|
||||
depends on ESP32_REV_MIN_3 && SECURE_BOOT_V2_ENABLED
|
||||
help
|
||||
Appends the RSA-3072 based Signature block to the application.
|
||||
Refer to <Secure Boot Version 2 documentation link> before enabling.
|
||||
endchoice
|
||||
|
||||
config SECURE_SIGNED_ON_BOOT_NO_SECURE_BOOT
|
||||
bool "Bootloader verifies app signatures"
|
||||
default n
|
||||
|
@ -334,23 +387,48 @@ menu "Security features"
|
|||
If hardware secure boot is not enabled, this option still adds significant security against network-based
|
||||
attackers by preventing spoofing of OTA updates.
|
||||
|
||||
config SECURE_BOOT_ENABLED
|
||||
bool "Enable hardware secure boot in bootloader (READ DOCS FIRST)"
|
||||
config SECURE_BOOT
|
||||
bool "Enable hardware Secure Boot in bootloader (READ DOCS FIRST)"
|
||||
default n
|
||||
help
|
||||
Build a bootloader which enables secure boot on first boot.
|
||||
Build a bootloader which enables Secure Boot on first boot.
|
||||
|
||||
Once enabled, secure boot will not boot a modified bootloader. The bootloader will only load a partition
|
||||
Once enabled, Secure Boot will not boot a modified bootloader. The bootloader will only load a partition
|
||||
table or boot an app if the data has a verified digital signature. There are implications for reflashing
|
||||
updated apps once secure boot is enabled.
|
||||
|
||||
When enabling secure boot, JTAG and ROM BASIC Interpreter are permanently disabled by default.
|
||||
|
||||
Refer to https://docs.espressif.com/projects/esp-idf/en/latest/security/secure-boot.html before enabling.
|
||||
choice SECURE_BOOT_VERSION
|
||||
bool "Select secure boot version"
|
||||
default SECURE_BOOT_V2_ENABLED if ESP32_REV_MIN_3
|
||||
depends on SECURE_BOOT
|
||||
help
|
||||
Select the Secure Boot Version. Depends on the Chip Revision.
|
||||
Secure Boot V2 is the new RSA based secure boot scheme.
|
||||
Supported in ESP32-ECO3. (ESP32 Chip Revision 3 onwards)
|
||||
Secure Boot V1 is the AES based secure boot scheme.
|
||||
Supported in ESP32 and ESP32-ECO3.
|
||||
|
||||
config SECURE_BOOT_V1_ENABLED
|
||||
bool "Enable Secure Boot version 1"
|
||||
depends on IDF_TARGET_ESP32
|
||||
help
|
||||
Build a bootloader which enables secure boot version 1 on first boot.
|
||||
Refer to the Secure Boot section of the ESP-IDF Programmer's Guide for this version before enabling.
|
||||
|
||||
config SECURE_BOOT_V2_ENABLED
|
||||
bool "Enable Secure Boot version 2"
|
||||
depends on ESP32_REV_MIN_3
|
||||
help
|
||||
Build a bootloader which enables Secure Boot version 2 on first boot.
|
||||
Refer to Secure Boot V2 section of the ESP-IDF Programmer's Guide for this version before enabling.
|
||||
|
||||
endchoice
|
||||
|
||||
choice SECURE_BOOTLOADER_MODE
|
||||
bool "Secure bootloader mode"
|
||||
depends on SECURE_BOOT_ENABLED
|
||||
depends on SECURE_BOOT_V1_ENABLED
|
||||
default SECURE_BOOTLOADER_ONE_TIME_FLASH
|
||||
|
||||
config SECURE_BOOTLOADER_ONE_TIME_FLASH
|
||||
|
@ -385,7 +463,8 @@ menu "Security features"
|
|||
If enabled (default), these binary files are signed as part of the build process. The file named in
|
||||
"Secure boot private signing key" will be used to sign the image.
|
||||
|
||||
If disabled, unsigned app/partition data will be built. They must be signed manually using espsecure.py
|
||||
If disabled, unsigned app/partition data will be built. They must be signed manually using espsecure.py.
|
||||
Version 1 to enable ECDSA Based Secure Boot and Version 2 to enable RSA based Secure Boot.
|
||||
(for example, on a remote signing server.)
|
||||
|
||||
config SECURE_BOOT_SIGNING_KEY
|
||||
|
@ -395,28 +474,32 @@ menu "Security features"
|
|||
help
|
||||
Path to the key file used to sign app images.
|
||||
|
||||
Key file is an ECDSA private key (NIST256p curve) in PEM format.
|
||||
Key file is an ECDSA private key (NIST256p curve) in PEM format for Secure Boot V1.
|
||||
Key file is an RSA private key in PEM format for Secure Boot V2.
|
||||
|
||||
Path is evaluated relative to the project directory.
|
||||
|
||||
You can generate a new signing key by running the following command:
|
||||
espsecure.py generate_signing_key secure_boot_signing_key.pem
|
||||
|
||||
See https://docs.espressif.com/projects/esp-idf/en/latest/security/secure-boot.html for details.
|
||||
See the Secure Boot section of the ESP-IDF Programmer's Guide for this version for details.
|
||||
|
||||
config SECURE_BOOT_VERIFICATION_KEY
|
||||
string "Secure boot public signature verification key"
|
||||
depends on SECURE_SIGNED_APPS && !SECURE_BOOT_BUILD_SIGNED_BINARIES
|
||||
depends on SECURE_SIGNED_APPS && !SECURE_BOOT_BUILD_SIGNED_BINARIES && !SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
default "signature_verification_key.bin"
|
||||
help
|
||||
Path to a public key file used to verify signed images. This key is compiled into the bootloader and/or
|
||||
Path to a public key file used to verify signed images.
|
||||
Secure Boot V1: This ECDSA public key is compiled into the bootloader and/or
|
||||
app, to verify app images.
|
||||
Secure Boot V2: This RSA public key is compiled into the signature block at
|
||||
the end of the bootloader/app.
|
||||
|
||||
Key file is in raw binary format, and can be extracted from a
|
||||
PEM formatted private key using the espsecure.py
|
||||
extract_public_key command.
|
||||
|
||||
Refer to https://docs.espressif.com/projects/esp-idf/en/latest/security/secure-boot.html before enabling.
|
||||
Refer to the Secure Boot section of the ESP-IDF Programmer's Guide for this version before enabling.
|
||||
|
||||
choice SECURE_BOOTLOADER_KEY_ENCODING
|
||||
bool "Hardware Key Encoding"
|
||||
|
@ -443,7 +526,7 @@ menu "Security features"
|
|||
|
||||
config SECURE_BOOT_INSECURE
|
||||
bool "Allow potentially insecure options"
|
||||
depends on SECURE_BOOT_ENABLED
|
||||
depends on SECURE_BOOT
|
||||
default N
|
||||
help
|
||||
You can disable some of the default protections offered by secure boot, in order to enable testing or a
|
||||
|
@ -451,7 +534,7 @@ menu "Security features"
|
|||
|
||||
Only enable these options if you are very sure.
|
||||
|
||||
Refer to https://docs.espressif.com/projects/esp-idf/en/latest/security/secure-boot.html before enabling.
|
||||
Refer to the Secure Boot section of the ESP-IDF Programmer's Guide for this version before enabling.
|
||||
|
||||
config SECURE_FLASH_ENC_ENABLED
|
||||
bool "Enable flash encryption on boot (READ DOCS FIRST)"
|
||||
|
@ -495,7 +578,7 @@ menu "Security features"
|
|||
Select Release mode only for production or manufacturing. Once enabled you can not reflash using UART
|
||||
bootloader
|
||||
|
||||
Refer to https://docs.espressif.com/projects/esp-idf/en/latest/security/secure-boot.html and
|
||||
Refer to the Secure Boot section of the ESP-IDF Programmer's Guide for this version and
|
||||
https://docs.espressif.com/projects/esp-idf/en/latest/security/flash-encryption.html for details.
|
||||
|
||||
config SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
|
||||
|
|
|
@ -45,7 +45,7 @@ clean: bootloader-clean
|
|||
bootloader-list-components:
|
||||
$(BOOTLOADER_MAKE) list-components
|
||||
|
||||
ifndef CONFIG_SECURE_BOOT_ENABLED
|
||||
ifndef CONFIG_SECURE_BOOT
|
||||
# If secure boot disabled, bootloader flashing is integrated
|
||||
# with 'make flash' and no warnings are printed.
|
||||
|
||||
|
@ -115,13 +115,35 @@ $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) | check_pyt
|
|||
@echo "DIGEST $(notdir $@)"
|
||||
$(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $<
|
||||
|
||||
else # CONFIG_SECURE_BOOT_ENABLED && !CONFIG_SECURE_BOOTLOADER_REFLASHABLE && !CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH
|
||||
else ifdef CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
BOOTLOADER_SIGNED_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-signed.bin
|
||||
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
|
||||
bootloader: $(BOOTLOADER_BIN) $(SDKCONFIG_MAKEFILE) | check_python_dependencies
|
||||
$(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) --version 2 \
|
||||
-o $(BOOTLOADER_SIGNED_BIN) $(BOOTLOADER_BIN)
|
||||
else
|
||||
bootloader: $(BOOTLOADER_BIN) $(SDKCONFIG_MAKEFILE) | check_python_dependencies
|
||||
@echo "Bootloader not signed. Sign the bootloader before flashing."
|
||||
@echo "To sign the bootloader, you can use this command:"
|
||||
@echo "espsecure.py sign_data --keyfile SECURE_BOOT_SIGNING_KEY --version 2 $(BOOTLOADER_BIN)"
|
||||
endif
|
||||
@echo $(SEPARATOR)
|
||||
@echo "Use the following command to flash the bootloader:"
|
||||
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
|
||||
@echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $(BOOTLOADER_SIGNED_BIN)"
|
||||
else
|
||||
@echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)"
|
||||
endif
|
||||
@echo $(SEPARATOR)
|
||||
|
||||
else # CONFIG_SECURE_BOOT && !CONFIG_SECURE_BOOTLOADER_REFLASHABLE \
|
||||
&& !CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH && !CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
bootloader:
|
||||
@echo "Invalid bootloader target: bad sdkconfig?"
|
||||
@exit 1
|
||||
endif
|
||||
|
||||
ifndef CONFIG_SECURE_BOOT_ENABLED
|
||||
ifndef CONFIG_SECURE_BOOT
|
||||
# don't build bootloader by default if secure boot is enabled
|
||||
all_binaries: $(BOOTLOADER_BIN)
|
||||
endif
|
||||
|
@ -131,3 +153,8 @@ bootloader-clean: $(SDKCONFIG_MAKEFILE)
|
|||
ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE
|
||||
rm -f $(SECURE_BOOTLOADER_KEY) $(BOOTLOADER_DIGEST_BIN)
|
||||
endif
|
||||
ifdef CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
|
||||
rm -f $(BOOTLOADER_SIGNED_BIN)
|
||||
endif
|
||||
endif
|
|
@ -19,8 +19,8 @@ set(bootloader_binary_files
|
|||
|
||||
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.
|
||||
# There are some additional processing when CONFIG_SECURE_SIGNED_APPS. This happens
|
||||
# when either CONFIG_SECURE_BOOT_V1_ENABLED or CONFIG_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.
|
||||
#
|
||||
|
@ -29,7 +29,13 @@ idf_build_get_property(project_dir PROJECT_DIR)
|
|||
if(CONFIG_SECURE_SIGNED_APPS)
|
||||
add_custom_target(gen_secure_boot_keys)
|
||||
|
||||
if(CONFIG_SECURE_BOOT_ENABLED)
|
||||
if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
|
||||
set(secure_apps_signing_scheme "1")
|
||||
elseif(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME)
|
||||
set(secure_apps_signing_scheme "2")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SECURE_BOOT_V1_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))
|
||||
|
@ -60,7 +66,8 @@ if(CONFIG_SECURE_SIGNED_APPS)
|
|||
# (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}")
|
||||
"\tespsecure.py generate_signing_key --version ${secure_apps_signing_scheme} \
|
||||
${CONFIG_SECURE_BOOT_SIGNING_KEY}")
|
||||
else()
|
||||
add_custom_target(gen_secure_boot_signing_key)
|
||||
endif()
|
||||
|
@ -70,7 +77,7 @@ if(CONFIG_SECURE_SIGNED_APPS)
|
|||
set(ver_key_arg)
|
||||
|
||||
add_dependencies(gen_secure_boot_keys gen_secure_boot_signing_key)
|
||||
else()
|
||||
elseif(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
|
||||
|
||||
get_filename_component(secure_boot_verification_key
|
||||
${CONFIG_SECURE_BOOT_VERIFICATION_KEY}
|
||||
|
@ -83,7 +90,7 @@ if(CONFIG_SECURE_SIGNED_APPS)
|
|||
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.")
|
||||
"\tSee docs/security/secure-boot-v1.rst for details.")
|
||||
else()
|
||||
add_custom_target(gen_secure_boot_verification_key)
|
||||
endif()
|
||||
|
|
|
@ -20,3 +20,6 @@ CONFIG_FLASH_ENCRYPTION_INSECURE CONFIG_SECURE_FLASH_
|
|||
CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_ENCRYPT CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC
|
||||
CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_DECRYPT CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC
|
||||
CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_CACHE CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE
|
||||
|
||||
# Secure Boot Scheme
|
||||
CONFIG_SECURE_BOOT_ENABLED CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
|
|
|
@ -90,6 +90,39 @@ if(CONFIG_SECURE_BOOTLOADER_REFLASHABLE)
|
|||
add_custom_target (gen_bootloader_digest_bin ALL DEPENDS "${bootloader_digest_bin}")
|
||||
endif()
|
||||
|
||||
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(bootloader_unsigned_bin "bootloader-unsigned.bin")
|
||||
add_custom_command(OUTPUT ".signed_bin_timestamp"
|
||||
COMMAND cp "${CMAKE_BINARY_DIR}/${PROJECT_BIN}" "${CMAKE_BINARY_DIR}/${bootloader_unsigned_bin}"
|
||||
COMMAND ${ESPSECUREPY} sign_data --version 2 --keyfile "${secure_boot_signing_key}"
|
||||
-o "${CMAKE_BINARY_DIR}/${PROJECT_BIN}" "${CMAKE_BINARY_DIR}/${bootloader_unsigned_bin}"
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Generated signed binary image ${build_dir}/${PROJECT_BIN}"
|
||||
"from ${CMAKE_BINARY_DIR}/${bootloader_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 Bootloader")
|
||||
else()
|
||||
add_custom_command(OUTPUT ".signed_bin_timestamp"
|
||||
VERBATIM
|
||||
COMMENT "Bootloader generated but not signed")
|
||||
endif()
|
||||
|
||||
add_custom_target (gen_signed_bootloader ALL DEPENDS "${build_dir}/.signed_bin_timestamp")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH)
|
||||
add_custom_command(TARGET bootloader.elf POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
|
@ -133,4 +166,18 @@ elseif(CONFIG_SECURE_BOOTLOADER_REFLASHABLE)
|
|||
"* Not recommended to re-use the same secure boot keyfile on multiple production devices."
|
||||
DEPENDS gen_secure_bootloader_key gen_bootloader_digest_bin
|
||||
VERBATIM)
|
||||
elseif(CONFIG_SECURE_BOOT_V2_ENABLED)
|
||||
add_custom_command(TARGET bootloader.elf POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
"=============================================================================="
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
"Bootloader built. Secure boot enabled, so bootloader not flashed automatically."
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
"Secure boot enabled, so bootloader not flashed automatically."
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
"\t${esptoolpy_write_flash} ${BOOTLOADER_OFFSET} ${CMAKE_BINARY_DIR}/bootloader.bin"
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
"=============================================================================="
|
||||
DEPENDS gen_signed_bootloader
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# only compile the "micro-ecc/uECC.c" source file
|
||||
idf_component_register(SRCS "micro-ecc/uECC.c"
|
||||
INCLUDE_DIRS micro-ecc)
|
||||
# only compile the "uECC_verify_antifault.c" file which includes the "micro-ecc/uECC.c" source file
|
||||
idf_component_register(SRCS "uECC_verify_antifault.c"
|
||||
INCLUDE_DIRS . micro-ecc)
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# only compile the micro-ecc/uECC.c source file
|
||||
# (SRCDIRS is needed so build system can find the source file)
|
||||
COMPONENT_SRCDIRS := micro-ecc
|
||||
COMPONENT_OBJS := micro-ecc/uECC.o
|
||||
# only compile the "uECC_verify_antifault.c" file which includes the "micro-ecc/uECC.c" source file
|
||||
COMPONENT_SRCDIRS := .
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := micro-ecc
|
||||
COMPONENT_ADD_INCLUDEDIRS := . micro-ecc
|
||||
|
||||
COMPONENT_SUBMODULES := micro-ecc
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license.
|
||||
|
||||
Modifications Copyright 2020, Espressif Systems (Shanghai) PTE LTD. Licensed under the BSD
|
||||
2-clause license.
|
||||
*/
|
||||
|
||||
/* uECC_verify() calls a number of static functions form here and
|
||||
uses other definitions, so we just build that whole source file here and then append
|
||||
our modified version uECC_verify_antifault(). */
|
||||
#include "micro-ecc/uECC.c"
|
||||
|
||||
/* Version of uECC_verify() which also copies message_hash into verified_hash,
|
||||
but only if the signature is valid. Does this in an FI resistant way.
|
||||
*/
|
||||
int uECC_verify_antifault(const uint8_t *public_key,
|
||||
const uint8_t *message_hash,
|
||||
unsigned hash_size,
|
||||
const uint8_t *signature,
|
||||
uECC_Curve curve,
|
||||
uint8_t *verified_hash) {
|
||||
uECC_word_t u1[uECC_MAX_WORDS], u2[uECC_MAX_WORDS];
|
||||
uECC_word_t z[uECC_MAX_WORDS];
|
||||
uECC_word_t sum[uECC_MAX_WORDS * 2];
|
||||
uECC_word_t rx[uECC_MAX_WORDS];
|
||||
uECC_word_t ry[uECC_MAX_WORDS];
|
||||
uECC_word_t tx[uECC_MAX_WORDS];
|
||||
uECC_word_t ty[uECC_MAX_WORDS];
|
||||
uECC_word_t tz[uECC_MAX_WORDS];
|
||||
const uECC_word_t *points[4];
|
||||
const uECC_word_t *point;
|
||||
bitcount_t num_bits;
|
||||
bitcount_t i;
|
||||
#if uECC_VLI_NATIVE_LITTLE_ENDIAN
|
||||
uECC_word_t *_public = (uECC_word_t *)public_key;
|
||||
#else
|
||||
uECC_word_t _public[uECC_MAX_WORDS * 2];
|
||||
#endif
|
||||
uECC_word_t r[uECC_MAX_WORDS], s[uECC_MAX_WORDS];
|
||||
wordcount_t num_words = curve->num_words;
|
||||
wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits);
|
||||
|
||||
rx[num_n_words - 1] = 0;
|
||||
r[num_n_words - 1] = 0;
|
||||
s[num_n_words - 1] = 0;
|
||||
|
||||
#if uECC_VLI_NATIVE_LITTLE_ENDIAN
|
||||
bcopy((uint8_t *) r, signature, curve->num_bytes);
|
||||
bcopy((uint8_t *) s, signature + curve->num_bytes, curve->num_bytes);
|
||||
#else
|
||||
uECC_vli_bytesToNative(_public, public_key, curve->num_bytes);
|
||||
uECC_vli_bytesToNative(
|
||||
_public + num_words, public_key + curve->num_bytes, curve->num_bytes);
|
||||
uECC_vli_bytesToNative(r, signature, curve->num_bytes);
|
||||
uECC_vli_bytesToNative(s, signature + curve->num_bytes, curve->num_bytes);
|
||||
#endif
|
||||
|
||||
/* r, s must not be 0. */
|
||||
if (uECC_vli_isZero(r, num_words) || uECC_vli_isZero(s, num_words)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* r, s must be < n. */
|
||||
if (uECC_vli_cmp(curve->n, r, num_n_words) != 1 ||
|
||||
uECC_vli_cmp(curve->n, s, num_n_words) != 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate u1 and u2. */
|
||||
uECC_vli_modInv(z, s, curve->n, num_n_words); /* z = 1/s */
|
||||
u1[num_n_words - 1] = 0;
|
||||
bits2int(u1, message_hash, hash_size, curve);
|
||||
uECC_vli_modMult(u1, u1, z, curve->n, num_n_words); /* u1 = e/s */
|
||||
uECC_vli_modMult(u2, r, z, curve->n, num_n_words); /* u2 = r/s */
|
||||
|
||||
/* Calculate sum = G + Q. */
|
||||
uECC_vli_set(sum, _public, num_words);
|
||||
uECC_vli_set(sum + num_words, _public + num_words, num_words);
|
||||
uECC_vli_set(tx, curve->G, num_words);
|
||||
uECC_vli_set(ty, curve->G + num_words, num_words);
|
||||
uECC_vli_modSub(z, sum, tx, curve->p, num_words); /* z = x2 - x1 */
|
||||
XYcZ_add(tx, ty, sum, sum + num_words, curve);
|
||||
uECC_vli_modInv(z, z, curve->p, num_words); /* z = 1/z */
|
||||
apply_z(sum, sum + num_words, z, curve);
|
||||
|
||||
/* Use Shamir's trick to calculate u1*G + u2*Q */
|
||||
points[0] = 0;
|
||||
points[1] = curve->G;
|
||||
points[2] = _public;
|
||||
points[3] = sum;
|
||||
num_bits = smax(uECC_vli_numBits(u1, num_n_words),
|
||||
uECC_vli_numBits(u2, num_n_words));
|
||||
|
||||
point = points[(!!uECC_vli_testBit(u1, num_bits - 1)) |
|
||||
((!!uECC_vli_testBit(u2, num_bits - 1)) << 1)];
|
||||
uECC_vli_set(rx, point, num_words);
|
||||
uECC_vli_set(ry, point + num_words, num_words);
|
||||
uECC_vli_clear(z, num_words);
|
||||
z[0] = 1;
|
||||
|
||||
for (i = num_bits - 2; i >= 0; --i) {
|
||||
uECC_word_t index;
|
||||
curve->double_jacobian(rx, ry, z, curve);
|
||||
|
||||
index = (!!uECC_vli_testBit(u1, i)) | ((!!uECC_vli_testBit(u2, i)) << 1);
|
||||
point = points[index];
|
||||
if (point) {
|
||||
uECC_vli_set(tx, point, num_words);
|
||||
uECC_vli_set(ty, point + num_words, num_words);
|
||||
apply_z(tx, ty, z, curve);
|
||||
uECC_vli_modSub(tz, rx, tx, curve->p, num_words); /* Z = x2 - x1 */
|
||||
XYcZ_add(tx, ty, rx, ry, curve);
|
||||
uECC_vli_modMult_fast(z, z, tz, curve);
|
||||
}
|
||||
}
|
||||
|
||||
uECC_vli_modInv(z, z, curve->p, num_words); /* Z = 1/Z */
|
||||
apply_z(rx, ry, z, curve);
|
||||
|
||||
/* v = x1 (mod n) */
|
||||
if (uECC_vli_cmp(curve->n, rx, num_n_words) != 1) {
|
||||
uECC_vli_sub(rx, rx, curve->n, num_n_words);
|
||||
}
|
||||
|
||||
/* Anti-FI addition. Copy message_hash into verified_hash, but do it in a
|
||||
way that it will only happen if v == r (ie, rx == r)
|
||||
*/
|
||||
const uECC_word_t *mhash_words = (const uECC_word_t *)message_hash;
|
||||
uECC_word_t *vhash_words = (uECC_word_t *)verified_hash;
|
||||
unsigned hash_words = hash_size / sizeof(uECC_word_t);
|
||||
for (int w = 0; w < hash_words; w++) {
|
||||
/* note: using curve->num_words here to encourage compiler to re-read this variable */
|
||||
vhash_words[w] = mhash_words[w] ^ rx[w % curve->num_words] ^ r[w % curve->num_words];
|
||||
}
|
||||
/* Curve may be longer than hash, in which case keep reading the rest of the bytes */
|
||||
for (int w = hash_words; w < curve->num_words; w++) {
|
||||
vhash_words[w % hash_words] |= rx[w] | r[w];
|
||||
}
|
||||
|
||||
/* Accept only if v == r. */
|
||||
return (int)(uECC_vli_equal(rx, r, num_words));
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license.
|
||||
|
||||
Modifications Copyright 2020, Espressif Systems (Shanghai) PTE LTD. Licensed under the BSD
|
||||
2-clause license.
|
||||
*/
|
||||
#pragma once
|
||||
#include "uECC.h"
|
||||
|
||||
/* Version uECC_verify() that also copies message_hash to verified_hash
|
||||
if the signature is valid, and does it in a way that is harder to attack
|
||||
with fault injection.
|
||||
*/
|
||||
int uECC_verify_antifault(const uint8_t *public_key,
|
||||
const uint8_t *message_hash,
|
||||
unsigned hash_size,
|
||||
const uint8_t *signature,
|
||||
uECC_Curve curve,
|
||||
uint8_t *verified_hash);
|
|
@ -17,6 +17,9 @@ ifdef CONFIG_IDF_TARGET_ESP32
|
|||
ifndef CONFIG_SPI_FLASH_ROM_DRIVER_PATCH
|
||||
LINKER_SCRIPTS += $(IDF_PATH)/components/esp_rom/$(IDF_TARGET)/ld/$(IDF_TARGET).rom.spiflash.ld
|
||||
endif
|
||||
ifdef CONFIG_ESP32_REV_MIN_3
|
||||
LINKER_SCRIPTS += $(IDF_PATH)/components/esp_rom/$(IDF_TARGET)/ld/$(IDF_TARGET).rom.eco3.ld
|
||||
endif
|
||||
endif
|
||||
|
||||
COMPONENT_ADD_LDFLAGS += -L $(COMPONENT_PATH) $(addprefix -T ,$(LINKER_SCRIPTS))
|
||||
|
|
|
@ -74,6 +74,7 @@ SECTIONS
|
|||
.dram0.bss (NOLOAD) :
|
||||
{
|
||||
. = ALIGN (8);
|
||||
_dram_start = ABSOLUTE(.);
|
||||
_bss_start = ABSOLUTE(.);
|
||||
*(.dynsbss)
|
||||
*(.sbss)
|
||||
|
@ -150,6 +151,7 @@ SECTIONS
|
|||
*(.gnu.linkonce.lit4.*)
|
||||
_lit4_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
_dram_end = ABSOLUTE(.);
|
||||
} >dram_seg
|
||||
|
||||
.iram.text :
|
||||
|
|
|
@ -59,6 +59,7 @@ SECTIONS
|
|||
.dram0.bss (NOLOAD) :
|
||||
{
|
||||
. = ALIGN (8);
|
||||
_dram_start = ABSOLUTE(.);
|
||||
_bss_start = ABSOLUTE(.);
|
||||
*(.dynsbss)
|
||||
*(.sbss)
|
||||
|
@ -135,7 +136,7 @@ SECTIONS
|
|||
*(.gnu.linkonce.lit4.*)
|
||||
_lit4_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
_heap_start = ABSOLUTE(.);
|
||||
_dram_end = ABSOLUTE(.);
|
||||
} >dram_seg
|
||||
|
||||
.iram.text :
|
||||
|
|
|
@ -20,22 +20,34 @@ if(BOOTLOADER_BUILD)
|
|||
set(include_dirs "include" "include_bootloader")
|
||||
set(priv_requires micro-ecc spi_flash efuse)
|
||||
list(APPEND srcs
|
||||
"src/bootloader_init.c"
|
||||
"src/${IDF_TARGET}/bootloader_sha.c"
|
||||
"src/${IDF_TARGET}/flash_encrypt.c"
|
||||
"src/${IDF_TARGET}/secure_boot_signatures.c"
|
||||
"src/${IDF_TARGET}/secure_boot.c"
|
||||
"src/${IDF_TARGET}/bootloader_${IDF_TARGET}.c"
|
||||
)
|
||||
"src/bootloader_init.c"
|
||||
"src/${IDF_TARGET}/bootloader_sha.c"
|
||||
"src/${IDF_TARGET}/flash_encrypt.c"
|
||||
"src/${IDF_TARGET}/bootloader_${IDF_TARGET}.c"
|
||||
)
|
||||
else()
|
||||
list(APPEND srcs
|
||||
"src/idf/bootloader_sha.c"
|
||||
"src/idf/secure_boot_signatures.c")
|
||||
"src/idf/bootloader_sha.c")
|
||||
set(include_dirs "include")
|
||||
set(priv_include_dirs "include_bootloader")
|
||||
set(priv_requires spi_flash mbedtls efuse)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME OR CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME)
|
||||
if(BOOTLOADER_BUILD)
|
||||
list(APPEND srcs
|
||||
"src/${IDF_TARGET}/secure_boot_signatures.c")
|
||||
else()
|
||||
list(APPEND srcs
|
||||
"src/idf/secure_boot_signatures.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_SECURE_BOOT AND BOOTLOADER_BUILD)
|
||||
list(APPEND srcs
|
||||
"src/${IDF_TARGET}/secure_boot.c")
|
||||
endif()
|
||||
|
||||
set(requires soc) #unfortunately the header directly uses SOC registers
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
|
@ -44,7 +56,7 @@ idf_component_register(SRCS "${srcs}"
|
|||
REQUIRES "${requires}"
|
||||
PRIV_REQUIRES "${priv_requires}")
|
||||
|
||||
if(CONFIG_SECURE_SIGNED_APPS)
|
||||
if(CONFIG_SECURE_SIGNED_APPS AND (CONFIG_SECURE_BOOT_V1_ENABLED OR CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME))
|
||||
if(BOOTLOADER_BUILD)
|
||||
# Whether CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES or not, we need verification key to embed
|
||||
# in the library.
|
||||
|
|
|
@ -22,11 +22,23 @@ endif
|
|||
COMPONENT_OBJEXCLUDE += src/bootloader_flash_config_esp32s2.o \
|
||||
src/bootloader_efuse_esp32s2.o
|
||||
|
||||
ifndef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
ifndef CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
COMPONENT_OBJEXCLUDE += src/$(IDF_TARGET)/secure_boot_signatures.o \
|
||||
src/idf/secure_boot_signatures.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef CONFIG_SECURE_BOOT
|
||||
COMPONENT_OBJEXCLUDE += src/$(IDF_TARGET)/secure_boot.o
|
||||
endif
|
||||
|
||||
#
|
||||
# Secure boot signing key support
|
||||
#
|
||||
ifdef CONFIG_SECURE_SIGNED_APPS
|
||||
|
||||
ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
# this path is created relative to the component build directory
|
||||
SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin)
|
||||
|
||||
|
@ -41,7 +53,7 @@ ORIG_SECURE_BOOT_VERIFICATION_KEY := $(call resolvepath,$(call dequote,$(CONFIG_
|
|||
$(ORIG_SECURE_BOOT_VERIFICATION_KEY):
|
||||
@echo "Secure boot verification public key '$@' missing."
|
||||
@echo "This can be extracted from the private signing key, see"
|
||||
@echo "docs/security/secure-boot.rst for details."
|
||||
@echo "docs/security/secure-boot-v1.rst for details."
|
||||
exit 1
|
||||
|
||||
# copy it into the build dir, so the secure boot verification key has
|
||||
|
@ -55,4 +67,5 @@ COMPONENT_EXTRA_CLEAN += $(SECURE_BOOT_VERIFICATION_KEY)
|
|||
|
||||
COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY)
|
||||
|
||||
endif #CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
endif #CONFIG_SECURE_SIGNED_APPS
|
||||
|
|
|
@ -16,13 +16,18 @@
|
|||
#include <stdbool.h>
|
||||
#include <esp_err.h>
|
||||
#include "soc/efuse_periph.h"
|
||||
#include "esp_image_format.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/efuse.h"
|
||||
#else
|
||||
#include "esp32/rom/secure_boot.h"
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
typedef struct ets_secure_boot_signature ets_secure_boot_signature_t;
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
#if !defined(CONFIG_SECURE_SIGNED_ON_BOOT) || !defined(CONFIG_SECURE_SIGNED_ON_UPDATE) || !defined(CONFIG_SECURE_SIGNED_APPS)
|
||||
#error "internal sdkconfig error, secure boot should always enable all signature options"
|
||||
#endif
|
||||
|
@ -47,16 +52,23 @@ extern "C" {
|
|||
static inline bool esp_secure_boot_enabled(void)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0;
|
||||
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0;
|
||||
#elif CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
return ets_use_secure_boot_v2();
|
||||
#endif
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
return ets_efuse_secure_boot_enabled();
|
||||
#endif
|
||||
return false; /* Secure Boot not enabled in menuconfig */
|
||||
}
|
||||
|
||||
/** @brief Generate secure digest from bootloader image
|
||||
*
|
||||
* @important This function is intended to be called from bootloader code only.
|
||||
*
|
||||
* This function is only used in the context of the Secure Boot V1 scheme.
|
||||
*
|
||||
* If secure boot is not yet enabled for bootloader, this will:
|
||||
* 1) generate the secure boot key and burn it on EFUSE
|
||||
* (without enabling R/W protection)
|
||||
|
@ -73,18 +85,17 @@ static inline bool esp_secure_boot_enabled(void)
|
|||
*/
|
||||
esp_err_t esp_secure_boot_generate_digest(void);
|
||||
|
||||
/** @brief Enable secure boot if it is not already enabled.
|
||||
/** @brief Enable secure boot V1 if it is not already enabled.
|
||||
*
|
||||
* @important If this function succeeds, secure boot is permanently
|
||||
* @important If this function succeeds, secure boot V1 is permanently
|
||||
* enabled on the chip via efuse.
|
||||
*
|
||||
* @important This function is intended to be called from bootloader code only.
|
||||
*
|
||||
* @important This will enable r/w protection of secure boot key on EFUSE,
|
||||
* therefore it is to be ensured that esp_secure_boot_generate_digest()
|
||||
* is called before this
|
||||
*
|
||||
* If secure boot is not yet enabled for bootloader, this will
|
||||
* @important In case of Secure Boot V1, this will enable r/w protection
|
||||
* of secure boot key on EFUSE, therefore it is to be ensured that
|
||||
* esp_secure_boot_generate_digest() is called before this .If secure boot is not
|
||||
* yet enabled for bootloader, this will
|
||||
* 1) enable R/W protection of secure boot key on EFUSE
|
||||
* 2) enable secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse.
|
||||
*
|
||||
|
@ -100,35 +111,97 @@ esp_err_t esp_secure_boot_generate_digest(void);
|
|||
*/
|
||||
esp_err_t esp_secure_boot_permanently_enable(void);
|
||||
|
||||
/** @brief Verify the secure boot signature (determinstic ECDSA w/ SHA256) appended to some binary data in flash.
|
||||
/** @brief Enables secure boot V2 if it is not already enabled.
|
||||
*
|
||||
* Public key is compiled into the calling program. See docs/security/secure-boot.rst for details.
|
||||
* @important If this function succeeds, secure boot V2 is permanently
|
||||
* enabled on the chip via efuse.
|
||||
*
|
||||
* @important This function is intended to be called from bootloader code only.
|
||||
*
|
||||
* @important In case of Secure Boot V2, this will enable write protection
|
||||
* of secure boot key on EFUSE in BLK2. .If secure boot is not
|
||||
* yet enabled for bootloader, this will
|
||||
* 1) enable W protection of secure boot key on EFUSE
|
||||
* 2) enable secure boot by blowing the EFUSE_RD_ABS_DONE_1 efuse.
|
||||
*
|
||||
* This function does not verify secure boot of the bootloader (the
|
||||
* ROM bootloader does this.)
|
||||
*
|
||||
* @param image_data Image metadata of the application to be loaded.
|
||||
*
|
||||
* Will fail if efuses have been part-burned in a way that indicates
|
||||
* secure boot should not or could not be correctly enabled.
|
||||
*
|
||||
* @return ESP_ERR_INVALID_STATE if efuse state doesn't allow
|
||||
* secure boot to be enabled cleanly. ESP_OK if secure boot
|
||||
* is enabled on this chip from now on.
|
||||
*/
|
||||
esp_err_t esp_secure_boot_v2_permanently_enable(const esp_image_metadata_t *image_data);
|
||||
|
||||
/** @brief Verify the secure boot signature appended to some binary data in flash.
|
||||
*
|
||||
* For ECDSA Scheme (Secure Boot V1) - deterministic ECDSA w/ SHA256 image
|
||||
* For RSA Scheme (Secure Boot V2) - RSA-PSS Verification of the SHA-256 image
|
||||
*
|
||||
* Public key is compiled into the calling program in the ECDSA Scheme.
|
||||
* See the apt docs/security/secure-boot-v1.rst or docs/security/secure-boot-v2.rst for details.
|
||||
*
|
||||
* @param src_addr Starting offset of the data in flash.
|
||||
* @param length Length of data in bytes. Signature is appended -after- length bytes.
|
||||
*
|
||||
* If flash encryption is enabled, the image will be transparently decrypted while being verified.
|
||||
*
|
||||
* @note This function doesn't have any fault injection resistance so should not be called
|
||||
* during a secure boot itself (but can be called when verifying an update, etc.)
|
||||
*
|
||||
* @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if
|
||||
* signature fails, ESP_FAIL for other failures (ie can't read flash).
|
||||
*/
|
||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length);
|
||||
|
||||
/** @brief Verify the secure boot signature block (deterministic ECDSA w/ SHA256) based on the SHA256 hash of some data.
|
||||
*
|
||||
* Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
|
||||
* @param sig_block Pointer to signature block data
|
||||
* @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
|
||||
*
|
||||
*/
|
||||
|
||||
/** @brief Secure boot verification block, on-flash data format. */
|
||||
typedef struct {
|
||||
uint32_t version;
|
||||
uint8_t signature[64];
|
||||
} esp_secure_boot_sig_block_t;
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest);
|
||||
/** @brief Verify the ECDSA secure boot signature block for Secure Boot V1.
|
||||
*
|
||||
* Calculates Deterministic ECDSA w/ SHA256 based on the SHA256 hash of the image. ECDSA signature
|
||||
* verification must be enabled in project configuration to use this function.
|
||||
*
|
||||
* Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
|
||||
* @param sig_block Pointer to ECDSA signature block data
|
||||
* @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
|
||||
* @param verified_digest Pointer to 32 byte buffer that will receive verified digest if verification completes. (Used during bootloader implementation only, result is invalid otherwise.)
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_secure_boot_verify_ecdsa_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest);
|
||||
|
||||
|
||||
/** @brief Verify the RSA secure boot signature block for Secure Boot V2.
|
||||
*
|
||||
* Performs RSA-PSS Verification of the SHA-256 image based on the public key
|
||||
* in the signature block, compared against the public key digest stored in efuse.
|
||||
*
|
||||
* Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
|
||||
* @param sig_block Pointer to RSA signature block data
|
||||
* @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
|
||||
* @param verified_digest Pointer to 32 byte buffer that will receive verified digest if verification completes. (Used during bootloader implementation only, result is invalid otherwise.)
|
||||
*
|
||||
*/
|
||||
esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest);
|
||||
|
||||
/** @brief Legacy ECDSA verification function
|
||||
*
|
||||
* @note Deprecated, call either esp_secure_boot_verify_ecdsa_signature_block() or esp_secure_boot_verify_rsa_signature_block() instead.
|
||||
*
|
||||
* @param sig_block Pointer to ECDSA signature block data
|
||||
* @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
|
||||
*/
|
||||
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest)
|
||||
__attribute__((deprecated("use esp_secure_boot_verify_ecdsa_signature_block instead")));
|
||||
|
||||
|
||||
#define FLASH_OFFS_SECURE_BOOT_IV_DIGEST 0
|
||||
|
||||
|
|
|
@ -18,9 +18,11 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_spi_flash.h> /* including in bootloader for error values */
|
||||
|
||||
#define FLASH_SECTOR_SIZE 0x1000
|
||||
#define FLASH_BLOCK_SIZE 0x10000
|
||||
#define MMAP_ALIGNED_MASK 0x0000FFFF
|
||||
|
||||
/* Provide a Flash API for bootloader_support code,
|
||||
that can be used from bootloader or app code.
|
||||
|
|
|
@ -108,3 +108,17 @@ esp_err_t bootloader_sha256_hex_to_str(char *out_str, const uint8_t *in_array_he
|
|||
* @param label Label to print at beginning of log line.
|
||||
*/
|
||||
void bootloader_debug_buffer(const void *buffer, size_t length, const char *label);
|
||||
|
||||
/** @brief Generates the digest of the data between offset & offset+length.
|
||||
*
|
||||
* This function should be used when the size of the data is larger than 3.2MB.
|
||||
* The MMU capacity is 3.2MB (50 pages - 64KB each). This function generates the SHA-256
|
||||
* of the data in chunks of 3.2MB, considering the MMU capacity.
|
||||
*
|
||||
* @param[in] flash_offset Offset of the data in flash.
|
||||
* @param[in] len Length of data in bytes.
|
||||
* @param[out] digest Pointer to buffer where the digest is written, if ESP_OK is returned.
|
||||
*
|
||||
* @return ESP_OK if secure boot digest is generated successfully.
|
||||
*/
|
||||
esp_err_t bootloader_sha256_flash_contents(uint32_t flash_offset, uint32_t len, uint8_t *digest);
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
#include <bootloader_flash.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_spi_flash.h> /* including in bootloader for error values */
|
||||
#include <esp_flash_encrypt.h>
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
#include "esp32s2/rom/spi_flash.h"
|
||||
|
|
|
@ -557,11 +557,19 @@ static void load_image(const esp_image_metadata_t *image_data)
|
|||
* then Step 6 enables secure boot.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_SECURE_FLASH_ENC_ENABLED)
|
||||
#if defined(CONFIG_SECURE_BOOT) || defined(CONFIG_SECURE_FLASH_ENC_ENABLED)
|
||||
esp_err_t err;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
#ifdef CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
err = esp_secure_boot_v2_permanently_enable(image_data);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Secure Boot v2 failed (%d)", err);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
/* Steps 1 & 2 (see above for full description):
|
||||
* 1) Generate secure boot EFUSE key
|
||||
* 2) Compute digest of plaintext bootloader
|
||||
|
@ -588,7 +596,7 @@ static void load_image(const esp_image_metadata_t *image_data)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
/* Step 6 (see above for full description):
|
||||
* 6) Burn EFUSE to enable secure boot
|
||||
*/
|
||||
|
@ -810,3 +818,37 @@ void bootloader_debug_buffer(const void *buffer, size_t length, const char *labe
|
|||
ESP_LOGD(TAG, "%s: %s", label, hexbuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t bootloader_sha256_flash_contents(uint32_t flash_offset, uint32_t len, uint8_t *digest)
|
||||
{
|
||||
if (digest == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Handling firmware images larger than MMU capacity */
|
||||
uint32_t mmu_free_pages_count = bootloader_mmap_get_free_pages();
|
||||
bootloader_sha256_handle_t sha_handle = NULL;
|
||||
|
||||
sha_handle = bootloader_sha256_start();
|
||||
if (sha_handle == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
while (len > 0) {
|
||||
uint32_t mmu_page_offset = ((flash_offset & MMAP_ALIGNED_MASK) != 0) ? 1 : 0; /* Skip 1st MMU Page if it is already populated */
|
||||
uint32_t partial_image_len = MIN(len, ((mmu_free_pages_count - mmu_page_offset) * SPI_FLASH_MMU_PAGE_SIZE)); /* Read the image that fits in the free MMU pages */
|
||||
|
||||
const void * image = bootloader_mmap(flash_offset, partial_image_len);
|
||||
if (image == NULL) {
|
||||
bootloader_sha256_finish(sha_handle, NULL);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
bootloader_sha256_data(sha_handle, image, partial_image_len);
|
||||
bootloader_munmap(image);
|
||||
|
||||
flash_offset += partial_image_len;
|
||||
len -= partial_image_len;
|
||||
}
|
||||
bootloader_sha256_finish(sha_handle, digest);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
|
|
@ -236,13 +236,24 @@ static esp_err_t encrypt_bootloader(void)
|
|||
/* Check for plaintext bootloader (verification will fail if it's already encrypted) */
|
||||
if (esp_image_verify_bootloader(&image_length) == ESP_OK) {
|
||||
ESP_LOGD(TAG, "bootloader is plaintext. Encrypting...");
|
||||
|
||||
#if CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
// Account for the signature sector after the bootloader
|
||||
image_length = (image_length + FLASH_SECTOR_SIZE - 1) & ~(FLASH_SECTOR_SIZE - 1);
|
||||
image_length += FLASH_SECTOR_SIZE;
|
||||
if (ESP_BOOTLOADER_OFFSET + image_length > ESP_PARTITION_TABLE_OFFSET) {
|
||||
ESP_LOGE(TAG, "Bootloader is too large to fit Secure Boot V2 signature sector and partition table (configured offset 0x%x)", ESP_PARTITION_TABLE_OFFSET);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
#endif // CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
|
||||
err = esp_flash_encrypt_region(ESP_BOOTLOADER_OFFSET, image_length);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to encrypt bootloader in place: 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
/* If secure boot is enabled and bootloader was plaintext, also
|
||||
* need to encrypt secure boot IV+digest.
|
||||
*/
|
||||
|
@ -355,4 +366,4 @@ esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length)
|
|||
flash_failed:
|
||||
ESP_LOGE(TAG, "flash operation failed: 0x%x", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,10 +20,12 @@
|
|||
|
||||
#include "esp32/rom/cache.h"
|
||||
#include "esp32/rom/ets_sys.h"
|
||||
#include "esp32/rom/secure_boot.h"
|
||||
#include "esp32/rom/crc.h"
|
||||
|
||||
#include "soc/efuse_periph.h"
|
||||
#include "soc/rtc_periph.h"
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_utility.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
|
@ -37,9 +39,14 @@
|
|||
/* The following API implementations are used only when called
|
||||
* from the bootloader code.
|
||||
*/
|
||||
/* Burn values written to the efuse write registers */
|
||||
static inline void burn_efuses(void)
|
||||
{
|
||||
esp_efuse_burn_new_values();
|
||||
}
|
||||
|
||||
static const char* TAG = "secure_boot";
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
static const char *TAG = "secure_boot_v1";
|
||||
/**
|
||||
* @function : secure_boot_generate
|
||||
* @description: generate boot digest (aka "abstract") & iv
|
||||
|
@ -94,12 +101,6 @@ static bool secure_boot_generate(uint32_t image_len){
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Burn values written to the efuse write registers */
|
||||
static inline void burn_efuses(void)
|
||||
{
|
||||
esp_efuse_burn_new_values();
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_generate_digest(void)
|
||||
{
|
||||
esp_err_t err;
|
||||
|
@ -219,3 +220,188 @@ esp_err_t esp_secure_boot_permanently_enable(void)
|
|||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
#elif CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
|
||||
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
static const char *TAG = "secure_boot_v2";
|
||||
|
||||
#define SIG_BLOCK_MAGIC_BYTE 0xe7
|
||||
#define CRC_SIGN_BLOCK_LEN 1196
|
||||
#define SIG_BLOCK_PADDING 4096
|
||||
|
||||
#define DIGEST_LEN 32
|
||||
|
||||
static esp_err_t validate_signature_block(const ets_secure_boot_signature_t *sig_block, uint8_t *digest)
|
||||
{
|
||||
uint32_t crc = crc32_le(0, (uint8_t *)sig_block, CRC_SIGN_BLOCK_LEN);
|
||||
if (sig_block->block[0].magic_byte == SIG_BLOCK_MAGIC_BYTE && sig_block->block[0].block_crc == crc && !memcmp(digest, sig_block->block[0].image_digest, DIGEST_LEN)) {
|
||||
ESP_LOGI(TAG, "valid signature block found");
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static esp_err_t secure_boot_v2_digest_generate(uint32_t flash_offset, uint32_t flash_size, uint8_t *public_key_digest)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
uint8_t image_digest[DIGEST_LEN] = {0};
|
||||
size_t sig_block_addr = flash_offset + ALIGN_UP(flash_size, FLASH_SECTOR_SIZE);
|
||||
ret = bootloader_sha256_flash_contents(flash_offset, sig_block_addr - flash_offset, image_digest);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "error generating image digest, %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "reading signature block");
|
||||
const ets_secure_boot_signature_t *sig_block = bootloader_mmap(sig_block_addr, sizeof(ets_secure_boot_signature_t));
|
||||
if (sig_block == NULL) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", sig_block_addr, sizeof(ets_secure_boot_signature_t));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Validating Signature block */
|
||||
ret = validate_signature_block(sig_block, image_digest);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "signature block (address 0x%x) validation failed %d", sig_block_addr, ret);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Verifying Signature block */
|
||||
uint8_t verified_digest[DIGEST_LEN] = {0};
|
||||
|
||||
/* Generating the SHA of the public key components in the signature block */
|
||||
bootloader_sha256_handle_t sig_block_sha;
|
||||
sig_block_sha = bootloader_sha256_start();
|
||||
bootloader_sha256_data(sig_block_sha, &sig_block->block[0].key, sizeof(sig_block->block[0].key));
|
||||
bootloader_sha256_finish(sig_block_sha, public_key_digest);
|
||||
|
||||
secure_boot_v2_status_t error;
|
||||
error = ets_secure_boot_verify_signature(sig_block, image_digest, public_key_digest, verified_digest);
|
||||
if (error != SBV2_SUCCESS) {
|
||||
ESP_LOGE(TAG, "secure boot v2 verification failed %d", error);
|
||||
ret = ESP_FAIL;
|
||||
goto done;
|
||||
} else {
|
||||
ret = ESP_OK;
|
||||
}
|
||||
|
||||
done:
|
||||
bootloader_munmap(sig_block);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_v2_permanently_enable(const esp_image_metadata_t *image_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "enabling secure boot v2...");
|
||||
esp_err_t ret;
|
||||
if (esp_secure_boot_enabled()) {
|
||||
ESP_LOGI(TAG, "secure boot v2 is already enabled. Continuing..");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint32_t coding_scheme = REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_CODING_SCHEME);
|
||||
if (coding_scheme != EFUSE_CODING_SCHEME_VAL_NONE) {
|
||||
ESP_LOGE(TAG, "No coding schemes are supported in secure boot v2.(Detected scheme: 0x%x)", coding_scheme);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/* Verify the bootloader */
|
||||
esp_image_metadata_t bootloader_data = { 0 };
|
||||
ret = esp_image_verify_bootloader_data(&bootloader_data);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "bootloader image appears invalid! error %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t boot_pub_key_digest[DIGEST_LEN];
|
||||
uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG);
|
||||
bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2;
|
||||
bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2;
|
||||
if (efuse_key_write_protected == false
|
||||
&& efuse_key_read_protected == false
|
||||
&& REG_READ(EFUSE_BLK2_RDATA0_REG) == 0
|
||||
&& REG_READ(EFUSE_BLK2_RDATA1_REG) == 0
|
||||
&& REG_READ(EFUSE_BLK2_RDATA2_REG) == 0
|
||||
&& REG_READ(EFUSE_BLK2_RDATA3_REG) == 0
|
||||
&& REG_READ(EFUSE_BLK2_RDATA4_REG) == 0
|
||||
&& REG_READ(EFUSE_BLK2_RDATA5_REG) == 0
|
||||
&& REG_READ(EFUSE_BLK2_RDATA6_REG) == 0
|
||||
&& REG_READ(EFUSE_BLK2_RDATA7_REG) == 0) {
|
||||
/* Verifies the signature block appended to the image matches with the signature block of the app to be loaded */
|
||||
ret = secure_boot_v2_digest_generate(bootloader_data.start_addr, bootloader_data.image_len - SIG_BLOCK_PADDING, boot_pub_key_digest);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Public key digest generation failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Burning public key hash to efuse.");
|
||||
uint32_t *boot_public_key_digest_ptr = (uint32_t *) boot_pub_key_digest;
|
||||
for (int i = 0; i < 8 ; i++) {
|
||||
REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4 * i, boot_public_key_digest_ptr[i]);
|
||||
ESP_LOGD(TAG, "EFUSE_BLKx_WDATA%d_REG = 0x%08x", i, boot_public_key_digest_ptr[i]);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Write protecting public key digest...");
|
||||
REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2);
|
||||
efuse_key_write_protected = true;
|
||||
efuse_key_read_protected = false;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Using pre-loaded secure boot v2 public key digest in EFUSE block 2");
|
||||
}
|
||||
|
||||
uint8_t app_pub_key_digest[DIGEST_LEN];
|
||||
ret = secure_boot_v2_digest_generate(image_data->start_addr, image_data->image_len - SIG_BLOCK_PADDING, app_pub_key_digest);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Application signature block is invalid.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Confirming if the public key in the bootloader's signature block matches with the one in the application's signature block */
|
||||
if (memcmp(boot_pub_key_digest, app_pub_key_digest, DIGEST_LEN) != 0) {
|
||||
ESP_LOGE(TAG, "Application not signed with a valid private key.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (efuse_key_read_protected) {
|
||||
ESP_LOGE(TAG, "Efuse BLK2 (public key digest) is read protected. Refusing to blow secure boot efuse.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!efuse_key_write_protected) {
|
||||
ESP_LOGE(TAG, "Efuse BLK2 (public key digest) is not write protected. Refusing to blow secure boot efuse.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "blowing secure boot efuse...");
|
||||
ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG));
|
||||
|
||||
uint32_t new_wdata6 = EFUSE_RD_ABS_DONE_1;
|
||||
|
||||
#ifndef CONFIG_SECURE_BOOT_ALLOW_JTAG
|
||||
ESP_LOGI(TAG, "Disable JTAG...");
|
||||
new_wdata6 |= EFUSE_RD_DISABLE_JTAG;
|
||||
#else
|
||||
ESP_LOGW(TAG, "Not disabling JTAG - SECURITY COMPROMISED");
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC
|
||||
ESP_LOGI(TAG, "Disable ROM BASIC interpreter fallback...");
|
||||
new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE;
|
||||
#else
|
||||
ESP_LOGW(TAG, "Not disabling ROM BASIC fallback - SECURITY COMPROMISED");
|
||||
#endif
|
||||
|
||||
REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6);
|
||||
burn_efuses();
|
||||
uint32_t after = REG_READ(EFUSE_BLK0_RDATA6_REG);
|
||||
ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after);
|
||||
if (after & EFUSE_RD_ABS_DONE_1) {
|
||||
ESP_LOGI(TAG, "secure boot v2 is now enabled.");
|
||||
return ESP_OK;
|
||||
} else {
|
||||
ESP_LOGE(TAG, " secure boot v2 not enabled, EFUSE_RD_ABS_DONE_1 is probably write protected!");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
|
|
|
@ -15,62 +15,40 @@
|
|||
|
||||
#include "bootloader_flash.h"
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_utility.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_secure_boot.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_fault.h"
|
||||
#include "esp32/rom/sha.h"
|
||||
#include "uECC.h"
|
||||
#include "uECC_verify_antifault.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *TAG = "secure_boot";
|
||||
#define DIGEST_LEN 32
|
||||
|
||||
#ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
extern const uint8_t signature_verification_key_start[] asm("_binary_signature_verification_key_bin_start");
|
||||
extern const uint8_t signature_verification_key_end[] asm("_binary_signature_verification_key_bin_end");
|
||||
|
||||
#define SIGNATURE_VERIFICATION_KEYLEN 64
|
||||
|
||||
#define DIGEST_LEN 32
|
||||
|
||||
/* Mmap source address mask */
|
||||
#define MMAP_ALIGNED_MASK 0x0000FFFF
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||
{
|
||||
uint8_t digest[DIGEST_LEN];
|
||||
const uint8_t *data;
|
||||
uint8_t verified_digest[DIGEST_LEN] = { 0 }; /* ignored in this function */
|
||||
const esp_secure_boot_sig_block_t *sigblock;
|
||||
|
||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
||||
|
||||
bootloader_sha256_handle_t handle = bootloader_sha256_start();
|
||||
|
||||
uint32_t free_page_count = bootloader_mmap_get_free_pages();
|
||||
ESP_LOGD(TAG, "free data page_count 0x%08x", free_page_count);
|
||||
|
||||
int32_t data_len_remain = length;
|
||||
uint32_t data_addr = src_addr;
|
||||
while (data_len_remain > 0) {
|
||||
uint32_t offset_page = ((data_addr & MMAP_ALIGNED_MASK) != 0) ? 1 : 0;
|
||||
/* Data we could map in case we are not aligned to PAGE boundary is one page size lesser. */
|
||||
uint32_t data_len = MIN(data_len_remain, ((free_page_count - offset_page) * SPI_FLASH_MMU_PAGE_SIZE));
|
||||
data = (const uint8_t *) bootloader_mmap(data_addr, data_len);
|
||||
if(!data) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", data_addr, data_len);
|
||||
bootloader_sha256_finish(handle, NULL);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
bootloader_sha256_data(handle, data, data_len);
|
||||
bootloader_munmap(data);
|
||||
|
||||
data_addr += data_len;
|
||||
data_len_remain -= data_len;
|
||||
esp_err_t err = bootloader_sha256_flash_contents(src_addr, length, digest);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Done! Get the digest */
|
||||
bootloader_sha256_finish(handle, digest);
|
||||
|
||||
// Map the signature block
|
||||
sigblock = (const esp_secure_boot_sig_block_t *) bootloader_mmap(src_addr + length, sizeof(esp_secure_boot_sig_block_t));
|
||||
if(!sigblock) {
|
||||
|
@ -78,7 +56,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||
return ESP_FAIL;
|
||||
}
|
||||
// Verify the signature
|
||||
esp_err_t err = esp_secure_boot_verify_signature_block(sigblock, digest);
|
||||
err = esp_secure_boot_verify_ecdsa_signature_block(sigblock, digest, verified_digest);
|
||||
// Unmap
|
||||
bootloader_munmap(sigblock);
|
||||
|
||||
|
@ -86,6 +64,12 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||
}
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest)
|
||||
{
|
||||
uint8_t verified_digest[DIGEST_LEN] = { 0 };
|
||||
return esp_secure_boot_verify_ecdsa_signature_block(sig_block, image_digest, verified_digest);
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_verify_ecdsa_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
ptrdiff_t keylen;
|
||||
|
||||
|
@ -103,11 +87,83 @@ esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block
|
|||
ESP_LOGD(TAG, "Verifying secure boot signature");
|
||||
|
||||
bool is_valid;
|
||||
is_valid = uECC_verify(signature_verification_key_start,
|
||||
is_valid = uECC_verify_antifault(signature_verification_key_start,
|
||||
image_digest,
|
||||
DIGEST_LEN,
|
||||
sig_block->signature,
|
||||
uECC_secp256r1());
|
||||
uECC_secp256r1(),
|
||||
verified_digest);
|
||||
ESP_LOGD(TAG, "Verification result %d", is_valid);
|
||||
|
||||
return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||
{
|
||||
uint8_t digest[DIGEST_LEN] = {0};
|
||||
uint8_t verified_digest[DIGEST_LEN] = {0}; // ignored in this function
|
||||
const uint8_t *data;
|
||||
|
||||
/* Padding to round off the input to the nearest 4k boundary */
|
||||
int padded_length = ALIGN_UP(length, FLASH_SECTOR_SIZE);
|
||||
ESP_LOGD(TAG, "verifying src_addr 0x%x length", src_addr, padded_length);
|
||||
|
||||
data = bootloader_mmap(src_addr, padded_length + sizeof(ets_secure_boot_signature_t));
|
||||
if (data == NULL) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, padded_length);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Calculate digest of main image */
|
||||
esp_err_t err = bootloader_sha256_flash_contents(src_addr, padded_length, digest);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Digest calculation failed 0x%x, 0x%x", src_addr, padded_length);
|
||||
bootloader_munmap(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
const ets_secure_boot_signature_t *sig_block = (const ets_secure_boot_signature_t *)(data + padded_length);
|
||||
err = esp_secure_boot_verify_rsa_signature_block(sig_block, digest, verified_digest);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
||||
}
|
||||
|
||||
bootloader_munmap(data);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
uint8_t efuse_trusted_digest[DIGEST_LEN] = {0}, sig_block_trusted_digest[DIGEST_LEN] = {0};
|
||||
|
||||
secure_boot_v2_status_t r;
|
||||
memcpy(efuse_trusted_digest, (uint8_t *)EFUSE_BLK2_RDATA0_REG, DIGEST_LEN); /* EFUSE_BLK2_RDATA0_REG - Stores the Secure Boot Public Key Digest */
|
||||
|
||||
if (!ets_use_secure_boot_v2()) {
|
||||
ESP_LOGI(TAG, "Secure Boot EFuse bit(ABS_DONE_1) not yet programmed.");
|
||||
|
||||
/* Generating the SHA of the public key components in the signature block */
|
||||
bootloader_sha256_handle_t sig_block_sha;
|
||||
sig_block_sha = bootloader_sha256_start();
|
||||
bootloader_sha256_data(sig_block_sha, &sig_block->block[0].key, sizeof(sig_block->block[0].key));
|
||||
bootloader_sha256_finish(sig_block_sha, (unsigned char *)sig_block_trusted_digest);
|
||||
|
||||
if (memcmp(efuse_trusted_digest, sig_block_trusted_digest, DIGEST_LEN) != 0) {
|
||||
ESP_LOGW(TAG, "Public key digest in eFuse BLK2 and the signature block don't match.");
|
||||
}
|
||||
|
||||
memcpy(efuse_trusted_digest, sig_block_trusted_digest, DIGEST_LEN);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Verifying with RSA-PSS...");
|
||||
r = ets_secure_boot_verify_signature(sig_block, image_digest, efuse_trusted_digest, verified_digest);
|
||||
if (r != SBV2_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
||||
}
|
||||
|
||||
return (r == SBV2_SUCCESS) ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#include "esp_log.h"
|
||||
#include "esp32s2/rom/secure_boot.h"
|
||||
|
||||
#define TAG "secure_boot"
|
||||
static const char *TAG = "secure_boot";
|
||||
|
||||
esp_err_t esp_secure_boot_permanently_enable(void)
|
||||
{
|
||||
|
|
|
@ -27,6 +27,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||
{
|
||||
ets_secure_boot_key_digests_t trusted_keys = { 0 };
|
||||
uint8_t digest[DIGEST_LEN];
|
||||
uint8_t verified_digest[DIGEST_LEN] = { 0 }; /* Note: this function doesn't do any anti-FI checks on this buffer */
|
||||
const uint8_t *data;
|
||||
|
||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
||||
|
@ -37,7 +38,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||
}
|
||||
|
||||
data = bootloader_mmap(src_addr, length + sizeof(struct ets_secure_boot_sig_block));
|
||||
if(data == NULL) {
|
||||
if (data == NULL) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(ets_secure_boot_signature_t));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
@ -57,36 +58,25 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
|||
if (r == ETS_OK) {
|
||||
const ets_secure_boot_signature_t *sig = (const ets_secure_boot_signature_t *)(data + length);
|
||||
// TODO: calling this function in IDF app context is unsafe
|
||||
r = ets_secure_boot_verify_signature(sig, digest, &trusted_keys);
|
||||
r = ets_secure_boot_verify_signature(sig, digest, &trusted_keys, verified_digest);
|
||||
}
|
||||
bootloader_munmap(data);
|
||||
|
||||
return (r == ETS_OK) ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature_block(uint32_t sig_block_flash_offs, const uint8_t *image_digest)
|
||||
esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
ets_secure_boot_key_digests_t trusted_keys;
|
||||
|
||||
assert(sig_block_flash_offs % 4096 == 0); // TODO: enforce this in a better way
|
||||
|
||||
const ets_secure_boot_signature_t *sig = bootloader_mmap(sig_block_flash_offs, sizeof(ets_secure_boot_signature_t));
|
||||
|
||||
if (sig == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to mmap data at offset 0x%x", sig_block_flash_offs);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int r = ets_secure_boot_read_key_digests(&trusted_keys);
|
||||
if (r != 0) {
|
||||
ESP_LOGE(TAG, "No trusted key digests were found in efuse!");
|
||||
} else {
|
||||
ESP_LOGD(TAG, "Verifying with RSA-PSS...");
|
||||
// TODO: calling this function in IDF app context is unsafe
|
||||
r = ets_secure_boot_verify_signature(sig, image_digest, &trusted_keys);
|
||||
r = ets_secure_boot_verify_signature(sig_block, image_digest, &trusted_keys, verified_digest);
|
||||
}
|
||||
|
||||
bootloader_munmap(sig);
|
||||
|
||||
return (r == 0) ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <soc/cpu.h>
|
||||
#include <bootloader_utility.h>
|
||||
#include <esp_secure_boot.h>
|
||||
#include <esp_fault.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_spi_flash.h>
|
||||
#include <bootloader_flash.h>
|
||||
|
@ -23,6 +24,7 @@
|
|||
#include <bootloader_sha.h>
|
||||
#include "bootloader_util.h"
|
||||
#include "bootloader_common.h"
|
||||
#include "soc/soc_memory_layout.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "esp32/rom/rtc.h"
|
||||
#include "esp32/rom/secure_boot.h"
|
||||
|
@ -37,11 +39,11 @@
|
|||
*/
|
||||
#ifdef BOOTLOADER_BUILD
|
||||
#ifdef CONFIG_SECURE_SIGNED_ON_BOOT
|
||||
#define SECURE_BOOT_CHECK_SIGNATURE
|
||||
#define SECURE_BOOT_CHECK_SIGNATURE 1
|
||||
#endif
|
||||
#else /* !BOOTLOADER_BUILD */
|
||||
#ifdef CONFIG_SECURE_SIGNED_ON_UPDATE
|
||||
#define SECURE_BOOT_CHECK_SIGNATURE
|
||||
#define SECURE_BOOT_CHECK_SIGNATURE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -55,18 +57,12 @@ static const char *TAG = "esp_image";
|
|||
/* Headroom to ensure between stack SP (at time of checking) and data loaded from flash */
|
||||
#define STACK_LOAD_HEADROOM 32768
|
||||
|
||||
/* Mmap source address mask */
|
||||
#define MMAP_ALIGNED_MASK 0x0000FFFF
|
||||
|
||||
#ifdef BOOTLOADER_BUILD
|
||||
/* 64 bits of random data to obfuscate loaded RAM with, until verification is complete
|
||||
(Means loaded code isn't executable until after the secure boot check.)
|
||||
*/
|
||||
static uint32_t ram_obfs_value[2];
|
||||
|
||||
/* Range of IRAM used by the loader, defined in ld script */
|
||||
extern int _loader_text_start;
|
||||
extern int _loader_text_end;
|
||||
#endif
|
||||
|
||||
/* Return true if load_addr is an address the bootloader should load into */
|
||||
|
@ -97,7 +93,7 @@ static esp_err_t verify_segment_header(int index, const esp_image_segment_header
|
|||
|
||||
static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data);
|
||||
|
||||
static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
|
||||
static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest);
|
||||
static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
|
||||
|
||||
static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
|
||||
|
@ -115,6 +111,11 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||
uint32_t checksum_word = ESP_ROM_CHECKSUM_INITIAL;
|
||||
uint32_t *checksum = NULL;
|
||||
bootloader_sha256_handle_t sha_handle = NULL;
|
||||
#if SECURE_BOOT_CHECK_SIGNATURE
|
||||
/* used for anti-FI checks */
|
||||
uint8_t image_digest[HASH_LEN] = { [ 0 ... 31] = 0xEE };
|
||||
uint8_t verified_digest[HASH_LEN] = { [ 0 ... 31 ] = 0x01 };
|
||||
#endif
|
||||
|
||||
if (data == NULL || part == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
@ -172,6 +173,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||
for (int i = 0; i < data->image.segment_count; i++) {
|
||||
esp_image_segment_header_t *header = &data->segments[i];
|
||||
ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr);
|
||||
|
||||
err = process_segment(i, next_addr, header, silent, do_load, sha_handle, checksum);
|
||||
if (err != ESP_OK) {
|
||||
goto err;
|
||||
|
@ -197,14 +199,14 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||
}
|
||||
}
|
||||
|
||||
/* For secure boot on ESP32, we don't calculate SHA or verify signautre on bootloaders.
|
||||
For ESP32S2, we do verify signature on bootloader which includes the SHA calculation.
|
||||
/* For secure boot V1 on ESP32, we don't calculate SHA or verify signature on bootloaders.
|
||||
For Secure Boot V2, we do verify signature on bootloader which includes the SHA calculation.
|
||||
|
||||
(For non-secure boot, we don't verify any SHA-256 hash appended to the bootloader because
|
||||
esptool.py may have rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead.)
|
||||
*/
|
||||
bool verify_sha;
|
||||
#if CONFIG_SECURE_BOOT_ENABLED && CONFIG_IDF_TARGET_ESP32S2
|
||||
#if CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
verify_sha = true;
|
||||
#else // ESP32, or ESP32S2 without secure boot enabled
|
||||
verify_sha = (data->start_addr != ESP_BOOTLOADER_OFFSET);
|
||||
|
@ -217,7 +219,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||
|
||||
#ifdef SECURE_BOOT_CHECK_SIGNATURE
|
||||
// secure boot images have a signature appended
|
||||
err = verify_secure_boot_signature(sha_handle, data);
|
||||
err = verify_secure_boot_signature(sha_handle, data, image_digest, verified_digest);
|
||||
#else
|
||||
// No secure boot, but SHA-256 can be appended for basic corruption detection
|
||||
if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) {
|
||||
|
@ -250,7 +252,28 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
|
|||
}
|
||||
|
||||
#ifdef BOOTLOADER_BUILD
|
||||
if (do_load && ram_obfs_value[0] != 0 && ram_obfs_value[1] != 0) { // Need to deobfuscate RAM
|
||||
|
||||
#ifdef SECURE_BOOT_CHECK_SIGNATURE
|
||||
/* If signature was checked in bootloader build, verified_digest should equal image_digest
|
||||
|
||||
This is to detect any fault injection that caused signature verification to not complete normally.
|
||||
|
||||
Any attack which bypasses this check should be of limited use as the RAM contents are still obfuscated, therefore we do the check
|
||||
immediately before we deobfuscate.
|
||||
|
||||
Note: the conditions for making this check are the same as for setting verify_sha above, but on ESP32 SB V1 we move the test for
|
||||
"only verify signature in bootloader" into the macro so it's tested multiple times.
|
||||
*/
|
||||
#if CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
ESP_FAULT_ASSERT(memcmp(image_digest, verified_digest, HASH_LEN) == 0);
|
||||
#else // Secure Boot V1 on ESP32, only verify signatures for apps not bootloaders
|
||||
ESP_FAULT_ASSERT(data->start_addr == ESP_BOOTLOADER_OFFSET || memcmp(image_digest, verified_digest, HASH_LEN) == 0);
|
||||
#endif
|
||||
|
||||
#endif // SECURE_BOOT_CHECK_SIGNATURE
|
||||
|
||||
// Deobfuscate RAM
|
||||
if (do_load && ram_obfs_value[0] != 0 && ram_obfs_value[1] != 0) {
|
||||
for (int i = 0; i < data->image.segment_count; i++) {
|
||||
uint32_t load_addr = data->segments[i].load_addr;
|
||||
if (should_load(load_addr)) {
|
||||
|
@ -336,6 +359,127 @@ static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t
|
|||
return err;
|
||||
}
|
||||
|
||||
#ifdef BOOTLOADER_BUILD
|
||||
/* Check the region load_addr - load_end doesn't overlap any memory used by the bootloader, registers, or other invalid memory
|
||||
*/
|
||||
static bool verify_load_addresses(int segment_index, intptr_t load_addr, intptr_t load_end, bool print_error, bool no_recurse)
|
||||
{
|
||||
/* Addresses of static data and the "loader" section of bootloader IRAM, all defined in ld script */
|
||||
const char *reason = NULL;
|
||||
extern int _dram_start, _dram_end, _loader_text_start, _loader_text_end;
|
||||
void *load_addr_p = (void *)load_addr;
|
||||
void *load_end_p = (void *)load_end;
|
||||
|
||||
if (load_end == load_addr) {
|
||||
return true; // zero-length segments are fine
|
||||
}
|
||||
assert(load_end > load_addr); // data_len<16MB is checked in verify_segment_header() which is called before this, so this should always be true
|
||||
|
||||
if (esp_ptr_in_dram(load_addr_p) && esp_ptr_in_dram(load_end_p)) { /* Writing to DRAM */
|
||||
/* Check if we're clobbering the stack */
|
||||
intptr_t sp = (intptr_t)get_sp();
|
||||
if (bootloader_util_regions_overlap(sp - STACK_LOAD_HEADROOM, SOC_ROM_STACK_START,
|
||||
load_addr, load_end)) {
|
||||
reason = "overlaps bootloader stack";
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
/* Check if we're clobbering static data
|
||||
|
||||
(_dram_start.._dram_end includes bss, data, rodata sections in DRAM)
|
||||
*/
|
||||
if (bootloader_util_regions_overlap((intptr_t)&_dram_start, (intptr_t)&_dram_end, load_addr, load_end)) {
|
||||
reason = "overlaps bootloader data";
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
/* LAST DRAM CHECK (recursive): for D/IRAM, check the equivalent IRAM addresses if needed
|
||||
|
||||
Allow for the possibility that even though both pointers are IRAM, only part of the region is in a D/IRAM
|
||||
section. In which case we recurse to check the part which falls in D/IRAM.
|
||||
|
||||
Note: We start with SOC_DIRAM_DRAM_LOW/HIGH and convert that address to IRAM to account for any reversing of word order
|
||||
(chip-specific).
|
||||
*/
|
||||
if (!no_recurse && bootloader_util_regions_overlap(SOC_DIRAM_DRAM_LOW, SOC_DIRAM_DRAM_HIGH, load_addr, load_end)) {
|
||||
intptr_t iram_load_addr, iram_load_end;
|
||||
|
||||
if (esp_ptr_in_diram_dram(load_addr_p)) {
|
||||
iram_load_addr = (intptr_t)esp_ptr_diram_dram_to_iram(load_addr_p);
|
||||
} else {
|
||||
iram_load_addr = (intptr_t)esp_ptr_diram_dram_to_iram((void *)SOC_DIRAM_DRAM_LOW);
|
||||
}
|
||||
|
||||
if (esp_ptr_in_diram_dram(load_end_p)) {
|
||||
iram_load_end = (intptr_t)esp_ptr_diram_dram_to_iram(load_end_p);
|
||||
} else {
|
||||
iram_load_end = (intptr_t)esp_ptr_diram_dram_to_iram((void *)SOC_DIRAM_DRAM_HIGH);
|
||||
}
|
||||
|
||||
if (iram_load_end < iram_load_addr) {
|
||||
return verify_load_addresses(segment_index, iram_load_end, iram_load_addr, print_error, true);
|
||||
} else {
|
||||
return verify_load_addresses(segment_index, iram_load_addr, iram_load_end, print_error, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (esp_ptr_in_iram(load_addr_p) && esp_ptr_in_iram(load_end_p)) { /* Writing to IRAM */
|
||||
/* Check for overlap of 'loader' section of IRAM */
|
||||
if (bootloader_util_regions_overlap((intptr_t)&_loader_text_start, (intptr_t)&_loader_text_end,
|
||||
load_addr, load_end)) {
|
||||
reason = "overlaps loader IRAM";
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
/* LAST IRAM CHECK (recursive): for D/IRAM, check the equivalent DRAM address if needed
|
||||
|
||||
Allow for the possibility that even though both pointers are IRAM, only part of the region is in a D/IRAM
|
||||
section. In which case we recurse to check the part which falls in D/IRAM.
|
||||
Note: We start with SOC_DIRAM_IRAM_LOW/HIGH and convert that address to DRAM to account for any reversing of word order
|
||||
(chip-specific).
|
||||
*/
|
||||
if (!no_recurse && bootloader_util_regions_overlap(SOC_DIRAM_IRAM_LOW, SOC_DIRAM_IRAM_HIGH, load_addr, load_end)) {
|
||||
intptr_t dram_load_addr, dram_load_end;
|
||||
|
||||
if (esp_ptr_in_diram_iram(load_addr_p)) {
|
||||
dram_load_addr = (intptr_t)esp_ptr_diram_iram_to_dram(load_addr_p);
|
||||
} else {
|
||||
dram_load_addr = (intptr_t)esp_ptr_diram_iram_to_dram((void *)SOC_DIRAM_IRAM_LOW);
|
||||
}
|
||||
|
||||
if (esp_ptr_in_diram_iram(load_end_p)) {
|
||||
dram_load_end = (intptr_t)esp_ptr_diram_iram_to_dram(load_end_p);
|
||||
} else {
|
||||
dram_load_end = (intptr_t)esp_ptr_diram_iram_to_dram((void *)SOC_DIRAM_IRAM_HIGH);
|
||||
}
|
||||
|
||||
if (dram_load_end < dram_load_addr) {
|
||||
return verify_load_addresses(segment_index, dram_load_end, dram_load_addr, print_error, true);
|
||||
} else {
|
||||
return verify_load_addresses(segment_index, dram_load_addr, dram_load_end, print_error, true);
|
||||
}
|
||||
}
|
||||
/* Sections entirely in RTC memory won't overlap with a vanilla bootloader but are valid load addresses, thus skipping them from the check */
|
||||
} else if (esp_ptr_in_rtc_iram_fast(load_addr_p) && esp_ptr_in_rtc_iram_fast(load_end_p)){
|
||||
return true;
|
||||
} else if (esp_ptr_in_rtc_dram_fast(load_addr_p) && esp_ptr_in_rtc_dram_fast(load_end_p)){
|
||||
return true;
|
||||
} else if (esp_ptr_in_rtc_slow(load_addr_p) && esp_ptr_in_rtc_slow(load_end_p)) {
|
||||
return true;
|
||||
} else { /* Not a DRAM or an IRAM or RTC Fast IRAM, RTC Fast DRAM or RTC Slow address */
|
||||
reason = "bad load address range";
|
||||
goto invalid;
|
||||
}
|
||||
return true;
|
||||
|
||||
invalid:
|
||||
if (print_error) {
|
||||
ESP_LOGE(TAG, "Segment %d 0x%08x-0x%08x invalid: %s", segment_index, load_addr, load_end, reason);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // BOOTLOADER_BUILD
|
||||
|
||||
static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segment_header_t *header, bool silent, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum)
|
||||
{
|
||||
esp_err_t err;
|
||||
|
@ -379,33 +523,8 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme
|
|||
#ifdef BOOTLOADER_BUILD
|
||||
/* Before loading segment, check it doesn't clobber bootloader RAM. */
|
||||
if (do_load && data_len > 0) {
|
||||
const intptr_t load_end = load_addr + data_len;
|
||||
if (load_end < (intptr_t) SOC_DRAM_HIGH) {
|
||||
/* Writing to DRAM */
|
||||
intptr_t sp = (intptr_t)get_sp();
|
||||
if (load_end > sp - STACK_LOAD_HEADROOM) {
|
||||
/* Bootloader .data/.rodata/.bss is above the stack, so this
|
||||
* also checks that we aren't overwriting these segments.
|
||||
*
|
||||
* TODO: This assumes specific arrangement of sections we have
|
||||
* in the ESP32. Rewrite this in a generic way to support other
|
||||
* layouts.
|
||||
*/
|
||||
ESP_LOGE(TAG, "Segment %d end address 0x%08x too high (bootloader stack 0x%08x limit 0x%08x)",
|
||||
index, load_end, sp, sp - STACK_LOAD_HEADROOM);
|
||||
return ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
} else {
|
||||
/* Writing to IRAM */
|
||||
const intptr_t loader_iram_start = (intptr_t) &_loader_text_start;
|
||||
const intptr_t loader_iram_end = (intptr_t) &_loader_text_end;
|
||||
|
||||
if (bootloader_util_regions_overlap(loader_iram_start, loader_iram_end,
|
||||
load_addr, load_end)) {
|
||||
ESP_LOGE(TAG, "Segment %d (0x%08x-0x%08x) overlaps bootloader IRAM (0x%08x-0x%08x)",
|
||||
index, load_addr, load_end, loader_iram_start, loader_iram_end);
|
||||
return ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
if (!verify_load_addresses(index, load_addr, load_addr + data_len, true, false)) {
|
||||
return ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
}
|
||||
#endif // BOOTLOADER_BUILD
|
||||
|
@ -415,6 +534,10 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme
|
|||
|
||||
int32_t data_len_remain = data_len;
|
||||
while (data_len_remain > 0) {
|
||||
#if SECURE_BOOT_CHECK_SIGNATURE && defined(BOOTLOADER_BUILD)
|
||||
/* Double check the address verification done above */
|
||||
ESP_FAULT_ASSERT(!do_load || verify_load_addresses(0, load_addr, load_addr + data_len_remain, false, false));
|
||||
#endif
|
||||
uint32_t offset_page = ((data_addr & MMAP_ALIGNED_MASK) != 0) ? 1 : 0;
|
||||
/* Data we could map in case we are not aligned to PAGE boundary is one page size lesser. */
|
||||
data_len = MIN(data_len_remain, ((free_page_count - offset_page) * SPI_FLASH_MMU_PAGE_SIZE));
|
||||
|
@ -440,7 +563,7 @@ static esp_err_t process_segment_data(intptr_t load_addr, uint32_t data_addr, ui
|
|||
{
|
||||
// If we are not loading, and the checksum is empty, skip processing this
|
||||
// segment for data
|
||||
if(!do_load && checksum == NULL) {
|
||||
if (!do_load && checksum == NULL) {
|
||||
ESP_LOGD(TAG, "skipping checksum for segment");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -623,9 +746,9 @@ static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t
|
|||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data)
|
||||
static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
uint8_t image_hash[HASH_LEN] = { 0 };
|
||||
#ifdef SECURE_BOOT_CHECK_SIGNATURE
|
||||
uint32_t end = data->start_addr + data->image_len;
|
||||
|
||||
ESP_LOGI(TAG, "Verifying image signature...");
|
||||
|
@ -638,25 +761,36 @@ static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_han
|
|||
bootloader_munmap(simple_hash);
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
// Pad to 4096 byte sector boundary
|
||||
if (end % FLASH_SECTOR_SIZE != 0) {
|
||||
uint32_t pad_len = FLASH_SECTOR_SIZE - (end % FLASH_SECTOR_SIZE);
|
||||
const void *padding = bootloader_mmap(end, pad_len);
|
||||
bootloader_sha256_data(sha_handle, padding, pad_len);
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
// End of the image needs to be padded all the way to a 4KB boundary, after the simple hash
|
||||
// (for apps they are usually already padded due to --secure-pad-v2, only a problem if this option was not used.)
|
||||
uint32_t padded_end = (end + FLASH_SECTOR_SIZE - 1) & ~(FLASH_SECTOR_SIZE-1);
|
||||
if (padded_end > end) {
|
||||
const void *padding = bootloader_mmap(end, padded_end - end);
|
||||
bootloader_sha256_data(sha_handle, padding, padded_end - end);
|
||||
bootloader_munmap(padding);
|
||||
end += pad_len;
|
||||
end = padded_end;
|
||||
}
|
||||
#endif
|
||||
|
||||
bootloader_sha256_finish(sha_handle, image_hash);
|
||||
bootloader_sha256_finish(sha_handle, image_digest);
|
||||
|
||||
// Log the hash for debugging
|
||||
bootloader_debug_buffer(image_hash, HASH_LEN, "Calculated secure boot hash");
|
||||
bootloader_debug_buffer(image_digest, HASH_LEN, "Calculated secure boot hash");
|
||||
|
||||
// Use hash to verify signature block
|
||||
const esp_secure_boot_sig_block_t *sig_block = bootloader_mmap(data->start_addr + data->image_len, sizeof(esp_secure_boot_sig_block_t));
|
||||
esp_err_t err = esp_secure_boot_verify_signature_block(sig_block, image_hash);
|
||||
esp_err_t err = ESP_ERR_IMAGE_INVALID;
|
||||
const void *sig_block;
|
||||
#ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
ESP_FAULT_ASSERT(memcmp(image_digest, verified_digest, HASH_LEN) != 0); /* sanity check that these values start differently */
|
||||
sig_block = bootloader_mmap(data->start_addr + data->image_len, sizeof(esp_secure_boot_sig_block_t));
|
||||
err = esp_secure_boot_verify_ecdsa_signature_block(sig_block, image_digest, verified_digest);
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
ESP_FAULT_ASSERT(memcmp(image_digest, verified_digest, HASH_LEN) != 0); /* sanity check that these values start differently */
|
||||
sig_block = bootloader_mmap(end, sizeof(ets_secure_boot_signature_t));
|
||||
err = esp_secure_boot_verify_rsa_signature_block(sig_block, image_digest, verified_digest);
|
||||
#endif
|
||||
|
||||
bootloader_munmap(sig_block);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Secure boot signature verification failed");
|
||||
|
@ -677,11 +811,12 @@ static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_han
|
|||
return ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
// Adjust image length result to include the appended signature
|
||||
data->image_len = end - data->start_addr + sizeof(ets_secure_boot_signature_t);
|
||||
#endif
|
||||
|
||||
#endif // SECURE_BOOT_CHECK_SIGNATURE
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ void esp_flash_encryption_init_checks()
|
|||
// FLASH_CRYPT_CNT *must* be write protected. This will have happened automatically
|
||||
// if bootloader is IDF V4.0 or newer but may not have happened for previous ESP-IDF bootloaders.
|
||||
#ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE
|
||||
#ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
#ifdef CONFIG_SECURE_BOOT
|
||||
if (esp_secure_boot_enabled() && esp_flash_encryption_enabled()) {
|
||||
uint8_t flash_crypt_cnt_wr_dis = 0;
|
||||
esp_efuse_read_field_blob(ESP_EFUSE_WR_DIS_FLASH_CRYPT_CNT, &flash_crypt_cnt_wr_dis, 1);
|
||||
|
@ -40,7 +40,7 @@ void esp_flash_encryption_init_checks()
|
|||
esp_flash_write_protect_crypt_cnt();
|
||||
}
|
||||
}
|
||||
#endif // CONFIG_SECURE_BOOT_ENABLED
|
||||
#endif // CONFIG_SECURE_BOOT
|
||||
#endif // CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE
|
||||
|
||||
// Second check is to print a warning or error if the current running flash encryption mode
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_flash.h"
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
|
|
@ -15,46 +15,55 @@
|
|||
|
||||
#include "bootloader_flash.h"
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_utility.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_secure_boot.h"
|
||||
#include "mbedtls/sha256.h"
|
||||
#include "mbedtls/x509.h"
|
||||
#include "mbedtls/md.h"
|
||||
#include "mbedtls/platform.h"
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "mbedtls/ctr_drbg.h"
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
static const char *TAG = "secure_boot";
|
||||
#define DIGEST_LEN 32
|
||||
|
||||
#ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
static const char *TAG = "secure_boot_v1";
|
||||
|
||||
extern const uint8_t signature_verification_key_start[] asm("_binary_signature_verification_key_bin_start");
|
||||
extern const uint8_t signature_verification_key_end[] asm("_binary_signature_verification_key_bin_end");
|
||||
|
||||
#define SIGNATURE_VERIFICATION_KEYLEN 64
|
||||
|
||||
#define DIGEST_LEN 32
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||
{
|
||||
uint8_t digest[DIGEST_LEN];
|
||||
const uint8_t *data;
|
||||
uint8_t verified_digest[DIGEST_LEN];
|
||||
const esp_secure_boot_sig_block_t *sigblock;
|
||||
|
||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
||||
|
||||
data = bootloader_mmap(src_addr, length + sizeof(esp_secure_boot_sig_block_t));
|
||||
if (data == NULL) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length + sizeof(esp_secure_boot_sig_block_t));
|
||||
return ESP_FAIL;
|
||||
esp_err_t err = bootloader_sha256_flash_contents(src_addr, length, digest);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Digest calculation failed 0x%x, 0x%x", src_addr, length);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Calculate digest of main image
|
||||
mbedtls_sha256_ret(data, length, digest, 0);
|
||||
|
||||
// Map the signature block and verify the signature
|
||||
sigblock = (const esp_secure_boot_sig_block_t *)(data + length);
|
||||
esp_err_t err = esp_secure_boot_verify_signature_block(sigblock, digest);
|
||||
bootloader_munmap(data);
|
||||
sigblock = (const esp_secure_boot_sig_block_t *)bootloader_mmap(src_addr + length, sizeof(esp_secure_boot_sig_block_t));
|
||||
if (sigblock == NULL) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr + length, sizeof(esp_secure_boot_sig_block_t));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
err = esp_secure_boot_verify_ecdsa_signature_block(sigblock, digest, verified_digest);
|
||||
bootloader_munmap(sigblock);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest)
|
||||
esp_err_t esp_secure_boot_verify_ecdsa_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
#if !(defined(CONFIG_MBEDTLS_ECDSA_C) && defined(CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED))
|
||||
ESP_LOGE(TAG, "Signature verification requires ECDSA & SECP256R1 curve enabled");
|
||||
|
@ -62,6 +71,9 @@ esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block
|
|||
#else
|
||||
ptrdiff_t keylen;
|
||||
|
||||
/* Note: in IDF app image verification we don't add any fault injection resistance, boot-time checks only */
|
||||
memset(verified_digest, 0, DIGEST_LEN);
|
||||
|
||||
keylen = signature_verification_key_end - signature_verification_key_start;
|
||||
if (keylen != SIGNATURE_VERIFICATION_KEYLEN) {
|
||||
ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen);
|
||||
|
@ -123,3 +135,139 @@ cleanup:
|
|||
return ret == 0 ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||
#endif // CONFIG_MBEDTLS_ECDSA_C && CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED
|
||||
}
|
||||
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
|
||||
static const char *TAG = "secure_boot_v2";
|
||||
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
#define RSA_KEY_SIZE 384 /* RSA 3072 Bits */
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||
{
|
||||
uint8_t digest[DIGEST_LEN] = {0};
|
||||
uint8_t verified_digest[DIGEST_LEN] = {0};
|
||||
|
||||
/* Rounding off length to the upper 4k boundary */
|
||||
uint32_t padded_length = ALIGN_UP(length, FLASH_SECTOR_SIZE);
|
||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
|
||||
|
||||
esp_err_t err = bootloader_sha256_flash_contents(src_addr, padded_length, digest);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Digest calculation failed 0x%x, 0x%x", src_addr, padded_length);
|
||||
return err;
|
||||
}
|
||||
|
||||
const ets_secure_boot_signature_t *sig_block = bootloader_mmap(src_addr + padded_length, sizeof(ets_secure_boot_signature_t));
|
||||
if (sig_block == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to mmap data at offset 0x%x", src_addr + padded_length);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
err = esp_secure_boot_verify_rsa_signature_block(sig_block, digest, verified_digest);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
||||
}
|
||||
bootloader_munmap(sig_block);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
uint8_t i = 0, efuse_trusted_digest[DIGEST_LEN] = {0}, sig_block_trusted_digest[DIGEST_LEN] = {0};
|
||||
memcpy(efuse_trusted_digest, (uint8_t *) EFUSE_BLK2_RDATA0_REG, sizeof(efuse_trusted_digest));
|
||||
|
||||
/* Note: in IDF verification we don't add any fault injection resistance, as we don't expect this to be called
|
||||
during boot-time verification. */
|
||||
memset(verified_digest, 0, DIGEST_LEN);
|
||||
|
||||
/* Generating the SHA of the public key components in the signature block */
|
||||
bootloader_sha256_handle_t sig_block_sha;
|
||||
sig_block_sha = bootloader_sha256_start();
|
||||
bootloader_sha256_data(sig_block_sha, &sig_block->block[0].key, sizeof(sig_block->block[0].key));
|
||||
bootloader_sha256_finish(sig_block_sha, (unsigned char *)sig_block_trusted_digest);
|
||||
|
||||
if (memcmp(efuse_trusted_digest, sig_block_trusted_digest, DIGEST_LEN) != 0) {
|
||||
if (esp_secure_boot_enabled()) {
|
||||
ESP_LOGE(TAG, "Public key digest in eFuse BLK2 and the signature block don't match.");
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Public key digest in eFuse BLK2 and the signature block don't match.");
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Verifying with RSA-PSS...");
|
||||
int ret = 0;
|
||||
mbedtls_rsa_context pk;
|
||||
mbedtls_entropy_context entropy;
|
||||
mbedtls_ctr_drbg_context ctr_drbg;
|
||||
unsigned char *sig_be = calloc(1, RSA_KEY_SIZE);
|
||||
unsigned char *buf = calloc(1, RSA_KEY_SIZE);
|
||||
if (sig_be == NULL || buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
mbedtls_entropy_init(&entropy);
|
||||
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned -0x%04x\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
const mbedtls_mpi N = { .s = 1,
|
||||
.n = sizeof(sig_block->block[i].key.n)/sizeof(mbedtls_mpi_uint),
|
||||
.p = (void *)sig_block->block[i].key.n,
|
||||
};
|
||||
const mbedtls_mpi e = { .s = 1,
|
||||
.n = sizeof(sig_block->block[i].key.e)/sizeof(mbedtls_mpi_uint), // 1
|
||||
.p = (void *)&sig_block->block[i].key.e,
|
||||
};
|
||||
mbedtls_rsa_init(&pk, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
|
||||
ret = mbedtls_rsa_import(&pk, &N, NULL, NULL, NULL, &e);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed mbedtls_rsa_import, err: %d", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_rsa_complete(&pk);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed mbedtls_rsa_complete, err: %d", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_rsa_check_pubkey(&pk);
|
||||
if (ret != 0) {
|
||||
ESP_LOGI(TAG, "Key is not an RSA key -%0x", -ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Signature needs to be byte swapped into BE representation */
|
||||
for (int j = 0; j < RSA_KEY_SIZE; j++) {
|
||||
sig_be[RSA_KEY_SIZE- j - 1] = sig_block->block[i].signature[j];
|
||||
}
|
||||
|
||||
ret = mbedtls_rsa_public( &pk, sig_be, buf);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "mbedtls_rsa_public failed, err: %d", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = mbedtls_rsa_rsassa_pss_verify( &pk, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA256, 32,
|
||||
sig_block->block[i].image_digest, sig_be);
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed mbedtls_rsa_rsassa_pss_verify, err: %d", ret);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Signature verified successfully!");
|
||||
}
|
||||
exit:
|
||||
mbedtls_rsa_free(&pk);
|
||||
if (ret == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(sig_be);
|
||||
free(buf);
|
||||
return (!ret) ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
#endif
|
||||
|
|
93
components/esp_common/include/esp_fault.h
Normal file
93
components/esp_common/include/esp_fault.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Assert a condition is true, in a way that should be resistant to fault injection for
|
||||
* single fault attacks.
|
||||
*
|
||||
* - Expands CONDITION multiple times (condition must have no side effects)
|
||||
* - Compiler is told all registers are invalid before evaluating CONDITION each time, to avoid a fault
|
||||
* causing a misread of a register used in all three evaluations of CONDITION.
|
||||
* - If CONDITION is ever false, a system reset is triggered.
|
||||
*
|
||||
* @note Place this macro after a "normal" check of CONDITION that will fail with a normal error
|
||||
* message. This is the fallback in case a fault injection attack skips or corrupts the result of
|
||||
* that check. (Although ensure that an attacker can't use fault injection to skip past the "normal"
|
||||
* error message, to avoid this check entirely.)
|
||||
*
|
||||
* @note This macro increases binary size and is slow and should be used sparingly.
|
||||
*
|
||||
* @note This macro does not guarantee fault injection resistance. In particular CONDITION must be
|
||||
* chosen carefully - a fault injection attack which sets CONDITION to true will not be detected by
|
||||
* this macro. Care must also be taken that an attacker can't use a fault to completely bypass calling
|
||||
* whatever function tests ESP_FAULT_ASSERT.
|
||||
*
|
||||
* @note This is difficult to debug as a failure triggers an instant software reset, and UART output
|
||||
* is often truncated (as FIFO is not flushed). Define the ESP_FAULT_ASSERT_DEBUG macro to debug any
|
||||
* failures of this macro due to software bugs.
|
||||
*
|
||||
* @param CONDITION A condition which will evaluate true unless an attacker used fault injection to skip or corrupt some other critical system calculation.
|
||||
*
|
||||
*/
|
||||
#define ESP_FAULT_ASSERT(CONDITION) do { \
|
||||
asm volatile ("" ::: "memory"); \
|
||||
if(!(CONDITION)) _ESP_FAULT_RESET(); \
|
||||
asm volatile ("" ::: "memory"); \
|
||||
if(!(CONDITION)) _ESP_FAULT_RESET(); \
|
||||
asm volatile ("" ::: "memory"); \
|
||||
if(!(CONDITION)) _ESP_FAULT_RESET(); \
|
||||
} while(0)
|
||||
|
||||
|
||||
// Uncomment this macro to get debug output if ESP_FAULT_ASSERT() fails
|
||||
//
|
||||
// Note that uncommenting this macro reduces the anti-FI effectiveness
|
||||
//
|
||||
//#define ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
/* Internal macro, purpose is to trigger a system reset if an inconsistency due to fault injection
|
||||
is detected.
|
||||
|
||||
Illegal instruction opcodes are there as a fallback to crash the CPU in case it doesn't
|
||||
reset as expected.
|
||||
*/
|
||||
#ifndef ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
#define _ESP_FAULT_RESET() do { \
|
||||
REG_WRITE(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST); \
|
||||
asm volatile("ill; ill; ill;"); \
|
||||
} while(0)
|
||||
|
||||
#else // ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
#warning "Enabling ESP_FAULT_ASSERT_DEBUG makes ESP_FAULT_ASSERT() less effective"
|
||||
|
||||
#define _ESP_FAULT_RESET() do { \
|
||||
ets_printf("ESP_FAULT_ASSERT %s:%d\n", __FILE__, __LINE__); \
|
||||
asm volatile("ill;"); \
|
||||
} while(0)
|
||||
|
||||
#endif // ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -12,6 +12,10 @@ if(BOOTLOADER_BUILD)
|
|||
list(APPEND scripts "esp32s2/ld/esp32s2.rom.spiflash.ld")
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP32_REV_MIN_3)
|
||||
list(APPEND scripts "esp32/ld/esp32.rom.eco3.ld")
|
||||
endif()
|
||||
|
||||
target_linker_script(${COMPONENT_LIB} INTERFACE "${scripts}")
|
||||
else() # Regular app build
|
||||
set(scripts
|
||||
|
@ -42,8 +46,13 @@ else() # Regular app build
|
|||
list(APPEND scripts "esp32/ld/esp32.rom.spiflash.ld")
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP32_REV_MIN_3)
|
||||
list(APPEND scripts "esp32/ld/esp32.rom.eco3.ld")
|
||||
endif()
|
||||
|
||||
elseif(target STREQUAL "esp32s2")
|
||||
# no SPIRAM workaround for esp32s2
|
||||
# no nano formatting function in ROM
|
||||
|
||||
list(APPEND scripts "esp32s2/ld/esp32s2.rom.newlib-funcs.ld"
|
||||
"esp32s2/ld/esp32s2.rom.spiflash.ld")
|
||||
|
|
|
@ -11,6 +11,10 @@ LINKER_SCRIPTS += esp32.rom.ld \
|
|||
ifndef CONFIG_SPIRAM_CACHE_WORKAROUND
|
||||
LINKER_SCRIPTS += esp32.rom.newlib-funcs.ld
|
||||
|
||||
ifdef CONFIG_ESP32_REV_MIN_3
|
||||
LINKER_SCRIPTS += esp32.rom.eco3.ld
|
||||
endif
|
||||
|
||||
# Include in newlib nano from ROM only if SPIRAM cache workaround is disabled
|
||||
ifdef CONFIG_NEWLIB_NANO_FORMAT
|
||||
LINKER_SCRIPTS += esp32.rom.newlib-nano.ld
|
||||
|
|
7
components/esp_rom/esp32/ld/esp32.rom.eco3.ld
Normal file
7
components/esp_rom/esp32/ld/esp32.rom.eco3.ld
Normal file
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
ESP32 ECO3 ROM address table
|
||||
Secure Boot Version 2 API's imported from the ROM
|
||||
*/
|
||||
PROVIDE ( ets_secure_boot_verify_signature = 0x4006543c);
|
||||
PROVIDE ( ets_secure_boot_verify_boot_bootloader = 0x400655ec);
|
||||
PROVIDE ( ets_use_secure_boot_v2 = 0x4000f8d4);
|
|
@ -12,6 +12,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef _ROM_SECURE_BOOT_H_
|
||||
#define _ROM_SECURE_BOOT_H_
|
||||
|
||||
|
@ -39,8 +41,74 @@ bool ets_secure_boot_check_start(uint8_t abs_index, uint32_t iv_addr);
|
|||
|
||||
int ets_secure_boot_check_finish(uint32_t *abstract);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_ESP32_REV_MIN_3
|
||||
#define SECURE_BOOT_NUM_BLOCKS 1
|
||||
|
||||
#endif /* _ROM_SECURE_BOOT_H_ */
|
||||
typedef enum {
|
||||
SBV2_SUCCESS = 0x3A5A5AA5,
|
||||
SBV2_FAILED = 0xA533885A,
|
||||
} secure_boot_v2_status_t;
|
||||
|
||||
/* Secure Boot Version 2 - Public Key format */
|
||||
typedef struct {
|
||||
uint8_t n[384]; /* Public key modulus */
|
||||
uint32_t e; /* Public key exponent */
|
||||
uint8_t rinv[384];
|
||||
uint32_t mdash;
|
||||
} ets_rsa_pubkey_t;
|
||||
|
||||
/* Secure Boot Version 2 signature format for ESP32 ECO3 */
|
||||
typedef struct {
|
||||
uint8_t magic_byte;
|
||||
uint8_t version;
|
||||
uint8_t _reserved1;
|
||||
uint8_t _reserved2;
|
||||
uint8_t image_digest[32];
|
||||
ets_rsa_pubkey_t key;
|
||||
uint8_t signature[384];
|
||||
uint32_t block_crc;
|
||||
uint8_t _padding[16];
|
||||
} ets_secure_boot_sig_block_t;
|
||||
|
||||
/* Multiple key block support */
|
||||
struct ets_secure_boot_signature {
|
||||
ets_secure_boot_sig_block_t block[SECURE_BOOT_NUM_BLOCKS];
|
||||
uint8_t _padding[4096 - (sizeof(ets_secure_boot_sig_block_t) * SECURE_BOOT_NUM_BLOCKS)];
|
||||
};
|
||||
typedef struct ets_secure_boot_signature ets_secure_boot_signature_t;
|
||||
|
||||
/** @brief Verifies the signature block appended to a firmware image. Implemented in the ROM.
|
||||
*
|
||||
* This function is used to verify the bootloader before burning its public key hash into Efuse.
|
||||
* Also, it is used to verify the app on loading the image on boot and on OTA.
|
||||
*
|
||||
* @param sig The signature block flashed aligned 4096 bytes from the firmware.
|
||||
* @param image_digest The SHA-256 Digest of the firmware to be verified
|
||||
* @param trusted_key_digest The SHA-256 Digest of the public key (ets_rsa_pubkey_t) of a single signature block.
|
||||
* @param verified_digest RSA-PSS signature of image_digest. Pass an uninitialised array.
|
||||
*
|
||||
* @return SBV2_SUCCESS if signature is valid
|
||||
* SBV2_FAILED for failures.
|
||||
*/
|
||||
secure_boot_v2_status_t ets_secure_boot_verify_signature(const ets_secure_boot_signature_t *sig, const uint8_t *image_digest, const uint8_t *trusted_key_digest, uint8_t *verified_digest);
|
||||
|
||||
/** @brief This function verifies the 1st stage bootloader. Implemented in the ROM.
|
||||
* Reboots post verification. It reads the Efuse key for verification of the public key.
|
||||
*
|
||||
* This function is not used in the current workflow.
|
||||
*
|
||||
*/
|
||||
void ets_secure_boot_verify_boot_bootloader(void);
|
||||
|
||||
/** @brief Confirms if the secure boot V2 has been enabled. Implemented in the ROM.
|
||||
*
|
||||
* In ESP32-ECO3 - It checks the value of ABS_DONE_1 in EFuse.
|
||||
*
|
||||
* @return true if is Secure boot v2 has been enabled
|
||||
* False if Secure boot v2 has not been enabled.
|
||||
*/
|
||||
bool ets_use_secure_boot_v2();
|
||||
|
||||
#endif /* CONFIG_ESP32_REV_MIN_3 */
|
||||
|
||||
#endif /* _ROM_SECURE_BOOT_H_ */
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -29,45 +29,77 @@ typedef struct ets_secure_boot_sig_block ets_secure_boot_sig_block_t;
|
|||
typedef struct ets_secure_boot_signature ets_secure_boot_signature_t;
|
||||
typedef struct ets_secure_boot_key_digests ets_secure_boot_key_digests_t;
|
||||
|
||||
/* Verify bootloader image (reconfigures cache to map,
|
||||
loads trusted key digests from efuse)
|
||||
/* 64KB 'staging buffer' for loading the verified bootloader
|
||||
|
||||
If allow_key_revoke is true and aggressive revoke efuse is set,
|
||||
any failed signature has its associated key revoked in efuse.
|
||||
Comes from the "shared buffers" region (see shared_buffers.h)
|
||||
|
||||
If result is ETS_OK, the "simple hash" of the bootloader
|
||||
is copied into verified_hash.
|
||||
The bootloader can't be safely linked into this address range
|
||||
(may be possible with some cleverness.)
|
||||
*/
|
||||
int ets_secure_boot_verify_bootloader(uint8_t *verified_hash, bool allow_key_revoke);
|
||||
#define SECURE_BOOT_STAGING_BUFFER_START ((uint32_t)(g_shared_buffers.secure_boot_staging_buf))
|
||||
#define SECURE_BOOT_STAGING_BUFFER_SZ sizeof(g_shared_buffers.secure_boot_staging_buf)
|
||||
#define SECURE_BOOT_STAGING_BUFFER_END (SECURE_BOOT_STAGING_BUFFER_START + SECURE_BOOT_STAGING_BUFFER_SZ)
|
||||
|
||||
/* Verify bootloader image (reconfigures cache to map), with
|
||||
key digests provided as parameters.)
|
||||
/* Anti-FI measure: use full words for success/fail, instead of
|
||||
0/non-zero
|
||||
*/
|
||||
typedef enum {
|
||||
SB_SUCCESS = 0x3A5A5AA5,
|
||||
SB_FAILED = 0x7533885E,
|
||||
} ets_secure_boot_status_t;
|
||||
|
||||
|
||||
/* Verify and stage-load the bootloader image
|
||||
(reconfigures cache to map, loads trusted key digests from efuse,
|
||||
copies the bootloader into the staging buffer.)
|
||||
|
||||
If allow_key_revoke is true and aggressive revoke efuse is set,
|
||||
any failed signature has its associated key revoked in efuse.
|
||||
|
||||
If result is SB_SUCCESS, the "simple hash" of the bootloader
|
||||
is copied into verified_hash.
|
||||
*/
|
||||
ets_secure_boot_status_t ets_secure_boot_verify_stage_bootloader(uint8_t *verified_hash, bool allow_key_revoke);
|
||||
|
||||
/* Verify bootloader image (reconfigures cache to map),
|
||||
with key digests provided as parameters.)
|
||||
|
||||
Can be used to verify secure boot status before enabling
|
||||
secure boot permanently.
|
||||
|
||||
If result is ETS_OK, the "simple hash" of the bootloader is
|
||||
If stage_load parameter is true, bootloader is copied into staging
|
||||
buffer in RAM at the same time.
|
||||
|
||||
If result is SB_SUCCESS, the "simple hash" of the bootloader is
|
||||
copied into verified_hash.
|
||||
*/
|
||||
int ets_secure_boot_verify_bootloader_with_keys(uint8_t *verified_hash, const ets_secure_boot_key_digests_t *trusted_keys);
|
||||
ets_secure_boot_status_t ets_secure_boot_verify_bootloader_with_keys(uint8_t *verified_hash, const ets_secure_boot_key_digests_t *trusted_keys, bool stage_load);
|
||||
|
||||
/* Read key digests from efuse. Any revoked/missing digests will be
|
||||
marked as NULL
|
||||
*/
|
||||
ETS_STATUS ets_secure_boot_read_key_digests(ets_secure_boot_key_digests_t *trusted_keys);
|
||||
|
||||
/* Verify supplied signature against supplied digest, using
|
||||
supplied trusted key digests.
|
||||
|
||||
Doesn't reconfigure cache or any other hardware access.
|
||||
*/
|
||||
int ets_secure_boot_verify_signature(const ets_secure_boot_signature_t *sig, const uint8_t *image_digest, const ets_secure_boot_key_digests_t *trusted_keys);
|
||||
Doesn't reconfigure cache or any other hardware access except for RSA peripheral.
|
||||
|
||||
/* Read key digests from efuse. Any revoked/missing digests will be
|
||||
marked as NULL
|
||||
|
||||
Returns 0 if at least one valid digest was found.
|
||||
If result is SB_SUCCESS, the image_digest value is copied into verified_digest.
|
||||
*/
|
||||
int ets_secure_boot_read_key_digests(ets_secure_boot_key_digests_t *trusted_keys);
|
||||
ets_secure_boot_status_t ets_secure_boot_verify_signature(const ets_secure_boot_signature_t *sig, const uint8_t *image_digest, const ets_secure_boot_key_digests_t *trusted_keys, uint8_t *verified_digest);
|
||||
|
||||
/* Revoke a public key digest in efuse.
|
||||
@param index Digest to revoke. Must be 0, 1 or 2.
|
||||
*/
|
||||
void ets_secure_boot_revoke_public_key_digest(int index);
|
||||
|
||||
#define ETS_SECURE_BOOT_V2_SIGNATURE_MAGIC 0xE7
|
||||
|
||||
/* Secure Boot V2 signature block (up to 3 can be appended) */
|
||||
/* Secure Boot V2 signature block
|
||||
|
||||
(Up to 3 in a signature sector are appended to the image)
|
||||
*/
|
||||
struct ets_secure_boot_sig_block {
|
||||
uint8_t magic_byte;
|
||||
uint8_t version;
|
||||
|
@ -92,8 +124,10 @@ struct ets_secure_boot_signature {
|
|||
|
||||
_Static_assert(sizeof(ets_secure_boot_signature_t) == 4096, "invalid sig sector size");
|
||||
|
||||
#define MAX_KEY_DIGESTS 3
|
||||
|
||||
struct ets_secure_boot_key_digests {
|
||||
const void *key_digests[3];
|
||||
const void *key_digests[MAX_KEY_DIGESTS];
|
||||
bool allow_key_revoke;
|
||||
};
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ if(NOT BOOTLOADER_BUILD)
|
|||
set(ESPTOOLPY_CHIP "${target}")
|
||||
set(ESPTOOLPY_WITH_STUB TRUE)
|
||||
|
||||
if(CONFIG_SECURE_BOOT_ENABLED OR CONFIG_SECURE_FLASH_ENC_ENABLED)
|
||||
if(CONFIG_SECURE_BOOT OR CONFIG_SECURE_FLASH_ENC_ENABLED)
|
||||
# If security enabled then override post flash option
|
||||
set(ESPTOOLPY_AFTER "no_reset")
|
||||
endif()
|
||||
|
|
|
@ -29,16 +29,22 @@ else
|
|||
ESPTOOL_WRITE_FLASH_OPTIONS := $(ESPTOOL_FLASH_OPTIONS)
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
ESPTOOL_WRITE_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size keep
|
||||
endif
|
||||
|
||||
ESPTOOL_ELF2IMAGE_OPTIONS :=
|
||||
|
||||
ifdef CONFIG_ESP32_REV_MIN
|
||||
ESPTOOL_ELF2IMAGE_OPTIONS += --min-rev $(CONFIG_ESP32_REV_MIN)
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
ifndef CONFIG_SECURE_BOOT_ALLOW_SHORT_APP_PARTITION
|
||||
ifndef IS_BOOTLOADER_BUILD
|
||||
ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
ESPTOOL_ELF2IMAGE_OPTIONS += --secure-pad
|
||||
else ifdef CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
ESPTOOL_ELF2IMAGE_OPTIONS += --secure-pad-v2
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
@ -47,6 +53,12 @@ ifndef IS_BOOTLOADER_BUILD
|
|||
ESPTOOL_ELF2IMAGE_OPTIONS += --elf-sha256-offset 0xb0
|
||||
endif
|
||||
|
||||
ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
SECURE_APPS_SIGNING_SCHEME = "1"
|
||||
else ifdef CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
SECURE_APPS_SIGNING_SCHEME = "2"
|
||||
endif
|
||||
|
||||
ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z,-u) $(ESPTOOL_WRITE_FLASH_OPTIONS)
|
||||
|
||||
ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
|
||||
|
@ -61,7 +73,7 @@ ifndef IS_BOOTLOADER_BUILD
|
|||
APP_BIN_UNSIGNED := $(APP_BIN:.bin=-unsigned.bin)
|
||||
|
||||
$(APP_BIN): $(APP_BIN_UNSIGNED) $(SECURE_BOOT_SIGNING_KEY) $(SDKCONFIG_MAKEFILE)
|
||||
$(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
|
||||
$(ESPSECUREPY) sign_data --version $(SECURE_APPS_SIGNING_SCHEME) --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
|
||||
endif
|
||||
endif
|
||||
# non-secure boot (or bootloader), both these files are the same
|
||||
|
@ -77,7 +89,7 @@ endif
|
|||
ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
|
||||
encrypted-flash: all_binaries $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info | check_python_dependencies
|
||||
@echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(APP_OFFSET))..."
|
||||
ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
ifdef CONFIG_SECURE_BOOT
|
||||
@echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)"
|
||||
endif
|
||||
$(ESPTOOLPY_WRITE_FLASH_ENCRYPT) $(ESPTOOL_ALL_FLASH_ARGS)
|
||||
|
@ -89,7 +101,7 @@ endif
|
|||
|
||||
flash: all_binaries $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info | check_python_dependencies
|
||||
@echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(APP_OFFSET))..."
|
||||
ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
ifdef CONFIG_SECURE_BOOT
|
||||
@echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)"
|
||||
endif
|
||||
$(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS)
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 939b3e987ac6efde171add89ee26a34031058760
|
||||
Subproject commit 91bdc71841d1b4e561e8e896405b58db86d5fd72
|
|
@ -22,10 +22,13 @@ if(NOT BOOTLOADER_BUILD)
|
|||
set(esptool_elf2image_args --elf-sha256-offset 0xb0)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SECURE_BOOT_ENABLED AND
|
||||
NOT CONFIG_SECURE_BOOT_ALLOW_SHORT_APP_PARTITION
|
||||
AND NOT BOOTLOADER_BUILD)
|
||||
list(APPEND esptool_elf2image_args --secure-pad)
|
||||
if(NOT CONFIG_SECURE_BOOT_ALLOW_SHORT_APP_PARTITION AND
|
||||
NOT BOOTLOADER_BUILD)
|
||||
if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
|
||||
list(APPEND esptool_elf2image_args --secure-pad)
|
||||
elseif(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME)
|
||||
list(APPEND esptool_elf2image_args --secure-pad-v2)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP32_REV_MIN)
|
||||
|
@ -38,6 +41,10 @@ if(CONFIG_ESPTOOLPY_FLASHSIZE_DETECT)
|
|||
set(ESPFLASHSIZE detect)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME)
|
||||
set(ESPFLASHSIZE keep)
|
||||
endif()
|
||||
|
||||
idf_build_get_property(build_dir BUILD_DIR)
|
||||
|
||||
idf_build_get_property(elf_name EXECUTABLE_NAME GENERATOR_EXPRESSION)
|
||||
|
@ -77,11 +84,17 @@ if(CONFIG_APP_BUILD_GENERATE_BINARIES)
|
|||
add_custom_target(app ALL DEPENDS gen_project_binary)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
|
||||
set(secure_boot_version "1")
|
||||
elseif(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME)
|
||||
set(secure_boot_version "2")
|
||||
endif()
|
||||
|
||||
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}
|
||||
COMMAND ${ESPSECUREPY} sign_data --version ${secure_boot_version} --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}"
|
||||
|
@ -103,7 +116,8 @@ if(NOT BOOTLOADER_BUILD AND CONFIG_SECURE_SIGNED_APPS)
|
|||
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}"
|
||||
"\t${espsecurepy} sign_data --keyfile KEYFILE --version ${secure_boot_version} \
|
||||
${build_dir}/${PROJECT_BIN}"
|
||||
VERBATIM)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -131,7 +145,7 @@ add_custom_target(monitor
|
|||
|
||||
set(esptool_flash_main_args "--before=${CONFIG_ESPTOOLPY_BEFORE}")
|
||||
|
||||
if(CONFIG_SECURE_BOOT_ENABLED OR CONFIG_SECURE_FLASH_ENC_ENABLED)
|
||||
if(CONFIG_SECURE_BOOT OR CONFIG_SECURE_FLASH_ENC_ENABLED)
|
||||
# If security enabled then override post flash option
|
||||
list(APPEND esptool_flash_main_args "--after=no_reset")
|
||||
else()
|
||||
|
|
|
@ -39,17 +39,16 @@ possible. This should optimize the amount of RAM accessible to the code without
|
|||
IRAM_ATTR static void *dram_alloc_to_iram_addr(void *addr, size_t len)
|
||||
{
|
||||
uintptr_t dstart = (uintptr_t)addr; //First word
|
||||
uintptr_t dend = dstart + len; //Last word + 4
|
||||
uintptr_t dend = dstart + len - 4; //Last word
|
||||
assert(esp_ptr_in_diram_dram((void *)dstart));
|
||||
assert(esp_ptr_in_diram_dram((void *)dend));
|
||||
assert((dstart & 3) == 0);
|
||||
assert((dend & 3) == 0);
|
||||
#if SOC_DIRAM_INVERTED
|
||||
uint32_t istart = SOC_DIRAM_IRAM_LOW + (SOC_DIRAM_DRAM_HIGH - dend);
|
||||
#ifdef SOC_DIRAM_INVERTED // We want the word before the result to hold the DRAM address
|
||||
uint32_t *iptr = esp_ptr_diram_dram_to_iram((void *)dend);
|
||||
#else
|
||||
uint32_t istart = SOC_DIRAM_IRAM_LOW + (dstart - SOC_DIRAM_DRAM_LOW);
|
||||
uint32_t *iptr = esp_ptr_diram_dram_to_iram((void *)dstart);
|
||||
#endif
|
||||
uint32_t *iptr = (uint32_t *)istart;
|
||||
*iptr = dstart;
|
||||
return iptr + 1;
|
||||
}
|
||||
|
|
74
components/heap/test/test_diram.c
Normal file
74
components/heap/test/test_diram.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Tests for D/IRAM support in heap capability allocator
|
||||
*/
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <stdio.h>
|
||||
#include "unity.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "soc/soc_memory_layout.h"
|
||||
|
||||
#define ALLOC_SZ 1024
|
||||
|
||||
static void *malloc_block_diram(uint32_t caps)
|
||||
{
|
||||
void *attempts[256] = { 0 }; // Allocate up to 256 ALLOC_SZ blocks to exhaust all non-D/IRAM memory temporarily
|
||||
int count = 0;
|
||||
void *result;
|
||||
|
||||
while(count < sizeof(attempts)/sizeof(void *)) {
|
||||
result = heap_caps_malloc(ALLOC_SZ, caps);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(result, "not enough free heap to perform test");
|
||||
|
||||
if (esp_ptr_in_diram_dram(result) || esp_ptr_in_diram_iram(result)) {
|
||||
break;
|
||||
}
|
||||
|
||||
attempts[count] = result;
|
||||
result = NULL;
|
||||
count++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
free(attempts[i]);
|
||||
}
|
||||
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(result, "not enough D/IRAM memory is free");
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_CASE("Allocate D/IRAM as DRAM", "[heap]")
|
||||
{
|
||||
uint32_t *dram = malloc_block_diram(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
|
||||
for (int i = 0; i < ALLOC_SZ / sizeof(uint32_t); i++) {
|
||||
uint32_t v = i + 0xAAAA;
|
||||
dram[i] = v;
|
||||
volatile uint32_t *iram = esp_ptr_diram_dram_to_iram(dram + i);
|
||||
TEST_ASSERT_EQUAL(v, dram[i]);
|
||||
TEST_ASSERT_EQUAL(v, *iram);
|
||||
*iram = UINT32_MAX;
|
||||
TEST_ASSERT_EQUAL(UINT32_MAX, *iram);
|
||||
TEST_ASSERT_EQUAL(UINT32_MAX, dram[i]);
|
||||
}
|
||||
|
||||
free(dram);
|
||||
}
|
||||
|
||||
TEST_CASE("Allocate D/IRAM as IRAM", "[heap]")
|
||||
{
|
||||
uint32_t *iram = malloc_block_diram(MALLOC_CAP_EXEC);
|
||||
|
||||
for (int i = 0; i < ALLOC_SZ / sizeof(uint32_t); i++) {
|
||||
uint32_t v = i + 0xEEE;
|
||||
iram[i] = v;
|
||||
volatile uint32_t *dram = esp_ptr_diram_iram_to_dram(iram + i);
|
||||
TEST_ASSERT_EQUAL_HEX32(v, iram[i]);
|
||||
TEST_ASSERT_EQUAL_HEX32(v, *dram);
|
||||
*dram = UINT32_MAX;
|
||||
TEST_ASSERT_EQUAL_HEX32(UINT32_MAX, *dram);
|
||||
TEST_ASSERT_EQUAL_HEX32(UINT32_MAX, iram[i]);
|
||||
}
|
||||
|
||||
free(iram);
|
||||
}
|
|
@ -6,7 +6,7 @@ endif()
|
|||
|
||||
set(partition_csv "${PARTITION_CSV_PATH}")
|
||||
|
||||
if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
|
||||
if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES AND CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
|
||||
set(unsigned_partition_bin "partition-table-unsigned.bin")
|
||||
set(final_partition_bin "partition-table.bin")
|
||||
set(final_partition_target "sign_partition_table")
|
||||
|
@ -24,7 +24,7 @@ if(CONFIG_ESPTOOLPY_FLASHSIZE)
|
|||
set(flashsize_opt --flash-size ${CONFIG_ESPTOOLPY_FLASHSIZE})
|
||||
endif()
|
||||
|
||||
if(CONFIG_SECURE_BOOT_ENABLED AND NOT CONFIG_SECURE_BOOT_ALLOW_SHORT_APP_PARTITION)
|
||||
if(CONFIG_SECURE_BOOT AND NOT CONFIG_SECURE_BOOT_ALLOW_SHORT_APP_PARTITION)
|
||||
set(partition_secure_opt --secure)
|
||||
else()
|
||||
set(partition_secure_opt "")
|
||||
|
@ -52,13 +52,13 @@ else()
|
|||
endif()
|
||||
|
||||
# Add signing steps
|
||||
if(CONFIG_SECURE_SIGNED_APPS)
|
||||
if(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
|
||||
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}"
|
||||
COMMAND ${ESPSECUREPY} sign_data --version 1 --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}"
|
||||
|
@ -72,6 +72,10 @@ if(CONFIG_SECURE_SIGNED_APPS)
|
|||
"\t${espsecurepy} sign_data --keyfile KEYFILE ${build_dir}/partition_table/${final_partition_bin}"
|
||||
VERBATIM)
|
||||
endif()
|
||||
elseif(CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME)
|
||||
add_custom_command(TARGET partition_table POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Partition table built:"
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
idf_component_get_property(main_args esptool_py FLASH_ARGS)
|
||||
|
|
|
@ -19,7 +19,7 @@ PARTITION_FLASHSIZE_OPT := --flash-size $(CONFIG_ESPTOOLPY_FLASHSIZE)
|
|||
endif
|
||||
|
||||
PARTITION_SECURE_OPT :=
|
||||
ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
ifdef CONFIG_SECURE_BOOT
|
||||
ifndef CONFIG_SECURE_BOOT_ALLOW_SHORT_APP_PARTITION
|
||||
PARTITION_SECURE_OPT += --secure
|
||||
endif
|
||||
|
@ -46,11 +46,16 @@ PARTITION_TABLE_CSV_NAME := $(notdir $(PARTITION_TABLE_CSV_PATH))
|
|||
|
||||
PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(PARTITION_TABLE_CSV_NAME:.csv=.bin)
|
||||
|
||||
ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
|
||||
PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN:.bin=-unsigned.bin)
|
||||
# add an extra signing step for secure partition table
|
||||
$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED) $(SDKCONFIG_MAKEFILE) $(SECURE_BOOT_SIGNING_KEY)
|
||||
$(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
|
||||
$(ESPSECUREPY) sign_data --version 1 --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
|
||||
else
|
||||
# secure bootloader disabled, both files are the same
|
||||
PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN)
|
||||
endif
|
||||
else
|
||||
# secure bootloader disabled, both files are the same
|
||||
PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN)
|
||||
|
|
|
@ -213,9 +213,47 @@ inline static bool IRAM_ATTR esp_ptr_in_diram_iram(const void *p) {
|
|||
return ((intptr_t)p >= SOC_DIRAM_IRAM_LOW && (intptr_t)p < SOC_DIRAM_IRAM_HIGH);
|
||||
}
|
||||
|
||||
inline static bool IRAM_ATTR esp_ptr_in_rtc_iram_fast(const void *p) {
|
||||
return ((intptr_t)p >= SOC_RTC_IRAM_LOW && (intptr_t)p < SOC_RTC_IRAM_HIGH);
|
||||
}
|
||||
|
||||
inline static bool IRAM_ATTR esp_ptr_in_rtc_dram_fast(const void *p) {
|
||||
return ((intptr_t)p >= SOC_RTC_DRAM_LOW && (intptr_t)p < SOC_RTC_DRAM_HIGH);
|
||||
}
|
||||
|
||||
inline static bool IRAM_ATTR esp_ptr_in_rtc_slow(const void *p) {
|
||||
return ((intptr_t)p >= SOC_RTC_DATA_LOW && (intptr_t)p < SOC_RTC_DATA_HIGH);
|
||||
}
|
||||
|
||||
/* Convert a D/IRAM DRAM pointer to equivalent word address in IRAM
|
||||
|
||||
- Address must be word aligned
|
||||
- Address must pass esp_ptr_in_diram_dram() test, or result will be invalid pointer
|
||||
*/
|
||||
inline static void * IRAM_ATTR esp_ptr_diram_dram_to_iram(const void *p) {
|
||||
#if SOC_DIRAM_INVERTED
|
||||
return (void *) ( SOC_DIRAM_IRAM_LOW + (SOC_DIRAM_DRAM_HIGH - (intptr_t)p) - 4);
|
||||
#else
|
||||
return (void *) ( SOC_DIRAM_IRAM_LOW + ((intptr_t)p - SOC_DIRAM_DRAM_LOW) );
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert a D/IRAM IRAM pointer to equivalent word address in DRAM
|
||||
|
||||
- Address must be word aligned
|
||||
- Address must pass esp_ptr_in_diram_iram() test, or result will be invalid pointer
|
||||
*/
|
||||
inline static void * IRAM_ATTR esp_ptr_diram_iram_to_dram(const void *p) {
|
||||
#if SOC_DIRAM_INVERTED
|
||||
return (void *) ( SOC_DIRAM_DRAM_LOW + (SOC_DIRAM_IRAM_HIGH - (intptr_t)p) - 4);
|
||||
#else
|
||||
return (void *) ( SOC_DIRAM_DRAM_LOW + ((intptr_t)p - SOC_DIRAM_IRAM_LOW) );
|
||||
#endif
|
||||
}
|
||||
|
||||
inline static bool IRAM_ATTR esp_stack_ptr_is_sane(uint32_t sp)
|
||||
{
|
||||
//Check if stack ptr is in between SOC_DRAM_LOW and SOC_DRAM_HIGH, and 16 byte aligned.
|
||||
return !(sp < SOC_DRAM_LOW + 0x10 || sp > SOC_DRAM_HIGH - 0x10 || ((sp & 0xF) != 0));
|
||||
}
|
||||
|
||||
|
|
|
@ -277,6 +277,9 @@
|
|||
#define SOC_MEM_INTERNAL_LOW 0x3FF90000
|
||||
#define SOC_MEM_INTERNAL_HIGH 0x400C2000
|
||||
|
||||
// Start (highest address) of ROM boot stack, only relevant during early boot
|
||||
#define SOC_ROM_STACK_START 0x3ffe3f20
|
||||
|
||||
//Interrupt hardware source table
|
||||
//This table is decided by hardware, don't touch this.
|
||||
#define ETS_WIFI_MAC_INTR_SOURCE 0/**< interrupt of WiFi MAC, level*/
|
||||
|
|
|
@ -283,6 +283,9 @@
|
|||
#define SOC_MEM_INTERNAL_LOW 0x3FF9E000
|
||||
#define SOC_MEM_INTERNAL_HIGH 0x40072000
|
||||
|
||||
// Start (highest address) of ROM boot stack, only relevant during early boot
|
||||
#define SOC_ROM_STACK_START 0x3fffe70c
|
||||
|
||||
//interrupt cpu using table, Please see the core-isa.h
|
||||
/*************************************************************************************************************
|
||||
* Intr num Level Type PRO CPU usage APP CPU uasge
|
||||
|
|
|
@ -147,6 +147,8 @@ def update_exclude_patterns(tags):
|
|||
'api-reference/protocols/esp_serial_slave_link.rst',
|
||||
'api-reference/system/ipc.rst',
|
||||
'get-started-legacy/**',
|
||||
'security/secure-boot-v1.rst',
|
||||
'security/secure-boot-v2.rst',
|
||||
'gnu-make-legacy.rst',
|
||||
'hw-reference/esp32/**',
|
||||
]:
|
||||
|
|
|
@ -57,7 +57,7 @@ After the GPIO input is deactivated and the device reboots, the normally configu
|
|||
|
||||
Fast boot from Deep Sleep
|
||||
-------------------------
|
||||
The bootloader has the :ref:`CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` option which allows to reduce the wake-up time (useful to reduce consumption). This option is available when the :ref:`CONFIG_SECURE_BOOT_ENABLED` option is disabled. Reduction of time is achieved due to the lack of image verification. During the first boot, the bootloader stores the address of the application being launched in the RTC FAST memory. And during the awakening, this address is used for booting without any checks, thus fast loading is achieved.
|
||||
The bootloader has the :ref:`CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP` option which allows to reduce the wake-up time (useful to reduce consumption). This option is available when the :ref:`CONFIG_SECURE_BOOT` option is disabled. Reduction of time is achieved due to the lack of image verification. During the first boot, the bootloader stores the address of the application being launched in the RTC FAST memory. And during the awakening, this address is used for booting without any checks, thus fast loading is achieved.
|
||||
|
||||
Customer bootloader
|
||||
---------------------
|
||||
|
|
|
@ -28,7 +28,8 @@ API Guides
|
|||
Partition Tables <partition-tables>
|
||||
:esp32: RF Calibration <RF_calibration>
|
||||
ROM debug console <romconsole>
|
||||
Secure Boot <../security/secure-boot>
|
||||
:esp32: Secure Boot <../security/secure-boot-v1>
|
||||
:esp32: Secure Boot V2 <../security/secure-boot-v2>
|
||||
Thread Local Storage <thread-local-storage>
|
||||
Tools <tools/index>
|
||||
ULP Coprocessor <ulp>
|
||||
|
|
|
@ -265,4 +265,4 @@ More information can be obtained by specifying `--help` as argument:
|
|||
parttool.py [subcommand] --help
|
||||
|
||||
|
||||
.. _secure boot: security/secure-boot.rst
|
||||
.. _secure boot: security/secure-boot-v1.rst
|
||||
|
|
|
@ -62,7 +62,7 @@ For more details on the type of memory segments and their address ranges, see th
|
|||
|
||||
3. The image has a single checksum byte after the last segment. This byte is written on a sixteen byte padded boundary, so the application image might need padding.
|
||||
4. If the ``hash_appended`` field from :cpp:type:`esp_image_header_t` is set then a SHA256 checksum will be appended. The value of SHA256 is calculated on the range from first byte and up to this field. The length of this field is 32 bytes.
|
||||
5. If the options :ref:`CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT` or :ref:`CONFIG_SECURE_BOOT_ENABLED` are enabled then the application image will have additional 68 bytes for an ECDSA signature, which includes:
|
||||
5. If the options :ref:`CONFIG_SECURE_SIGNED_APPS_SCHEME` is set to ECDSA then the application image will have additional 68 bytes for an ECDSA signature, which includes:
|
||||
|
||||
* version word (4 bytes),
|
||||
* signature data (64 bytes).
|
||||
|
|
|
@ -29,10 +29,12 @@ Application Example
|
|||
return ESP_OK;
|
||||
}
|
||||
|
||||
Signature Verification
|
||||
----------------------
|
||||
.. only:: esp32
|
||||
|
||||
For additional security, signature of OTA firmware images can be verified. For that, refer :ref:`secure-ota-updates`
|
||||
Signature Verification
|
||||
----------------------
|
||||
|
||||
For additional security, signature of OTA firmware images can be verified. For that, refer :ref:`secure-ota-updates`
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
|
|
@ -194,14 +194,16 @@ Restrictions:
|
|||
|
||||
- In ESP32 it is stored in efuse ``EFUSE_BLK3_RDATA4_REG``. (when a eFuse bit is programmed to 1, it can never be reverted to 0). The number of bits set in this register is the ``security_version`` from app.
|
||||
|
||||
.. _secure-ota-updates:
|
||||
.. only:: esp32
|
||||
|
||||
Secure OTA Updates Without Secure boot
|
||||
--------------------------------------
|
||||
.. _secure-ota-updates:
|
||||
|
||||
The verification of signed OTA updates can be performed even without enabling hardware secure boot. For doing so, refer :ref:`signed-app-verify`
|
||||
Secure OTA Updates Without Secure boot
|
||||
--------------------------------------
|
||||
|
||||
The verification of signed OTA updates can be performed even without enabling hardware secure boot. For doing so, refer :ref:`signed-app-verify`
|
||||
|
||||
|
||||
OTA Tool (otatool.py)
|
||||
---------------------
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ With flash encryption enabled, following kinds of flash data are encrypted by de
|
|||
- Secure boot bootloader digest (if secure boot is enabled)
|
||||
- Any partition marked with the "encrypted" flag in the partition table
|
||||
|
||||
Flash encryption is separate from the :doc:`Secure Boot <secure-boot>` feature, and you can use flash encryption without enabling secure boot. However, for a secure environment both should be used simultaneously.
|
||||
.. only:: esp32
|
||||
|
||||
Flash encryption is separate from the :doc:`Secure Boot <secure-boot-v2>` feature, and you can use flash encryption without enabling secure boot. However, for a secure environment both should be used simultaneously.
|
||||
|
||||
.. important::
|
||||
For production use, flash encryption should be enabled in the "Release" mode only.
|
||||
|
@ -130,7 +132,9 @@ As mentioned above :ref:`flash_enc_development_mode` allows user to download as
|
|||
|
||||
- Select appropriate Bootloader log verbosity under Bootloader config.
|
||||
|
||||
- Update to the partition table offset may be required since after enabling flash encryption the size of bootloader is increased. See :ref:`secure-boot-bootloader-size`
|
||||
.. only:: esp32
|
||||
|
||||
- Update to the partition table offset may be required since after enabling flash encryption the size of bootloader is increased. See :ref:`secure-boot-bootloader-size`
|
||||
|
||||
- Save the configuration and exit.
|
||||
|
||||
|
@ -326,7 +330,9 @@ It is possible to pregenerate the flash encryption key on the host computer and
|
|||
|
||||
- Select appropriate Bootloader log verbosity under Bootloader config.
|
||||
|
||||
- Update to the partition table offset may be required since after enabling flash encryption the size of bootloader is increased. See :ref:`secure-boot-bootloader-size`
|
||||
.. only:: esp32
|
||||
|
||||
- Update to the partition table offset may be required since after enabling flash encryption the size of bootloader is increased. See :ref:`secure-boot-bootloader-size`
|
||||
|
||||
- Save the configuration and exit.
|
||||
|
||||
|
@ -364,7 +370,9 @@ In Release mode UART bootloader can not perform flash encryption operations and
|
|||
|
||||
- Select appropriate Bootloader log verbosity under Bootloader config.
|
||||
|
||||
- Update to the partition table offset may be required since after enabling flash encryption the size of bootloader is increased. See :ref:`secure-boot-bootloader-size`
|
||||
.. only:: esp32
|
||||
|
||||
- Update to the partition table offset may be required since after enabling flash encryption the size of bootloader is increased. See :ref:`secure-boot-bootloader-size`
|
||||
|
||||
- Save the configuration and exit.
|
||||
|
||||
|
@ -487,7 +495,9 @@ Key Points About Flash Encryption
|
|||
|
||||
- If secure boot is enabled, reflashing the bootloader of an encrypted device requires a "Reflashable" secure boot digest (see :ref:`flash-encryption-and-secure-boot`).
|
||||
|
||||
.. note:: The bootloader app binary ``bootloader.bin`` may become too large when both secure boot and flash encryption are enabled. See :ref:`secure-boot-bootloader-size`.
|
||||
.. only:: esp32
|
||||
|
||||
.. note:: The bootloader app binary ``bootloader.bin`` may become too large when both secure boot and flash encryption are enabled. See :ref:`secure-boot-bootloader-size`.
|
||||
|
||||
.. important::
|
||||
Do not interrupt power to the {IDF_TARGET_NAME} while the first boot encryption pass is running. If power is interrupted, the flash contents will be corrupted and require flashing with unencrypted data again. A reflash like this will not count towards the flashing limit.
|
||||
|
@ -587,7 +597,9 @@ Flash encryption prevents plaintext readout of the encrypted flash, to protect f
|
|||
|
||||
- For the same reason, an attacker can always tell when a pair of adjacent 16 byte blocks (32 byte aligned) contain two identical 16 byte sequences. Keep this in mind if storing sensitive data on the flash, design your flash storage so this doesn't happen (using a counter byte or some other non-identical value every 16 bytes is sufficient). :ref:`NVS Encryption <nvs_encryption>` deals with this and is suitable for many uses.
|
||||
|
||||
- Flash encryption alone may not prevent an attacker from modifying the firmware of the device. To prevent unauthorised firmware from running on the device, use flash encryption in combination with :doc:`Secure Boot <secure-boot>`.
|
||||
.. only:: esp32
|
||||
|
||||
- Flash encryption alone may not prevent an attacker from modifying the firmware of the device. To prevent unauthorised firmware from running on the device, use flash encryption in combination with :doc:`Secure Boot <secure-boot-v2>`.
|
||||
|
||||
.. _flash-encryption-and-secure-boot:
|
||||
|
||||
|
@ -597,8 +609,12 @@ Flash Encryption and Secure Boot
|
|||
It is recommended to use flash encryption and secure boot together. However, if Secure Boot is enabled then additional restrictions apply to reflashing the device:
|
||||
|
||||
- :ref:`updating-encrypted-flash-ota` are not restricted (provided the new app is signed correctly with the Secure Boot signing key).
|
||||
- :ref:`Plaintext serial flash updates <updating-encrypted-flash-serial>` are only possible if the :ref:`Reflashable <CONFIG_SECURE_BOOTLOADER_MODE>` Secure Boot mode is selected and a Secure Boot key was pre-generated and burned to the {IDF_TARGET_NAME} (refer to :ref:`Secure Boot <secure-boot-reflashable>` docs.). In this configuration, ``idf.py bootloader`` will produce a pre-digested bootloader and secure boot digest file for flashing at offset 0x0. When following the plaintext serial reflashing steps it is necessary to re-flash this file before flashing other plaintext data.
|
||||
- :ref:`Reflashing via Pregenerated Flash Encryption Key <pregenerated-flash-encryption-key>` is still possible, provided the bootloader is not reflashed. Reflashing the bootloader requires the same :ref:`Reflashable <CONFIG_SECURE_BOOTLOADER_MODE>` option to be enabled in the Secure Boot config.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
- :ref:`Plaintext serial flash updates <updating-encrypted-flash-serial>` are only possible if the :ref:`Reflashable <CONFIG_SECURE_BOOTLOADER_MODE>` Secure Boot mode is selected and a Secure Boot key was pre-generated and burned to the {IDF_TARGET_NAME} (refer to :ref:`Secure Boot <secure-boot-reflashable>` docs.). In this configuration, ``idf.py bootloader`` will produce a pre-digested bootloader and secure boot digest file for flashing at offset 0x0. When following the plaintext serial reflashing steps it is necessary to re-flash this file before flashing other plaintext data.
|
||||
|
||||
- :ref:`Reflashing via Pregenerated Flash Encryption Key <pregenerated-flash-encryption-key>` is still possible, provided the bootloader is not reflashed. Reflashing the bootloader requires the same :ref:`Reflashable <CONFIG_SECURE_BOOTLOADER_MODE>` option to be enabled in the Secure Boot config.
|
||||
|
||||
.. _flash-encryption-advanced-features:
|
||||
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
Secure Boot
|
||||
===========
|
||||
|
||||
.. important::
|
||||
|
||||
All references in this document are related to Secure Boot V1 (The AES based Secure Boot Scheme). ESP32 Revision 3 onwards, the preferred secure boot scheme is :doc:`Secure Boot V2 <secure-boot-v2>`.
|
||||
Please refer to Secure Boot V2 document for ESP32 Revision 3 or ESP32S2.
|
||||
|
||||
Secure Boot is a feature for ensuring only your code can run on the chip. Data loaded from flash is verified on each reset.
|
||||
|
||||
Secure Boot is separate from the :doc:`Flash Encryption <flash-encryption>` feature, and you can use secure boot without encrypting the flash contents. However, for a secure environment both should be used simultaneously. See :ref:`secure-boot-and-flash-encr` for more details.
|
||||
|
@ -133,7 +138,7 @@ In the esp-idf build process, this 256-bit key file is derived from the ECDSA ap
|
|||
|
||||
To enable a reflashable bootloader:
|
||||
|
||||
1. In the :ref:`project-configuration-menu`, select "Bootloader Config" -> :ref:`CONFIG_SECURE_BOOT_ENABLED` -> :ref:`CONFIG_SECURE_BOOTLOADER_MODE` -> Reflashable.
|
||||
1. In the :ref:`project-configuration-menu`, select "Bootloader Config" -> :ref:`CONFIG_SECURE_BOOT` -> CONFIG_SECURE_BOOT_V1_ENABLED -> :ref:`CONFIG_SECURE_BOOTLOADER_MODE` -> Reflashable.
|
||||
|
||||
2. If necessary, set the :ref:`CONFIG_SECURE_BOOTLOADER_KEY_ENCODING` based on the coding scheme used by the device. The coding scheme is shown in the ``Features`` line when ``esptool.py`` connects to the chip, or in the ``espefuse.py summary`` output.
|
||||
|
239
docs/en/security/secure-boot-v2.rst
Normal file
239
docs/en/security/secure-boot-v2.rst
Normal file
|
@ -0,0 +1,239 @@
|
|||
Secure Boot V2
|
||||
==============
|
||||
|
||||
.. important::
|
||||
|
||||
The references in this document are related to Secure Boot v2, the preferred scheme from ESP32-ECO3 onwards and in ESP32S2. (Refer to :doc:`Secure Boot <secure-boot-v1>` for ESP32)
|
||||
|
||||
Secure Boot V2 uses RSA based app and bootloader verification. This document can also be referred for signing apps with the RSA scheme without signing the bootloader.
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
Secure Boot protects a device from running unsigned code (verification at time of load). A new RSA based secure boot
|
||||
verification scheme (Secure Boot V2) has been introduced for ESP32S2 and ESP32 ECO3 onwards.
|
||||
|
||||
- The software bootloader’s RSA-PSS signature is verified by the Mask ROM and it is executed post successful verification.
|
||||
- The verified software bootloader verifies the RSA-PSS signature of the application image before it is executed.
|
||||
|
||||
Advantages
|
||||
----------
|
||||
|
||||
- The RSA public key is stored on the device. The corresponding RSA private key is kept secret on a server and is never accessed by the device.
|
||||
|
||||
- Upto three public keys can store can be generated and stored in ESP32S2 during manufacturing. (ESP32 ECO3: only one key)
|
||||
|
||||
- ESP32S2 also provides the facility to revoke individual public keys.
|
||||
|
||||
- Same image format & signature verification is applied for applications & software bootloader.
|
||||
|
||||
- No secrets are stored on the device. Therefore immune to passive side-channel attacks (timing or power analysis, etc.)
|
||||
|
||||
|
||||
Secure Boot V2 Process
|
||||
----------------------
|
||||
|
||||
This is an overview of the Secure Boot V2 Process, Step by step instructions are supplied under :ref:`secure-boot-v2-howto`.
|
||||
|
||||
1. Secure Boot V2 verifies the signature blocks appended to the bootloader and application binaries. The signature block contains the image binary signed by a RSA-3072 private key and its corresponding public key. More details on the :ref:`signature-block-format`.
|
||||
|
||||
2. On startup, ROM code checks the secure boot v2 bit in eFuse.
|
||||
|
||||
3. If secure boot is enabled, ROM checks the SHA-256 of the public key in the signature block in the eFuse.
|
||||
|
||||
4. The ROM code validates the public key embedded in the software bootloader's signature block by matching the SHA-256 of its public key to the SHA-256 in eFuse as per the earlier step. Boot process will be aborted if a valid hash of the public key isn’t found in the eFuse.
|
||||
|
||||
5. The ROM code verifies the signature of the bootloader with the pre-validated public key with the RSA-PSS Scheme. In depth information on :ref:`verify_signature-block`.
|
||||
|
||||
6. Software bootloader, reads the app partition and performs similar verification on the application. The application is verified on every boot up and OTA update. If selected OTA app partition fails verification, bootloader will fall back and look for another correctly signed partition.
|
||||
|
||||
.. _signature-block-format:
|
||||
|
||||
Signature Block Format
|
||||
----------------------
|
||||
|
||||
The bootloader and application images are padded to the next 4096 byte boundary, thus the signature has a flash sector of its own. The signature is calculated over all bytes in the image including the padding bytes.
|
||||
|
||||
Each signature block contains the following:
|
||||
|
||||
* **Offset 0 (1 byte):** Magic byte (0xe7)
|
||||
|
||||
* **Offset 1 (1 byte):** Version number byte (currently 0x02), 0x01 is for Secure Boot V1.
|
||||
|
||||
* **Offset 2 (2 bytes):** Padding bytes, Reserved. Should be zero.
|
||||
|
||||
* **Offset 4 (32 bytes):** SHA-256 hash of only the image content, not including the signature block.
|
||||
|
||||
* **Offset 36 (384 bytes):** RSA Public Modulus used for signature verification. (value ‘n’ in RFC8017).
|
||||
|
||||
* **Offset 420 (4 bytes):** RSA Public Exponent used for signature verification (value ‘e’ in RFC8017).
|
||||
|
||||
* **Offset 424 (384 bytes):** Precalculated R, derived from ‘n’.
|
||||
|
||||
* **Offset 808 (4 bytes):** Precalculated M’, derived from ‘n’
|
||||
|
||||
* **Offset 812 (384 bytes):** RSA-PSS Signature result (section 8.1.1 of RFC8017) of image content, computed using following PSS parameters: SHA256 hash, MFG1 function, 0 length salt, default trailer field (0xBC).
|
||||
|
||||
* **Offset 1196:** CRC32 of the preceding 1095 bytes.
|
||||
|
||||
* **Offset 1200 (16 bytes):** Zero padding to length 1216 bytes.
|
||||
|
||||
.. note::
|
||||
R and M' are used for hardware-assisted Montgomery Multiplication.
|
||||
|
||||
The remainder of the signature sector is erased flash (0xFF) which allows writing other signature blocks after previous signature block.
|
||||
|
||||
.. _verify_signature-block:
|
||||
|
||||
Verifying the signature Block
|
||||
-----------------------------
|
||||
|
||||
A signature block is “valid” if the first byte is 0xe7 and a valid CRC32 is stored at offset 1196. Upto 3 signature blocks can be appended to the bootloader or application image in ESP32S2. (ESP32 ECO3: only one key)
|
||||
|
||||
An image is “verified” if the public key stored in any signature block is valid for this device, and if the stored signature is valid for the image data read from flash.
|
||||
|
||||
1. The magic byte, signature block CRC is validated.
|
||||
|
||||
2. Public key digests are generated per signature block and compared with the digests from eFuse. If none of the digests match, the verification process is aborted.
|
||||
|
||||
3. The application image digest is generated and matched with the image digest in the signature blocks. The verification process is aborted is the digests don't match.
|
||||
|
||||
4. The public key is used to verify the signature of the bootloader image, using RSA-PSS (section 8.1.2 of RFC8017) with the image digest calculated in step (3) for comparison.
|
||||
|
||||
- The application signing scheme is set to RSA for secure boot V2 and to ECDSA for secure boot V1.
|
||||
|
||||
.. important::
|
||||
It is recommended to use secure boot V2 on the chip versions supporting them.
|
||||
|
||||
.. _secure-boot-v2-bootloader-size:
|
||||
|
||||
Bootloader Size
|
||||
---------------
|
||||
|
||||
When secure boot is enabled the bootloader app binary ``bootloader.bin`` may exceed the default bootloader size limit. This is especially likely if flash encryption is enabled as well. The default size limit is 0x7000 (28672) bytes (partition table offset 0x8000 - bootloader offset 0x1000).
|
||||
|
||||
If the bootloader becomes too large, the ESP32 will fail to boot - errors will be logged about either invalid partition table or invalid bootloader checksum.
|
||||
|
||||
Options to work around this are:
|
||||
|
||||
- Reduce :ref:`bootloader log level <CONFIG_BOOTLOADER_LOG_LEVEL>`. Setting log level to Warning, Error or None all significantly reduce the final binary size (but may make it harder to debug).
|
||||
- Set :ref:`partition table offset <CONFIG_PARTITION_TABLE_OFFSET>` to a higher value than 0x8000, to place the partition table later in the flash. This increases the space available for the bootloader. If the :doc:`partition table </api-guides/partition-tables>` CSV file contains explicit partition offsets, they will need changing so no partition has an offset lower than ``CONFIG_PARTITION_TABLE_OFFSET + 0x1000``. (This includes the default partition CSV files supplied with ESP-IDF.)
|
||||
|
||||
.. _efuse-usage:
|
||||
|
||||
eFuse usage
|
||||
-----------
|
||||
|
||||
ESP32-ECO3:
|
||||
|
||||
- ABS_DONE_1 - Enables secure boot protection on boot.
|
||||
|
||||
- BLK2 - Stores the SHA-256 digest of the public key. SHA-256 hash of public key modulus, exponent, precalculated R & M’ values (represented as 776 bytes – offsets 36 to 812 - as per the :ref:`signature-block-format`) is written to an eFuse key block.
|
||||
|
||||
|
||||
.. _secure-boot-v2-howto:
|
||||
|
||||
How To Enable Secure Boot V2
|
||||
----------------------------
|
||||
|
||||
1. Open the :ref:`project-configuration-menu`, in "Security Features" set "Enable hardware Secure Boot in bootloader" to enable Secure Boot. The chip revision should be changed to revision 3(ESP32- ECO3) to view the Secure Boot V2 option.
|
||||
|
||||
2. To change the chip revision, set "Minimum Supported ESP32 Revision" to Rev 3 in "Component Config" -> "ESP32- Specific", the Secure Boot V2 option can be enabled under "Enable hardware Secure Boot in bootloader" -> "Secure Boot Version". Secure Boot V2 is available for ESP32 ECO3 onwards and in ESP32S2.
|
||||
|
||||
3. Specify the secure boot signing key path. The file can be anywhere on your system. A relative path will be evaluated from the project directory. The file does not need to exist yet.
|
||||
|
||||
4. Set other menuconfig options (as desired). Pay particular attention to the "Bootloader Config" options, as you can only flash the bootloader once. Then exit menuconfig and save your configuration
|
||||
|
||||
5. The first time you run ``make`` or ``idf.py build``, if the signing key is not found then an error message will be printed with a command to generate a signing key via ``espsecure.py generate_signing_key``.
|
||||
|
||||
.. important::
|
||||
A signing key generated this way will use the best random number source available to the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the private key will be weak.
|
||||
|
||||
.. important::
|
||||
For production environments, we recommend generating the keypair using openssl or another industry standard encryption program. See :ref:`secure-boot-v2-generate-key` for more details.
|
||||
|
||||
6. Run ``idf.py bootloader`` to build a secure boot enabled bootloader. The build output will include a prompt for a flashing command, using ``esptool.py write_flash``.
|
||||
|
||||
7. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not performed by the build system) and then wait for flashing to complete.
|
||||
|
||||
8. Run ``idf.py flash`` to build and flash the partition table and the just-built app image. The app image will be signed using the signing key you generated in step 4.
|
||||
|
||||
.. note:: ``idf.py flash`` doesn't flash the bootloader if secure boot is enabled.
|
||||
|
||||
9. Reset the ESP32 and it will boot the software bootloader you flashed. The software bootloader will enable secure boot on the chip, and then it verifies the app image signature and boots the app. You should watch the serial console output from the ESP32 to verify that secure boot is enabled and no errors have occurred due to the build configuration.
|
||||
|
||||
.. note:: Secure boot won't be enabled until after a valid partition table and app image have been flashed. This is to prevent accidents before the system is fully configured.
|
||||
|
||||
.. note:: If the ESP32 is reset or powered down during the first boot, it will start the process again on the next boot.
|
||||
|
||||
10. On subsequent boots, the secure boot hardware will verify the software bootloader has not changed and the software bootloader will verify the signed app image (using the validated public key portion of its appended signature block).
|
||||
|
||||
.. _secure-boot-v2-generate-key:
|
||||
|
||||
Generating Secure Boot Signing Key
|
||||
----------------------------------
|
||||
|
||||
The build system will prompt you with a command to generate a new signing key via ``espsecure.py generate_signing_key``. The --version 2 parameter will generate the RSA 3072 private key for Secure Boot V2.
|
||||
|
||||
The strength of the signing key is proportional to (a) the random number source of the system, and (b) the correctness of the algorithm used. For production devices, we recommend generating signing keys from a system with a quality entropy source, and using the best available RSA key generation utilities.
|
||||
|
||||
For example, to generate a signing key using the openssl command line:
|
||||
|
||||
```
|
||||
openssl genrsa -out my_secure_boot_signing_key.pem 3072
|
||||
```
|
||||
|
||||
Remember that the strength of the secure boot system depends on keeping the signing key private.
|
||||
|
||||
.. _remote-sign-v2-image:
|
||||
|
||||
Remote Signing of Images
|
||||
------------------------
|
||||
|
||||
For production builds, it can be good practice to use a remote signing server rather than have the signing key on the build machine (which is the default esp-idf secure boot configuration). The espsecure.py command line program can be used to sign app images & partition table data for secure boot, on a remote system.
|
||||
|
||||
To use remote signing, disable the option "Sign binaries during build". The private signing key does not need to be present on the build system.
|
||||
|
||||
After the app image and partition table are built, the build system will print signing steps using espsecure.py::
|
||||
|
||||
espsecure.py sign_data --version 2 --keyfile PRIVATE_SIGNING_KEY BINARY_FILE
|
||||
|
||||
The above command appends the image signature to the existing binary. You can use the `--output` argument to write the signed binary to a separate file::
|
||||
|
||||
espsecure.py sign_data --version 2 --keyfile PRIVATE_SIGNING_KEY --output SIGNED_BINARY_FILE BINARY_FILE
|
||||
|
||||
Secure Boot Best Practices
|
||||
--------------------------
|
||||
|
||||
* Generate the signing key on a system with a quality source of entropy.
|
||||
* Keep the signing key private at all times. A leak of this key will compromise the secure boot system.
|
||||
* Do not allow any third party to observe any aspects of the key generation or signing process using espsecure.py. Both processes are vulnerable to timing or other side-channel attacks.
|
||||
* Enable all secure boot options in the Secure Boot Configuration. These include flash encryption, disabling of JTAG, disabling BASIC ROM interpeter, and disabling the UART bootloader encrypted flash access.
|
||||
* Use secure boot in combination with :doc:`flash encryption<flash-encryption>` to prevent local readout of the flash contents.
|
||||
|
||||
.. _secure-boot-v2-technical-details:
|
||||
|
||||
Technical Details
|
||||
-----------------
|
||||
|
||||
The following sections contain low-level reference descriptions of various secure boot elements:
|
||||
|
||||
Manual Commands
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Secure boot is integrated into the esp-idf build system, so ``make`` or ``idf.py build`` will sign an app image and ``idf.py bootloader`` will produce a signed bootloader if secure signed binaries on build is enabled.
|
||||
|
||||
However, it is possible to use the ``espsecure.py`` tool to make standalone signatures and digests.
|
||||
|
||||
To sign a binary image::
|
||||
|
||||
espsecure.py sign_data --version 2 --keyfile ./my_signing_key.pem --output ./image_signed.bin image-unsigned.bin
|
||||
|
||||
Keyfile is the PEM file containing an RSA-3072 private signing key.
|
||||
|
||||
.. _secure-boot-v2-and-flash-encr:
|
||||
|
||||
Secure Boot & Flash Encryption
|
||||
------------------------------
|
||||
|
||||
If secure boot is used without :doc:`Flash Encryption <flash-encryption>`, it is possible to launch "time-of-check to time-of-use" attack, where flash contents are swapped after the image is verified and running. Therefore, it is recommended to use both the features together.
|
|
@ -50,4 +50,7 @@ api-guide/build-system-cmake api-guide/build-system
|
|||
api-guide/ulp-cmake api-guide/ulp
|
||||
api-guide/unit-tests-cmake api-guide/unit-tests
|
||||
|
||||
api-reference/network/tcpip_adapter api-reference/network/esp_netif
|
||||
api-reference/network/tcpip_adapter api-reference/network/esp_netif
|
||||
|
||||
# The 'secure boot' guides are now 'secure boot v1' guides
|
||||
security/secure-boot.rst security/secure-boot-v1.rst
|
||||
|
|
|
@ -20,7 +20,8 @@ API 指南
|
|||
JTAG 调试 <jtag-debugging/index>
|
||||
引导加载程序 <bootloader>
|
||||
分区表 <partition-tables>
|
||||
Secure Boot <../security/secure-boot>
|
||||
:esp32: Secure Boot <../security/secure-boot-v1>
|
||||
:esp32: Secure Boot V2 <../security/secure-boot-v2>
|
||||
ULP 协处理器 <ulp>
|
||||
:esp32: ULP (传统 GNU Make) <ulp-legacy>
|
||||
单元测试 <unit-tests>
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
|
||||
每片 {IDF_TARGET_NAME} 的 flash 可以包含多个应用程序,以及多种不同类型的数据(例如校准数据、文件系统数据、参数存储器数据等)。因此,我们需要引入分区表的概念。
|
||||
|
||||
具体来说,{IDF_TARGET_NAME} 在 flash 的 :ref:`默认偏移地址 <CONFIG_PARTITION_TABLE_OFFSET>` 0x8000 处烧写一张分区表。该分区表的长度为 0xC00 字节(最多可以保存 95 条分区表条目)。分区表数据后还保存着该表的 MD5 校验和,用于验证分区表的完整性。此外,如果芯片使能了 :doc:`安全启动 </security/secure-boot>` 功能,则该分区表后还会保存签名信息。
|
||||
.. only:: esp32
|
||||
|
||||
具体来说,{IDF_TARGET_NAME} 在 flash 的 :ref:`默认偏移地址 <CONFIG_PARTITION_TABLE_OFFSET>` 0x8000 处烧写一张分区表。该分区表的长度为 0xC00 字节(最多可以保存 95 条分区表条目)。分区表数据后还保存着该表的 MD5 校验和,用于验证分区表的完整性。此外,如果芯片使能了 :doc:`安全启动 </security/secure-boot-v2>` 功能,则该分区表后还会保存签名信息。
|
||||
|
||||
分区表中的每个条目都包括以下几个部分:Name(标签)、Type(app、data 等)、SubType 以及在 flash 中的偏移量(分区的加载地址)。
|
||||
|
||||
|
@ -175,4 +177,4 @@ MD5 校验和
|
|||
|
||||
分区表的更新并不会擦除根据之前分区表存储的数据。此时,您可以使用 ``idf.py erase_flash`` 命令或者 ``esptool.py erase_flash`` 命令来擦除 flash 中的所有内容。
|
||||
|
||||
.. _secure boot: security/secure-boot.rst
|
||||
.. _secure boot: security/secure-boot-v1.rst
|
||||
|
|
|
@ -24,7 +24,9 @@ Flash 加密功能用于加密与 {IDF_TARGET_NAME} 搭载使用的 SPI Flash
|
|||
- 安全启动引导加载程序摘要(如果已启用安全启动)
|
||||
- 分区表中标有“加密”标记的分区
|
||||
|
||||
Flash 加密与 :doc:`安全启动<secure-boot>` 功能各自独立,您可以在关闭安全启动的状态下使用 Flash 加密。但是,为了安全的计算机环境,二者应同时使用。在关闭安全启动的状态下,需运行其他配置来确保 Flash 加密的有效性。详细信息可参见 :ref:`flash-encryption-without-secure-boot`。
|
||||
.. only:: esp32
|
||||
|
||||
Flash 加密与 :doc:`安全启动<secure-boot-v2>` 功能各自独立,您可以在关闭安全启动的状态下使用 Flash 加密。但是,为了安全的计算机环境,二者应同时使用。在关闭安全启动的状态下,需运行其他配置来确保 Flash 加密的有效性。详细信息可参见 :ref:`flash-encryption-without-secure-boot`。
|
||||
|
||||
.. important::
|
||||
启用 Flash 加密将限制后续 {IDF_TARGET_NAME} 更新。请务必阅读本文档(包括 :ref:`flash-encryption-limitations`)了解启用 Flash 加密的影响。
|
||||
|
@ -116,7 +118,9 @@ Flash 的加密过程
|
|||
|
||||
- 在引导加载程序 config 下选择适当详细程度的日志。
|
||||
|
||||
- 启用 Flash 加密将增大引导加载程序,因而可能需更新分区表偏移。请参见 :ref:`secure-boot-bootloader-size`。
|
||||
.. only:: esp32
|
||||
|
||||
- 启用 Flash 加密将增大引导加载程序,因而可能需更新分区表偏移。请参见 :ref:`secure-boot-bootloader-size`。
|
||||
|
||||
- 保存配置并退出。
|
||||
|
||||
|
@ -310,7 +314,9 @@ Flash 的加密过程
|
|||
|
||||
- 在引导加载程序 config 下选择适当详细程度的日志。
|
||||
|
||||
- 启用 Flash 加密将增大引导加载程序,因而可能需要更新分区表偏移。可参见 See :ref:`secure-boot-bootloader-size`。
|
||||
.. only:: esp32
|
||||
|
||||
- 启用 Flash 加密将增大引导加载程序,因而可能需要更新分区表偏移。可参见 See :ref:`secure-boot-bootloader-size`。
|
||||
|
||||
- 保存配置并退出。
|
||||
|
||||
|
@ -347,7 +353,9 @@ Flash 的加密过程
|
|||
|
||||
- 在引导加载程序 config 下选择适当详细程度的日志。
|
||||
|
||||
- 启用 Flash 加密将增大引导加载程序,因而可能需要更新分区表偏移。可参见 See :ref:`secure-boot-bootloader-size`。
|
||||
.. only:: esp32
|
||||
|
||||
- 启用 Flash 加密将增大引导加载程序,因而可能需要更新分区表偏移。可参见 See :ref:`secure-boot-bootloader-size`。
|
||||
|
||||
- 保存配置并退出。
|
||||
|
||||
|
@ -467,7 +475,9 @@ Flash 加密的要点
|
|||
|
||||
- 如果已启用安全启动,则重新烧录加密设备的引导加载程序则需要“可重新烧录”的安全启动摘要(可参见 :ref:`flash-encryption-and-secure-boot`)。
|
||||
|
||||
.. note:: 同时启用安全启动和 Flash 加密后,引导加载程序 app 二进制文件 ``bootloader.bin`` 可能会过大。参见 :ref:`secure-boot-bootloader-size`。
|
||||
.. only:: esp32
|
||||
|
||||
.. note:: 同时启用安全启动和 Flash 加密后,引导加载程序 app 二进制文件 ``bootloader.bin`` 可能会过大。参见 :ref:`secure-boot-bootloader-size`。
|
||||
|
||||
.. important::
|
||||
在首次启动加密过程中,请勿中断 {IDF_TARGET_NAME} 的电源。如果电源中断,Flash 的内容将受到破坏,并需要重新烧录未加密数据。而这类重新烧录将不计入烧录限制次数。
|
||||
|
@ -562,7 +572,9 @@ Flash 加密可防止从加密 Flash 中读取明文,从而保护固件防止
|
|||
|
||||
- 出于相同原因,攻击者始终可获知一对相邻的 16 字节块(32 字节对齐)何时包含相同内容。因此,在 Flash 上存储敏感数据时应牢记这点,并进行相关设置避免该情况发生(可使用计数器字节或每 16 字节设置不同的值即可)。
|
||||
|
||||
- 单独使用 Flash 加密可能无法防止攻击者修改本设备的固件。为防止设备上运行未经授权的固件,可搭配 Flash 加密使用 :doc:`安全启动 <secure-boot>`。
|
||||
.. only:: esp32
|
||||
|
||||
- 单独使用 Flash 加密可能无法防止攻击者修改本设备的固件。为防止设备上运行未经授权的固件,可搭配 Flash 加密使用 :doc:`安全启动 <secure-boot-v2>`。
|
||||
|
||||
.. _flash-encryption-and-secure-boot:
|
||||
|
||||
|
@ -572,8 +584,12 @@ Flash 加密与安全启动
|
|||
推荐搭配使用 Flash 加密与安全启动。但是,如果已启用安全启动,则重新烧录设备时会受到其他限制:
|
||||
|
||||
- :ref:`updating-encrypted-flash-ota` 不受限制(如果新的 app 已使用安全启动签名密钥进行正确签名)。
|
||||
- 只有当选择 :ref:`可再次烧录 <CONFIG_SECURE_BOOTLOADER_MODE>` 安全启动模式,且安全启动密钥已预生成并烧录至 {IDF_TARGET_NAME}(可参见 :ref:`安全启动 <secure-boot-reflashable>`),则 :ref:`明文串行 flash 更新 <updating-encrypted-flash-serial>` 可实现。在该配置下,``idf.py bootloader`` 将生成简化的引导加载程序和安全启动摘要文件,用于在偏移量 0x0 处进行烧录。当进行明文串行重新烧录步骤时,须在烧录其他明文数据前重新烧录此文件。
|
||||
- 假设未重新烧录引导加载程序,:ref:`使用预生成的 Flash 加密密钥重新烧录 <pregenerated-flash-encryption-key>` 仍可实现。重新烧录引导加载程序时,需在安全启动配置中启用相同的 :ref:`可重新烧录 <CONFIG_SECURE_BOOTLOADER_MODE>` 选项。
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
- 只有当选择 :ref:`可再次烧录 <CONFIG_SECURE_BOOTLOADER_MODE>` 安全启动模式,且安全启动密钥已预生成并烧录至 {IDF_TARGET_NAME}(可参见 :ref:`安全启动 <secure-boot-reflashable>`),则 :ref:`明文串行 flash 更新 <updating-encrypted-flash-serial>` 可实现。在该配置下,``idf.py bootloader`` 将生成简化的引导加载程序和安全启动摘要文件,用于在偏移量 0x0 处进行烧录。当进行明文串行重新烧录步骤时,须在烧录其他明文数据前重新烧录此文件。
|
||||
|
||||
- 假设未重新烧录引导加载程序,:ref:`使用预生成的 Flash 加密密钥重新烧录 <pregenerated-flash-encryption-key>` 仍可实现。重新烧录引导加载程序时,需在安全启动配置中启用相同的 :ref:`可重新烧录 <CONFIG_SECURE_BOOTLOADER_MODE>` 选项。
|
||||
|
||||
.. _flash-encryption-without-secure-boot:
|
||||
|
||||
|
|
1
docs/zh_CN/security/secure-boot-v1.rst
Normal file
1
docs/zh_CN/security/secure-boot-v1.rst
Normal file
|
@ -0,0 +1 @@
|
|||
.. include:: ../../en/security/secure-boot-v1.rst
|
1
docs/zh_CN/security/secure-boot-v2.rst
Normal file
1
docs/zh_CN/security/secure-boot-v2.rst
Normal file
|
@ -0,0 +1 @@
|
|||
.. include:: ../../en/security/secure-boot-v2.rst
|
|
@ -1 +0,0 @@
|
|||
.. include:: ../../en/security/secure-boot.rst
|
|
@ -311,12 +311,17 @@ COMPONENT_INCLUDES += $(abspath $(BUILD_DIR_BASE)/include/)
|
|||
export COMPONENT_INCLUDES
|
||||
|
||||
all:
|
||||
ifdef CONFIG_SECURE_BOOT_ENABLED
|
||||
ifdef CONFIG_SECURE_BOOT
|
||||
@echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)"
|
||||
ifndef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
|
||||
ifdef SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
@echo "App built but not signed. Sign app & partition data before flashing, via espsecure.py:"
|
||||
@echo "espsecure.py sign_data --keyfile KEYFILE $(APP_BIN)"
|
||||
@echo "espsecure.py sign_data --keyfile KEYFILE $(PARTITION_TABLE_BIN)"
|
||||
@echo "espsecure.py sign_data --version 1 --keyfile KEYFILE $(APP_BIN)"
|
||||
@echo "espsecure.py sign_data --version 1 --keyfile KEYFILE $(PARTITION_TABLE_BIN)"
|
||||
else
|
||||
@echo "App built but not signed. Sign app & partition data before flashing, via espsecure.py:"
|
||||
@echo "espsecure.py sign_data --version 2 --keyfile KEYFILE $(APP_BIN)"
|
||||
endif
|
||||
endif
|
||||
@echo "To flash app & partition table, run 'make flash' or:"
|
||||
else
|
||||
|
@ -420,7 +425,6 @@ endif
|
|||
ifdef CONFIG_COMPILER_STACK_CHECK_MODE_ALL
|
||||
COMMON_FLAGS += -fstack-protector-all
|
||||
endif
|
||||
endif
|
||||
|
||||
# Optimization flags are set based on menuconfig choice
|
||||
ifdef CONFIG_COMPILER_OPTIMIZATION_SIZE
|
||||
|
@ -443,6 +447,27 @@ ifdef CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE
|
|||
CPPFLAGS += -DNDEBUG
|
||||
endif
|
||||
|
||||
else # IS_BOOTLOADER_BUILD
|
||||
|
||||
ifdef CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE
|
||||
OPTIMIZATION_FLAGS = -Os -freorder-blocks
|
||||
endif
|
||||
|
||||
ifdef CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG
|
||||
OPTIMIZATION_FLAGS = -Og
|
||||
endif
|
||||
|
||||
ifdef CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE
|
||||
OPTIMIZATION_FLAGS = -O0
|
||||
endif
|
||||
|
||||
ifdef CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF
|
||||
OPTIMIZATION_FLAGS = -O2
|
||||
endif
|
||||
|
||||
endif # IS_BOOTLOADER_BUILD
|
||||
|
||||
|
||||
# IDF uses some GNU extension from libc
|
||||
CPPFLAGS += -D_GNU_SOURCE
|
||||
|
||||
|
@ -538,11 +563,17 @@ $(APP_ELF): $(foreach libcomp,$(COMPONENT_LIBRARIES),$(BUILD_DIR_BASE)/$(libcomp
|
|||
$(summary) LD $(patsubst $(PWD)/%,%,$@)
|
||||
$(CC) $(LDFLAGS) -o $@ -Wl,-Map=$(APP_MAP)
|
||||
|
||||
ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
SECURE_APPS_SIGNING_SCHEME = "1"
|
||||
else ifdef CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
SECURE_APPS_SIGNING_SCHEME = "2"
|
||||
endif
|
||||
|
||||
app: $(APP_BIN) partition_table_get_info
|
||||
ifeq ("$(CONFIG_APP_BUILD_GENERATE_BINARIES)","y")
|
||||
ifeq ("$(CONFIG_SECURE_BOOT_ENABLED)$(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)","y") # secure boot enabled, but remote sign app image
|
||||
ifeq ("$(CONFIG_SECURE_BOOT)$(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)","y") # secure boot enabled, but remote sign app image
|
||||
@echo "App built but not signed. Signing step via espsecure.py:"
|
||||
@echo "espsecure.py sign_data --keyfile KEYFILE $(APP_BIN)"
|
||||
@echo "espsecure.py sign_data --version $(SECURE_APPS_SIGNING_SCHEME) --keyfile KEYFILE $(APP_BIN)"
|
||||
@echo "Then flash app command is:"
|
||||
@echo $(ESPTOOLPY_WRITE_FLASH) $(APP_OFFSET) $(APP_BIN)
|
||||
else
|
||||
|
|
|
@ -336,7 +336,7 @@ example_test_012:
|
|||
|
||||
UT_001:
|
||||
extends: .unit_test_template
|
||||
parallel: 34
|
||||
parallel: 36
|
||||
tags:
|
||||
- ESP32_IDF
|
||||
- UT_T1_1
|
||||
|
|
|
@ -21,7 +21,7 @@ CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y
|
|||
#
|
||||
# Security features
|
||||
#
|
||||
CONFIG_SECURE_BOOT_ENABLED=
|
||||
CONFIG_SECURE_BOOT=
|
||||
CONFIG_SECURE_FLASH_ENC_ENABLED=
|
||||
|
||||
#
|
||||
|
|
|
@ -23,6 +23,7 @@ The test apps should be grouped into subdirectories by category. Categories are:
|
|||
* `protocols` contains test of protocol interactions.
|
||||
* `network` contains system network tests
|
||||
* `system` contains tests on the internal chip features, debugging and development tools.
|
||||
* `security` contains tests on the chip security features.
|
||||
|
||||
# Test Apps local execution
|
||||
|
||||
|
|
7
tools/test_apps/security/secure_boot/CMakeLists.txt
Normal file
7
tools/test_apps/security/secure_boot/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
# 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.5)
|
||||
|
||||
set(SUPPORTED_TARGETS esp32) # Secure Boot not currently supported for ESP32-S2beta
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(secure_boot)
|
9
tools/test_apps/security/secure_boot/Makefile
Normal file
9
tools/test_apps/security/secure_boot/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := secure_boot
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
53
tools/test_apps/security/secure_boot/README.md
Normal file
53
tools/test_apps/security/secure_boot/README.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Secure Boot
|
||||
|
||||
The example checks if the secure boot feature is enabled/disabled and if enabled prints the secure boot version (Version 1 / Version 2) and FLASH_CRYPT_CNT eFuse value.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
ESP32 (supports Secure Boot V1) or ESP32-ECO3 (supports Secure Boot V2 & Secure Boot V1). It is recommended to use Secure Boot V2 from ESP32-ECO3 onwards.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Set serial port under Serial Flasher Options.
|
||||
|
||||
* Enable the `Enable hardware Secure Boot` under Security Features. Default version for ESP32 is Secure Boot V1. The chip revision should be changed to revision 3(ESP32- ECO3) to view the Secure Boot V2 option.
|
||||
|
||||
* To change the chip revision, set "Component Config" -> "ESP32- Specific"->"Minimum Supported ESP32 Revision" to Rev 3. Now, set Secure Boot V2 option can now be enabled under "Enable hardware Secure Boot in bootloader" -> "Secure Boot Version".
|
||||
|
||||
* Specify the apt secure boot signing key path. If not present generate one using `python $IDF_PATH/components/esptool_py/esptool/espsecure.py generate_signing_key --version {VERSION} secure_boot_signing_key.pem`
|
||||
|
||||
* Ensure Bootloader log verbosity is Info under Bootloader config.
|
||||
|
||||
* Select Single factory app, no OTA under Partition Table options. Change the partition table offset to 0xb000 from 0x8000 since after enabling secure boot the size of bootloader is increased.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
- The below steps can be used in any application to enable secure boot.
|
||||
|
||||
- Secure Boot is an irreversible operation, please read the Secure Boot section in ESP-IDF Programming Manual.
|
||||
|
||||
- Secure boot will be enabled on building the project after enabling hardware secure boot feature in _Security features_ in menuconfig and flashing it to the board FOR THE FIRST TIME.
|
||||
|
||||
- The bootloader will not be automatically flashed when secure boot is enabled. Running `idf.py bootloader` will print a command to flash the bootloader on the ESP32. Run this command manually to flash the bootloader.
|
||||
|
||||
Run following command to program the partition table and application on the ESP32 and monitor the output:
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
- On subsequent boots, the secure boot hardware will verify the software bootloader has not changed and the software bootloader will verify the signed app image (using the validated public key portion of its appended signature block).
|
||||
|
||||
## Example Output
|
||||
|
||||
## Troubleshooting
|
||||
|
4
tools/test_apps/security/secure_boot/main/CMakeLists.txt
Normal file
4
tools/test_apps/security/secure_boot/main/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
set(COMPONENT_SRCS "secure_boot_main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS "")
|
||||
|
||||
register_component()
|
5
tools/test_apps/security/secure_boot/main/component.mk
Normal file
5
tools/test_apps/security/secure_boot/main/component.mk
Normal file
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
96
tools/test_apps/security/secure_boot/main/secure_boot_main.c
Normal file
96
tools/test_apps/security/secure_boot/main/secure_boot_main.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/* Flash encryption Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "soc/efuse_reg.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_spi_flash.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_efuse_table.h"
|
||||
#include <string.h>
|
||||
|
||||
static void example_print_chip_info(void);
|
||||
static void example_secure_boot_status(void);
|
||||
|
||||
#define TAG "example_secure_boot"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("\nExample to check Secure Boot status\n");
|
||||
|
||||
example_print_chip_info();
|
||||
example_secure_boot_status();
|
||||
}
|
||||
|
||||
|
||||
static void example_print_chip_info(void)
|
||||
{
|
||||
/* Print chip information */
|
||||
esp_chip_info_t chip_info;
|
||||
esp_chip_info(&chip_info);
|
||||
printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ",
|
||||
chip_info.cores,
|
||||
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
|
||||
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
|
||||
|
||||
printf("silicon revision %d, ", chip_info.revision);
|
||||
|
||||
printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
|
||||
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
|
||||
}
|
||||
|
||||
#define DIGEST_LEN 32
|
||||
|
||||
static void example_secure_boot_status(void)
|
||||
{
|
||||
uint32_t efuse_block0 = REG_READ(EFUSE_BLK0_RDATA6_REG);
|
||||
|
||||
#ifdef CONFIG_ESP32_REV_MIN_3
|
||||
uint8_t efuse_trusted_digest[DIGEST_LEN] = {0}, i;
|
||||
ESP_LOGI(TAG, "Checking for secure boot v2..");
|
||||
if(efuse_block0 & EFUSE_RD_ABS_DONE_1) {
|
||||
ESP_LOGI(TAG, "ABS_DONE_1 is set. Secure Boot V2 enabled");
|
||||
memcpy(efuse_trusted_digest, (uint8_t *)EFUSE_BLK2_RDATA0_REG, DIGEST_LEN);
|
||||
ESP_LOGI(TAG, "Reading the public key digest from BLK2.");
|
||||
for (i = 0; i < DIGEST_LEN; i++) {
|
||||
ESP_LOGI(TAG, "%02x \t", efuse_trusted_digest[i]);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Secure boot v2 not enabled. Enable Secure Boot V2 in menuconfig, build & flash again.");
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_LOGI(TAG, "Checking for secure boot v1..");
|
||||
uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG);
|
||||
if (efuse_block0 & EFUSE_RD_ABS_DONE_0) {
|
||||
ESP_LOGI(TAG, "ABS_DONE_0 is set. Secure Boot V1 enabled");
|
||||
#ifdef CONFIG_ESP32_REV_MIN_3
|
||||
ESP_LOGW(TAG, "This chip version supports Secure Boot V2. It is recommended to use Secure Boot V2.");
|
||||
#endif
|
||||
bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2;
|
||||
bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2;
|
||||
|
||||
ESP_LOGI(TAG, "Checking the integrityof the key in BLK2..");
|
||||
if (!efuse_key_read_protected) {
|
||||
ESP_LOGE(TAG, "Key is not read protected. Refusing to blow secure boot efuse.");
|
||||
return;
|
||||
}
|
||||
if (!efuse_key_write_protected) {
|
||||
ESP_LOGE(TAG, "Key is not write protected. Refusing to blow secure boot efuse.");
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Key is read/write protected in eFuse.");
|
||||
return;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Secure Boot V1 not enabled. Enable Secure Boot in menuconfig, build & flash again.");
|
||||
}
|
||||
}
|
6
tools/test_apps/security/secure_boot/sdkconfig.ci.00
Normal file
6
tools/test_apps/security/secure_boot/sdkconfig.ci.00
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Secure Boot V2 - ESP-ECO3+
|
||||
CONFIG_SECURE_BOOT=y
|
||||
CONFIG_ESP32_REV_MIN_3=y
|
||||
CONFIG_SECURE_BOOT_V2_ENABLED=y
|
||||
CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y
|
||||
CONFIG_SECURE_BOOT_SIGNING_KEY="test_rsa_3072_key.pem"
|
4
tools/test_apps/security/secure_boot/sdkconfig.ci.01
Normal file
4
tools/test_apps/security/secure_boot/sdkconfig.ci.01
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Secure Boot V1
|
||||
CONFIG_SECURE_BOOT=y
|
||||
CONFIG_SECURE_BOOT_V1_ENABLED=y
|
||||
CONFIG_SECURE_BOOT_SIGNING_KEY="test_ecdsa_key.pem"
|
4
tools/test_apps/security/secure_boot/sdkconfig.ci.02
Normal file
4
tools/test_apps/security/secure_boot/sdkconfig.ci.02
Normal file
|
@ -0,0 +1,4 @@
|
|||
# ECDSA App signing without secure boot
|
||||
CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT=y
|
||||
CONFIG_SECURE_SIGNED_ON_BOOT_NO_SECURE_BOOT=y
|
||||
CONFIG_SECURE_BOOT_SIGNING_KEY="test_ecdsa_key.pem"
|
5
tools/test_apps/security/secure_boot/test_ecdsa_key.pem
Normal file
5
tools/test_apps/security/secure_boot/test_ecdsa_key.pem
Normal file
|
@ -0,0 +1,5 @@
|
|||
-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEIJLB2fFAa7istjO0AWfEbvJM8Kn0T+R38GXalwX3oP6GoAoGCCqGSM49
|
||||
AwEHoUQDQgAEy2N3ohJ1hIjU2AHNyVKGafSrmGhizG1/xOTOtASbJpiVI3ccUVXI
|
||||
zrDSnrTwg331qOAT7WWkY1p4ixZvP6HWzA==
|
||||
-----END EC PRIVATE KEY-----
|
39
tools/test_apps/security/secure_boot/test_rsa_3072_key.pem
Normal file
39
tools/test_apps/security/secure_boot/test_rsa_3072_key.pem
Normal file
|
@ -0,0 +1,39 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIG4gIBAAKCAYEA0rvS0tvQ4dF/mW62K2QAt13Wnlsd5NDRtZmCB94XaPWzV2D1
|
||||
JDZw2M0PH+ylXvVsVN4Q4gjULwxHFO1ebnR3Ws61Se/x3KM4MoC0EjCvV21VfjFB
|
||||
2sB/bark+2yHd5ChvikCfXWrIRYFkBTQLnZOJB7nVXY4XzHoZ+d9MLGfVcZoufDa
|
||||
Uaj+3WmrgBzYifZ2dWHfAm1KQ+cVhFHiPEw75bX67ZcJyXb95vBRzylubOqWquWp
|
||||
AML/ygKz/JP6SKCOihUUyIrC/lz9H3eduWnzaqsx6E/8Nv3vVv6A1y5UbzPLCP9a
|
||||
j2G2I2DoqB4QlQic4GKjBwgMfQQPSka0h5pNYAyyiJKncnvgIweQKH7UY3cRLPWi
|
||||
B37Q/E3J+bHdZzGHXReiyiSpmLlvRbohgPRnlsawjySqXCFrHicRF7wElnrLrzcc
|
||||
iFWatWUK2Ky6ODWD/l/hIEtvjbZZaeXa0UTWwfeXDQscES8z+BODRshy2iVhpnGh
|
||||
/+wa7NabzIOaX8b9AgMBAAECggGAIbRsydDinduWHwI4HMSH4MwfcYB9TYWgpP0C
|
||||
cSOydtUldApL6xjR/7r8ekytPnzecMx7wstKtKOwEsbMXbo/BMUe9c5szq9EY792
|
||||
DZq/0KnqDJ7wO36iYvX8XcJEAHmfhPymK07QqiANj7fkfCnr4ZcXxPF9nqwq0cOS
|
||||
oGEobN2pDmRZZsoyyVMHXRw//gDvfWLD+m1kfWvOYzV9i8tdoSX0FmAb7p8biP5S
|
||||
92FXChCTPI4y8648nyDDjNhWJZuYJmTFWnovFNK8MI6pz9y3db58iksBATsVpbRr
|
||||
HZ9bSMOyUMwV0Cg3DSgBOHcEj1/cWY9LNtYd1B4d049JjocRe7IT6KzGDpegdfhK
|
||||
razu5/UQKriZAQJt1m6UNSbaQ2x2tFo6JS/BQVEQyFc1M27OTlU7AXn3LcU0AUpz
|
||||
NHoW7047SbfPf/KYLbUeaXlKl1BzEs1OE2hq1kok1uf5JK+wZI7nZ9lUCVIaIUcu
|
||||
Y7XLouN8BzkJAM6oBi0x5+FAI+W5AoHBAO9KAEyzYKnjhbmWb8D4FUPcbRbVJ6ie
|
||||
E1WO/6JhmXON/jEXkOE32LPdd1xEX+q70zZ6kDrZvlDrPJqiJyOad+nSsRRR5c01
|
||||
UGHWqGmjQ0JU4B/GczuyBMhaLjJbLhco/laQb6mL/ipFpDtljJK8500NP+kfoCrW
|
||||
9OUw2ryGAaw45HmSvPRAs1Yv/AF9k/4TGL/jT3Mz/wUSmsYyXVgWQVjUN+5nXCxD
|
||||
tGXew/M6DvO3uDBe+33ZrmoThiG7aGUIMwKBwQDhc0+B9VZWlE6IDPwvFxSN7BG7
|
||||
KyGg+4JORKWk45Fpk0LkJ+hAdJ4+pFr2XVpYbKRPmYK/pNZlo3LC3FsJCJOyakZb
|
||||
X7Tpkc6Uepj1VH6YXdRAA5IC3OVi4dAl1Dt+LLoL7V9lUxpI7lgrfy+067L2QizV
|
||||
YFD58H5eH0eG/nB9mKN2PO/42RCXGpSpw9V4b6JoupTh7jU3xrDKXfXQG1xm3A7M
|
||||
j6DDgZDHqPWSdwJWsnaL+oPhYM0Cai7945jehA8CgcAtrxMfkZ+Cz81YAUCUtshY
|
||||
jFzHXyqTJprKWuKzPa7uQM2m0bj3RpI4xK9lDijBx9orLHscwTV0fXS0kQCn63W5
|
||||
TmBAYOJeqy8Nfs3oXSMaJtojNuAJJZOELLNlKcNC6LCmFi07UV7U8zbHoDuWSDpg
|
||||
m4b4GvGZPDDFEO6xz2PCXZpBG6K3fyK98atLHY6Dk2HGQL+KXwLxFPw3mqX3i0gu
|
||||
jVWgTltqmAJ48G64oPz5yrl/gqLBBC4oUlHpXr4vi8ECgcBq/gxXgpUM1alHS9JK
|
||||
jsuEZuorR1bYTUQT3OQ3koAp+GcgXAgOvslytREuJjOAD18TH6k7RgExjxYhf+38
|
||||
JYPigikNqCf1SOse7+ezVfwWV0EpeAhNL4P1H3Fm4oexY4yPqIFDVuL8hZB2ZA/B
|
||||
7rGpyNH6GZGUbBusk2+gkxPTpyK8NEM2d901uLmgr32ZgHE0/oc1iZTb+YFhKKJF
|
||||
txZtAjZLwkXrQovxFTAl6DDF8D/uQl9gEE56vOW8O80KnOUCgcAsolCIngfomSTJ
|
||||
ZIUomwn7bkO3Npaaxeew01LJT9/0zKkiazrqtNV6lfFBgE4/dbpufccCz16OQ/65
|
||||
cnCjggxDxPTIoO184AUDxpbM+4XmOmn/dLt6NdK/2jVEdWFN3fdApaDO0OExcU/L
|
||||
HZ2lfR38+aJrbPp3rZwUFjHcmSZu999XdmhCWLo2qBND0zgb8//bFTWb1coL6ucM
|
||||
O7EQiOmmlG6QW24JCmbjBuNY3oVs0aDTxeYJ8yGM6WJOUwNtGZM=
|
||||
-----END RSA PRIVATE KEY-----
|
Loading…
Reference in a new issue