diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 394bcc1bd1..d6736b33db 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -20,12 +20,65 @@ config LOG_BOOTLOADER_LEVEL_VERBOSE endchoice config LOG_BOOTLOADER_LEVEL - int - default 0 if LOG_BOOTLOADER_LEVEL_NONE - default 1 if LOG_BOOTLOADER_LEVEL_ERROR - default 2 if LOG_BOOTLOADER_LEVEL_WARN - default 3 if LOG_BOOTLOADER_LEVEL_INFO - default 4 if LOG_BOOTLOADER_LEVEL_DEBUG - default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE + int + default 0 if LOG_BOOTLOADER_LEVEL_NONE + default 1 if LOG_BOOTLOADER_LEVEL_ERROR + default 2 if LOG_BOOTLOADER_LEVEL_WARN + default 3 if LOG_BOOTLOADER_LEVEL_INFO + default 4 if LOG_BOOTLOADER_LEVEL_DEBUG + default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE + +choice SECURE_BOOTLOADER + bool "Secure bootloader" + default SECURE_BOOTLOADER_DISABLED + help + Build a bootloader with the secure boot flag enabled. + + Secure bootloader can be one-time-flash (chip will only ever + boot that particular bootloader), or a digest key can be used + to allow the secure bootloader to be re-flashed with + modifications. Secure boot also permanently disables JTAG. + + See docs/security/secure-boot.rst for details. + +config SECURE_BOOTLOADER_DISABLED + bool "Disabled" + +config SECURE_BOOTLOADER_ONE_TIME_FLASH + bool "One-time flash" + help + On first boot, the bootloader will generate a key which is not readable externally or by software. A digest is generated from the bootloader image itself. This digest will be verified on each subsequent boot. + + Enabling this option means that the bootloader cannot be changed after the first time it is booted. + +config SECURE_BOOTLOADER_REFLASHABLE + bool "Reflashable" + help + Generate the bootloader digest key on the computer instead of inside + the chip. Allows the secure bootloader to be re-flashed by using the + same key. + + This option is less secure than one-time flash, because a leak of the digest key allows reflashing of any device that uses it. + +endchoice + +config SECURE_BOOTLOADER_KEY_FILE + string "Secure bootloader digest key file" + depends on SECURE_BOOTLOADER_REFLASHABLE + default secure_boot_key.bin + help + Path to the key file for a reflashable secure boot digest. + File must contain 32 randomly generated bytes. + + Path is evaluated relative to the project directory. + + You can generate a new key by running the following command: + espsecure.py generate_key secure_boot_key.bin + + See docs/security/secure-boot.rst for details. + +config SECURE_BOOTLOADER_ENABLED + bool + default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE endmenu diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 50c95f9fec..8edabdb6ef 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -15,6 +15,8 @@ BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig +SECURE_BOOT_KEYFILE=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) + # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ @@ -31,18 +33,67 @@ bootloader-clean: clean: bootloader-clean +ifdef CONFIG_SECURE_BOOTLOADER_DISABLED +# If secure boot disabled, bootloader flashing is integrated +# with 'make flash' and no warnings are printed. + bootloader: $(BOOTLOADER_BIN) + @echo "$(SEPARATOR)" @echo "Bootloader built. Default flash command is:" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" -all_binaries: $(BOOTLOADER_BIN) - ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN) -# bootloader-flash calls flash in the bootloader dummy project bootloader-flash: $(BOOTLOADER_BIN) $(BOOTLOADER_MAKE) flash +else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH +# One time flashing requires user to run esptool.py command themselves, +# and warning is printed about inability to reflash. + +bootloader: $(BOOTLOADER_BIN) + @echo $(SEPARATOR) + @echo "Bootloader built. One-time flash command is:" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" + @echo $(SEPARATOR) + @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" + +else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE +# Reflashable secure bootloader (recommended for testing only) +# generates a digest binary (bootloader + digest) and prints +# instructions for reflashing. User must run commands manually. + +BOOTLOADER_DIGEST_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin + +bootloader: $(BOOTLOADER_DIGEST_BIN) + @echo $(SEPARATOR) + @echo "Bootloader built and secure digest generated. First time flash command is:" + @echo "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOT_KEYFILE)" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" + @echo $(SEPARATOR) + @echo "To reflash the bootloader after initial flash:" + @echo "$(ESPTOOLPY_WRITE_FLASH) 0x0 $(BOOTLOADER_DIGEST_BIN)" + @echo $(SEPARATOR) + @echo "* After first boot, only re-flashes of this kind (with same key) will be accepted." + @echo "* NOT RECOMMENDED TO RE-FLASH the same bootloader-reflash-digest.bin to multiple production devices" + +$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOT_KEYFILE) + @echo "DIGEST $< + $(SECURE_BOOT_KEYFILE) -> $@" + $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOT_KEYFILE) -o $@ $< + +$(SECURE_BOOT_KEYFILE): + @echo $(SEPARATOR) + @echo "Need to generate secure boot digest key first. Run following command:" + @echo "$(ESPSECUREPY) generate_key $@" + @echo "Keep key file safe after generating." + @exit 1 + +else +$(error Bad sdkconfig - one of CONFIG_SECURE_BOOTLOADER_DISABLED, CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH, CONFIG_SECURE_BOOTLOADER_REFLASHABLE must be set) +endif + +all_binaries: $(BOOTLOADER_BIN) + # synchronise the project level config to the bootloader's # config $(BOOTLOADER_SDKCONFIG): $(PROJECT_PATH)/sdkconfig | $(BOOTLOADER_BUILD_DIR) diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader/src/main/secure_boot.c index 9247f2cbd9..070fbf24d3 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader/src/main/secure_boot.c @@ -148,7 +148,9 @@ bool secure_boot_generate_bootloader_digest(void) { /* reuse the secure boot IV generation function to generate the key, as this generator uses the hardware RNG. */ uint32_t buf[32]; + ets_secure_boot_start(); ets_secure_boot_rd_iv(buf); + ets_secure_boot_finish(); for (int i = 0; i < 8; i++) { ESP_LOGV(TAG, "EFUSE_BLK2_WDATA%d_REG = 0x%08x", i, buf[i]); REG_WRITE(EFUSE_BLK2_WDATA0_REG + 4*i, buf[i]); diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 69c01e1e7f..dfb9dfc4c2 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -11,23 +11,34 @@ PYTHON ?= $(call dequote,$(CONFIG_PYTHON)) # two commands that can be used from other components # to invoke esptool.py (with or without serial port args) # -# NB: esptool.py lives in the sdk/bin directory not the component directory ESPTOOLPY_SRC := $(COMPONENT_PATH)/esptool/esptool.py ESPTOOLPY := $(PYTHON) $(ESPTOOLPY_SRC) --chip esp32 ESPTOOLPY_SERIAL := $(ESPTOOLPY) --port $(ESPPORT) --baud $(ESPBAUD) +# Supporting esptool command line tools +ESPEFUSEPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espefuse.py +ESPSECUREPY := $(PYTHON) $(COMPONENT_PATH)/esptool/espsecure.py + ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) -# the no-stub argument is temporary until esptool.py fully supports compressed uploads +ESPTOOL_ELF2IMAGE_OPTIONS := + +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ESPTOOL_ELF2IMAGE_OPTIONS += "--set-secure-boot-flag" +endif + ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z) $(ESPTOOL_FLASH_OPTIONS) ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) - $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) -o $@ $< + $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" +endif $(Q) $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 5c6962e894..95dae1651e 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 5c6962e894e0a118c9a4b5760876433493449260 +Subproject commit 95dae1651e5aea1adb2b6018b23f65a305f67387 diff --git a/make/project.mk b/make/project.mk index 67e2e92bdb..17fb83e0d8 100644 --- a/make/project.mk +++ b/make/project.mk @@ -11,13 +11,13 @@ # .PHONY: build-components menuconfig defconfig all build clean all_binaries -all: all_binaries # other components will add dependencies to 'all_binaries' - @echo "To flash all build output, run 'make flash' or:" - @echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) - -# (the reason all_binaries is used instead of 'all' is so that the flash target -# can build everything without triggering the per-component "to flash..." -# output targets.) +all: all_binaries +# see below for recipe of 'all' target +# +# # other components will add dependencies to 'all_binaries'. The +# reason all_binaries is used instead of 'all' is so that the flash +# target can build everything without triggering the per-component "to +# flash..." output targets.) help: @echo "Welcome to Espressif IDF build system. Some useful make targets:" @@ -135,6 +135,15 @@ export PROJECT_PATH #Include functionality common to both project & component -include $(IDF_PATH)/make/common.mk +all: +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + @echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)" + @echo "To flash app & partition table, run 'make flash' or:" +else + @echo "To flash all build output, run 'make flash' or:" +endif + @echo $(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS) + # Set default LDFLAGS LDFLAGS ?= -nostdlib \