secure boot: Functional partition table & app signature verification

This commit is contained in:
Angus Gratton 2016-11-07 15:45:57 +11:00
parent fe66dd85f0
commit e459f803da
14 changed files with 116 additions and 79 deletions

View file

@ -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);
}
}

View file

@ -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
*

View file

@ -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.

View file

@ -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.

View file

@ -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 */

View file

@ -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 */

View file

@ -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);

View file

@ -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;
}

View file

@ -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))..."

@ -1 +1 @@
Subproject commit 98e5dbfa78fa53cebcb4c56530e683f889bf21c3
Subproject commit d8b1ffdd500bd80a35fb66b64e2733195b39e519

View file

@ -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)

View file

@ -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):

View file

@ -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<Secure Boot Process Overview>` 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

View file

@ -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
#