From f10cc7dc8eb09bcdd3075b7f8ef9303e3dc3da28 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 25 Oct 2016 14:55:35 +1100 Subject: [PATCH 01/19] bootloader: Refactor secure boot digest generation --- .../bootloader/src/main/bootloader_config.h | 4 +- .../bootloader/src/main/bootloader_start.c | 15 +- components/bootloader/src/main/secure_boot.c | 135 +++++++++++++----- 3 files changed, 114 insertions(+), 40 deletions(-) diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 8a837693c2..1655280dc7 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -36,7 +36,6 @@ extern "C" #define RTC_DATA_LOW 0x50000000 #define RTC_DATA_HIGH 0x50002000 - #define PART_TYPE_APP 0x00 #define PART_SUBTYPE_FACTORY 0x00 #define PART_SUBTYPE_OTA_FLAG 0x10 @@ -66,8 +65,7 @@ void boot_cache_redirect( uint32_t pos, size_t size ); uint32_t get_bin_len(uint32_t pos); bool flash_encrypt(bootloader_state_t *bs); -bool secure_boot(void); - +bool secure_boot_generate_bootloader_digest(void); #ifdef __cplusplus } diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 5b1e152070..a4b27702c4 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -256,6 +256,7 @@ void bootloader_main() bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; esp_ota_select_entry_t sa,sb; + memset(&bs, 0, sizeof(bs)); ESP_LOGI(TAG, "compile time " __TIME__ ); @@ -329,16 +330,20 @@ void bootloader_main() } ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); + if(fhdr.secure_boot_flag == 0x01) { - /* protect the 2nd_boot */ - if(false == secure_boot()){ - ESP_LOGE(TAG, "secure boot failed"); - return; + /* Generate secure digest from this bootloader to protect future + modifications */ + if (secure_boot_generate_bootloader_digest() == false){ + ESP_LOGE(TAG, "Bootloader digest generation failed. SECURE BOOT IS NOT ENABLED."); + /* Allow booting to continue, as the failure is probably + due to user-configured EFUSEs for testing... + */ } } if(fhdr.encrypt_flag == 0x01) { - /* encrypt flash */ + /* encrypt flash */ if (false == flash_encrypt(&bs)) { ESP_LOGE(TAG, "flash encrypt failed"); return; diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader/src/main/secure_boot.c index 3dfdbd1412..9247f2cbd9 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader/src/main/secure_boot.c @@ -40,7 +40,7 @@ static const char* TAG = "secure_boot"; * * @inputs: bool */ -bool secure_boot_generate(uint32_t bin_len){ +static bool secure_boot_generate(uint32_t bin_len){ SpiFlashOpResult spiRet; uint16_t i; uint32_t buf[32]; @@ -87,41 +87,112 @@ bool secure_boot_generate(uint32_t bin_len){ return true; } +/* Burn values written to the efuse write registers */ +static inline void burn_efuses() +{ + REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ + REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ + REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ + REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ +} /** - * @function : secure_boot - * @description: protect boot code in flash + * @function : secure_boot_generate_bootloader_digest * - * @inputs: bool + * @description: Called if the secure boot flag is set on the + * bootloader image in flash. If secure boot is not yet enabled for + * bootloader, this will generate the secure boot digest and enable + * secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse. + * + * This function does not verify secure boot of the bootloader (the + * ROM bootloader does this.) + * + * @return true if secure boot is enabled (either was already enabled, + * or is freshly enabled as a result of calling this function.) */ -bool secure_boot(void){ - uint32_t bin_len = 0; - if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) - { - ESP_LOGD(TAG, "already secure boot !"); - return true; - } else { - boot_cache_redirect( 0, 64*1024); - bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); - if (bin_len == 0) { - ESP_LOGE(TAG, "boot len is error"); - return false; - } - if (false == secure_boot_generate(bin_len)){ - ESP_LOGE(TAG, "secure boot generate failed"); - return false; - } - } +bool secure_boot_generate_bootloader_digest(void) { + uint32_t bin_len = 0; + if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) + { + ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); + return true; + } - REG_SET_BIT(EFUSE_BLK0_WDATA6_REG, EFUSE_RD_ABS_DONE_0); - REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ - REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ - ESP_LOGW(TAG, "burn abstract_done_0"); - REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ - REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ - ESP_LOGI(TAG, "read EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); - return true; + boot_cache_redirect( 0, 64*1024); + bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); + if (bin_len == 0) { + ESP_LOGE(TAG, "Invalid bootloader image length zero."); + return false; + } + if (bin_len > 0x100000) { + ESP_LOGE(TAG, "Invalid bootloader image length %x", bin_len); + return false; + } + 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_read_protected == false + && efuse_key_write_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) { + ESP_LOGI(TAG, "Generating new secure boot key..."); + /* 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_rd_iv(buf); + 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]); + } + bzero(buf, sizeof(buf)); + burn_efuses(); + ESP_LOGI(TAG, "Read & write protecting new key..."); + REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2); + burn_efuses(); + efuse_key_read_protected = true; + efuse_key_write_protected = true; + + } else { + ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2"); + } + + ESP_LOGI(TAG, "Generating secure boot digest..."); + if (false == secure_boot_generate(bin_len)){ + ESP_LOGE(TAG, "secure boot generation failed"); + return false; + } + ESP_LOGI(TAG, "Digest generation complete."); + + if (!efuse_key_read_protected) { + ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse."); + return false; + } + if (!efuse_key_write_protected) { + ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse."); + return false; + } + + ESP_LOGI(TAG, "blowing secure boot efuse & disabling JTAG..."); + ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); + REG_WRITE(EFUSE_BLK0_WDATA6_REG, + EFUSE_RD_ABS_DONE_0 | EFUSE_RD_DISABLE_JTAG); + 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_0) { + ESP_LOGI(TAG, "secure boot is now enabled for bootloader image"); + return true; + } else { + ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!"); + return false; + } } From cf732bb663a4c8690107e354d958042ef3ea5bfb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 31 Oct 2016 16:27:26 +1100 Subject: [PATCH 02/19] esp32: efuse_reg add bit values for read & write disable flags --- components/esp32/include/soc/efuse_reg.h | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/components/esp32/include/soc/efuse_reg.h b/components/esp32/include/soc/efuse_reg.h index a0f0a07da6..291e3984ee 100644 --- a/components/esp32/include/soc/efuse_reg.h +++ b/components/esp32/include/soc/efuse_reg.h @@ -29,6 +29,16 @@ #define EFUSE_RD_EFUSE_RD_DIS_M ((EFUSE_RD_EFUSE_RD_DIS_V)<<(EFUSE_RD_EFUSE_RD_DIS_S)) #define EFUSE_RD_EFUSE_RD_DIS_V 0xF #define EFUSE_RD_EFUSE_RD_DIS_S 16 + +/* Read disable bits for efuse blocks 1-3 */ +#define EFUSE_RD_DIS_BLK1 (1<<16) +#define EFUSE_RD_DIS_BLK2 (1<<17) +#define EFUSE_RD_DIS_BLK3 (1<<18) +/* Read disable FLASH_CRYPT_CONFIG, CODING_SCHEME & KEY_STATUS + in efuse block 0 +*/ +#define EFUSE_RD_DIS_BLK0_PARTIAL (1<<19) + /* EFUSE_RD_EFUSE_WR_DIS : RO ;bitpos:[15:0] ;default: 16'b0 ; */ /*description: read for efuse_wr_disable*/ #define EFUSE_RD_EFUSE_WR_DIS 0x0000FFFF @@ -36,6 +46,22 @@ #define EFUSE_RD_EFUSE_WR_DIS_V 0xFFFF #define EFUSE_RD_EFUSE_WR_DIS_S 0 +/* Write disable bits */ +#define EFUSE_WR_DIS_RD_DIS (1<<0) /*< disable writing read disable reg */ +#define EFUSE_WR_DIS_WR_DIS (1<<1) /*< disable writing write disable reg */ +#define EFUSE_WR_DIS_FLASH_CRYPT_CNT (1<<2) +#define EFUSE_WR_DIS_MAC_SPI_CONFIG_HD (1<<3) /*< disable writing MAC & SPI config hd efuses */ +#define EFUSE_WR_DIS_XPD_SDIO (1<<5) /*< disable writing SDIO config efuses */ +#define EFUSE_WR_DIS_SPI_PAD_CONFIG (1<<6) /*< disable writing SPI_PAD_CONFIG efuses */ +#define EFUSE_WR_DIS_BLK1 (1<<7) /*< disable writing BLK1 efuses */ +#define EFUSE_WR_DIS_BLK2 (1<<8) /*< disable writing BLK2 efuses */ +#define EFUSE_WR_DIS_BLK3 (1<<9) /*< disable writing BLK3 efuses */ +#define EFUSE_WR_DIS_FLASH_CRYPT_CODING_SCHEME (1<<10) /*< disable writing FLASH_CRYPT_CONFIG and CODING_SCHEME efuses */ +#define EFUSE_WR_DIS_ABS_DONE_0 (1<<12) /*< disable writing ABS_DONE_0 efuse */ +#define EFUSE_WR_DIS_ABS_DONE_1 (1<<13) /*< disable writing ABS_DONE_1 efuse */ +#define EFUSE_WR_DIS_JTAG_DISABLE (1<<14) /*< disable writing JTAG_DISABLE efuse */ +#define EFUSE_WR_DIS_CONSOLE_DL_DISABLE (1<<15) /*< disable writing CONSOLE_DEBUG_DISABLE, DISABLE_DL_ENCRYPT, DISABLE_DL_DECRYPT and DISABLE_DL_CACHE efuses */ + #define EFUSE_BLK0_RDATA1_REG (DR_REG_EFUSE_BASE + 0x004) /* EFUSE_RD_WIFI_MAC_CRC_LOW : RO ;bitpos:[31:0] ;default: 32'b0 ; */ /*description: read for low 32bit WIFI_MAC_Address*/ From 4ba1b73eba78893d2117a94a6e65c6ad63e6705a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 1 Nov 2016 10:50:16 +1100 Subject: [PATCH 03/19] build system: Add espefuse/espsecure support for secure boot --- components/bootloader/Kconfig.projbuild | 67 ++++++++++++++++++-- components/bootloader/Makefile.projbuild | 57 ++++++++++++++++- components/bootloader/src/main/secure_boot.c | 2 + components/esptool_py/Makefile.projbuild | 17 ++++- components/esptool_py/esptool | 2 +- make/project.mk | 23 +++++-- 6 files changed, 147 insertions(+), 21 deletions(-) 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 \ From 04beb8baba3c087622558851285ccd038f8b59d0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Tue, 1 Nov 2016 17:41:27 +1100 Subject: [PATCH 04/19] Add documentation for bootloader secure boot stage --- components/bootloader/Kconfig.projbuild | 4 +- components/bootloader/Makefile.projbuild | 2 +- docs/security/secure-boot.rst | 146 +++++++++++++++++++++++ 3 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 docs/security/secure-boot.rst diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index d6736b33db..55c2eebd14 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -63,11 +63,11 @@ config SECURE_BOOTLOADER_REFLASHABLE endchoice config SECURE_BOOTLOADER_KEY_FILE - string "Secure bootloader digest key file" + string "Secure bootloader key file" depends on SECURE_BOOTLOADER_REFLASHABLE default secure_boot_key.bin help - Path to the key file for a reflashable secure boot digest. + Path to the key file for a reflashable secure bootloader digest. File must contain 32 randomly generated bytes. Path is evaluated relative to the project directory. diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 8edabdb6ef..b9dab3e093 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -75,7 +75,7 @@ bootloader: $(BOOTLOADER_DIGEST_BIN) @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" + @echo "* Not recommended to re-use the same secure boot keyfile on multiple production devices." $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOT_KEYFILE) @echo "DIGEST $< + $(SECURE_BOOT_KEYFILE) -> $@" diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst new file mode 100644 index 0000000000..5e33367b49 --- /dev/null +++ b/docs/security/secure-boot.rst @@ -0,0 +1,146 @@ +Secure Boot +=========== + +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 Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However for maximum protection we recommend using both features together. + +Background +---------- + +- Most data is stored in flash. Flash access does not need to protected for secure boot to function, because critical data is stored in Efuses internal to the chip. + +- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. + +- To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. + +Secure Boot Process Overview +---------------------------- + +This is a high level overview of the secure boot process. Step by step instructions are supplied under `How To Enable Secure Boot`. Further in-depth details are supplied under `Technical Details`: + +1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Bootloader Config". + +2. Bootloader Config includes the path to a ECDSA public key. This "image public key" is compiled into the software bootloader. + +2. The software bootloader image is built by esp-idf with the public key embedded, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. + +3. On first boot, the software bootloader tests the secure boot flag. If it is set, the following process is followed to enable secure boot: + - Hardware secure boot support generates a device secure bootloader key (stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. + - The secure digest is flashed at offset 0x0 in the flash. + - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot this bootloader image.) + - Bootloader also disables JTAG via efuse. + +4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. + +5. When running in secure boot mode, the software bootloader uses the image public key (embedded in the bootloader itself) to verify all subsequent partition tables and app images before they are booted. + +Keys +---- + +The following keys are used by the secure boot process: + +- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from hardware, the user does not need to supply it. The Efuse holding this key must be read & write protected (preventing software access) before secure boot is enabled. + +- "image public key" is a ECDSA public key (curve TBD) in format TBD. This public key is flashed into the software bootloader and used to verify the remaining parts of the flash (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. + +- "image private key" is a ECDSA private key, a matching pair with "image public key". This private key is used to sign partition tables and app images for the secure boot process. **This private key must be securely kept private** as anyone who has this key can authenticate to the secure boot process. It is acceptable to use the same private key across multiple production devices. + +How To Enable Secure Boot +------------------------- + +1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (For the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) + +2. Select names for public & private image key files "Image Public Key File" and "Image Private Key File". These options will appear after secure boot is enabled. The files can be anywhere on your system. Relative paths are evaluated from the project directory. The files don't need to exist yet. + +3. Set other config 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 + +4. Run ``make generate_image_keypair`` to generate an image public key and a matching image private key. + + **IMPORTANT** The key pair is generated using the best random number source available via 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. + +5. Run ``make bootloader`` to build a secure boot enabled bootloader. The output of `make` will include a prompt for a flashing command, using `esptool.py write_flash`. + +6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not automated) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**. + +7. Run `make flash` to build and flash the partition table and the just-built app image. The app image will be signed using the private key you generated in step 4. + + *NOTE*: `make flash` doesn't flash the bootloader if secure boot is enabled. + +8. 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 occured due to the build configuration. + +**IMPORTANT** Secure boot won't ever 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. + +9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the image public key). + +Re-Flashable Software Bootloader +-------------------------------- + +The "Secure Boot: One-Time Flash" is the recommended software bootloader configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. + +However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. + +*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. + +1. In the ``make menuconfig`` step, select "Bootloader Config" -> "Secure Boot" -> "Reflashable". + +2. Select a name for the "Secure bootloader key file". The file can be anywhere on your system, and does not have to exist yet. A path is evaluated relative to the project directory. The file doesn't have to exist yet. + +3. The first time you run ``make bootloader``, the system will prompt you with a ``espsecure.py generate_key`` command that can be used to generate the secure bootloader key. + + **IMPORTANT** The new key is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the secure bootloader key will be weak. + +4. Run ``make bootloader`` again. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the secure bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the secure bootloader key file). + +5. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. + + +Technical Details +----------------- + +The following sections contain low-level descriptions of various technical functions: + +Hardware Secure Boot Support +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Secure Boot support hardware can perform three basic operations: + +1. Generate a random sequence of bytes from a hardware random number generator. + +2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read from hardware if Efuse ABS_DONE_0 is *not* burned (ie still 0), to prevent new digests from being calculated on the device after secure boot is enabled. + +3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. + +Secure Bootloader Digest Algorithm +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Starting with an "image" of binary data as input, this algorithm generates a digest as output. + +For a Python version of this algorithm, see the `espsecure.py` tool in the components/esptool_py directory. + +Items marked with (^) are to fulfill hardware restrictions, as opposed to cryptographic restrictions. + +1. Prefix the image with a 128 byte randomly generated IV. +2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) +3. For each 16 byte block of the input image: + - Reverse the byte order of the block (^) + - Use the AES256 algorithm in ECB mode to encrypt the block. + - Reverse the byte order of the 16 bytes of ciphertext output. (^) + - Append to the overall ciphertext output. +4. Byte-swap each 4 byte word of the ciphertext (^) +5. Calculate SHA-512 of the ciphertext. + +Output digest is 192 bytes of data: The 128 byte IV, followed by the 64 byte SHA-512 digest. + +Image Signing Algorithm +~~~~~~~~~~~~~~~~~~~~~~~ + +Deterministic ECDSA as specified by `RFC6979`. + +Curve is TBD. +Key format is TBD. +Output format is TBD. + + +.. _esp-idf boot process: ../boot-process.rst +.. _RFC6979: https://tools.ietf.org/html/rfc6979 From aceb6517c05e807c7f9ad27d31e9b8aa12568172 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 2 Nov 2016 10:41:58 +1100 Subject: [PATCH 05/19] Refactor existing bootloader common functionality into bootloader_support component --- components/bootloader/Makefile.projbuild | 6 +- components/bootloader/src/Makefile | 2 +- .../bootloader/src/main/bootloader_config.h | 5 - .../bootloader/src/main/bootloader_start.c | 276 ++++++++---------- .../bootloader/src/main/flash_encrypt.c | 158 +++++----- components/bootloader/src/main/secure_boot.c | 72 +++-- components/bootloader_support/README.rst | 9 + components/bootloader_support/component.mk | 11 + .../include/esp_image_format.h | 133 +++++++++ .../include/esp_secureboot.h | 51 ++++ .../include_priv/bootloader_flash.h | 69 +++++ .../bootloader_support/src/bootloader_flash.c | 122 ++++++++ .../bootloader_support/src/esp_image_format.c | 155 ++++++++++ .../bootloader_support/src/secureboot.c | 7 + .../esp32/include/esp_flash_data_types.h | 48 --- components/esp32/include/rom/secure_boot.h | 2 +- components/log/include/esp_log.h | 4 + make/project.mk | 2 +- 18 files changed, 813 insertions(+), 319 deletions(-) create mode 100644 components/bootloader_support/README.rst create mode 100755 components/bootloader_support/component.mk create mode 100644 components/bootloader_support/include/esp_image_format.h create mode 100644 components/bootloader_support/include/esp_secureboot.h create mode 100644 components/bootloader_support/include_priv/bootloader_flash.h create mode 100644 components/bootloader_support/src/bootloader_flash.c create mode 100644 components/bootloader_support/src/esp_image_format.c create mode 100644 components/bootloader_support/src/secureboot.c diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index b9dab3e093..3799e913c8 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -20,7 +20,7 @@ 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) \ - BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) \ + BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) IS_BOOTLOADER_BUILD=1 .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) @@ -89,7 +89,9 @@ $(SECURE_BOOT_KEYFILE): @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) +bootloader: + @echo "Invalid bootloader target: bad sdkconfig?" + @exit 1 endif all_binaries: $(BOOTLOADER_BIN) diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index add9c15d61..278e0e9afb 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,7 +4,7 @@ # PROJECT_NAME := bootloader -COMPONENTS := esptool_py bootloader log spi_flash +COMPONENTS := esptool_py bootloader bootloader_support log spi_flash # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. # diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 1655280dc7..0823581824 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -25,8 +25,6 @@ extern "C" #define BOOT_VERSION "V0.1" #define SPI_SEC_SIZE 0x1000 -#define MEM_CACHE(offset) (uint8_t *)(0x3f400000 + (offset)) -#define CACHE_READ_32(offset) ((uint32_t *)(0x3f400000 + (offset))) #define IROM_LOW 0x400D0000 #define IROM_HIGH 0x40400000 #define DROM_LOW 0x3F400000 @@ -61,9 +59,6 @@ typedef struct { uint32_t selected_subtype; } bootloader_state_t; -void boot_cache_redirect( uint32_t pos, size_t size ); -uint32_t get_bin_len(uint32_t pos); - bool flash_encrypt(bootloader_state_t *bs); bool secure_boot_generate_bootloader_digest(void); diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index a4b27702c4..3bc2696a4b 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -33,6 +33,8 @@ #include "soc/timer_group_reg.h" #include "sdkconfig.h" +#include "esp_image_format.h" +#include "bootloader_flash.h" #include "bootloader_config.h" @@ -49,7 +51,7 @@ flash cache is down and the app CPU is in reset. We do have a stack, so we can d extern void Cache_Flush(int); void bootloader_main(); -void unpack_load_app(const esp_partition_pos_t *app_node); +static void unpack_load_app(const esp_partition_pos_t *app_node); void print_flash_info(const esp_image_header_t* pfhdr); void IRAM_ATTR set_cache_and_start_app(uint32_t drom_addr, uint32_t drom_load_addr, @@ -94,53 +96,6 @@ void IRAM_ATTR call_start_cpu0() bootloader_main(); } -/** - * @function : get_bin_len - * @description: get bin's length - * - * @inputs: pos bin locate address in flash - * @return: uint32 length of bin,if bin MAGIC error return 0 - */ - -uint32_t get_bin_len(uint32_t pos) -{ - uint32_t len = 8 + 16; - uint8_t i; - ESP_LOGD(TAG, "pos %d %x",pos,*(uint8_t *)pos); - if(0xE9 != *(uint8_t *)pos) { - return 0; - } - for (i = 0; i < *(uint8_t *)(pos + 1); i++) { - len += *(uint32_t *)(pos + len + 4) + 8; - } - if (len % 16 != 0) { - len = (len / 16 + 1) * 16; - } else { - len += 16; - } - ESP_LOGD(TAG, "bin length = %d", len); - return len; -} - -/** - * @function : boot_cache_redirect - * @description: Configure several pages in flash map so that `size` bytes - * starting at `pos` are mapped to 0x3f400000. - * This sets up mapping only for PRO CPU. - * - * @inputs: pos address in flash - * size size of the area to map, in bytes - */ -void boot_cache_redirect( uint32_t pos, size_t size ) -{ - uint32_t pos_aligned = pos & 0xffff0000; - uint32_t count = (size + 0xffff) / 0x10000; - Cache_Read_Disable( 0 ); - Cache_Flush( 0 ); - ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", pos_aligned, count ); - cache_flash_mmu_set( 0, 0, 0x3f400000, pos_aligned, 64, count ); - Cache_Read_Enable( 0 ); -} /** * @function : load_partition_table @@ -154,79 +109,86 @@ void boot_cache_redirect( uint32_t pos, size_t size ) */ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) { - esp_partition_info_t partition; - uint32_t end = addr + 0x1000; - int index = 0; + const esp_partition_info_t *partitions; + const int PARTITION_TABLE_SIZE = 0x1000; + const int MAX_PARTITIONS = PARTITION_TABLE_SIZE / sizeof(esp_partition_info_t); char *partition_usage; ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); - while (addr < end) { - ESP_LOGD(TAG, "load partition table entry from %x(%08x)", addr, MEM_CACHE(addr)); - memcpy(&partition, MEM_CACHE(addr), sizeof(partition)); - ESP_LOGD(TAG, "type=%x subtype=%x", partition.type, partition.subtype); + partitions = bootloader_mmap(addr, 0x1000); + if (!partitions) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", addr, 0x1000); + return false; + } + ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", addr, (intptr_t)partitions); + + for(int i = 0; i < MAX_PARTITIONS; i++) { + const esp_partition_info_t *partition = &partitions[i]; + ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition); + ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype); partition_usage = "unknown"; - if (partition.magic == ESP_PARTITION_MAGIC) { /* valid partition definition */ - switch(partition.type) { - case PART_TYPE_APP: /* app partition */ - switch(partition.subtype) { - case PART_SUBTYPE_FACTORY: /* factory binary */ - bs->factory = partition.pos; - partition_usage = "factory app"; - break; - case PART_SUBTYPE_TEST: /* test binary */ - bs->test = partition.pos; - partition_usage = "test app"; - break; - default: - /* OTA binary */ - if ((partition.subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { - bs->ota[partition.subtype & PART_SUBTYPE_OTA_MASK] = partition.pos; - ++bs->app_count; - partition_usage = "OTA app"; - } - else { - partition_usage = "Unknown app"; - } - break; + if (partition->magic != ESP_PARTITION_MAGIC) { + /* invalid partition definition indicates end-of-table */ + break; + } + + /* valid partition table */ + switch(partition->type) { + case PART_TYPE_APP: /* app partition */ + switch(partition->subtype) { + case PART_SUBTYPE_FACTORY: /* factory binary */ + bs->factory = partition->pos; + partition_usage = "factory app"; + break; + case PART_SUBTYPE_TEST: /* test binary */ + bs->test = partition->pos; + partition_usage = "test app"; + break; + default: + /* OTA binary */ + if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { + bs->ota[partition->subtype & PART_SUBTYPE_OTA_MASK] = partition->pos; + ++bs->app_count; + partition_usage = "OTA app"; } - break; /* PART_TYPE_APP */ - case PART_TYPE_DATA: /* data partition */ - switch(partition.subtype) { - case PART_SUBTYPE_DATA_OTA: /* ota data */ - bs->ota_info = partition.pos; - partition_usage = "OTA data"; - break; - case PART_SUBTYPE_DATA_RF: - partition_usage = "RF data"; - break; - case PART_SUBTYPE_DATA_WIFI: - partition_usage = "WiFi data"; - break; - default: - partition_usage = "Unknown data"; - break; + else { + partition_usage = "Unknown app"; } - break; /* PARTITION_USAGE_DATA */ - default: /* other partition type */ break; } - } - /* invalid partition magic number */ - else { - break; /* todo: validate md5 */ + break; /* PART_TYPE_APP */ + case PART_TYPE_DATA: /* data partition */ + switch(partition->subtype) { + case PART_SUBTYPE_DATA_OTA: /* ota data */ + bs->ota_info = partition->pos; + partition_usage = "OTA data"; + break; + case PART_SUBTYPE_DATA_RF: + partition_usage = "RF data"; + break; + case PART_SUBTYPE_DATA_WIFI: + partition_usage = "WiFi data"; + break; + default: + partition_usage = "Unknown data"; + break; + } + break; /* PARTITION_USAGE_DATA */ + default: /* other partition type */ + break; } /* print partition type info */ - ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", index, partition.label, partition_usage, - partition.type, partition.subtype, - partition.pos.offset, partition.pos.size); - index++; - addr += sizeof(partition); + ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", i, partition->label, partition_usage, + partition->type, partition->subtype, + partition->pos.offset, partition->pos.size); } + bootloader_unmap(partitions); + ESP_LOGI(TAG,"End of partition table"); return true; } @@ -254,8 +216,9 @@ void bootloader_main() esp_image_header_t fhdr; bootloader_state_t bs; - SpiFlashOpResult spiRet1,spiRet2; + SpiFlashOpResult spiRet1,spiRet2; esp_ota_select_entry_t sa,sb; + const esp_ota_select_entry_t *ota_select_map; memset(&bs, 0, sizeof(bs)); @@ -264,10 +227,11 @@ void bootloader_main() REG_CLR_BIT( RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN ); REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); SPIUnlock(); - /*register first sector in drom0 page 0 */ - boot_cache_redirect( 0, 0x5000 ); - memcpy((unsigned int *) &fhdr, MEM_CACHE(0x1000), sizeof(esp_image_header_t) ); + if(esp_image_load_header(0x1000, &fhdr) != ESP_OK) { + ESP_LOGE(TAG, "failed to load bootloader header!"); + return; + } print_flash_info(&fhdr); @@ -282,9 +246,19 @@ void bootloader_main() if (bs.ota_info.offset != 0) { // check if partition table has OTA info partition //ESP_LOGE("OTA info sector handling is not implemented"); - boot_cache_redirect(bs.ota_info.offset, bs.ota_info.size ); - memcpy(&sa,MEM_CACHE(bs.ota_info.offset & 0x0000ffff),sizeof(sa)); - memcpy(&sb,MEM_CACHE((bs.ota_info.offset + 0x1000)&0x0000ffff) ,sizeof(sb)); + if (bs.ota_info.size < 2 * sizeof(esp_ota_select_entry_t)) { + ESP_LOGE(TAG, "ERROR: ota_info partition size %d is too small (minimum %d bytes)", bs.ota_info.size, sizeof(esp_ota_select_entry_t)); + return; + } + ota_select_map = bootloader_mmap(bs.ota_info.offset, bs.ota_info.size); + if (!ota_select_map) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", bs.ota_info.offset, bs.ota_info.size); + return; + } + sa = ota_select_map[0]; + sb = ota_select_map[1]; + bootloader_unmap(ota_select_map); + if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) { // init status flash load_part_pos = bs.ota[0]; @@ -355,14 +329,14 @@ void bootloader_main() } -void unpack_load_app(const esp_partition_pos_t* partition) +static void unpack_load_app(const esp_partition_pos_t* partition) { - boot_cache_redirect(partition->offset, partition->size); - - uint32_t pos = 0; esp_image_header_t image_header; - memcpy(&image_header, MEM_CACHE(pos), sizeof(image_header)); - pos += sizeof(image_header); + + if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { + ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); + return; + } uint32_t drom_addr = 0; uint32_t drom_load_addr = 0; @@ -376,19 +350,22 @@ void unpack_load_app(const esp_partition_pos_t* partition) bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET; ESP_LOGD(TAG, "bin_header: %u %u %u %u %08x", image_header.magic, - image_header.blocks, + image_header.segment_count, image_header.spi_mode, image_header.spi_size, (unsigned)image_header.entry_addr); - for (uint32_t section_index = 0; - section_index < image_header.blocks; - ++section_index) { - esp_image_section_header_t section_header = {0}; - memcpy(§ion_header, MEM_CACHE(pos), sizeof(section_header)); - pos += sizeof(section_header); + for (int segment = 0; segment < image_header.segment_count; segment++) { + esp_image_segment_header_t segment_header; + uint32_t data_offs; + if(esp_image_load_segment_header(segment, partition->offset, + &image_header, &segment_header, + &data_offs) != ESP_OK) { + ESP_LOGE(TAG, "failed to load segment header #%d", segment); + return; + } - const uint32_t address = section_header.load_addr; + const uint32_t address = segment_header.load_addr; bool load = true; bool map = false; if (address == 0x00000000) { // padding, ignore block @@ -400,47 +377,50 @@ void unpack_load_app(const esp_partition_pos_t* partition) } if (address >= DROM_LOW && address < DROM_HIGH) { - ESP_LOGD(TAG, "found drom section, map from %08x to %08x", pos, - section_header.load_addr); - drom_addr = partition->offset + pos - sizeof(section_header); - drom_load_addr = section_header.load_addr; - drom_size = section_header.data_len + sizeof(section_header); + ESP_LOGD(TAG, "found drom section, map from %08x to %08x", data_offs, + segment_header.load_addr); + drom_addr = data_offs; + drom_load_addr = segment_header.load_addr; + drom_size = segment_header.data_len + sizeof(segment_header); load = false; map = true; } if (address >= IROM_LOW && address < IROM_HIGH) { - ESP_LOGD(TAG, "found irom section, map from %08x to %08x", pos, - section_header.load_addr); - irom_addr = partition->offset + pos - sizeof(section_header); - irom_load_addr = section_header.load_addr; - irom_size = section_header.data_len + sizeof(section_header); + ESP_LOGD(TAG, "found irom section, map from %08x to %08x", data_offs, + segment_header.load_addr); + irom_addr = data_offs; + irom_load_addr = segment_header.load_addr; + irom_size = segment_header.data_len + sizeof(segment_header); load = false; map = true; } if (!load_rtc_memory && address >= RTC_IRAM_LOW && address < RTC_IRAM_HIGH) { - ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", pos); + ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", data_offs); load = false; } if (!load_rtc_memory && address >= RTC_DATA_LOW && address < RTC_DATA_HIGH) { - ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", pos); + ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", data_offs); load = false; } - ESP_LOGI(TAG, "section %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", section_index, pos, - section_header.load_addr, section_header.data_len, section_header.data_len, (load)?"load":(map)?"map":""); + ESP_LOGI(TAG, "segment %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", segment, data_offs - sizeof(esp_image_segment_header_t), + segment_header.load_addr, segment_header.data_len, segment_header.data_len, (load)?"load":(map)?"map":""); - if (!load) { - pos += section_header.data_len; - continue; + if (load) { + const void *data = bootloader_mmap(data_offs, segment_header.data_len); + if(!data) { + ESP_LOGE(TAG, "bootloader_mmap(0x%xc, 0x%x) failed", + data_offs, segment_header.data_len); + return; + } + memcpy((void *)segment_header.load_addr, data, segment_header.data_len); + bootloader_unmap(data); } - - memcpy((void*) section_header.load_addr, MEM_CACHE(pos), section_header.data_len); - pos += section_header.data_len; } - + set_cache_and_start_app(drom_addr, drom_load_addr, drom_size, @@ -526,7 +506,7 @@ void print_flash_info(const esp_image_header_t* phdr) #if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE) ESP_LOGD(TAG, "magic %02x", phdr->magic ); - ESP_LOGD(TAG, "blocks %02x", phdr->blocks ); + ESP_LOGD(TAG, "segments %02x", phdr->segment_count ); ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode ); ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed ); ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size ); diff --git a/components/bootloader/src/main/flash_encrypt.c b/components/bootloader/src/main/flash_encrypt.c index 2fb57a987d..2b1479097d 100644 --- a/components/bootloader/src/main/flash_encrypt.c +++ b/components/bootloader/src/main/flash_encrypt.c @@ -17,6 +17,7 @@ #include "esp_types.h" #include "esp_attr.h" #include "esp_log.h" +#include "esp_err.h" #include "rom/cache.h" #include "rom/ets_sys.h" @@ -30,6 +31,7 @@ #include "sdkconfig.h" #include "bootloader_config.h" +#include "esp_image_format.h" static const char* TAG = "flash_encrypt"; @@ -90,103 +92,97 @@ bool flash_encrypt_write(uint32_t pos, uint32_t len) Cache_Read_Enable(0); return true; } + + /** * @function : flash_encrypt * @description: encrypt 2nd boot ,partition table ,factory bin ��test bin (if use)��ota bin * ��OTA info sector. * * @inputs: bs bootloader state structure used to save the data - * + * * @return: return true, if the encrypt flash success - * + * */ bool flash_encrypt(bootloader_state_t *bs) { - uint32_t bin_len = 0; - uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_FLASH_CRYPT_CNT); - uint8_t count = bitcount(flash_crypt_cnt); - int i = 0; - ESP_LOGD(TAG, "flash encrypt cnt %x, bitcount %d", flash_crypt_cnt, count); + esp_err_t err; + uint32_t image_len = 0; + uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_FLASH_CRYPT_CNT); + uint8_t count = bitcount(flash_crypt_cnt); + ESP_LOGD(TAG, "flash encrypt cnt %x, bitcount %d", flash_crypt_cnt, count); - if ((count % 2) == 0) { - boot_cache_redirect( 0, 64*1024); - /* encrypt iv and abstruct */ - if (false == flash_encrypt_write(0, SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt iv and abstract error"); - return false; - } + if ((count % 2) == 0) { + /* encrypt iv and abstract */ + if (false == flash_encrypt_write(0, SPI_SEC_SIZE)) { + ESP_LOGE(TAG, "encrypt iv and abstract error"); + return false; + } + + /* encrypt bootloader image */ + err = esp_image_basic_verify(0x1000, &image_len); + if(err == ESP_OK && image_len != 0) { + if (false == flash_encrypt_write(0x1000, image_len)) { + ESP_LOGE(TAG, "encrypt 2nd boot error"); + return false; + } + } else { + ESP_LOGE(TAG, "2nd boot len error"); + return false; + } - /* encrypt write boot bin*/ - bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); - if(bin_len != 0) { - if (false == flash_encrypt_write(0x1000, bin_len)) { - ESP_LOGE(TAG, "encrypt 2nd boot error"); - return false; - } - } else { - ESP_LOGE(TAG, "2nd boot len error"); - return false; - } /* encrypt partition table */ - if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt partition table error"); - return false; - } + if (false == flash_encrypt_write(ESP_PARTITION_TABLE_ADDR, SPI_SEC_SIZE)) { + ESP_LOGE(TAG, "encrypt partition table error"); + return false; + } /* encrypt write factory bin */ - if(bs->factory.offset != 0x00) { - ESP_LOGD(TAG, "have factory bin"); - boot_cache_redirect(bs->factory.offset, bs->factory.size); - bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->factory.offset&0xffff)); - if(bin_len != 0) { - if (false == flash_encrypt_write(bs->factory.offset, bin_len)) { - ESP_LOGE(TAG, "encrypt factory bin error"); - return false; - } - } - } + if(bs->factory.offset != 0 && bs->factory.size != 0) { + ESP_LOGD(TAG, "have factory bin"); + if (false == flash_encrypt_write(bs->factory.offset, bs->factory.size)) { + ESP_LOGE(TAG, "encrypt factory bin error"); + return false; + } + } + /* encrypt write test bin */ - if(bs->test.offset != 0x00) { - ESP_LOGD(TAG, "have test bin"); - boot_cache_redirect(bs->test.offset, bs->test.size); - bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->test.offset&0xffff)); - if(bin_len != 0) { - if (false == flash_encrypt_write(bs->test.offset, bin_len)) { - ESP_LOGE(TAG, "encrypt test bin error"); - return false; - } - } - } + if(bs->test.offset != 0 && bs->test.size != 0) { + ESP_LOGD(TAG, "have test bin"); + if (false == flash_encrypt_write(bs->test.offset, bs->test.size)) { + ESP_LOGE(TAG, "encrypt test bin error"); + return false; + } + } + /* encrypt write ota bin */ - for (i = 0;i<16;i++) { - if(bs->ota[i].offset != 0x00) { - ESP_LOGD(TAG, "have ota[%d] bin",i); - boot_cache_redirect(bs->ota[i].offset, bs->ota[i].size); - bin_len = get_bin_len((uint32_t)MEM_CACHE(bs->ota[i].offset&0xffff)); - if(bin_len != 0) { - if (false == flash_encrypt_write(bs->ota[i].offset, bin_len)) { - ESP_LOGE(TAG, "encrypt ota bin error"); - return false; - } - } - } - } + for (int i = 0; i < 16; i++) { + if(bs->ota[i].offset != 0 && bs->ota[i].size != 0) { + ESP_LOGD(TAG, "have ota[%d] bin",i); + if (false == flash_encrypt_write(bs->ota[i].offset, bs->ota[i].size)) { + ESP_LOGE(TAG, "encrypt ota bin error"); + return false; + } + } + } + /* encrypt write ota info bin */ - if (false == flash_encrypt_write(bs->ota_info.offset, 2*SPI_SEC_SIZE)) { - ESP_LOGE(TAG, "encrypt ota info error"); - return false; - } - REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, 0x04); - REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ - REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ - ESP_LOGW(TAG, "burn flash_crypt_cnt"); - REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ - REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ - while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ - return true; - } else { - ESP_LOGI(TAG, "flash already encrypted."); - return true; - } + if (false == flash_encrypt_write(bs->ota_info.offset, 2*SPI_SEC_SIZE)) { + ESP_LOGE(TAG, "encrypt ota info error"); + return false; + } + + REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, 0x04); + REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ + REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ + ESP_LOGW(TAG, "burn flash_crypt_cnt"); + REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ + REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ + while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ + return true; + } else { + ESP_LOGI(TAG, "flash already encrypted."); + return true; + } } diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader/src/main/secure_boot.c index 070fbf24d3..2b1b8573fc 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader/src/main/secure_boot.c @@ -31,6 +31,8 @@ #include "sdkconfig.h" #include "bootloader_config.h" +#include "bootloader_flash.h" +#include "esp_image_format.h" static const char* TAG = "secure_boot"; @@ -40,43 +42,52 @@ static const char* TAG = "secure_boot"; * * @inputs: bool */ -static bool secure_boot_generate(uint32_t bin_len){ +static bool secure_boot_generate(uint32_t image_len){ SpiFlashOpResult spiRet; - uint16_t i; uint32_t buf[32]; - if (bin_len % 128 != 0) { - bin_len = (bin_len / 128 + 1) * 128; - } + const void *image; + + if (image_len % 128 != 0) { + image_len = (image_len / 128 + 1) * 128; + } ets_secure_boot_start(); ets_secure_boot_rd_iv(buf); ets_secure_boot_hash(NULL); Cache_Read_Disable(0); - /* iv stored in sec 0 */ + /* iv stored in sec 0 */ spiRet = SPIEraseSector(0); if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ - spiRet = SPIWrite(0, buf, 128); - if (spiRet != SPI_FLASH_RESULT_OK) { ESP_LOGE(TAG, SPI_ERROR_LOG); return false; } - ESP_LOGD(TAG, "write iv to flash."); Cache_Read_Enable(0); - /* read 4K code image from flash, for test */ - for (i = 0; i < bin_len; i+=128) { - ets_secure_boot_hash((uint32_t *)(0x3f400000 + 0x1000 + i)); + + /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ + ESP_LOGD(TAG, "write iv to flash."); + spiRet = SPIWrite(0, buf, 128); + if (spiRet != SPI_FLASH_RESULT_OK) + { + ESP_LOGE(TAG, SPI_ERROR_LOG); + return false; } + /* generate digest from image contents */ + image = bootloader_mmap(0x1000, image_len); + if (!image) { + ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len); + return false; + } + for (int i = 0; i < image_len; i+=128) { + ets_secure_boot_hash(image + i/sizeof(void *)); + } + bootloader_unmap(image); + ets_secure_boot_obtain(); ets_secure_boot_rd_abstract(buf); ets_secure_boot_finish(); - Cache_Read_Disable(0); - /* write abstract to flash, 0x0080, 64 bytes (512 bits) */ + + ESP_LOGD(TAG, "write abstract to flash."); spiRet = SPIWrite(0x80, buf, 64); if (spiRet != SPI_FLASH_RESULT_OK) { ESP_LOGE(TAG, SPI_ERROR_LOG); @@ -99,9 +110,9 @@ static inline void burn_efuses() } /** - * @function : secure_boot_generate_bootloader_digest + * @brief Enable secure boot if it is not already enabled. * - * @description: Called if the secure boot flag is set on the + * Called if the secure boot flag is set on the * bootloader image in flash. If secure boot is not yet enabled for * bootloader, this will generate the secure boot digest and enable * secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse. @@ -110,24 +121,21 @@ static inline void burn_efuses() * ROM bootloader does this.) * * @return true if secure boot is enabled (either was already enabled, - * or is freshly enabled as a result of calling this function.) + * or is freshly enabled as a result of calling this function.) false + * implies an error occured (possibly secure boot is part-enabled.) */ bool secure_boot_generate_bootloader_digest(void) { - uint32_t bin_len = 0; + esp_err_t err; + uint32_t image_len = 0; if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) { ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); return true; } - boot_cache_redirect( 0, 64*1024); - bin_len = get_bin_len((uint32_t)MEM_CACHE(0x1000)); - if (bin_len == 0) { - ESP_LOGE(TAG, "Invalid bootloader image length zero."); - return false; - } - if (bin_len > 0x100000) { - ESP_LOGE(TAG, "Invalid bootloader image length %x", bin_len); + err = esp_image_basic_verify(0x1000, &image_len); + if (err != ESP_OK) { + ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err); return false; } @@ -168,7 +176,7 @@ bool secure_boot_generate_bootloader_digest(void) { } ESP_LOGI(TAG, "Generating secure boot digest..."); - if (false == secure_boot_generate(bin_len)){ + if (false == secure_boot_generate(image_len)){ ESP_LOGE(TAG, "secure boot generation failed"); return false; } diff --git a/components/bootloader_support/README.rst b/components/bootloader_support/README.rst new file mode 100644 index 0000000000..54ac861c9b --- /dev/null +++ b/components/bootloader_support/README.rst @@ -0,0 +1,9 @@ +Bootloader Support Component +============================ + +Overview +-------- + +"Bootloader support" contains APIs which are used by the bootloader but are also needed for the main app. + +Code in this component needs to be aware of being executed in a bootloader environment (no RTOS available, BOOTLOADER_BUILD macro set) or in an esp-idf app environment (RTOS running, need locking support.) diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk new file mode 100755 index 0000000000..2988fe287e --- /dev/null +++ b/components/bootloader_support/component.mk @@ -0,0 +1,11 @@ +COMPONENT_ADD_INCLUDEDIRS := include +COMPONENT_PRIV_INCLUDEDIRS := include_priv + +ifdef IS_BOOTLOADER_BUILD +# share "private" headers with the bootloader component +COMPONENT_ADD_INCLUDEDIRS += include_priv +endif + +COMPONENT_SRCDIRS := src + +include $(IDF_PATH)/make/component_common.mk diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h new file mode 100644 index 0000000000..a8b1739d73 --- /dev/null +++ b/components/bootloader_support/include/esp_image_format.h @@ -0,0 +1,133 @@ +// Copyright 2015-2016 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. +#ifndef __ESP32_IMAGE_FORMAT_H +#define __ESP32_IMAGE_FORMAT_H + +#include +#include + +#define ESP_ERR_IMAGE_BASE 0x2000 +#define ESP_ERR_IMAGE_FLASH_FAIL (ESP_ERR_IMAGE_BASE + 1) +#define ESP_ERR_IMAGE_INVALID (ESP_ERR_IMAGE_BASE + 2) + +/* Support for app/bootloader image parsing + Can be compiled as part of app or bootloader code. +*/ + +/* SPI flash mode, used in esp_image_header_t */ +typedef enum { + ESP_IMAGE_SPI_MODE_QIO, + ESP_IMAGE_SPI_MODE_QOUT, + ESP_IMAGE_SPI_MODE_DIO, + ESP_IMAGE_SPI_MODE_DOUT, + ESP_IMAGE_SPI_MODE_FAST_READ, + ESP_IMAGE_SPI_MODE_SLOW_READ +} esp_image_spi_mode_t; + +/* SPI flash clock frequency */ +enum { + ESP_IMAGE_SPI_SPEED_40M, + ESP_IMAGE_SPI_SPEED_26M, + ESP_IMAGE_SPI_SPEED_20M, + ESP_IMAGE_SPI_SPEED_80M = 0xF +} esp_image_spi_freq_t; + +/* Supported SPI flash sizes */ +typedef enum { + ESP_IMAGE_FLASH_SIZE_1MB = 0, + ESP_IMAGE_FLASH_SIZE_2MB, + ESP_IMAGE_FLASH_SIZE_4MB, + ESP_IMAGE_FLASH_SIZE_8MB, + ESP_IMAGE_FLASH_SIZE_16MB, + ESP_IMAGE_FLASH_SIZE_MAX +} esp_image_flash_size_t; + +#define ESP_IMAGE_HEADER_MAGIC 0xE9 + +/* Main header of binary image */ +typedef struct { + uint8_t magic; + uint8_t segment_count; + uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */ + uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */ + uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ + uint32_t entry_addr; + uint8_t encrypt_flag; /* encrypt flag */ + uint8_t secure_boot_flag; /* secure boot flag */ + uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ +} esp_image_header_t; + +/* Header of binary image segment */ +typedef struct { + uint32_t load_addr; + uint32_t data_len; +} esp_image_segment_header_t; + + +/** + * @brief Read an ESP image header from flash. + * + * @param src_addr Address in flash to load image header. Must be 4 byte aligned. + * @param[out] image_header Pointer to an esp_image_header_t struture to be filled with data. If the function fails, contents are undefined. + * + * @return ESP_OK if image header was loaded, ESP_ERR_IMAGE_FLASH_FAIL + * if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header + * appears invalid. + */ +esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header); + +/** + * @brief Read the segment header and data offset of a segment in the image. + * + * @param index Index of the segment to load information for. + * @param src_addr Base address in flash of the image. + * @param[in] image_header Pointer to the flash image header, already loaded by @ref esp_image_load_header(). + * @param[out] segment_header Pointer to a segment header structure to be filled with data. If the function fails, contents are undefined. + * @param[out] segment_data_offset Pointer to the data offset of the segment. + * + * @return ESP_OK if segment_header & segment_data_offset were loaded successfully, ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs, ESP_ERR_IMAGE_INVALID if the image header appears invalid, ESP_ERR_INVALID_ARG if the index is invalid. + */ +esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset); + + +/** + * @brief Return length of an image in flash. Non-cryptographically validates image integrity in the process. + * + * If the image has a secure boot signature appended, the signature is not checked and this length is not included in the result. + * + * Image validation checks: + * - Magic byte + * - No single section longer than 16MB + * - Total image no longer than 16MB + * - 8 bit image checksum is valid + * + * @param src_addr Offset of the start of the image in flash. Must be 4 byte aligned. + * @param[out] length Length of the image, set to a value if the image is valid. Can be null. + * + * @return ESP_OK if image is valid, ESP_FAIL or ESP_ERR_IMAGE_INVALID on errors. + * + */ +esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *length); + + +typedef struct { + uint32_t drom_addr; + uint32_t drom_load_addr; + uint32_t drom_size; + uint32_t irom_addr; + uint32_t irom_load_addr; + uint32_t irom_size; +} esp_image_flash_mapping_t; + +#endif diff --git a/components/bootloader_support/include/esp_secureboot.h b/components/bootloader_support/include/esp_secureboot.h new file mode 100644 index 0000000000..b0097df8a6 --- /dev/null +++ b/components/bootloader_support/include/esp_secureboot.h @@ -0,0 +1,51 @@ +// Copyright 2015-2016 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. +#ifndef __ESP32_SECUREBOOT_H +#define __ESP32_SECUREBOOT_H + +#include +#include + +/* Support functions for secure boot features. + + Can be compiled as part of app or bootloader code. +*/ + +/** @brief Is secure boot currently enabled in hardware? + * + * Secure boot is enabled if the ABS_DONE_0 efuse is blown. This means + * that the ROM bootloader code will only boot a verified secure + * bootloader digest from now on. + * + * @return true if secure boot is enabled. + */ +bool esp_secure_boot_enabled(void); + + +/** @brief Enable secure boot if it isw not already enabled. + * + * @important If this function succeeds, secure boot is permanentl + * enabled on the chip via efuse. + * + * This function is intended to be called from bootloader code. + * + * @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_enable(void); + + + +#endif diff --git a/components/bootloader_support/include_priv/bootloader_flash.h b/components/bootloader_support/include_priv/bootloader_flash.h new file mode 100644 index 0000000000..d70ec22d56 --- /dev/null +++ b/components/bootloader_support/include_priv/bootloader_flash.h @@ -0,0 +1,69 @@ +// Copyright 2015-2016 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. +#ifndef __BOOTLOADER_FLASH_H +#define __BOOTLOADER_FLASH_H + +#include +#include +#include +#include + +/* Provide a Flash API for bootloader_support code, + that can be used from bootloader or app code. + + This header is available to source code in the bootloader & + bootloader_support components only. +*/ + +/** + * @brief Map a region of flash to data memory + * + * @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_unmapped before another region is mapped. + * + * @important In app code, these functions are not thread safe. + * + * Call bootloader_unmap once for each successful call to bootloader_mmap. + * + * In esp-idf app, this function maps directly to spi_flash_mmap. + * + * @param offset - Starting flash offset to map to memory. + * @param length - Length of data to map. + * + * @return Pointer to mapped data memory (at src_addr), or NULL + * if an allocation error occured. + */ +const void *bootloader_mmap(uint32_t src_addr, uint32_t size); + + +/** + * @brief Unmap a previously mapped region of flash + * + * Call bootloader_unmap once for each successful call to bootloader_mmap. + */ +void bootloader_unmap(const void *mapping); + +/** + * @brief Read data from Flash. + * + * @note Both src and dest have to be 4-byte aligned. + * + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data + * + * @return esp_err_t + */ +esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size); + +#endif diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c new file mode 100644 index 0000000000..a50cd157e9 --- /dev/null +++ b/components/bootloader_support/src/bootloader_flash.c @@ -0,0 +1,122 @@ +// Copyright 2015-2016 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 + +#include +#include +#include /* including in bootloader for error values */ + +#ifndef BOOTLOADER_BUILD +/* Normal app version maps to esp_spi_flash.h operations... + */ +static const char *TAG = "bootloader_mmap"; + +static spi_flash_mmap_memory_t map; + +const void *bootloader_mmap(uint32_t src_addr, uint32_t size) +{ + if (map) { + ESP_LOGE(TAG, "tried to bootloader_mmap twice"); + return NULL; /* existing mapping in use... */ + } + const void *result = NULL; + esp_err_t err = spi_flash_mmap(src_addr, size, SPI_FLASH_MMAP_DATA, &result, &map); + if (err != ESP_OK) { + result = NULL; + } + return result; +} + +void bootloader_unmap(const void *mapping) +{ + if(mapping && map) { + spi_flash_munmap(map); + } + map = 0; +} + +esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size) +{ + return spi_flash_read(src, dest, size); +} + +#else +/* Bootloader version, uses ROM functions only */ +#include +#include + +static const char *TAG = "bootloader_flash"; + +static bool mapped; + +const void *bootloader_mmap(uint32_t src_addr, uint32_t size) +{ + if (mapped) { + ESP_LOGE(TAG, "tried to bootloader_mmap twice"); + return NULL; /* can't map twice */ + } + + uint32_t src_addr_aligned = src_addr & 0xffff0000; + uint32_t count = (size + 0xffff) / 0x10000; + Cache_Read_Disable(0); + Cache_Flush(0); + ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", src_addr_aligned, count ); + cache_flash_mmu_set( 0, 0, 0x3f400000, src_addr_aligned, 64, count ); + Cache_Read_Enable( 0 ); + + mapped = true; + + return (void *)(0x3f400000 + (src_addr - src_addr_aligned)); +} + +void bootloader_unmap(const void *mapping) +{ + if (mapped) { + /* Full MMU reset */ + Cache_Read_Disable(0); + Cache_Flush(0); + mmu_init(0); + mapped = false; + } +} + +esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size) +{ + if(src_addr & 3) { + ESP_LOGE(TAG, "bootloader_flash_read src_addr not 4-byte aligned"); + return ESP_FAIL; + } + if((intptr_t)dest & 3) { + ESP_LOGE(TAG, "bootloader_flash_read dest not 4-byte aligned"); + return ESP_FAIL; + } + + Cache_Read_Disable(0); + Cache_Flush(0); + SpiFlashOpResult r = SPIRead(src_addr, dest, size); + Cache_Read_Enable(0); + + switch(r) { + case SPI_FLASH_RESULT_OK: + return ESP_OK; + case SPI_FLASH_RESULT_ERR: + return ESP_ERR_FLASH_OP_FAIL; + case SPI_FLASH_RESULT_TIMEOUT: + return ESP_ERR_FLASH_OP_TIMEOUT; + default: + return ESP_FAIL; + } +} + +#endif diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c new file mode 100644 index 0000000000..ad3cd33f13 --- /dev/null +++ b/components/bootloader_support/src/esp_image_format.c @@ -0,0 +1,155 @@ +// Copyright 2015-2016 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 + +#include +#include +#include + +const static char *TAG = "esp_image"; + +#define SIXTEEN_MB 0x1000000 +#define ESP_ROM_CHECKSUM_INITIAL 0xEF + +esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_header) +{ + esp_err_t err; + ESP_LOGD(TAG, "reading image header @ 0x%x", src_addr); + + err = bootloader_flash_read(src_addr, image_header, sizeof(esp_image_header_t)); + + if (err == ESP_OK) { + if (image_header->magic != ESP_IMAGE_HEADER_MAGIC) { + ESP_LOGE(TAG, "image at 0x%x has invalid magic byte", src_addr); + err = ESP_ERR_IMAGE_INVALID; + } + if (image_header->spi_mode > ESP_IMAGE_SPI_MODE_SLOW_READ) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI mode %d", src_addr, image_header->spi_mode); + } + if (image_header->spi_speed > ESP_IMAGE_SPI_SPEED_80M) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI speed %d", src_addr, image_header->spi_speed); + } + if (image_header->spi_size > ESP_IMAGE_FLASH_SIZE_MAX) { + ESP_LOGW(TAG, "image at 0x%x has invalid SPI size %d", src_addr, image_header->spi_size); + } + } + + if (err != ESP_OK) { + bzero(image_header, sizeof(esp_image_header_t)); + } + return err; +} + +esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, esp_image_segment_header_t *segment_header, uint32_t *segment_data_offset) +{ + esp_err_t err = ESP_OK; + uint32_t next_addr = src_addr + sizeof(esp_image_header_t); + + if(index >= image_header->segment_count) { + ESP_LOGE(TAG, "index %d higher than segment count %d", index, image_header->segment_count); + return ESP_ERR_INVALID_ARG; + } + + for(int i = 0; i <= index && err == ESP_OK; i++) { + err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t)); + if (err == ESP_OK) { + if ((segment_header->data_len & 3) != 0 + || segment_header->data_len >= SIXTEEN_MB) { + ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); + err = ESP_ERR_IMAGE_INVALID; + } + next_addr += sizeof(esp_image_segment_header_t); + *segment_data_offset = next_addr; + next_addr += segment_header->data_len; + } + } + + if (err != ESP_OK) { + *segment_data_offset = 0; + bzero(segment_header, sizeof(esp_image_segment_header_t)); + } + + return err; +} + +esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) +{ + esp_err_t err; + uint8_t buf[16]; + uint8_t checksum = ESP_ROM_CHECKSUM_INITIAL; + esp_image_header_t image_header; + esp_image_segment_header_t segment_header = { 0 }; + uint32_t segment_data_offs = 0; + const uint8_t *segment_data; + uint32_t end_addr; + uint32_t length; + + if (p_length != NULL) { + *p_length = 0; + } + + err = esp_image_load_header(src_addr, &image_header); + if (err != ESP_OK) { + return err; + } + + ESP_LOGD(TAG, "reading %d image segments", image_header.segment_count); + + /* Checksum each segment's data */ + for (int i = 0; i < image_header.segment_count; i++) { + err = esp_image_load_segment_header(i, src_addr, &image_header, + &segment_header, &segment_data_offs); + if (err != ESP_OK) { + return err; + } + + segment_data = bootloader_mmap(segment_data_offs, segment_header.data_len); + if (segment_data == NULL) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", segment_data_offs, segment_header.data_len); + return ESP_FAIL; + } + for(int i = 0; i < segment_header.data_len; i++) { + checksum ^= segment_data[i]; + } + bootloader_unmap(segment_data); + } + + /* End of image, verify checksum */ + end_addr = segment_data_offs + segment_header.data_len; + + if (end_addr < src_addr) { + ESP_LOGE(TAG, "image offset has wrapped"); + return ESP_ERR_IMAGE_INVALID; + } + + length = end_addr - src_addr; + if (length >= SIXTEEN_MB) { + ESP_LOGE(TAG, "invalid total length 0x%x", length); + return ESP_ERR_IMAGE_INVALID; + } + + /* image padded to next full 16 byte block, with checksum byte at very end */ + length += 15 - (length % 16); + bootloader_flash_read(src_addr + length - 16, buf, 16); + if (checksum != buf[15]) { + ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x", + checksum, buf[15]); + return ESP_ERR_IMAGE_INVALID; + } + + if (p_length != NULL) { + *p_length = length; + } + return ESP_OK; +} diff --git a/components/bootloader_support/src/secureboot.c b/components/bootloader_support/src/secureboot.c new file mode 100644 index 0000000000..e55c1f33fa --- /dev/null +++ b/components/bootloader_support/src/secureboot.c @@ -0,0 +1,7 @@ +#include +#include + +#include "esp_log.h" + + + diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h index 4bf886c842..ce4acb7bc9 100644 --- a/components/esp32/include/esp_flash_data_types.h +++ b/components/esp32/include/esp_flash_data_types.h @@ -24,54 +24,6 @@ extern "C" #define ESP_PARTITION_TABLE_ADDR 0x4000 #define ESP_PARTITION_MAGIC 0x50AA -/* SPI flash mode, used in esp_image_header_t */ -typedef enum { - ESP_IMAGE_SPI_MODE_QIO, - ESP_IMAGE_SPI_MODE_QOUT, - ESP_IMAGE_SPI_MODE_DIO, - ESP_IMAGE_SPI_MODE_DOUT, - ESP_IMAGE_SPI_MODE_FAST_READ, - ESP_IMAGE_SPI_MODE_SLOW_READ -} esp_image_spi_mode_t; - -/* SPI flash clock frequency */ -enum { - ESP_IMAGE_SPI_SPEED_40M, - ESP_IMAGE_SPI_SPEED_26M, - ESP_IMAGE_SPI_SPEED_20M, - ESP_IMAGE_SPI_SPEED_80M = 0xF -} esp_image_spi_freq_t; - -/* Supported SPI flash sizes */ -typedef enum { - ESP_IMAGE_FLASH_SIZE_1MB = 0, - ESP_IMAGE_FLASH_SIZE_2MB, - ESP_IMAGE_FLASH_SIZE_4MB, - ESP_IMAGE_FLASH_SIZE_8MB, - ESP_IMAGE_FLASH_SIZE_16MB, - ESP_IMAGE_FLASH_SIZE_MAX -} esp_image_flash_size_t; - -/* Main header of binary image */ -typedef struct { - uint8_t magic; - uint8_t blocks; - uint8_t spi_mode; /* flash read mode (esp_image_spi_mode_t as uint8_t) */ - uint8_t spi_speed: 4; /* flash frequency (esp_image_spi_freq_t as uint8_t) */ - uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ - uint32_t entry_addr; - uint8_t encrypt_flag; /* encrypt flag */ - uint8_t secure_boot_flag; /* secure boot flag */ - uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ -} esp_image_header_t; - -/* Header of binary image segment */ -typedef struct { - uint32_t load_addr; - uint32_t data_len; -} esp_image_section_header_t; - - /* OTA selection structure (two copies in the OTA data partition.) Size of 32 bytes is friendly to flash encryption */ typedef struct { diff --git a/components/esp32/include/rom/secure_boot.h b/components/esp32/include/rom/secure_boot.h index cfeda08933..bd4f32ed95 100644 --- a/components/esp32/include/rom/secure_boot.h +++ b/components/esp32/include/rom/secure_boot.h @@ -25,7 +25,7 @@ void ets_secure_boot_start(void); void ets_secure_boot_finish(void); -void ets_secure_boot_hash(uint32_t *buf); +void ets_secure_boot_hash(const uint32_t *buf); void ets_secure_boot_obtain(void); diff --git a/components/log/include/esp_log.h b/components/log/include/esp_log.h index 6716f6e10f..f4b9aa2885 100644 --- a/components/log/include/esp_log.h +++ b/components/log/include/esp_log.h @@ -19,6 +19,10 @@ #include #include "sdkconfig.h" +#ifdef BOOTLOADER_BUILD +#include +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/make/project.mk b/make/project.mk index 17fb83e0d8..7e8dc46d54 100644 --- a/make/project.mk +++ b/make/project.mk @@ -100,7 +100,7 @@ COMPONENT_LDFLAGS := # # Debugging this? Replace $(shell with $(error and you'll see the full command as-run. define GetVariable -$(shell "$(MAKE)" -s --no-print-directory -C $(1) -f component.mk get_variable PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) | sed -En "s/^$(2)=(.+)/\1/p" ) +$(shell "$(MAKE)" -s --no-print-directory -C $(1) -f component.mk get_variable PROJECT_PATH=$(PROJECT_PATH) GET_VARIABLE=$(2) IS_BOOTLOADER_BUILD=$(IS_BOOTLOADER_BUILD) | sed -En "s/^$(2)=(.+)/\1/p" ) endef COMPONENT_INCLUDES := $(abspath $(foreach comp,$(COMPONENT_PATHS_BUILDABLE),$(addprefix $(comp)/, \ From 98a0387854918b73d10e00820e40a36786ee8eab Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 2 Nov 2016 17:54:47 +1100 Subject: [PATCH 06/19] bootloader_support: Move secure boot code to bootloader_support --- .../bootloader/src/main/bootloader_config.h | 1 - .../bootloader/src/main/bootloader_start.c | 7 +- .../{esp_secureboot.h => esp_secure_boot.h} | 24 +++- .../src}/secure_boot.c | 128 +++++++++--------- .../bootloader_support/src/secureboot.c | 7 - 5 files changed, 85 insertions(+), 82 deletions(-) rename components/bootloader_support/include/{esp_secureboot.h => esp_secure_boot.h} (64%) rename components/{bootloader/src/main => bootloader_support/src}/secure_boot.c (71%) delete mode 100644 components/bootloader_support/src/secureboot.c diff --git a/components/bootloader/src/main/bootloader_config.h b/components/bootloader/src/main/bootloader_config.h index 0823581824..4559d5f816 100644 --- a/components/bootloader/src/main/bootloader_config.h +++ b/components/bootloader/src/main/bootloader_config.h @@ -60,7 +60,6 @@ typedef struct { } bootloader_state_t; bool flash_encrypt(bootloader_state_t *bs); -bool secure_boot_generate_bootloader_digest(void); #ifdef __cplusplus } diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 3bc2696a4b..59b180fd84 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -34,6 +34,7 @@ #include "sdkconfig.h" #include "esp_image_format.h" +#include "esp_secure_boot.h" #include "bootloader_flash.h" #include "bootloader_config.h" @@ -214,6 +215,7 @@ void bootloader_main() { ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); + esp_err_t err; esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; @@ -308,8 +310,9 @@ void bootloader_main() if(fhdr.secure_boot_flag == 0x01) { /* Generate secure digest from this bootloader to protect future modifications */ - if (secure_boot_generate_bootloader_digest() == false){ - ESP_LOGE(TAG, "Bootloader digest generation failed. SECURE BOOT IS NOT ENABLED."); + err = esp_secure_boot_permanently_enable(); + if (err != ESP_OK){ + ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); /* Allow booting to continue, as the failure is probably due to user-configured EFUSEs for testing... */ diff --git a/components/bootloader_support/include/esp_secureboot.h b/components/bootloader_support/include/esp_secure_boot.h similarity index 64% rename from components/bootloader_support/include/esp_secureboot.h rename to components/bootloader_support/include/esp_secure_boot.h index b0097df8a6..4bf2dc8b23 100644 --- a/components/bootloader_support/include/esp_secureboot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -16,6 +16,7 @@ #include #include +#include "soc/efuse_reg.h" /* Support functions for secure boot features. @@ -30,21 +31,34 @@ * * @return true if secure boot is enabled. */ -bool esp_secure_boot_enabled(void); +static inline bool esp_secure_boot_enabled(void) { + return REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_RD_ABS_DONE_0); +} -/** @brief Enable secure boot if it isw not already enabled. +/** @brief Enable secure boot if it is not already enabled. * - * @important If this function succeeds, secure boot is permanentl + * @important If this function succeeds, secure boot is permanently * enabled on the chip via efuse. * - * This function is intended to be called from bootloader code. + * @important This function is intended to be called from bootloader code only. + * + * If secure boot is not yet enabled for bootloader, this will + * generate the secure boot digest and enable secure boot by blowing + * the EFUSE_RD_ABS_DONE_0 efuse. + * + * This function does not verify secure boot of the bootloader (the + * ROM bootloader does this.) + * + * 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_enable(void); +esp_err_t esp_secure_boot_permanently_enable(void); diff --git a/components/bootloader/src/main/secure_boot.c b/components/bootloader_support/src/secure_boot.c similarity index 71% rename from components/bootloader/src/main/secure_boot.c rename to components/bootloader_support/src/secure_boot.c index 2b1b8573fc..c17aebfbea 100644 --- a/components/bootloader/src/main/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -30,72 +30,81 @@ #include "sdkconfig.h" -#include "bootloader_config.h" #include "bootloader_flash.h" #include "esp_image_format.h" +#include "esp_secure_boot.h" static const char* TAG = "secure_boot"; +#define HASH_BLOCK_SIZE 128 +#define IV_LEN HASH_BLOCK_SIZE +#define DIGEST_LEN 64 + /** * @function : secure_boot_generate - * @description: generate boot abstract & iv + * @description: generate boot digest (aka "abstract") & iv * - * @inputs: bool + * @inputs: image_len - length of image to calculate digest for */ static bool secure_boot_generate(uint32_t image_len){ - SpiFlashOpResult spiRet; - uint32_t buf[32]; + SpiFlashOpResult spiRet; + /* buffer is uint32_t not uint8_t to meet ROM SPI API signature */ + uint32_t buf[IV_LEN / sizeof(uint32_t)]; const void *image; - if (image_len % 128 != 0) { - image_len = (image_len / 128 + 1) * 128; - } - ets_secure_boot_start(); - ets_secure_boot_rd_iv(buf); - ets_secure_boot_hash(NULL); - Cache_Read_Disable(0); - /* iv stored in sec 0 */ - spiRet = SPIEraseSector(0); - if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - Cache_Read_Enable(0); + /* hardware secure boot engine only takes full blocks, so round up the + image length. The additional data should all be 0xFF. + */ + if (image_len % HASH_BLOCK_SIZE != 0) { + image_len = (image_len / HASH_BLOCK_SIZE + 1) * HASH_BLOCK_SIZE; + } + ets_secure_boot_start(); + ets_secure_boot_rd_iv(buf); + ets_secure_boot_hash(NULL); + Cache_Read_Disable(0); + /* iv stored in sec 0 */ + spiRet = SPIEraseSector(0); + if (spiRet != SPI_FLASH_RESULT_OK) + { + ESP_LOGE(TAG, "SPI erase failed %d", spiRet); + return false; + } + Cache_Read_Enable(0); - /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ + /* write iv to flash, 0x0000, 128 bytes (1024 bits) */ ESP_LOGD(TAG, "write iv to flash."); - spiRet = SPIWrite(0, buf, 128); - if (spiRet != SPI_FLASH_RESULT_OK) - { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } + spiRet = SPIWrite(0, buf, IV_LEN); + if (spiRet != SPI_FLASH_RESULT_OK) + { + ESP_LOGE(TAG, "SPI write failed %d", spiRet); + return false; + } + bzero(buf, sizeof(buf)); - /* generate digest from image contents */ + /* generate digest from image contents */ image = bootloader_mmap(0x1000, image_len); if (!image) { ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len); return false; } - for (int i = 0; i < image_len; i+=128) { - ets_secure_boot_hash(image + i/sizeof(void *)); - } + for (int i = 0; i < image_len; i+= HASH_BLOCK_SIZE) { + ets_secure_boot_hash(image + i/sizeof(void *)); + } bootloader_unmap(image); - ets_secure_boot_obtain(); - ets_secure_boot_rd_abstract(buf); - ets_secure_boot_finish(); + ets_secure_boot_obtain(); + ets_secure_boot_rd_abstract(buf); + ets_secure_boot_finish(); - ESP_LOGD(TAG, "write abstract to flash."); - spiRet = SPIWrite(0x80, buf, 64); - if (spiRet != SPI_FLASH_RESULT_OK) { - ESP_LOGE(TAG, SPI_ERROR_LOG); - return false; - } - ESP_LOGD(TAG, "write abstract to flash."); - Cache_Read_Enable(0); - return true; + ESP_LOGD(TAG, "write digest to flash."); + spiRet = SPIWrite(0x80, buf, DIGEST_LEN); + if (spiRet != SPI_FLASH_RESULT_OK) { + ESP_LOGE(TAG, "SPI write failed %d", spiRet); + return false; + } + ESP_LOGD(TAG, "write digest to flash."); + Cache_Read_Enable(0); + return true; } /* Burn values written to the efuse write registers */ @@ -109,34 +118,19 @@ static inline void burn_efuses() while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ } -/** - * @brief Enable secure boot if it is not already enabled. - * - * Called if the secure boot flag is set on the - * bootloader image in flash. If secure boot is not yet enabled for - * bootloader, this will generate the secure boot digest and enable - * secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse. - * - * This function does not verify secure boot of the bootloader (the - * ROM bootloader does this.) - * - * @return true if secure boot is enabled (either was already enabled, - * or is freshly enabled as a result of calling this function.) false - * implies an error occured (possibly secure boot is part-enabled.) - */ -bool secure_boot_generate_bootloader_digest(void) { +esp_err_t esp_secure_boot_permanently_enable(void) { esp_err_t err; uint32_t image_len = 0; - if (REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0) + if (esp_secure_boot_enabled()) { ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); - return true; + return ESP_OK; } err = esp_image_basic_verify(0x1000, &image_len); if (err != ESP_OK) { ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err); - return false; + return err; } uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG); @@ -178,17 +172,17 @@ bool secure_boot_generate_bootloader_digest(void) { ESP_LOGI(TAG, "Generating secure boot digest..."); if (false == secure_boot_generate(image_len)){ ESP_LOGE(TAG, "secure boot generation failed"); - return false; + return ESP_FAIL; } ESP_LOGI(TAG, "Digest generation complete."); if (!efuse_key_read_protected) { ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse."); - return false; + return ESP_ERR_INVALID_STATE; } if (!efuse_key_write_protected) { ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse."); - return false; + return ESP_ERR_INVALID_STATE; } ESP_LOGI(TAG, "blowing secure boot efuse & disabling JTAG..."); @@ -200,9 +194,9 @@ bool secure_boot_generate_bootloader_digest(void) { ESP_LOGD(TAG, "after updating, EFUSE_BLK0_RDATA6 %x", after); if (after & EFUSE_RD_ABS_DONE_0) { ESP_LOGI(TAG, "secure boot is now enabled for bootloader image"); - return true; + return ESP_OK; } else { ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!"); - return false; + return ESP_ERR_INVALID_STATE; } } diff --git a/components/bootloader_support/src/secureboot.c b/components/bootloader_support/src/secureboot.c deleted file mode 100644 index e55c1f33fa..0000000000 --- a/components/bootloader_support/src/secureboot.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -#include "esp_log.h" - - - From fce359b240cc3869f609256701a202c788ba4634 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 6 Oct 2016 12:51:47 +1100 Subject: [PATCH 07/19] build system: Add support for embedded arbitrary binary or text files in .rodata Simplifies examples of embedding a certificate file or a root cert. This is a much cruder mechanism than the full flash filesystem we want eventually, but still sometimes useful. --- docs/build_system.rst | 22 +++++++ examples/04_https_request/main/cert.c | 44 -------------- examples/04_https_request/main/component.mk | 9 ++- .../main/https_request_main.c | 18 +++++- .../main/server_root_cert.pem | 27 +++++++++ make/component_common.mk | 57 ++++++++++++++++--- 6 files changed, 117 insertions(+), 60 deletions(-) delete mode 100644 examples/04_https_request/main/cert.c create mode 100644 examples/04_https_request/main/server_root_cert.pem diff --git a/docs/build_system.rst b/docs/build_system.rst index 34db487e0a..85ebfe46d5 100644 --- a/docs/build_system.rst +++ b/docs/build_system.rst @@ -280,6 +280,28 @@ component and resides under the component path. Because logo.h is a generated file, it needs to be cleaned when make clean is called which why it is added to the COMPONENT_EXTRA_CLEAN variable. +Embedding Binary Data +===================== + +Sometimes you have a file with some binary or text data that you'd like to make available to your component - but you don't want to reformat the file as C source. + +You can set a variable COMPONENT_EMBED_FILES in component.mk, giving the names of the files to embed in this way:: + + COMPONENT_EMBED_FILES := server_root_cert.der + +Or if the file is a string, you can use the variable COMPONENT_EMBED_TXTFILES. This will embed the contents of the text file as a null-terminated string:: + + COMPONENT_EMBED_TXTFILES := server_root_cert.pem + +The file's contents will be added to the .rodata section in flash, and are available via symbol names as follows:: + + extern const uint8_t server_root_cert_pem_start[] asm("_binary_server_root_cert_pem_start"); + extern const uint8_t server_root_cert_pem_end[] asm("_binary_server_root_cert_pem_end"); + +The names are generated from the full name of the file, as given in COMPONENT_EMBED_FILES. Characters /, ., etc. are replaced with underscores. The _binary prefix in the symbol name is added by objcopy and is the same for both text and binary files. + +For an example of using this technique, see examples/04_https_request - the certificate file contents are loaded from the text .pem file at compile time. + Cosmetic Improvements ===================== diff --git a/examples/04_https_request/main/cert.c b/examples/04_https_request/main/cert.c deleted file mode 100644 index 7acc438ef3..0000000000 --- a/examples/04_https_request/main/cert.c +++ /dev/null @@ -1,44 +0,0 @@ -/* This is the CA certificate for the CA trust chain of - www.howsmyssl.com in PEM format, as dumped via: - - openssl s_client -showcerts -connect www.howsmyssl.com:443 -#include -#include - -/* - 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 - i:/O=Digital Signature Trust Co./CN=DST Root CA X3 - */ -const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n" -"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n" -"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n" -"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n" -"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n" -"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n" -"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n" -"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n" -"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n" -"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n" -"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n" -"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n" -"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n" -"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n" -"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n" -"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n" -"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n" -"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n" -"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n" -"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n" -"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n" -"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n" -"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n" -"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n" -"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n" -"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n" -"-----END CERTIFICATE-----\r\n"; - - diff --git a/examples/04_https_request/main/component.mk b/examples/04_https_request/main/component.mk index 24356f23ed..7fbfcc55d2 100644 --- a/examples/04_https_request/main/component.mk +++ b/examples/04_https_request/main/component.mk @@ -1,10 +1,9 @@ # # Main Makefile. This is basically the same as a component makefile. # -# This Makefile should, at the very least, just include $(SDK_PATH)/make/component_common.mk. By default, -# this will take the sources in the src/ directory, compile them and link them into -# lib(subdirectory_name).a in the build directory. This behaviour is entirely configurable, -# please read the ESP-IDF documents if you need to do this. -# + +# embed files from the "certs" directory as binary data symbols +# in the app +COMPONENT_EMBED_TXTFILES := server_root_cert.pem include $(IDF_PATH)/make/component_common.mk diff --git a/examples/04_https_request/main/https_request_main.c b/examples/04_https_request/main/https_request_main.c index 0c8b2463f8..7f302409d8 100644 --- a/examples/04_https_request/main/https_request_main.c +++ b/examples/04_https_request/main/https_request_main.c @@ -74,8 +74,18 @@ static const char *REQUEST = "GET " WEB_URL " HTTP/1.1\n" "User-Agent: esp-idf/1.0 esp32\n" "\n"; -/* Root cert for howsmyssl.com, found in cert.c */ -extern const char *server_root_cert; +/* Root cert for howsmyssl.com, taken from server_root_cert.pem + + The PEM file was extracted from the output of this command: + openssl s_client -showcerts -connect www.howsmyssl.com:443 > $$(notdir $$<) ) + $$(Q) $$(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ + $$(Q) rm $$(notdir $$<) +endef + +# generate targets to embed binary & text files +$(foreach binfile,$(COMPONENT_EMBED_FILES), $(eval $(call GenerateEmbedTarget,$(binfile),bin))) + +$(foreach txtfile,$(COMPONENT_EMBED_TXTFILES), $(eval $(call GenerateEmbedTarget,$(txtfile),txt))) + +# generate targets to create binary embed directories +$(foreach bindir,$(sort $(dir $(COMPONENT_EMBED_FILES))), $(eval $(call GenerateBuildDirTarget,$(bindir)))) From b5de58139919e1143f289efc426569bb2d111106 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 3 Nov 2016 17:33:30 +1100 Subject: [PATCH 08/19] Secure boot: initial image signature support --- .gitmodules | 3 + components/bootloader/Kconfig.projbuild | 16 +++ components/bootloader/Makefile.projbuild | 24 +++-- components/bootloader/src/Makefile | 2 +- .../bootloader/src/main/bootloader_start.c | 25 +++++ .../bootloader_support/Makefile.projbuild | 3 + components/bootloader_support/component.mk | 25 +++++ .../include/esp_secure_boot.h | 10 +- .../src/secure_boot_signatures.c | 99 +++++++++++++++++++ components/esptool_py/Makefile.projbuild | 6 ++ components/esptool_py/esptool | 2 +- components/micro-ecc/component.mk | 8 ++ components/micro-ecc/micro-ecc | 1 + components/partition_table/Makefile.projbuild | 3 + docs/security/secure-boot.rst | 87 +++++++++------- make/common.mk | 20 +++- make/component_common.mk | 6 +- make/project.mk | 1 + 18 files changed, 291 insertions(+), 50 deletions(-) create mode 100644 components/bootloader_support/Makefile.projbuild create mode 100644 components/bootloader_support/src/secure_boot_signatures.c create mode 100644 components/micro-ecc/component.mk create mode 160000 components/micro-ecc/micro-ecc diff --git a/.gitmodules b/.gitmodules index df40848261..c26f92e80c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "components/bt/lib"] path = components/bt/lib url = https://github.com/espressif/esp32-bt-lib.git +[submodule "components/micro-ecc/micro-ecc"] + path = components/micro-ecc/micro-ecc + url = https://github.com/kmackay/micro-ecc.git diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 55c2eebd14..b568f61a04 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -77,6 +77,22 @@ config SECURE_BOOTLOADER_KEY_FILE See docs/security/secure-boot.rst for details. +config SECURE_BOOT_SIGNING_KEY + string "Secure boot signing key" + depends on SECURE_BOOTLOADER_ENABLED + default secure_boot_signing_key.pem + help + Path to the key file used to sign partition tables and app images for secure boot. + + Key file is an ECDSA private key (NIST256p curve) in PEM format. + + 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 docs/security/secure-boot.rst for details. + config SECURE_BOOTLOADER_ENABLED bool default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 3799e913c8..831d988584 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -15,13 +15,17 @@ 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))) +# both signing key paths are resolved relative to the project directory +SECURE_BOOTLOADER_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) +SECURE_BOOT_SIGNING_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOT_SIGNING_KEY))) +export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component # Custom recursive make for bootloader sub-project BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ V=$(V) SDKCONFIG=$(BOOTLOADER_SDKCONFIG) \ BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) IS_BOOTLOADER_BUILD=1 + .PHONY: bootloader-clean bootloader-flash bootloader $(BOOTLOADER_BIN) $(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig @@ -59,16 +63,15 @@ bootloader: $(BOOTLOADER_BIN) @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. +# Reflashable secure bootloader +# generates a digest binary (bootloader + digest) 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 "$(ESPEFUSEPY) burn_key secure_boot $(SECURE_BOOTLOADER_KEY)" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" @echo $(SEPARATOR) @echo "To reflash the bootloader after initial flash:" @@ -77,15 +80,16 @@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo "* After first boot, only re-flashes of this kind (with same key) will be accepted." @echo "* Not recommended to re-use the same secure boot keyfile on 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 $@ $< +$(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) + @echo "DIGEST $(notdir $@)" + $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< -$(SECURE_BOOT_KEYFILE): +$(SECURE_BOOTLOADER_KEY): @echo $(SEPARATOR) - @echo "Need to generate secure boot digest key first. Run following command:" + @echo "Need to generate secure boot signing key. Run following command:" @echo "$(ESPSECUREPY) generate_key $@" @echo "Keep key file safe after generating." + @echo "(See secure boot documentation for caveats & alternatives.)") @exit 1 else diff --git a/components/bootloader/src/Makefile b/components/bootloader/src/Makefile index 278e0e9afb..1cfc80c5fe 100644 --- a/components/bootloader/src/Makefile +++ b/components/bootloader/src/Makefile @@ -4,7 +4,7 @@ # PROJECT_NAME := bootloader -COMPONENTS := esptool_py bootloader bootloader_support log spi_flash +COMPONENTS := esptool_py bootloader bootloader_support log spi_flash micro-ecc # The bootloader pseudo-component is also included in this build, for its Kconfig.projbuild to be included. # diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 59b180fd84..3ebb8cf520 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -110,6 +110,7 @@ void IRAM_ATTR call_start_cpu0() */ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) { + esp_err_t err; const esp_partition_info_t *partitions; const int PARTITION_TABLE_SIZE = 0x1000; const int MAX_PARTITIONS = PARTITION_TABLE_SIZE / sizeof(esp_partition_info_t); @@ -118,6 +119,14 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + if(esp_secure_boot_enabled()) { + err = esp_secure_boot_verify_signature(addr, 0x1000); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify partition table signature."); + return false; + } + } + partitions = bootloader_mmap(addr, 0x1000); if (!partitions) { ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", addr, 0x1000); @@ -334,7 +343,23 @@ void bootloader_main() static void unpack_load_app(const esp_partition_pos_t* partition) { + esp_err_t err; esp_image_header_t image_header; + uint32_t image_length; + + if (esp_secure_boot_enabled()) { + /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ + err = esp_image_basic_verify(partition->offset, &image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); + return; + } + err = esp_secure_boot_verify_signature(partition->offset, image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "App image @ 0x%x failed signature verification (%d)", partition->offset, err); + return; + } + } if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); diff --git a/components/bootloader_support/Makefile.projbuild b/components/bootloader_support/Makefile.projbuild new file mode 100644 index 0000000000..f93967a719 --- /dev/null +++ b/components/bootloader_support/Makefile.projbuild @@ -0,0 +1,3 @@ +# projbuild file for bootloader support +# (included in bootloader & main app) + diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk index 2988fe287e..7f546dcba9 100755 --- a/components/bootloader_support/component.mk +++ b/components/bootloader_support/component.mk @@ -1,11 +1,36 @@ COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_PRIV_INCLUDEDIRS := include_priv +# include configuration macros early +include $(IDF_PATH)/make/common.mk + ifdef IS_BOOTLOADER_BUILD # share "private" headers with the bootloader component +# eventual goal: all functionality that needs this lives in bootloader_support COMPONENT_ADD_INCLUDEDIRS += include_priv endif COMPONENT_SRCDIRS := src +# +# Secure boot signing key support +# +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + +SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin) + +COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY) + +$(SECURE_BOOT_SIGNING_KEY): + @echo "Need to generate secure boot signing key." + @echo "One way is to run this command:" + @echo "$(ESPSECUREPY) generate_signing_key $@" + @echo "Keep key file safe after generating." + @echo "(See secure boot documentation for risks & alternatives.)" + @exit 1 + +$(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) + $(ESPSECUREPY) extract_public_key --keyfile $< $@ +endif + include $(IDF_PATH)/make/component_common.mk diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index 4bf2dc8b23..c1c0171512 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -60,6 +60,14 @@ static inline bool esp_secure_boot_enabled(void) { */ esp_err_t esp_secure_boot_permanently_enable(void); - +/** @brief Verify the signature appended to some binary data in flash. + * + * @param src_addr Starting offset of the data in flash. + * @param length Length of data in bytes. Signature is appended -after- length bytes. + * + * @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); #endif diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c new file mode 100644 index 0000000000..5f02de38b0 --- /dev/null +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -0,0 +1,99 @@ +// Copyright 2015-2016 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 "bootloader_flash.h" +#include "esp_log.h" +#include "esp_image_format.h" +#include "esp_secure_boot.h" + +#include "uECC.h" + +#ifdef BOOTLOADER_BUILD +#include "rom/sha.h" +typedef SHA_CTX sha_context; +#else +#include "hwcrypto/sha.h" +typedef esp_sha_context sha_context; +#endif + +typedef struct { + uint32_t version; + uint8_t signature[64]; +} signature_block_t; + +static const char* TAG = "secure_boot"; + +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 + +esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) +{ + sha_context sha; + uint8_t digest[64]; + ptrdiff_t keylen; + const uint8_t *data; + const signature_block_t *sigblock; + bool is_valid; + + ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length); + + data = bootloader_mmap(src_addr, length + sizeof(signature_block_t)); + if(data == NULL) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(signature_block_t)); + return ESP_FAIL; + } + + sigblock = (const signature_block_t *)(data + length); + + if (sigblock->version != 0) { + ESP_LOGE(TAG, "src 0x%x has invalid signature version field 0x%08x", src_addr, sigblock->version); + goto unmap_and_fail; + } + +#ifdef BOOTLOADER_BUILD + /* Use ROM SHA functions directly */ + ets_sha_enable(); + ets_sha_init(&sha); + ets_sha_update(&sha, SHA2_512, data, length); + ets_sha_finish(&sha, SHA2_512, digest); + ets_sha_disable(); +#else + /* Use thread-safe esp-idf SHA layer */ + esp_sha512_init(&sha); + esp_sha512_start(&sha, false); + esp_sha512_update(&sha, data, length); + esp_sha512_finish(&sha, digest); + esp_sha512_free(&sha); +#endif + + 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); + goto unmap_and_fail; + } + + is_valid = uECC_verify(signature_verification_key_start, + digest, sizeof(digest), sigblock->signature, + uECC_secp256r1()); + + bootloader_unmap(data); + return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID; + + unmap_and_fail: + bootloader_unmap(data); + return ESP_FAIL; +} diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index dfb9dfc4c2..5807512b86 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -18,6 +18,7 @@ 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 +export ESPSECUREPY # is used in bootloader_support component ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFREQ) --flash_size $(ESPFLASHSIZE) @@ -33,6 +34,11 @@ ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN) $(APP_BIN): $(APP_ELF) $(ESPTOOLPY_SRC) $(Q) $(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +ifndef IS_BOOTLOADER_BUILD + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place +endif +endif flash: all_binaries $(ESPTOOLPY_SRC) @echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..." diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 95dae1651e..68ed7c7a4e 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 95dae1651e5aea1adb2b6018b23f65a305f67387 +Subproject commit 68ed7c7a4e4409899f10dddda1e02b20e5cb32f0 diff --git a/components/micro-ecc/component.mk b/components/micro-ecc/component.mk new file mode 100644 index 0000000000..df29f400d4 --- /dev/null +++ b/components/micro-ecc/component.mk @@ -0,0 +1,8 @@ +# 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 + +COMPONENT_ADD_INCLUDEDIRS := micro-ecc + +include $(IDF_PATH)/make/component_common.mk diff --git a/components/micro-ecc/micro-ecc b/components/micro-ecc/micro-ecc new file mode 160000 index 0000000000..14222e062d --- /dev/null +++ b/components/micro-ecc/micro-ecc @@ -0,0 +1 @@ +Subproject commit 14222e062d77f45321676e813d9525f32a88e8fa diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 98631cc854..e6f3bce8f9 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -21,6 +21,9 @@ PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.cs $(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH) @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..." $(Q) $(GEN_ESP32PART) $< $@ +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place +endif all_binaries: $(PARTITION_TABLE_BIN) diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 5e33367b49..169f2d56bc 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -8,9 +8,9 @@ Secure Boot is separate from the Encrypted Flash feature, and you can use secure Background ---------- -- Most data is stored in flash. Flash access does not need to protected for secure boot to function, because critical data is stored in Efuses internal to the chip. +- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored in Efuses internal to the chip. -- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. +- Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. - To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. @@ -21,57 +21,62 @@ This is a high level overview of the secure boot process. Step by step instructi 1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Bootloader Config". -2. Bootloader Config includes the path to a ECDSA public key. This "image public key" is compiled into the software bootloader. +2. Bootloader Config includes the path to a secure boot signing key. This is a ECDSA public/private key pair in a PEM format file. -2. The software bootloader image is built by esp-idf with the public key embedded, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. +2. The software bootloader image is built by esp-idf with the public key (signature verification) portion of the secure boot signing key compiled in, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. 3. On first boot, the software bootloader tests the secure boot flag. If it is set, the following process is followed to enable secure boot: - - Hardware secure boot support generates a device secure bootloader key (stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. + - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. - - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot this bootloader image.) + - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) - Bootloader also disables JTAG via efuse. -4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. +4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, for technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the image public key (embedded in the bootloader itself) to verify all subsequent partition tables and app images before they are booted. +5. When running in secure boot mode, the software bootloader uses the secure boot signing key's public key (embedded in the bootloader itself, and therefore validated as part of the bootloader digest) to verify all subsequent partition tables and app images before they are booted. Keys ---- The following keys are used by the secure boot process: -- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from hardware, the user does not need to supply it. The Efuse holding this key must be read & write protected (preventing software access) before secure boot is enabled. +- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from a hardware random number generation, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. -- "image public key" is a ECDSA public key (curve TBD) in format TBD. This public key is flashed into the software bootloader and used to verify the remaining parts of the flash (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. +- "secure boot signing key" is a standard ECDSA public/private key pair (NIST256p aka prime256v1 curve) in PEM format. + + - The public key from this key pair (for signature verificaton but not signature creation) is compiled into the software bootloader and used to verify the second stage of booting (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. + + - The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to a bootloader with secure boot using the matching public key. -- "image private key" is a ECDSA private key, a matching pair with "image public key". This private key is used to sign partition tables and app images for the secure boot process. **This private key must be securely kept private** as anyone who has this key can authenticate to the secure boot process. It is acceptable to use the same private key across multiple production devices. How To Enable Secure Boot ------------------------- -1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (For the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) +1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) -2. Select names for public & private image key files "Image Public Key File" and "Image Private Key File". These options will appear after secure boot is enabled. The files can be anywhere on your system. Relative paths are evaluated from the project directory. The files don't need to exist yet. +2. Select a name for the secure boot signing key. This option will appear after secure boot is enabled. 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. 3. Set other config 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 -4. Run ``make generate_image_keypair`` to generate an image public key and a matching image private key. +4. The first time you run ``make``, 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** The key pair is generated using the best random number source available via 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** A signing key genereated 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 `Generating Secure Boot Signing Key` for more details. 5. Run ``make bootloader`` to build a secure boot enabled bootloader. The output of `make` will include a prompt for a flashing command, using `esptool.py write_flash`. -6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not automated) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**. +6. When you're ready to flash the bootloader, run the specified command (you have to enter it yourself, this step is not performed by make) and then wait for flashing to complete. **Remember this is a one time flash, you can't change the bootloader after this!**. -7. Run `make flash` to build and flash the partition table and the just-built app image. The app image will be signed using the private key you generated in step 4. +7. Run `make 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*: `make flash` doesn't flash the bootloader if secure boot is enabled. 8. 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 occured due to the build configuration. -**IMPORTANT** Secure boot won't ever 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* 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. -9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the image public key). +9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the signing key). Re-Flashable Software Bootloader -------------------------------- @@ -80,19 +85,34 @@ The "Secure Boot: One-Time Flash" is the recommended software bootloader configu However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. -*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. +In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 value is used to generate an 256-bit value which is then burned to efuse and used to protect the bootloader. This is a convenience so you only need to generate/protect a single private key. + +*NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. The "One-Time Flash" option is recommended for production environments. + +To enable a reflashable bootloader: 1. In the ``make menuconfig`` step, select "Bootloader Config" -> "Secure Boot" -> "Reflashable". -2. Select a name for the "Secure bootloader key file". The file can be anywhere on your system, and does not have to exist yet. A path is evaluated relative to the project directory. The file doesn't have to exist yet. +2. Follow the steps shown above to choose a signing key file, and generate the key file. -3. The first time you run ``make bootloader``, the system will prompt you with a ``espsecure.py generate_key`` command that can be used to generate the secure bootloader key. +3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key you generated for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the derived key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the derived key). - **IMPORTANT** The new key is generated using the best random number source available via the OS and its Python installation (`/dev/urandom` on OSX/Linux and `CryptGenRandom()` on Windows). If this random number source is weak, then the secure bootloader key will be weak. +4. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. -4. Run ``make bootloader`` again. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the secure bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the secure bootloader key file). +Generating Secure Boot Signing Key +---------------------------------- -5. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. +The build system will prompt you with a command to generate a new signing key via ``espsecure.py generate_signing_key``. This uses the python-ecdsa library, which in turn uses Python's os.urandom() as a random number source. + +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 EC key generation utilities. + +For example, to generate a signing key using the openssl command line: + +``` +openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key.pem +``` + +Remember that the strength of the secure boot system depends on keeping the signing key private. Technical Details @@ -107,9 +127,9 @@ The Secure Boot support hardware can perform three basic operations: 1. Generate a random sequence of bytes from a hardware random number generator. -2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read from hardware if Efuse ABS_DONE_0 is *not* burned (ie still 0), to prevent new digests from being calculated on the device after secure boot is enabled. +2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read back by software if Efuse ABS_DONE_0 is *not* burned (ie still 0). -3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. +3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. Secure Bootloader Digest Algorithm ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -122,9 +142,9 @@ Items marked with (^) are to fulfill hardware restrictions, as opposed to crypto 1. Prefix the image with a 128 byte randomly generated IV. 2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) -3. For each 16 byte block of the input image: - - Reverse the byte order of the block (^) - - Use the AES256 algorithm in ECB mode to encrypt the block. +3. For each 16 byte plaintext block of the input image: + - Reverse the byte order of the plaintext block (^) + - Apply AES256 in ECB mode to the plaintext block. - Reverse the byte order of the 16 bytes of ciphertext output. (^) - Append to the overall ciphertext output. 4. Byte-swap each 4 byte word of the ciphertext (^) @@ -137,9 +157,10 @@ Image Signing Algorithm Deterministic ECDSA as specified by `RFC6979`. -Curve is TBD. -Key format is TBD. -Output format is TBD. +- Curve is NIST256p (openssl calls this curve "prime256v1", it is also sometimes called secp256r1). +- Key format used for storage is PEM. + - In the bootloader, the public key (for signature verification) is flashed as 64 raw bytes. +- Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. .. _esp-idf boot process: ../boot-process.rst diff --git a/make/common.mk b/make/common.mk index 2b7376a4db..0c523c8775 100644 --- a/make/common.mk +++ b/make/common.mk @@ -53,8 +53,26 @@ endef # convenience variable for printing an 80 asterisk wide separator line SEPARATOR:="*******************************************************************************" -# macro to remove quotes from an argument, ie $(call dequote (CONFIG_BLAH)) +# macro to remove quotes from an argument, ie $(call dequote,$(CONFIG_BLAH)) define dequote $(subst ",,$(1)) endef # " comment kept here to keep syntax highlighting happy + + +# macro to keep an absolute path as-is, but resolve a relative path +# against a particular parent directory +# +# $(1) path to resolve +# $(2) directory to resolve non-absolute path against +# +# Path and directory don't have to exist (definition of a "relative +# path" is one that doesn't start with /) +# +# $(2) can contain a trailing forward slash or not, result will not +# double any path slashes. +# +# example $(call resolvepath,$(CONFIG_PATH),$(CONFIG_DIR)) +define resolvepath +$(if $(filter /%,$(1)),$(1),$(subst //,/,$(2)/$(1))) +endef diff --git a/make/component_common.mk b/make/component_common.mk index 58711b7c6e..70a6f60f07 100644 --- a/make/component_common.mk +++ b/make/component_common.mk @@ -134,10 +134,10 @@ OBJCOPY_EMBED_ARGS := --input binary --output elf32-xtensa-le --binary-architect # because objcopy generates the symbol name from the full command line # path to the input file. define GenerateEmbedTarget -$(1).$(2).o: $$(COMPONENT_PATH)/$(1) | $$(dir $(1)) +$(1).$(2).o: $(call resolvepath,$(1),$(COMPONENT_PATH)) | $$(dir $(1)) $$(summary) EMBED $$@ - $$(Q) cp $$< $$(notdir $$<) - $$(Q) $(if $(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) + $$(Q) $(if $(filter-out $$(notdir $$(abspath $$<)),$$(abspath $$(notdir $$<))), cp $$< $$(notdir $$<) ) # copy input file to build dir, unless already in build dir + $$(Q) $(if $(subst bin,,$(2)),echo -ne '\0' >> $$(notdir $$<) ) # trailing NUL byte on text output $$(Q) $$(OBJCOPY) $(OBJCOPY_EMBED_ARGS) $$(notdir $$<) $$@ $$(Q) rm $$(notdir $$<) endef diff --git a/make/project.mk b/make/project.mk index 7e8dc46d54..5a12732a4e 100644 --- a/make/project.mk +++ b/make/project.mk @@ -151,6 +151,7 @@ LDFLAGS ?= -nostdlib \ -L$(IDF_PATH)/ld \ $(addprefix -L$(BUILD_DIR_BASE)/,$(COMPONENTS) $(SRCDIRS)) \ -u call_user_start_cpu0 \ + $(EXTRA_LDFLAGS) \ -Wl,--gc-sections \ -Wl,-static \ -Wl,--start-group \ From 64f3893cb9c7a4c792f4ecbb7c4375f25bf768f7 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 4 Nov 2016 16:05:00 +1100 Subject: [PATCH 09/19] secure boot: Derive secure bootloader key from private key Means only one key needs to be managed. --- components/bootloader/Kconfig.projbuild | 23 +++-------------- components/bootloader/Makefile.projbuild | 25 ++++++++----------- .../bootloader_support/Makefile.projbuild | 3 --- components/bootloader_support/component.mk | 8 ++++-- components/esptool_py/esptool | 2 +- make/project.mk | 5 +++- make/project_config.mk | 1 - 7 files changed, 25 insertions(+), 42 deletions(-) delete mode 100644 components/bootloader_support/Makefile.projbuild diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index b568f61a04..536b971268 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -54,29 +54,14 @@ config SECURE_BOOTLOADER_ONE_TIME_FLASH 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. + Generate a reusable secure bootloader key, derived (via SHA-256) from the secure boot signing 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. + This allows the secure bootloader to be re-flashed by anyone with access to the secure boot signing key. + + This option is less secure than one-time flash, because a leak of the digest key from one device allows reflashing of any device that uses it. endchoice -config SECURE_BOOTLOADER_KEY_FILE - string "Secure bootloader key file" - depends on SECURE_BOOTLOADER_REFLASHABLE - default secure_boot_key.bin - help - Path to the key file for a reflashable secure bootloader 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_BOOT_SIGNING_KEY string "Secure boot signing key" depends on SECURE_BOOTLOADER_ENABLED diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 831d988584..1b1c07bea3 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -15,8 +15,7 @@ BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader) BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig -# both signing key paths are resolved relative to the project directory -SECURE_BOOTLOADER_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOTLOADER_KEY_FILE))) +# signing key path is resolved relative to the project directory SECURE_BOOT_SIGNING_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOT_SIGNING_KEY))) export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component @@ -31,10 +30,6 @@ BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \ $(BOOTLOADER_BIN): | $(BOOTLOADER_BUILD_DIR)/sdkconfig $(Q) $(BOOTLOADER_MAKE) $@ -bootloader-clean: - $(Q) $(BOOTLOADER_MAKE) app-clean config-clean - $(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old - clean: bootloader-clean ifdef CONFIG_SECURE_BOOTLOADER_DISABLED @@ -66,7 +61,11 @@ else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE # Reflashable secure bootloader # generates a digest binary (bootloader + digest) -BOOTLOADER_DIGEST_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin +BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin +SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin + +$(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY) + $(Q) $(ESPSECUREPY) digest_private_key -k $< $@ bootloader: $(BOOTLOADER_DIGEST_BIN) @echo $(SEPARATOR) @@ -84,20 +83,16 @@ $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) @echo "DIGEST $(notdir $@)" $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< -$(SECURE_BOOTLOADER_KEY): - @echo $(SEPARATOR) - @echo "Need to generate secure boot signing key. Run following command:" - @echo "$(ESPSECUREPY) generate_key $@" - @echo "Keep key file safe after generating." - @echo "(See secure boot documentation for caveats & alternatives.)") - @exit 1 - else bootloader: @echo "Invalid bootloader target: bad sdkconfig?" @exit 1 endif +bootloader-clean: + $(Q) $(BOOTLOADER_MAKE) app-clean config-clean + $(Q) rm -f $(BOOTLOADER_SDKCONFIG) $(BOOTLOADER_SDKCONFIG).old $(SECURE_BOOTLOADER_KEY) $(BOOTLOADER_DIGEST_BIN) + all_binaries: $(BOOTLOADER_BIN) # synchronise the project level config to the bootloader's diff --git a/components/bootloader_support/Makefile.projbuild b/components/bootloader_support/Makefile.projbuild deleted file mode 100644 index f93967a719..0000000000 --- a/components/bootloader_support/Makefile.projbuild +++ /dev/null @@ -1,3 +0,0 @@ -# projbuild file for bootloader support -# (included in bootloader & main app) - diff --git a/components/bootloader_support/component.mk b/components/bootloader_support/component.mk index 7f546dcba9..e68949d82a 100755 --- a/components/bootloader_support/component.mk +++ b/components/bootloader_support/component.mk @@ -17,10 +17,9 @@ COMPONENT_SRCDIRS := src # ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +# this path is created relative to the component build directory SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin) -COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY) - $(SECURE_BOOT_SIGNING_KEY): @echo "Need to generate secure boot signing key." @echo "One way is to run this command:" @@ -31,6 +30,11 @@ $(SECURE_BOOT_SIGNING_KEY): $(SECURE_BOOT_VERIFICATION_KEY): $(SECURE_BOOT_SIGNING_KEY) $(ESPSECUREPY) extract_public_key --keyfile $< $@ + +COMPONENT_EXTRA_CLEAN += $(SECURE_BOOT_VERIFICATION_KEY) + +COMPONENT_EMBED_FILES := $(SECURE_BOOT_VERIFICATION_KEY) + endif include $(IDF_PATH)/make/component_common.mk diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 68ed7c7a4e..98e5dbfa78 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 68ed7c7a4e4409899f10dddda1e02b20e5cb32f0 +Subproject commit 98e5dbfa78fa53cebcb4c56530e683f889bf21c3 diff --git a/make/project.mk b/make/project.mk index 5a12732a4e..e0d578c104 100644 --- a/make/project.mk +++ b/make/project.mk @@ -306,6 +306,9 @@ app-clean: $(addsuffix -clean,$(notdir $(COMPONENT_PATHS_BUILDABLE))) $(summary) RM $(APP_ELF) $(Q) rm -f $(APP_ELF) $(APP_BIN) $(APP_MAP) -clean: app-clean +# NB: this ordering is deliberate (app-clean before config-clean), +# so config remains valid during all component clean targets +config-clean: app-clean +clean: config-clean diff --git a/make/project_config.mk b/make/project_config.mk index 7ca83ce5af..56a05090bc 100644 --- a/make/project_config.mk +++ b/make/project_config.mk @@ -59,7 +59,6 @@ $(AUTO_CONF_REGEN_TARGET) $(BUILD_DIR_BASE)/include/sdkconfig.h: $(SDKCONFIG) $( # sometimes you can get an infinite make loop on Windows where sdkconfig always gets regenerated newer # than the target(!) -clean: config-clean .PHONY: config-clean config-clean: $(summary RM CONFIG) From 7402a1b9738a7542e6e37de8768c9580a196aee0 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 14:35:23 +1100 Subject: [PATCH 10/19] partition_table: Move from 0x4000 to 0x8000 Also fix a bug with correctly padding bootloader image when length is already a multiple of 16. --- components/bootloader_support/src/bootloader_flash.c | 4 ++-- components/bootloader_support/src/esp_image_format.c | 8 +++++++- components/esp32/include/esp_flash_data_types.h | 2 +- components/partition_table/Makefile.projbuild | 6 ++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index a50cd157e9..fcaf24ad2c 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -94,11 +94,11 @@ void bootloader_unmap(const void *mapping) esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size) { if(src_addr & 3) { - ESP_LOGE(TAG, "bootloader_flash_read src_addr not 4-byte aligned"); + ESP_LOGE(TAG, "bootloader_flash_read src_addr 0x%x not 4-byte aligned", src_addr); return ESP_FAIL; } if((intptr_t)dest & 3) { - ESP_LOGE(TAG, "bootloader_flash_read dest not 4-byte aligned"); + ESP_LOGE(TAG, "bootloader_flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest); return ESP_FAIL; } diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index ad3cd33f13..3e7dcafa9e 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -62,6 +62,7 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const } for(int i = 0; i <= index && err == ESP_OK; i++) { + ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr); err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t)); if (err == ESP_OK) { if ((segment_header->data_len & 3) != 0 @@ -69,6 +70,7 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); err = ESP_ERR_IMAGE_INVALID; } + ESP_LOGV(TAG, "segment data length 0x%x", segment_header->data_len); next_addr += sizeof(esp_image_segment_header_t); *segment_data_offset = next_addr; next_addr += segment_header->data_len; @@ -140,7 +142,11 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) } /* image padded to next full 16 byte block, with checksum byte at very end */ - length += 15 - (length % 16); + ESP_LOGV(TAG, "unpadded image length 0x%x", length); + length += 16; /* always pad by at least 1 byte */ + length = length - (length % 16); + ESP_LOGV(TAG, "padded image length 0x%x", length); + ESP_LOGD(TAG, "reading checksum block at 0x%x", src_addr + length - 16); bootloader_flash_read(src_addr + length - 16, buf, 16); if (checksum != buf[15]) { ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x", diff --git a/components/esp32/include/esp_flash_data_types.h b/components/esp32/include/esp_flash_data_types.h index ce4acb7bc9..783f2c59bb 100644 --- a/components/esp32/include/esp_flash_data_types.h +++ b/components/esp32/include/esp_flash_data_types.h @@ -21,7 +21,7 @@ extern "C" { #endif -#define ESP_PARTITION_TABLE_ADDR 0x4000 +#define ESP_PARTITION_TABLE_ADDR 0x8000 #define ESP_PARTITION_MAGIC 0x50AA /* OTA selection structure (two copies in the OTA data partition.) diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index e6f3bce8f9..4273f53c3a 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -11,6 +11,8 @@ # NB: gen_esp32part.py lives in the sdk/bin/ dir not component dir GEN_ESP32PART := $(PYTHON) $(COMPONENT_PATH)/gen_esp32part.py -q +PARTITION_TABLE_OFFSET := 0x8000 + # Path to partition CSV file is relative to project path for custom # partition CSV files, but relative to component dir otherwise.$ PARTITION_TABLE_ROOT := $(call dequote,$(if $(CONFIG_PARTITION_TABLE_CUSTOM),$(PROJECT_PATH),$(COMPONENT_PATH))) @@ -27,8 +29,8 @@ endif all_binaries: $(PARTITION_TABLE_BIN) -PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash 0x4000 $(PARTITION_TABLE_BIN) -ESPTOOL_ALL_FLASH_ARGS += 0x4000 $(PARTITION_TABLE_BIN) +PARTITION_TABLE_FLASH_CMD = $(ESPTOOLPY_SERIAL) write_flash $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN) +ESPTOOL_ALL_FLASH_ARGS += $(PARTITION_TABLE_OFFSET) $(PARTITION_TABLE_BIN) partition_table: $(PARTITION_TABLE_BIN) @echo "Partition table binary generated. Contents:" From ff1b2c603911f58f3602fe8d9f7cf6c1cddff63b Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 15:32:21 +1100 Subject: [PATCH 11/19] partition_table: Pad generated table to 0xC00 length, for easier signing --- components/partition_table/gen_esp32part.py | 8 +++++++- .../partition_table/tests/gen_esp32part_tests.py | 15 ++++++++++----- docs/partition-tables.rst | 5 +++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index 8b5df2b3b5..b399f31b78 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -9,6 +9,8 @@ import struct import argparse import sys +MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature + __version__ = '1.0' quiet = False @@ -92,7 +94,11 @@ class PartitionTable(list): return result def to_binary(self): - return "".join(e.to_binary() for e in self) + result = "".join(e.to_binary() for e in self) + if len(result )>= MAX_PARTITION_LENGTH: + raise InputError("Binary partition table length (%d) longer than max" % len(result)) + result += "\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing + return result def to_csv(self, simple_formatting=False): rows = [ "# Espressif ESP32 Partition Table", diff --git a/components/partition_table/tests/gen_esp32part_tests.py b/components/partition_table/tests/gen_esp32part_tests.py index 413f1aac91..d12539ea87 100755 --- a/components/partition_table/tests/gen_esp32part_tests.py +++ b/components/partition_table/tests/gen_esp32part_tests.py @@ -37,6 +37,10 @@ LONGER_BINARY_TABLE += "\xAA\x50\x10\x00" + \ "second" + ("\0"*10) + \ "\x00\x00\x00\x00" +def _strip_trailing_ffs(binary_table): + while binary_table.endswith("\xFF"): + binary_table = binary_table[0:len(binary_table)-1] + return binary_table class CSVParserTests(unittest.TestCase): @@ -156,7 +160,7 @@ class BinaryOutputTests(unittest.TestCase): first, 0x30, 0xEE, 0x100400, 0x300000 """ t = PartitionTable.from_csv(csv) - tb = t.to_binary() + tb = _strip_trailing_ffs(t.to_binary()) self.assertEqual(len(tb), 32) self.assertEqual('\xAA\x50', tb[0:2]) # magic self.assertEqual('\x30\xee', tb[2:4]) # type, subtype @@ -170,7 +174,7 @@ first, 0x30, 0xEE, 0x100400, 0x300000 second,0x31, 0xEF, , 0x100000 """ t = PartitionTable.from_csv(csv) - tb = t.to_binary() + tb = _strip_trailing_ffs(t.to_binary()) self.assertEqual(len(tb), 64) self.assertEqual('\xAA\x50', tb[0:2]) self.assertEqual('\xAA\x50', tb[32:34]) @@ -215,7 +219,7 @@ class BinaryParserTests(unittest.TestCase): self.assertEqual(t[2].type, 0x10) self.assertEqual(t[2].name, "second") - round_trip = t.to_binary() + round_trip = _strip_trailing_ffs(t.to_binary()) self.assertEqual(round_trip, LONGER_BINARY_TABLE) def test_bad_magic(self): @@ -267,7 +271,7 @@ class CSVOutputTests(unittest.TestCase): self.assertEqual(row[0], "factory") self.assertEqual(row[1], "app") self.assertEqual(row[2], "2") - self.assertEqual(row[3], "64K") + self.assertEqual(row[3], "0x10000") self.assertEqual(row[4], "1M") # round trip back to a PartitionTable and check is identical @@ -291,7 +295,7 @@ class CommandLineTests(unittest.TestCase): # reopen the CSV and check the generated binary is identical with open(csvpath, 'r') as f: from_csv = PartitionTable.from_csv(f.read()) - self.assertEqual(from_csv.to_binary(), LONGER_BINARY_TABLE) + self.assertEqual(_strip_trailing_ffs(from_csv.to_binary()), LONGER_BINARY_TABLE) # run gen_esp32part.py to conver the CSV to binary again subprocess.check_call([sys.executable, "../gen_esp32part.py", @@ -299,6 +303,7 @@ class CommandLineTests(unittest.TestCase): # assert that file reads back as identical with open(binpath, 'rb') as f: binary_readback = f.read() + binary_readback = _strip_trailing_ffs(binary_readback) self.assertEqual(binary_readback, LONGER_BINARY_TABLE) finally: diff --git a/docs/partition-tables.rst b/docs/partition-tables.rst index 88597532d2..5f5911bd52 100644 --- a/docs/partition-tables.rst +++ b/docs/partition-tables.rst @@ -6,6 +6,8 @@ Overview A single ESP32's flash can contain multiple apps, as well as many different kinds of data (calibration data, filesystems, parameter storage, etc). For this reason a partition table is flashed to offset 0x4000 in the flash. +Partition table length is 0xC00 bytes (maximum 95 partition table entries). If the partition table is signed due to `secure boot`, the signature is appended after the table data. + Each entry in the partition table has a name (label), type (app, data, or something else), subtype and the offset in flash where the partition is loaded. The simplest way to use the partition table is to `make menuconfig` and choose one of the simple predefined partition tables: @@ -130,3 +132,6 @@ Flashing the partition table * ``make flash``: Will flash everything including the partition table. A manual flashing command is also printed as part of ``make partition_table``. + + +.. _secure boot: security/secure-boot.rst From fe66dd85f09881c4ab8c48e37cda715f065fccfe Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 15:45:26 +1100 Subject: [PATCH 12/19] secure boot: Enable based on sdkconfig, remove "secure boot flag" from binary image --- components/bootloader/Kconfig.projbuild | 35 ++++++++++++++++++- .../bootloader/src/main/bootloader_start.c | 24 +++++++------ .../include/esp_image_format.h | 3 +- .../include/esp_secure_boot.h | 2 +- .../bootloader_support/src/secure_boot.c | 26 ++++++++++++-- 5 files changed, 73 insertions(+), 17 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 536b971268..949638594d 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -28,6 +28,12 @@ config LOG_BOOTLOADER_LEVEL default 4 if LOG_BOOTLOADER_LEVEL_DEBUG default 5 if LOG_BOOTLOADER_LEVEL_VERBOSE +endmenu + + + +menu "Secure boot configuration" + choice SECURE_BOOTLOADER bool "Secure bootloader" default SECURE_BOOTLOADER_DISABLED @@ -78,8 +84,35 @@ config SECURE_BOOT_SIGNING_KEY See docs/security/secure-boot.rst for details. +config SECURE_BOOT_DISABLE_JTAG + bool "First boot: Permanently disable JTAG" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. + + It is recommended this option remains set for production environments. + +config SECURE_BOOT_DISABLE_UART_BOOTLOADER + bool "First boot: Permanently disable UART bootloader" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disables UART and other bootloader modes when enabling secure boot. This happens on first boot. + + It is recommended this option remains set for production environments. + +config SECURE_BOOT_TEST_MODE + bool "Test mode: don't actually enable secure boot" + depends on SECURE_BOOTLOADER_ENABLED + default N + help + If this option is set, all permanent secure boot changes (via Efuse) are disabled. + + This option is for testing purposes only - it effectively completely disables secure boot protection. + config SECURE_BOOTLOADER_ENABLED bool default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE -endmenu +endmenu \ No newline at end of file diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 3ebb8cf520..6c23257be5 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -316,17 +316,17 @@ void bootloader_main() ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); - if(fhdr.secure_boot_flag == 0x01) { - /* Generate secure digest from this bootloader to protect future - modifications */ - err = esp_secure_boot_permanently_enable(); - if (err != ESP_OK){ - ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); - /* Allow booting to continue, as the failure is probably - due to user-configured EFUSEs for testing... - */ - } +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + /* Generate secure digest from this bootloader to protect future + modifications */ + err = esp_secure_boot_permanently_enable(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); + /* Allow booting to continue, as the failure is probably + due to user-configured EFUSEs for testing... + */ } +#endif if(fhdr.encrypt_flag == 0x01) { /* encrypt flash */ @@ -354,12 +354,16 @@ static void unpack_load_app(const esp_partition_pos_t* partition) ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); return; } +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, image_length); err = esp_secure_boot_verify_signature(partition->offset, image_length); if (err != ESP_OK) { ESP_LOGE(TAG, "App image @ 0x%x failed signature verification (%d)", partition->offset, err); return; } + ESP_LOGD(TAG, "App signature is valid"); } +#endif if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index a8b1739d73..326ff130d5 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -64,8 +64,7 @@ typedef struct { uint8_t spi_size: 4; /* flash chip size (esp_image_flash_size_t as uint8_t) */ uint32_t entry_addr; uint8_t encrypt_flag; /* encrypt flag */ - uint8_t secure_boot_flag; /* secure boot flag */ - uint8_t extra_header[14]; /* ESP32 additional header, unused by second bootloader */ + uint8_t extra_header[15]; /* ESP32 additional header, unused by second bootloader */ } esp_image_header_t; /* Header of binary image segment */ diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index c1c0171512..eaa1048628 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -32,7 +32,7 @@ * @return true if secure boot is enabled. */ static inline bool esp_secure_boot_enabled(void) { - return REG_GET_FIELD(EFUSE_BLK0_RDATA6_REG, EFUSE_RD_ABS_DONE_0); + return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0; } diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index c17aebfbea..2cfe505492 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -110,12 +110,16 @@ static bool secure_boot_generate(uint32_t image_len){ /* Burn values written to the efuse write registers */ static inline void burn_efuses() { +#ifdef CONFIG_SECURE_BOOT_TEST_MODE + ESP_LOGE(TAG, "SECURE BOOT TEST MODE. Not really burning any efuses!"); +#else REG_WRITE(EFUSE_CONF_REG, 0x5A5A); /* efuse_pgm_op_ena, force no rd/wr disable */ REG_WRITE(EFUSE_CMD_REG, 0x02); /* efuse_pgm_cmd */ while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_pagm_cmd=0 */ REG_WRITE(EFUSE_CONF_REG, 0x5AA5); /* efuse_read_op_ena, release force */ REG_WRITE(EFUSE_CMD_REG, 0x01); /* efuse_read_cmd */ while (REG_READ(EFUSE_CMD_REG)); /* wait for efuse_read_cmd=0 */ +#endif } esp_err_t esp_secure_boot_permanently_enable(void) { @@ -185,10 +189,22 @@ esp_err_t esp_secure_boot_permanently_enable(void) { return ESP_ERR_INVALID_STATE; } - ESP_LOGI(TAG, "blowing secure boot efuse & disabling JTAG..."); + ESP_LOGI(TAG, "blowing secure boot efuse..."); ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG)); - REG_WRITE(EFUSE_BLK0_WDATA6_REG, - EFUSE_RD_ABS_DONE_0 | EFUSE_RD_DISABLE_JTAG); + + uint32_t new_wdata6 = EFUSE_RD_ABS_DONE_0; + + #ifdef CONFIG_SECURE_BOOT_DISABLE_JTAG + ESP_LOGI(TAG, "disabling JTAG..."); + new_wdata6 |= EFUSE_RD_DISABLE_JTAG; + #endif + + #ifdef CONFIG_SECURE_BOOT_DISABLE_UART_BOOTLOADER + ESP_LOGI(TAG, "disabling UART bootloader..."); + new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE_S; + #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); @@ -196,7 +212,11 @@ esp_err_t esp_secure_boot_permanently_enable(void) { ESP_LOGI(TAG, "secure boot is now enabled for bootloader image"); return ESP_OK; } else { +#ifdef CONFIG_SECURE_BOOT_TEST_MODE + ESP_LOGE(TAG, "secure boot not enabled due to test mode"); +#else ESP_LOGE(TAG, "secure boot not enabled for bootloader image, EFUSE_RD_ABS_DONE_0 is probably write protected!"); +#endif return ESP_ERR_INVALID_STATE; } } From e459f803da9cc3bd6640c9d56215ebeb63c27946 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Mon, 7 Nov 2016 15:45:57 +1100 Subject: [PATCH 13/19] secure boot: Functional partition table & app signature verification --- .../bootloader/src/main/bootloader_start.c | 54 ++++++++++--------- .../include/esp_image_format.h | 2 +- .../include/esp_secure_boot.h | 4 +- .../include_priv/bootloader_flash.h | 8 +-- .../bootloader_support/src/bootloader_flash.c | 6 +-- .../bootloader_support/src/esp_image_format.c | 4 +- .../bootloader_support/src/secure_boot.c | 2 +- .../src/secure_boot_signatures.c | 30 +++++++---- components/esptool_py/Makefile.projbuild | 17 +++--- components/esptool_py/esptool | 2 +- components/partition_table/Makefile.projbuild | 15 ++++-- components/partition_table/gen_esp32part.py | 9 ++-- docs/security/secure-boot.rst | 39 ++++++++------ make/common.mk | 3 +- 14 files changed, 116 insertions(+), 79 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 6c23257be5..36d3ff0d29 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -104,35 +104,38 @@ void IRAM_ATTR call_start_cpu0() * OTA info sector, factory app sector, and test app sector. * * @inputs: bs bootloader state structure used to save the data - * addr address of partition table in flash * @return: return true, if the partition table is loaded (and MD5 checksum is valid) * */ -bool load_partition_table(bootloader_state_t* bs, uint32_t addr) +bool load_partition_table(bootloader_state_t* bs) { esp_err_t err; const esp_partition_info_t *partitions; - const int PARTITION_TABLE_SIZE = 0x1000; - const int MAX_PARTITIONS = PARTITION_TABLE_SIZE / sizeof(esp_partition_info_t); + const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ + const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); char *partition_usage; ESP_LOGI(TAG, "Partition Table:"); ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED if(esp_secure_boot_enabled()) { - err = esp_secure_boot_verify_signature(addr, 0x1000); + ESP_LOGI(TAG, "Verifying partition table signature..."); + err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify partition table signature."); return false; } + ESP_LOGD(TAG, "Partition table signature verified"); } +#endif - partitions = bootloader_mmap(addr, 0x1000); + partitions = bootloader_mmap(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (!partitions) { - ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", addr, 0x1000); + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); return false; } - ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", addr, (intptr_t)partitions); + ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions); for(int i = 0; i < MAX_PARTITIONS; i++) { const esp_partition_info_t *partition = &partitions[i]; @@ -197,7 +200,7 @@ bool load_partition_table(bootloader_state_t* bs, uint32_t addr) partition->pos.offset, partition->pos.size); } - bootloader_unmap(partitions); + bootloader_munmap(partitions); ESP_LOGI(TAG,"End of partition table"); return true; @@ -248,7 +251,7 @@ void bootloader_main() update_flash_config(&fhdr); - if (!load_partition_table(&bs, ESP_PARTITION_TABLE_ADDR)) { + if (!load_partition_table(&bs)) { ESP_LOGE(TAG, "load partition table error!"); return; } @@ -268,7 +271,7 @@ void bootloader_main() } sa = ota_select_map[0]; sb = ota_select_map[1]; - bootloader_unmap(ota_select_map); + bootloader_munmap(ota_select_map); if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) { // init status flash @@ -336,7 +339,7 @@ void bootloader_main() } } - // copy sections to RAM, set up caches, and start application + // copy loaded segments to RAM, set up caches for mapped segments, and start application unpack_load_app(&load_part_pos); } @@ -347,14 +350,15 @@ static void unpack_load_app(const esp_partition_pos_t* partition) esp_image_header_t image_header; uint32_t image_length; - if (esp_secure_boot_enabled()) { - /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ - err = esp_image_basic_verify(partition->offset, &image_length); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); - return; - } + /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ + err = esp_image_basic_verify(partition->offset, &image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); + return; + } + #ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + if (esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, image_length); err = esp_secure_boot_verify_signature(partition->offset, image_length); if (err != ESP_OK) { @@ -377,7 +381,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) uint32_t irom_load_addr = 0; uint32_t irom_size = 0; - /* Reload the RTC memory sections whenever a non-deepsleep reset + /* Reload the RTC memory segments whenever a non-deepsleep reset is occurring */ bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET; @@ -409,7 +413,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } if (address >= DROM_LOW && address < DROM_HIGH) { - ESP_LOGD(TAG, "found drom section, map from %08x to %08x", data_offs, + ESP_LOGD(TAG, "found drom segment, map from %08x to %08x", data_offs, segment_header.load_addr); drom_addr = data_offs; drom_load_addr = segment_header.load_addr; @@ -419,7 +423,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } if (address >= IROM_LOW && address < IROM_HIGH) { - ESP_LOGD(TAG, "found irom section, map from %08x to %08x", data_offs, + ESP_LOGD(TAG, "found irom segment, map from %08x to %08x", data_offs, segment_header.load_addr); irom_addr = data_offs; irom_load_addr = segment_header.load_addr; @@ -429,12 +433,12 @@ static void unpack_load_app(const esp_partition_pos_t* partition) } if (!load_rtc_memory && address >= RTC_IRAM_LOW && address < RTC_IRAM_HIGH) { - ESP_LOGD(TAG, "Skipping RTC code section at %08x\n", data_offs); + ESP_LOGD(TAG, "Skipping RTC code segment at %08x\n", data_offs); load = false; } if (!load_rtc_memory && address >= RTC_DATA_LOW && address < RTC_DATA_HIGH) { - ESP_LOGD(TAG, "Skipping RTC data section at %08x\n", data_offs); + ESP_LOGD(TAG, "Skipping RTC data segment at %08x\n", data_offs); load = false; } @@ -449,7 +453,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition) return; } memcpy((void *)segment_header.load_addr, data, segment_header.data_len); - bootloader_unmap(data); + bootloader_munmap(data); } } diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index 326ff130d5..a32a50a4a5 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -107,7 +107,7 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const * * Image validation checks: * - Magic byte - * - No single section longer than 16MB + * - No single segment longer than 16MB * - Total image no longer than 16MB * - 8 bit image checksum is valid * diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index eaa1048628..f9fc57708d 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -60,7 +60,9 @@ static inline bool esp_secure_boot_enabled(void) { */ esp_err_t esp_secure_boot_permanently_enable(void); -/** @brief Verify the signature appended to some binary data in flash. +/** @brief Verify the secure boot signature (determinstic ECDSA w/ SHA256) appended to some binary data in flash. + * + * Public key is compiled into the calling program. See docs/security/secure-boot.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. diff --git a/components/bootloader_support/include_priv/bootloader_flash.h b/components/bootloader_support/include_priv/bootloader_flash.h index d70ec22d56..769c47b904 100644 --- a/components/bootloader_support/include_priv/bootloader_flash.h +++ b/components/bootloader_support/include_priv/bootloader_flash.h @@ -29,11 +29,11 @@ /** * @brief Map a region of flash to data memory * - * @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_unmapped before another region is mapped. + * @important In bootloader code, only one region can be bootloader_mmaped at once. The previous region must be bootloader_munmapped before another region is mapped. * * @important In app code, these functions are not thread safe. * - * Call bootloader_unmap once for each successful call to bootloader_mmap. + * Call bootloader_munmap once for each successful call to bootloader_mmap. * * In esp-idf app, this function maps directly to spi_flash_mmap. * @@ -49,9 +49,9 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size); /** * @brief Unmap a previously mapped region of flash * - * Call bootloader_unmap once for each successful call to bootloader_mmap. + * Call bootloader_munmap once for each successful call to bootloader_mmap. */ -void bootloader_unmap(const void *mapping); +void bootloader_munmap(const void *mapping); /** * @brief Read data from Flash. diff --git a/components/bootloader_support/src/bootloader_flash.c b/components/bootloader_support/src/bootloader_flash.c index fcaf24ad2c..3fd107dea8 100644 --- a/components/bootloader_support/src/bootloader_flash.c +++ b/components/bootloader_support/src/bootloader_flash.c @@ -38,7 +38,7 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size) return result; } -void bootloader_unmap(const void *mapping) +void bootloader_munmap(const void *mapping) { if(mapping && map) { spi_flash_munmap(map); @@ -68,7 +68,7 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size) } uint32_t src_addr_aligned = src_addr & 0xffff0000; - uint32_t count = (size + 0xffff) / 0x10000; + uint32_t count = (size + (src_addr - src_addr_aligned) + 0xffff) / 0x10000; Cache_Read_Disable(0); Cache_Flush(0); ESP_LOGD(TAG, "mmu set paddr=%08x count=%d", src_addr_aligned, count ); @@ -80,7 +80,7 @@ const void *bootloader_mmap(uint32_t src_addr, uint32_t size) return (void *)(0x3f400000 + (src_addr - src_addr_aligned)); } -void bootloader_unmap(const void *mapping) +void bootloader_munmap(const void *mapping) { if (mapped) { /* Full MMU reset */ diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 3e7dcafa9e..42a3e4ffe2 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -70,8 +70,8 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const ESP_LOGE(TAG, "invalid segment length 0x%x", segment_header->data_len); err = ESP_ERR_IMAGE_INVALID; } - ESP_LOGV(TAG, "segment data length 0x%x", segment_header->data_len); next_addr += sizeof(esp_image_segment_header_t); + ESP_LOGV(TAG, "segment data length 0x%x data starts 0x%x", segment_header->data_len, next_addr); *segment_data_offset = next_addr; next_addr += segment_header->data_len; } @@ -124,7 +124,7 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length) for(int i = 0; i < segment_header.data_len; i++) { checksum ^= segment_data[i]; } - bootloader_unmap(segment_data); + bootloader_munmap(segment_data); } /* End of image, verify checksum */ diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index 2cfe505492..d908d39c38 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -90,7 +90,7 @@ static bool secure_boot_generate(uint32_t image_len){ for (int i = 0; i < image_len; i+= HASH_BLOCK_SIZE) { ets_secure_boot_hash(image + i/sizeof(void *)); } - bootloader_unmap(image); + bootloader_munmap(image); ets_secure_boot_obtain(); ets_secure_boot_rd_abstract(buf); diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 5f02de38b0..5106eb396f 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -43,9 +43,10 @@ extern const uint8_t signature_verification_key_end[] asm("_binary_signature_ver esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) { sha_context sha; - uint8_t digest[64]; + uint8_t digest[32]; ptrdiff_t keylen; - const uint8_t *data; + const uint8_t *data, *digest_data; + uint32_t digest_len, chunk_len; const signature_block_t *sigblock; bool is_valid; @@ -68,16 +69,23 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) /* Use ROM SHA functions directly */ ets_sha_enable(); ets_sha_init(&sha); - ets_sha_update(&sha, SHA2_512, data, length); - ets_sha_finish(&sha, SHA2_512, digest); + digest_len = length * 8; + digest_data = data; + while (digest_len > 0) { + uint32_t chunk_len = (digest_len > 64) ? 64 : digest_len; + ets_sha_update(&sha, SHA2_256, digest_data, chunk_len); + digest_len -= chunk_len; + digest_data += chunk_len / 8; + } + ets_sha_finish(&sha, SHA2_256, digest); ets_sha_disable(); #else /* Use thread-safe esp-idf SHA layer */ - esp_sha512_init(&sha); - esp_sha512_start(&sha, false); - esp_sha512_update(&sha, data, length); - esp_sha512_finish(&sha, digest); - esp_sha512_free(&sha); + esp_sha256_init(&sha); + esp_sha256_start(&sha, false); + esp_sha256_update(&sha, data, length); + esp_sha256_finish(&sha, digest); + esp_sha256_free(&sha); #endif keylen = signature_verification_key_end - signature_verification_key_start; @@ -90,10 +98,10 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) digest, sizeof(digest), sigblock->signature, uECC_secp256r1()); - bootloader_unmap(data); + bootloader_munmap(data); return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID; unmap_and_fail: - bootloader_unmap(data); + bootloader_munmap(data); return ESP_FAIL; } diff --git a/components/esptool_py/Makefile.projbuild b/components/esptool_py/Makefile.projbuild index 5807512b86..bfbece45e6 100644 --- a/components/esptool_py/Makefile.projbuild +++ b/components/esptool_py/Makefile.projbuild @@ -24,21 +24,24 @@ ESPTOOL_FLASH_OPTIONS := --flash_mode $(ESPFLASHMODE) --flash_freq $(ESPFLASHFRE 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) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $< ifdef CONFIG_SECURE_BOOTLOADER_ENABLED ifndef IS_BOOTLOADER_BUILD - $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place +# for secure boot, add a signing step to get from unsiged app to signed app +APP_BIN_UNSIGNED := $(APP_BIN:.bin=-unsigned.bin) + +$(APP_BIN): $(APP_BIN_UNSIGNED) + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $^ # signed in-place endif endif +# non-secure boot (or bootloader), both these files are the same +APP_BIN_UNSIGNED ?= $(APP_BIN) + +$(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) + $(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))..." diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 98e5dbfa78..d8b1ffdd50 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 98e5dbfa78fa53cebcb4c56530e683f889bf21c3 +Subproject commit d8b1ffdd500bd80a35fb66b64e2733195b39e519 diff --git a/components/partition_table/Makefile.projbuild b/components/partition_table/Makefile.projbuild index 4273f53c3a..f8b822ef2c 100644 --- a/components/partition_table/Makefile.projbuild +++ b/components/partition_table/Makefile.projbuild @@ -20,12 +20,19 @@ PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(s PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin)) -$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_CSV_PATH) +ifdef CONFIG_SECURE_BOOTLOADER_ENABLED +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) + $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $< +else +# secure bootloader disabled, both files are the same +PARTITION_TABLE_BIN_UNSIGNED := $(PARTITION_TABLE_BIN) +endif + +$(PARTITION_TABLE_BIN_UNSIGNED): $(PARTITION_TABLE_CSV_PATH) $(SDKCONFIG_MAKEFILE) @echo "Building partitions from $(PARTITION_TABLE_CSV_PATH)..." $(Q) $(GEN_ESP32PART) $< $@ -ifdef CONFIG_SECURE_BOOTLOADER_ENABLED - $(Q) $(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) $@ # signed in-place -endif all_binaries: $(PARTITION_TABLE_BIN) diff --git a/components/partition_table/gen_esp32part.py b/components/partition_table/gen_esp32part.py index b399f31b78..cb6a5f24a1 100755 --- a/components/partition_table/gen_esp32part.py +++ b/components/partition_table/gen_esp32part.py @@ -86,11 +86,14 @@ class PartitionTable(list): @classmethod def from_binary(cls, b): - if len(b) % 32 != 0: - raise InputError("Partition table length must be a multiple of 32 bytes. Got %d bytes." % len(b)) result = cls() for o in range(0,len(b),32): - result.append(PartitionDefinition.from_binary(b[o:o+32])) + data = b[o:o+32] + if len(data) != 32: + raise InputError("Ran out of partition table data before reaching end marker") + if data == '\xFF'*32: + break # end of partition table + result.append(PartitionDefinition.from_binary(data)) return result def to_binary(self): diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 169f2d56bc..9a22aeca97 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -19,28 +19,28 @@ Secure Boot Process Overview This is a high level overview of the secure boot process. Step by step instructions are supplied under `How To Enable Secure Boot`. Further in-depth details are supplied under `Technical Details`: -1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Bootloader Config". +1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Secure Boot Configuration". 2. Bootloader Config includes the path to a secure boot signing key. This is a ECDSA public/private key pair in a PEM format file. -2. The software bootloader image is built by esp-idf with the public key (signature verification) portion of the secure boot signing key compiled in, and with a secure boot flag set in its header. This software bootloader image is flashed at offset 0x1000. +2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. -3. On first boot, the software bootloader tests the secure boot flag. If it is set, the following process is followed to enable secure boot: +3. On first boot, the software bootloader follows the following process to enable secure boot: - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) - - Bootloader also disables JTAG via efuse. + - Depending on menuconfig choices, the bootloader may also disable JTAG and the UART bootloader function, both via efuse. (This is recommended.) 4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, for technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the secure boot signing key's public key (embedded in the bootloader itself, and therefore validated as part of the bootloader digest) to verify all subsequent partition tables and app images before they are booted. +5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify all subsequent partition tables and app images before they are booted. Keys ---- The following keys are used by the secure boot process: -- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from a hardware random number generation, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. +- "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from the internal hardware random number generator, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. - "secure boot signing key" is a standard ECDSA public/private key pair (NIST256p aka prime256v1 curve) in PEM format. @@ -52,15 +52,15 @@ The following keys are used by the secure boot process: How To Enable Secure Boot ------------------------- -1. Run ``make menuconfig``, navigate to "Bootloader Config" -> "Secure Boot" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) +1. Run ``make menuconfig``, navigate to "Secure Boot Configuration" and select the option "One-time Flash". (To understand the alternative "Reflashable" choice, see `Re-Flashable Software Bootloader`.) 2. Select a name for the secure boot signing key. This option will appear after secure boot is enabled. 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. -3. Set other config 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 +3. 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 4. The first time you run ``make``, 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 genereated 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** 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 `Generating Secure Boot Signing Key` for more details. @@ -76,16 +76,16 @@ How To Enable Secure Boot *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. -9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the signing key). +9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the public key portion of the secure boot signing key). Re-Flashable Software Bootloader -------------------------------- -The "Secure Boot: One-Time Flash" is the recommended software bootloader configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. +Configuration "Secure Boot: One-Time Flash" is the recommended configuration for production devices. In this mode, each device gets a unique key that is never stored outside the device. However, an alternative mode "Secure Boot: Reflashable" is also available. This mode allows you to supply a 256-bit key file that is used for the secure bootloader key. As you have the key file, you can generate new bootloader images and secure boot digests for them. -In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 value is used to generate an 256-bit value which is then burned to efuse and used to protect the bootloader. This is a convenience so you only need to generate/protect a single private key. +In the esp-idf build process, this 256-bit key file is derived from the app signing key generated during the generate_signing_key step above. The private key's SHA-256 digest is used as the 256-bit secure bootloader key. This is a convenience so you only need to generate/protect a single private key. *NOTE*: Although it's possible, we strongly recommend not generating one secure boot key and flashing it to every device in a production environment. The "One-Time Flash" option is recommended for production environments. @@ -95,7 +95,7 @@ To enable a reflashable bootloader: 2. Follow the steps shown above to choose a signing key file, and generate the key file. -3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key you generated for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the derived key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-generated digest (generated during the build process, using the derived key). +3. Run ``make bootloader``. A 256-bit key file will be created, derived from the private key that is used for signing. Two sets of flashing steps will be printed - the first set of steps includes an ``espefuse.py burn_key`` command which is used to write the bootloader key to efuse. (Flashing this key is a one-time-only process.) The second set of steps can be used to reflash the bootloader with a pre-calculated digest (generated during the build process). 4. Resume from `Step 6` of the one-time process, to flash the bootloader and enable secure boot. Watch the console log output closely to ensure there were no errors in the secure boot configuration. @@ -114,6 +114,13 @@ openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key. Remember that the strength of the secure boot system depends on keeping the signing key private. +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. These include flash encryption, disabling of JTAG, and disabling alternative boot modes. Technical Details ----------------- @@ -143,9 +150,9 @@ Items marked with (^) are to fulfill hardware restrictions, as opposed to crypto 1. Prefix the image with a 128 byte randomly generated IV. 2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) 3. For each 16 byte plaintext block of the input image: - - Reverse the byte order of the plaintext block (^) + - Reverse the byte order of the plaintext input block (^) - Apply AES256 in ECB mode to the plaintext block. - - Reverse the byte order of the 16 bytes of ciphertext output. (^) + - Reverse the byte order of the ciphertext output block. (^) - Append to the overall ciphertext output. 4. Byte-swap each 4 byte word of the ciphertext (^) 5. Calculate SHA-512 of the ciphertext. @@ -158,10 +165,12 @@ Image Signing Algorithm Deterministic ECDSA as specified by `RFC6979`. - Curve is NIST256p (openssl calls this curve "prime256v1", it is also sometimes called secp256r1). +- Hash function is SHA256. - Key format used for storage is PEM. - In the bootloader, the public key (for signature verification) is flashed as 64 raw bytes. - Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. + .. _esp-idf boot process: ../boot-process.rst .. _RFC6979: https://tools.ietf.org/html/rfc6979 diff --git a/make/common.mk b/make/common.mk index 0c523c8775..bec8151b55 100644 --- a/make/common.mk +++ b/make/common.mk @@ -6,7 +6,8 @@ # # (Note that we only rebuild auto.conf automatically for some targets, # see project_config.mk for details.) --include $(BUILD_DIR_BASE)/include/config/auto.conf +SDKCONFIG_MAKEFILE := $(BUILD_DIR_BASE)/include/config/auto.conf +-include $(SDKCONFIG_MAKEFILE) #Handling of V=1/VERBOSE=1 flag # From 5a5e19cd8b5b1ea09adce8ae73e62e400b09b65a Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Wed, 9 Nov 2016 11:55:09 +1100 Subject: [PATCH 14/19] Bump esptool revision (espefuse.py fixes) --- components/esptool_py/esptool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index d8b1ffdd50..b1e00025fa 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit d8b1ffdd500bd80a35fb66b64e2733195b39e519 +Subproject commit b1e00025fa6cbc63062b205259ee70d91bfe4989 From bcdebda8e47961751d35031272350f6a33a004b4 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 14:44:10 +1100 Subject: [PATCH 15/19] Build system: Don't shell-quote SEPARATOR variable or it evaluates as a bunch of wildcards! --- components/bootloader/Makefile.projbuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 1b1c07bea3..3ed5e18783 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -37,7 +37,7 @@ ifdef CONFIG_SECURE_BOOTLOADER_DISABLED # with 'make flash' and no warnings are printed. bootloader: $(BOOTLOADER_BIN) - @echo "$(SEPARATOR)" + @echo $(SEPARATOR) @echo "Bootloader built. Default flash command is:" @echo "$(ESPTOOLPY_WRITE_FLASH) 0x1000 $(BOOTLOADER_BIN)" From 8691b54758102275b7e635bdaa52c2fafe8b4334 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:14:13 +1100 Subject: [PATCH 16/19] secure boot: Rename efuse option for UART bootloader to option for ROM interpreter --- components/bootloader/Kconfig.projbuild | 38 +++++++++---------- .../src/secure_boot_signatures.c | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/components/bootloader/Kconfig.projbuild b/components/bootloader/Kconfig.projbuild index 949638594d..50165d0e58 100644 --- a/components/bootloader/Kconfig.projbuild +++ b/components/bootloader/Kconfig.projbuild @@ -85,31 +85,31 @@ config SECURE_BOOT_SIGNING_KEY See docs/security/secure-boot.rst for details. config SECURE_BOOT_DISABLE_JTAG - bool "First boot: Permanently disable JTAG" - depends on SECURE_BOOTLOADER_ENABLED - default Y - help - Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. + bool "First boot: Permanently disable JTAG" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader. - It is recommended this option remains set for production environments. + It is recommended this option remains set for production environments. -config SECURE_BOOT_DISABLE_UART_BOOTLOADER - bool "First boot: Permanently disable UART bootloader" - depends on SECURE_BOOTLOADER_ENABLED - default Y - help - Bootloader permanently disables UART and other bootloader modes when enabling secure boot. This happens on first boot. +config SECURE_BOOT_DISABLE_ROM_BASIC + bool "First boot: Permanently disable ROM BASIC fallback" + depends on SECURE_BOOTLOADER_ENABLED + default Y + help + Bootloader permanently disables ROM BASIC (on UART console) as a fallback if the bootloader image becomes invalid. This happens on first boot. - It is recommended this option remains set for production environments. + It is recommended this option remains set in production environments. config SECURE_BOOT_TEST_MODE - bool "Test mode: don't actually enable secure boot" - depends on SECURE_BOOTLOADER_ENABLED - default N - help - If this option is set, all permanent secure boot changes (via Efuse) are disabled. + bool "Test mode: don't actually enable secure boot" + depends on SECURE_BOOTLOADER_ENABLED + default N + help + If this option is set, all permanent secure boot changes (via Efuse) are disabled. - This option is for testing purposes only - it effectively completely disables secure boot protection. + This option is for testing purposes only - it effectively completely disables secure boot protection. config SECURE_BOOTLOADER_ENABLED bool diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 5106eb396f..6d47651b2f 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -46,7 +46,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length) uint8_t digest[32]; ptrdiff_t keylen; const uint8_t *data, *digest_data; - uint32_t digest_len, chunk_len; + uint32_t digest_len; const signature_block_t *sigblock; bool is_valid; From 572f62928b9dd6a5036b98893a6a2059c34d9e87 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:40:29 +1100 Subject: [PATCH 17/19] Secure boot: Documentation tweaks --- docs/index.rst | 1 + docs/security/secure-boot.rst | 30 ++++++++++++++++-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index c973950615..30cfa177c3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,6 +34,7 @@ Contents: partition-tables build_system openocd + Secure Boot .. toctree:: :caption: API Reference diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index 9a22aeca97..c08b17cb47 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -3,17 +3,20 @@ Secure Boot 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 Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However for maximum protection we recommend using both features together. +Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. + Background ---------- -- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored in Efuses internal to the chip. +- Most data is stored in flash. Flash access does not need to be protected from physical access in order for secure boot to function, because critical data is stored (non-software-accessible) in Efuses internal to the chip. - Efuses are used to store the secure bootloader key (in efuse block 2), and also a single Efuse bit (ABS_DONE_0) is burned (written to 1) to permanently enable secure boot on the chip. For more details about efuse, see the (forthcoming) chapter in the Technical Reference Manual. - To understand the secure boot process, first familiarise yourself with the standard `esp-idf boot process`. +- Both stages of the boot process (initial software bootloader load, and subsequent partition & app loading) are verified by the secure boot process, in a "chain of trust" relationship. + Secure Boot Process Overview ---------------------------- @@ -21,19 +24,19 @@ This is a high level overview of the secure boot process. Step by step instructi 1. The options to enable secure boot are provided in the ``make menuconfig`` hierarchy, under "Secure Boot Configuration". -2. Bootloader Config includes the path to a secure boot signing key. This is a ECDSA public/private key pair in a PEM format file. +2. Secure Boot Configuration includes "Secure boot signing key", which is a file path. This file is a ECDSA public/private key pair in a PEM format file. 2. The software bootloader image is built by esp-idf with secure boot support enabled and the public key (signature verification) portion of the secure boot signing key compiled in. This software bootloader image is flashed at offset 0x1000. 3. On first boot, the software bootloader follows the following process to enable secure boot: - Hardware secure boot support generates a device secure bootloader key (generated via hardware RNG, then stored read/write protected in efuse), and a secure digest. The digest is derived from the key, an IV, and the bootloader image contents. - The secure digest is flashed at offset 0x0 in the flash. + - Depending on Secure Boot Configuration, efuses are burned to disable JTAG and the ROM BASIC interpreter (it is strongly recommended these options are turned on.) - Bootloader permanently enables secure boot by burning the ABS_DONE_0 efuse. The software bootloader then becomes protected (the chip will only boot a bootloader image if the digest matches.) - - Depending on menuconfig choices, the bootloader may also disable JTAG and the UART bootloader function, both via efuse. (This is recommended.) -4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, for technical details see `Hardware Secure Boot Support`. +4. On subsequent boots the ROM bootloader sees that the secure boot efuse is burned, reads the saved digest at 0x0 and uses hardware secure boot support to compare it with a newly calculated digest. If the digest does not match then booting will not continue. The digest and comparison are performed entirely by hardware, and the calculated digest is not readable by software. For technical details see `Hardware Secure Boot Support`. -5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify all subsequent partition tables and app images before they are booted. +5. When running in secure boot mode, the software bootloader uses the secure boot signing key (the public key of which is embedded in the bootloader itself, and therefore validated as part of the bootloader) to verify the signature appended to all subsequent partition tables and app images before they are booted. Keys ---- @@ -42,11 +45,11 @@ The following keys are used by the secure boot process: - "secure bootloader key" is a 256-bit AES key that is stored in Efuse block 2. The bootloader can generate this key itself from the internal hardware random number generator, the user does not need to supply it (it is optionally possible to supply this key, see `Re-Flashable Software Bootloader`). The Efuse holding this key is read & write protected (preventing software access) before secure boot is enabled. -- "secure boot signing key" is a standard ECDSA public/private key pair (NIST256p aka prime256v1 curve) in PEM format. +- "secure boot signing key" is a standard ECDSA public/private key pair (see `Image Signing Algorithm`) in PEM format. - The public key from this key pair (for signature verificaton but not signature creation) is compiled into the software bootloader and used to verify the second stage of booting (partition table, app image) before booting continues. The public key can be freely distributed, it does not need to be kept secret. - - The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to a bootloader with secure boot using the matching public key. + - The private key from this key pair *must be securely kept private*, as anyone who has this key can authenticate to any bootloader that is configured with secure boot and the matching public key. How To Enable Secure Boot @@ -76,7 +79,7 @@ How To Enable Secure Boot *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. -9. On subsequent boots, the secure boot hardware will verify the software bootloader (using the secure bootloader key) and then the software bootloader will verify the partition table and app image (using the public key portion of the secure boot signing key). +9. On subsequent boots, the secure boot hardware will verify the software bootloader has not changed (using the secure bootloader key) and then the software bootloader will verify the signed partition table and app image (using the public key portion of the secure boot signing key). Re-Flashable Software Bootloader -------------------------------- @@ -120,7 +123,7 @@ 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. These include flash encryption, disabling of JTAG, and disabling alternative boot modes. +* 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. Technical Details ----------------- @@ -136,19 +139,19 @@ The Secure Boot support hardware can perform three basic operations: 2. Generate a digest from data (usually the bootloader image from flash) using a key stored in Efuse block 2. The key in Efuse can (& should) be read/write protected, which prevents software access. For full details of this algorithm see `Secure Bootloader Digest Algorithm`. The digest can only be read back by software if Efuse ABS_DONE_0 is *not* burned (ie still 0). -3. Verify a digest from data (usually the bootloader image from flash), and compare it to a pre-existing digest (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. +3. Generate a digest from data (usually the bootloader image from flash) using the same algorithm as step 2 and compare it to a pre-calculated digest supplied in a buffer (usually read from flash offset 0x0). The hardware returns a true/false comparison without making the digest available to software. This function is available even when Efuse ABS_DONE_0 is burned. Secure Bootloader Digest Algorithm ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Starting with an "image" of binary data as input, this algorithm generates a digest as output. +Starting with an "image" of binary data as input, this algorithm generates a digest as output. The digest is sometimes referred to as an "abstract" in hardware documentation. For a Python version of this algorithm, see the `espsecure.py` tool in the components/esptool_py directory. Items marked with (^) are to fulfill hardware restrictions, as opposed to cryptographic restrictions. 1. Prefix the image with a 128 byte randomly generated IV. -2. If the image is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) +2. If the image length is not modulo 128, pad the image to a 128 byte boundary with 0xFF. (^) 3. For each 16 byte plaintext block of the input image: - Reverse the byte order of the plaintext input block (^) - Apply AES256 in ECB mode to the plaintext block. @@ -171,6 +174,5 @@ Deterministic ECDSA as specified by `RFC6979`. - Image signature is 68 bytes - a 4 byte version word (currently zero), followed by a 64 bytes of signature data. These 68 bytes are appended to an app image or partition table data. - .. _esp-idf boot process: ../boot-process.rst .. _RFC6979: https://tools.ietf.org/html/rfc6979 From 0b4fe9dd6dc62571abcc66c0235582f00d94d2e5 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:40:58 +1100 Subject: [PATCH 18/19] secure boot: Add warnings this feature is not finished yet --- components/bootloader/Makefile.projbuild | 21 +++++++++++++++++++++ docs/security/secure-boot.rst | 1 + 2 files changed, 22 insertions(+) diff --git a/components/bootloader/Makefile.projbuild b/components/bootloader/Makefile.projbuild index 3ed5e18783..551bceb981 100644 --- a/components/bootloader/Makefile.projbuild +++ b/components/bootloader/Makefile.projbuild @@ -47,6 +47,16 @@ bootloader-flash: $(BOOTLOADER_BIN) $(BOOTLOADER_MAKE) flash else ifdef CONFIG_SECURE_BOOTLOADER_ONE_TIME_FLASH + +#### TEMPORARILY DISABLE THIS OPTION +ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1") +bootloader: + @echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device" + @echo "If you flash this bootloader, you will be left with an non-updateable bootloader that is missing features." + @echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make." + exit 1 +else + # One time flashing requires user to run esptool.py command themselves, # and warning is printed about inability to reflash. @@ -57,10 +67,20 @@ bootloader: $(BOOTLOADER_BIN) @echo $(SEPARATOR) @echo "* IMPORTANT: After first boot, BOOTLOADER CANNOT BE RE-FLASHED on same device" +endif # IDF_INSECURE_SECURE_BOOT else ifdef CONFIG_SECURE_BOOTLOADER_REFLASHABLE # Reflashable secure bootloader # generates a digest binary (bootloader + digest) +#### TEMPORARILY DISABLE THIS OPTION +ifneq ("$(IDF_INSECURE_SECURE_BOOT)","1") +bootloader: + @echo "Secure boot features are not yet mature, so the current secure bootloader will not properly secure the device." + @echo "If using this feature, expect to reflash the bootloader at least one more time." + @echo "If you really want to do this, set the environment variable IDF_INSECURE_SECURE_BOOT=1 and rerun make." + exit 1 +else + BOOTLOADER_DIGEST_BIN := $(BOOTLOADER_BUILD_DIR)/bootloader-reflash-digest.bin SECURE_BOOTLOADER_KEY := $(BOOTLOADER_BUILD_DIR)/secure-bootloader-key.bin @@ -83,6 +103,7 @@ $(BOOTLOADER_DIGEST_BIN): $(BOOTLOADER_BIN) $(SECURE_BOOTLOADER_KEY) @echo "DIGEST $(notdir $@)" $(Q) $(ESPSECUREPY) digest_secure_bootloader -k $(SECURE_BOOTLOADER_KEY) -o $@ $< +endif # IDF_INSECURE_SECURE_BOOT else bootloader: @echo "Invalid bootloader target: bad sdkconfig?" diff --git a/docs/security/secure-boot.rst b/docs/security/secure-boot.rst index c08b17cb47..bdc1b71699 100644 --- a/docs/security/secure-boot.rst +++ b/docs/security/secure-boot.rst @@ -5,6 +5,7 @@ Secure Boot is a feature for ensuring only your code can run on the chip. Data l Secure Boot is separate from the Encrypted Flash feature, and you can use secure boot without encrypting the flash contents. However we recommend using both features together for a secure environment. +**IMPORTANT: As Encrypted Flash feature and related security features are not yet released, Secure Boot should not be considered sufficient for a secure device and we strongly recommend not enabling the one-time secure bootloader feature until it is mature.** Background ---------- From 09c7ccfa2cd2c21f14c0c599e25e3bb473dd64eb Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Fri, 11 Nov 2016 15:47:38 +1100 Subject: [PATCH 19/19] bootloader: Fix unused variable errors when secure boot is disabled --- components/bootloader/src/main/bootloader_start.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 36d3ff0d29..a811294a8f 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -109,7 +109,6 @@ void IRAM_ATTR call_start_cpu0() */ bool load_partition_table(bootloader_state_t* bs) { - esp_err_t err; const esp_partition_info_t *partitions; const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); @@ -121,7 +120,7 @@ bool load_partition_table(bootloader_state_t* bs) #ifdef CONFIG_SECURE_BOOTLOADER_ENABLED if(esp_secure_boot_enabled()) { ESP_LOGI(TAG, "Verifying partition table signature..."); - err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + esp_err_t err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to verify partition table signature."); return false; @@ -227,7 +226,6 @@ void bootloader_main() { ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); - esp_err_t err; esp_image_header_t fhdr; bootloader_state_t bs; SpiFlashOpResult spiRet1,spiRet2; @@ -322,7 +320,7 @@ void bootloader_main() #ifdef CONFIG_SECURE_BOOTLOADER_ENABLED /* Generate secure digest from this bootloader to protect future modifications */ - err = esp_secure_boot_permanently_enable(); + esp_err_t err = esp_secure_boot_permanently_enable(); if (err != ESP_OK) { ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); /* Allow booting to continue, as the failure is probably