Merge branch 'feature/esptool_secure_boot' into 'master'

Secure boot support

Also includes a lot of esptool.py changes (two new command line tools, espefuse.py and espsecure.py)
https://github.com/themadinventor/esptool/compare/feature/esp32_v20_refactor...feature/esp32_secure_boot?expand=1


See merge request !163
This commit is contained in:
Angus Gratton
2016-11-14 11:32:04 +08:00
40 changed files with 1821 additions and 531 deletions

View File

@@ -20,12 +20,99 @@ 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
endmenu
menu "Secure boot configuration"
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 a reusable secure bootloader key, derived (via SHA-256) from the secure boot signing key.
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_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_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_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 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.
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

View File

@@ -15,34 +15,107 @@ BOOTLOADER_BUILD_DIR=$(abspath $(BUILD_DIR_BASE)/bootloader)
BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin
BOOTLOADER_SDKCONFIG=$(BOOTLOADER_BUILD_DIR)/sdkconfig
# 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
# 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)
$(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
# 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 $^"
all_binaries: $(BOOTLOADER_BIN)
ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN)
# bootloader-flash calls flash in the bootloader dummy project
bootloader-flash: $(BOOTLOADER_BIN)
$(ESPTOOLPY_WRITE_FLASH) 0x1000 $^
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.
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"
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
$(SECURE_BOOTLOADER_KEY): $(SECURE_BOOT_SIGNING_KEY)
$(Q) $(ESPSECUREPY) digest_private_key -k $< $@
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_BOOTLOADER_KEY)"
@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-use the same secure boot keyfile on multiple production devices."
$(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?"
@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
# config
$(BOOTLOADER_SDKCONFIG): $(PROJECT_PATH)/sdkconfig | $(BOOTLOADER_BUILD_DIR)

View File

@@ -4,7 +4,7 @@
#
PROJECT_NAME := bootloader
COMPONENTS := esptool_py bootloader 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.
#

View File

@@ -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
@@ -36,7 +34,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
@@ -62,12 +59,7 @@ 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(void);
#ifdef __cplusplus
}

View File

@@ -33,6 +33,9 @@
#include "soc/timer_group_reg.h"
#include "sdkconfig.h"
#include "esp_image_format.h"
#include "esp_secure_boot.h"
#include "bootloader_flash.h"
#include "bootloader_config.h"
@@ -49,7 +52,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 set_cache_and_start_app(uint32_t drom_addr,
uint32_t drom_load_addr,
@@ -94,53 +97,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
@@ -148,85 +104,103 @@ void boot_cache_redirect( uint32_t pos, size_t size )
* 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_partition_info_t partition;
uint32_t end = addr + 0x1000;
int index = 0;
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);
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);
#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
if(esp_secure_boot_enabled()) {
ESP_LOGI(TAG, "Verifying partition table signature...");
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;
}
ESP_LOGD(TAG, "Partition table signature verified");
}
#endif
partitions = bootloader_mmap(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN);
if (!partitions) {
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", ESP_PARTITION_TABLE_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_munmap(partitions);
ESP_LOGI(TAG,"End of partition table");
return true;
}
@@ -254,8 +228,10 @@ 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));
ESP_LOGI(TAG, "compile time " __TIME__ );
@@ -263,16 +239,17 @@ 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);
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;
}
@@ -281,9 +258,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_munmap(ota_select_map);
if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) {
// init status flash
load_part_pos = bs.ota[0];
@@ -329,35 +316,61 @@ 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;
}
#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
/* Generate secure digest from this bootloader to protect future
modifications */
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
due to user-configured EFUSEs for testing...
*/
}
#endif
if(fhdr.encrypt_flag == 0x01) {
/* encrypt flash */
/* encrypt flash */
if (false == flash_encrypt(&bs)) {
ESP_LOGE(TAG, "flash encrypt failed");
return;
}
}
// 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);
}
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_err_t err;
esp_image_header_t image_header;
memcpy(&image_header, MEM_CACHE(pos), sizeof(image_header));
pos += sizeof(image_header);
uint32_t image_length;
/* 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) {
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);
return;
}
uint32_t drom_addr = 0;
uint32_t drom_load_addr = 0;
@@ -366,24 +379,27 @@ 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;
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(&section_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
@@ -395,47 +411,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 segment, 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 segment, 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 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", pos);
ESP_LOGD(TAG, "Skipping RTC data segment 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_munmap(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,
@@ -520,7 +539,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 );

View File

@@ -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 <20><>test bin (if use)<29><>ota bin
* <20><>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;
}
}

View File

@@ -1,127 +0,0 @@
// 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 <string.h>
#include "esp_attr.h"
#include "esp_types.h"
#include "esp_log.h"
#include "rom/cache.h"
#include "rom/ets_sys.h"
#include "rom/spi_flash.h"
#include "rom/secure_boot.h"
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include "soc/efuse_reg.h"
#include "soc/rtc_cntl_reg.h"
#include "sdkconfig.h"
#include "bootloader_config.h"
static const char* TAG = "secure_boot";
/**
* @function : secure_boot_generate
* @description: generate boot abstract & iv
*
* @inputs: bool
*/
bool secure_boot_generate(uint32_t bin_len){
SpiFlashOpResult spiRet;
uint16_t i;
uint32_t buf[32];
if (bin_len % 128 != 0) {
bin_len = (bin_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;
}
/* 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));
}
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) */
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;
}
/**
* @function : secure_boot
* @description: protect boot code in flash
*
* @inputs: bool
*/
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;
}
}
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;
}