Merge branch 'feature/esptool_flash_encryption' into 'master'

Flash encryption support

Flash encryption support in build system, tooling

To come in future MR:
* On-device key generation on first boot (for production devices), need to finalise testing of bootloader entropy seeding.
* spi_flash_encrypted_write to support non-32-byte block writes (at least optionally.)
* I think a lot of the bootloader_support component can possibly be rolled into "spiflash" and other components, to use a common API.

See merge request !240
This commit is contained in:
Angus Gratton 2016-12-12 06:50:46 +08:00
commit bab1d49f1f
41 changed files with 1686 additions and 475 deletions

View file

@ -1,7 +1,7 @@
menu "Bootloader config"
choice LOG_BOOTLOADER_LEVEL
bool "Bootloader log verbosity"
default LOG_BOOTLOADER_LEVEL_WARN
default LOG_BOOTLOADER_LEVEL_INFO
help
Specify how much output to see in bootloader logs.
@ -32,23 +32,24 @@ endmenu
menu "Secure boot configuration"
menu "Security features"
choice SECURE_BOOTLOADER
bool "Secure bootloader"
default SECURE_BOOTLOADER_DISABLED
config SECURE_BOOT_ENABLED
bool "Enable secure boot in bootloader"
default N
help
Build a bootloader with the secure boot flag enabled.
Build a bootloader which enables secure boot on first boot.
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.
Once enabled, secure boot will not boot a modified bootloader. The bootloader will only load a partition table or boot an app if the data has a verified digital signature.
When enabling secure boot, JTAG and ROM BASIC Interpreter are permanently disabled by default.
See docs/security/secure-boot.rst for details.
config SECURE_BOOTLOADER_DISABLED
bool "Disabled"
choice SECURE_BOOTLOADER_MODE
bool "Secure bootloader mode"
depends on SECURE_BOOT_ENABLED
default SECURE_BOOTLOADER_ONE_TIME_FLASH
config SECURE_BOOTLOADER_ONE_TIME_FLASH
bool "One-time flash"
@ -62,7 +63,7 @@ config SECURE_BOOTLOADER_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 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.
@ -70,10 +71,10 @@ endchoice
config SECURE_BOOT_SIGNING_KEY
string "Secure boot signing key"
depends on SECURE_BOOTLOADER_ENABLED
default secure_boot_signing_key.pem
depends on SECURE_BOOT_ENABLED
default secure_boot_signing_key.pem
help
Path to the key file used to sign partition tables and app images for secure boot.
Path to the key file used to sign partition tables and app images for secure boot. Once secure boot is enabled, bootloader will only boot if partition table and app image are signed.
Key file is an ECDSA private key (NIST256p curve) in PEM format.
@ -84,35 +85,106 @@ 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
config SECURE_BOOT_INSECURE
bool "Allow potentially insecure options"
depends on SECURE_BOOT_ENABLED
default N
help
You can disable some of the default protections offered by secure boot, in order to enable testing or a custom combination of security features.
Only enable these options if you are very sure.
Refer to docs/security/secure-boot.rst and docs/security/flash-encryption.rst for details.
config FLASH_ENCRYPTION_ENABLED
bool "Enable flash encryption on boot"
default N
help
If this option is set, flash contents will be encrypted by the bootloader on first boot.
Note: After first boot, the system will be permanently encrypted.
See docs/securityflash-encryption.rst for details.
config FLASH_ENCRYPTION_INSECURE
bool "Allow potentially insecure options"
depends on FLASH_ENCRYPTION_ENABLED
default N
help
You can disable some of the default protections offered by flash encryption, in order to enable testing or a custom combination of security features.
Only enable these options if you are very sure.
Refer to docs/security/secure-boot.rst and docs/security/flash-encryption.rst for details.
menu "Potentially insecure options"
visible if FLASH_ENCRYPTION_INSECURE || SECURE_BOOT_INSECURE
# NOTE: Options in this menu NEED to have SECURE_BOOT_INSECURE
# and/or FLASH_ENCRYPTION_INSECURE in "depends on", as the menu
# itself doesn't enable/disable its children (if it's not set,
# it's possible for the insecure menu to be disabled but the insecure option
# to remain on which is very bad.)
config SECURE_BOOT_ALLOW_ROM_BASIC
bool "Leave ROM BASIC Interpreter available on reset"
depends on SECURE_BOOT_INSECURE
default N
help
Bootloader permanently disable JTAG (across entire chip) when enabling secure boot. This happens on first boot of the bootloader.
If not set (default), 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.
Only set this option in testing environments.
config SECURE_BOOT_DISABLE_ROM_BASIC
bool "First boot: Permanently disable ROM BASIC fallback"
depends on SECURE_BOOTLOADER_ENABLED
default Y
config SECURE_BOOT_ALLOW_JTAG
bool "Allow JTAG Debugging"
depends on SECURE_BOOT_INSECURE || FLASH_ENCRYPTION_INSECURE
default N
help
Bootloader permanently disables ROM BASIC (on UART console) as a fallback if the bootloader image becomes invalid. This happens on first boot.
If not set (default), the bootloader will permanently disable JTAG (across entire chip) on first boot when either secure boot or flash encryption is enabled.
It is recommended this option remains set in production environments.
Setting this option leaves JTAG on for debugging, which negates all protections of flash encryption and some of the protections of secure boot.
Only set this option in testing environments.
config FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_ENCRYPT
bool "Leave UART bootloader encryption enabled"
depends on FLASH_ENCRYPTION_INSECURE
default N
help
If not set (default), the bootloader will permanently disable UART bootloader encryption access on first boot. If set, the UART bootloader will still be able to access hardware encryption.
It is recommended to only set this option in testing environments.
config FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_DECRYPT
bool "Leave UART bootloader decryption enabled"
depends on FLASH_ENCRYPTION_INSECURE
default N
help
If not set (default), the bootloader will permanently disable UART bootloader decryption access on first boot. If set, the UART bootloader will still be able to access hardware decryption.
Only set this option in testing environments. Setting this option allows complete bypass of flash encryption.
config FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_CACHE
bool "Leave UART bootloader flash cache enabled"
depends on FLASH_ENCRYPTION_INSECURE
default N
help
If not set (default), the bootloader will permanently disable UART bootloader flash cache access on first boot. If set, the UART bootloader will still be able to access the flash cache.
Only set this option in testing environments.
config SECURE_BOOT_TEST_MODE
bool "Test mode: don't actually enable secure boot"
depends on SECURE_BOOTLOADER_ENABLED
bool "Secure boot test mode: don't permanently set any efuses"
depends on SECURE_BOOT_INSECURE
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.
Log output will state changes which would be applied, but they will not be.
config SECURE_BOOTLOADER_ENABLED
bool
default SECURE_BOOTLOADER_ONE_TIME_FLASH || SECURE_BOOTLOADER_REFLASHABLE
This option is for testing purposes only - it completely disables secure boot protection.
endmenu # potentially insecure
endmenu

View file

@ -18,6 +18,9 @@ BOOTLOADER_BIN=$(BOOTLOADER_BUILD_DIR)/bootloader.bin
SECURE_BOOT_SIGNING_KEY=$(abspath $(call dequote,$(CONFIG_SECURE_BOOT_SIGNING_KEY)))
export SECURE_BOOT_SIGNING_KEY # used by bootloader_support component
# Has a matching value in bootloader_support esp_flash_partitions.h
BOOTLOADER_OFFSET := 0x1000
# Custom recursive make for bootloader sub-project
BOOTLOADER_MAKE=+$(MAKE) -C $(BOOTLOADER_COMPONENT_PATH)/src \
V=$(V) BUILD_DIR_BASE=$(BOOTLOADER_BUILD_DIR) TEST_COMPONENTS=
@ -29,55 +32,36 @@ $(BOOTLOADER_BIN): $(SDKCONFIG_MAKEFILE)
clean: bootloader-clean
ifdef CONFIG_SECURE_BOOTLOADER_DISABLED
ifndef CONFIG_SECURE_BOOT_ENABLED
# 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 $^"
@echo "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $^"
ESPTOOL_ALL_FLASH_ARGS += 0x1000 $(BOOTLOADER_BIN)
ESPTOOL_ALL_FLASH_ARGS += $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)
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 "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $(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
@ -88,7 +72,7 @@ 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 "$(ESPTOOLPY_WRITE_FLASH) $(BOOTLOADER_OFFSET) $(BOOTLOADER_BIN)"
@echo $(SEPARATOR)
@echo "To reflash the bootloader after initial flash:"
@echo "$(ESPTOOLPY_WRITE_FLASH) 0x0 $(BOOTLOADER_DIGEST_BIN)"
@ -100,14 +84,16 @@ $(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
ifndef CONFIG_SECURE_BOOT_ENABLED
# don't build bootloader by default is secure boot is enabled
all_binaries: $(BOOTLOADER_BIN)
endif
bootloader-clean:
$(BOOTLOADER_MAKE) app-clean

View file

@ -34,20 +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
#define PART_SUBTYPE_OTA_MASK 0x0f
#define PART_SUBTYPE_TEST 0x20
#define PART_TYPE_DATA 0x01
#define PART_SUBTYPE_DATA_OTA 0x00
#define PART_SUBTYPE_DATA_RF 0x01
#define PART_SUBTYPE_DATA_WIFI 0x02
#define PART_TYPE_END 0xff
#define PART_SUBTYPE_END 0xff
#define SPI_ERROR_LOG "spi flash error"
typedef struct {

View file

@ -39,6 +39,7 @@
#include "sdkconfig.h"
#include "esp_image_format.h"
#include "esp_secure_boot.h"
#include "esp_flash_encrypt.h"
#include "bootloader_flash.h"
#include "bootloader_config.h"
@ -121,7 +122,7 @@ bool load_partition_table(bootloader_state_t* bs)
ESP_LOGI(TAG, "Partition Table:");
ESP_LOGI(TAG, "## Label Usage Type ST Offset Length");
#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
#ifdef CONFIG_SECURE_BOOT_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);
@ -230,7 +231,9 @@ void bootloader_main()
{
uart_console_configure();
ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION);
#if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_FLASH_ENCRYPTION_ENABLED)
esp_err_t err;
#endif
esp_image_header_t fhdr;
bootloader_state_t bs;
SpiFlashOpResult spiRet1,spiRet2;
@ -245,7 +248,7 @@ void bootloader_main()
REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN );
SPIUnlock();
if(esp_image_load_header(0x1000, &fhdr) != ESP_OK) {
if(esp_image_load_header(0x1000, true, &fhdr) != ESP_OK) {
ESP_LOGE(TAG, "failed to load bootloader header!");
return;
}
@ -324,12 +327,11 @@ void bootloader_main()
return;
}
ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos);
#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
#ifdef CONFIG_SECURE_BOOT_ENABLED
/* Generate secure digest from this bootloader to protect future
modifications */
esp_err_t err = esp_secure_boot_permanently_enable();
ESP_LOGI(TAG, "Checking secure boot...");
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
@ -338,15 +340,28 @@ void bootloader_main()
}
#endif
if(fhdr.encrypt_flag == 0x01) {
/* encrypt flash */
if (false == flash_encrypt(&bs)) {
ESP_LOGE(TAG, "flash encrypt failed");
return;
}
#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED
/* encrypt flash */
ESP_LOGI(TAG, "Checking flash encryption...");
bool flash_encryption_enabled = esp_flash_encryption_enabled();
err = esp_flash_encrypt_check_and_update();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Flash encryption check failed (%d).", err);
return;
}
if (!flash_encryption_enabled && esp_flash_encryption_enabled()) {
/* Flash encryption was just enabled for the first time,
so issue a system reset to ensure flash encryption
cache resets properly */
ESP_LOGI(TAG, "Resetting with flash encryption enabled...");
REG_WRITE(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST);
return;
}
#endif
// copy loaded segments to RAM, set up caches for mapped segments, and start application
ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos);
unpack_load_app(&load_part_pos);
}
@ -358,13 +373,13 @@ static void unpack_load_app(const esp_partition_pos_t* partition)
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);
err = esp_image_basic_verify(partition->offset, true, &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
#ifdef CONFIG_SECURE_BOOT_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);
@ -376,7 +391,7 @@ static void unpack_load_app(const esp_partition_pos_t* partition)
}
#endif
if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) {
if (esp_image_load_header(partition->offset, true, &image_header) != ESP_OK) {
ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset);
return;
}
@ -402,8 +417,8 @@ static void unpack_load_app(const esp_partition_pos_t* partition)
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) {
&image_header, true,
&segment_header, &data_offs) != ESP_OK) {
ESP_LOGE(TAG, "failed to load segment header #%d", segment);
return;
}

View file

@ -1,188 +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_types.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_err.h"
#include "rom/cache.h"
#include "rom/ets_sys.h"
#include "rom/spi_flash.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"
#include "esp_image_format.h"
static const char* TAG = "flash_encrypt";
/**
* @function : bitcount
* @description: calculate bit 1 in flash_crypt_cnt
* if it's even number, need encrypt flash data, and burn efuse
*
* @inputs: n flash_crypt_cnt
* @return: number of 1 in flash_crypt_cnt
*
*/
int bitcount(int n){
int count = 0;
while (n > 0) {
count += n & 1;
n >>= 1;
}
return count;
}
/**
* @function : flash_encrypt_write
* @description: write encrypted data in flash
*
* @inputs: pos address in flash
* len size of data need encrypt
* @return: return true, if the write flash success
*
*/
bool flash_encrypt_write(uint32_t pos, uint32_t len)
{
SpiFlashOpResult spiRet;
uint32_t buf[1024];
int i = 0;
Cache_Read_Disable(0);
for (i = 0;i<((len-1)/0x1000 + 1);i++) {
spiRet = SPIRead(pos, buf, SPI_SEC_SIZE);
if (spiRet != SPI_FLASH_RESULT_OK) {
Cache_Read_Enable(0);
ESP_LOGE(TAG, SPI_ERROR_LOG);
return false;
}
spiRet = SPIEraseSector(pos/SPI_SEC_SIZE);
if (spiRet != SPI_FLASH_RESULT_OK) {
Cache_Read_Enable(0);
ESP_LOGE(TAG, SPI_ERROR_LOG);
return false;
}
spiRet = SPI_Encrypt_Write(pos, buf, SPI_SEC_SIZE);
if (spiRet != SPI_FLASH_RESULT_OK) {
Cache_Read_Enable(0);
ESP_LOGE(TAG, SPI_ERROR_LOG);
return false;
}
pos += SPI_SEC_SIZE;
}
Cache_Read_Enable(0);
return true;
}
/**
* @function : flash_encrypt
* @description: encrypt 2nd boot ,partition table ,factory bin <EFBFBD><EFBFBD>test bin (if use)<EFBFBD><EFBFBD>ota bin
* <EFBFBD><EFBFBD>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)
{
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) {
/* 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 partition table */
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 != 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 != 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 (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;
}
}

View file

@ -0,0 +1,7 @@
$(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

View file

@ -12,19 +12,11 @@ COMPONENT_SRCDIRS := src
#
# Secure boot signing key support
#
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
ifdef CONFIG_SECURE_BOOT_ENABLED
# this path is created relative to the component build directory
SECURE_BOOT_VERIFICATION_KEY := $(abspath signature_verification_key.bin)
$(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 $< $@

View file

@ -0,0 +1,56 @@
// 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 _ESP_EFUSE_H
#define _ESP_EFUSE_H
#include "soc/efuse_reg.h"
#ifdef __cplusplus
extern "C" {
#endif
/* @brief Permanently update values written to the efuse write registers
*
* After updating EFUSE_BLKx_WDATAx_REG registers with new values to
* write, call this function to permanently write them to efuse.
*
* @note Setting bits in efuse is permanent, they cannot be unset.
*
* @note Due to this restriction you don't need to copy values to
* Efuse write registers from the matching read registers, bits which
* are set in the read register but unset in the matching write
* register will be unchanged when new values are burned.
*
* @note This function is not threadsafe, if calling code updates
* efuse values from multiple tasks then this is caller's
* responsibility to serialise.
*
* After burning new efuses, the read registers are updated to match
* the new efuse values.
*/
void esp_efuse_burn_new_values(void);
/* @brief Reset efuse write registers
*
* Efuse write registers are written to zero, to negate
* any changes that have been staged here.
*/
void esp_efuse_reset(void);
#ifdef __cplusplus
}
#endif
#endif /* __ESP_EFUSE_H */

View file

@ -0,0 +1,91 @@
// 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_FLASH_ENCRYPT_H
#define __ESP32_FLASH_ENCRYPT_H
#include <stdbool.h>
#include <esp_err.h>
#include "esp_spi_flash.h"
#include "soc/efuse_reg.h"
/* Support functions for flash encryption features.
Can be compiled as part of app or bootloader code.
*/
/** @brief Is flash encryption currently enabled in hardware?
*
* Flash encryption is enabled if the FLASH_CRYPT_CNT efuse has an odd number of bits set.
*
* @return true if flash encryption is enabled.
*/
static inline bool esp_flash_encryption_enabled(void) {
uint32_t flash_crypt_cnt = REG_GET_FIELD(EFUSE_BLK0_RDATA0_REG, EFUSE_RD_FLASH_CRYPT_CNT);
return __builtin_parity(flash_crypt_cnt) == 1;
}
/* @brief Update on-device flash encryption
*
* Intended to be called as part of the bootloader process if flash
* encryption is enabled in device menuconfig.
*
* If FLASH_CRYPT_CNT efuse parity is 1 (ie odd number of bits set),
* then return ESP_OK immediately (indicating flash encryption is enabled
* and functional).
*
* If FLASH_CRYPT_CNT efuse parity is 0 (ie even number of bits set),
* assume the flash has just been written with plaintext that needs encrypting.
*
* The following regions of flash are encrypted in place:
*
* - The bootloader image, if a valid plaintext image is found.[*]
* - The partition table, if a valid plaintext table is found.
* - Any app partition that contains a valid plaintext app image.
* - Any other partitions with the "encrypt" flag set. [**]
*
* After the re-encryption process completes, a '1' bit is added to the
* FLASH_CRYPT_CNT value (setting the parity to 1) and the EFUSE is re-burned.
*
* [*] If reflashing bootloader with secure boot enabled, pre-encrypt
* the bootloader before writing it to flash or secure boot will fail.
*
* [**] For this reason, if serial re-flashing a previous flashed
* device with secure boot enabled and using FLASH_CRYPT_CNT to
* trigger re-encryption, you must simultaneously re-flash plaintext
* content to all partitions with the "encrypt" flag set or this
* data will be corrupted (encrypted twice).
*
* @note The post-condition of this function is that all
* partitions that should be encrypted are encrypted.
*
* @note Take care not to power off the device while this function
* is running, or the partition currently being encrypted will be lost.
*
* @return ESP_OK if all operations succeeded, ESP_ERR_INVALID_STATE
* if a fatal error occured during encryption of all partitions.
*/
esp_err_t esp_flash_encrypt_check_and_update(void);
/** @brief Encrypt-in-place a block of flash sectors
*
* @param src_addr Source offset in flash. Should be multiple of 4096 bytes.
* @param data_length Length of data to encrypt in bytes. Will be rounded up to next multiple of 4096 bytes.
*
* @return ESP_OK if all operations succeeded, ESP_ERR_FLASH_OP_FAIL
* if SPI flash fails, ESP_ERR_FLASH_OP_TIMEOUT if flash times out.
*/
esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length);
#endif

View file

@ -0,0 +1,39 @@
// 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 __ESP_FLASH_PARTITIONS_H
#define __ESP_FLASH_PARTITIONS_H
#include "esp_err.h"
#include "esp_flash_data_types.h"
#include <stdbool.h>
/* Pre-partition table fixed flash offsets */
#define ESP_BOOTLOADER_DIGEST_OFFSET 0x0
#define ESP_BOOTLOADER_OFFSET 0x1000 /* Offset of bootloader image. Has matching value in bootloader KConfig.projbuild file. */
#define ESP_PARTITION_TABLE_OFFSET 0x8000 /* Offset of partition table. Has matching value in partition_table Kconfig.projbuild file. */
#define ESP_PARTITION_TABLE_MAX_LEN 0xC00 /* Maximum length of partition table data */
#define ESP_PARTITION_TABLE_MAX_ENTRIES (ESP_PARTITION_TABLE_MAX_LEN / sizeof(esp_partition_info_t)) /* Maximum length of partition table data, including terminating entry */
/* @brief Verify the partition table (does not include verifying secure boot cryptographic signature)
*
* @param partition_table Pointer to at least ESP_PARTITION_TABLE_MAX_ENTRIES of potential partition table data. (ESP_PARTITION_TABLE_MAX_LEN bytes.)
* @param log_errors Log errors if the partition table is invalid.
* @param num_partitions If result is ESP_OK, num_partitions is updated with total number of partitions (not including terminating entry).
*
* @return ESP_OK on success, ESP_ERR_INVALID_STATE if partition table is not valid.
*/
esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions);
#endif

View file

@ -77,33 +77,40 @@ typedef struct {
/**
* @brief Read an ESP image header from flash.
*
* If encryption is enabled, data will be transparently decrypted.
*
* @param src_addr Address in flash to load image header. Must be 4 byte aligned.
* @param log_errors Log error output if image header appears invalid.
* @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);
esp_err_t esp_image_load_header(uint32_t src_addr, bool log_errors, esp_image_header_t *image_header);
/**
* @brief Read the segment header and data offset of a segment in the image.
*
* If encryption is enabled, data will be transparently decrypted.
*
* @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 log_errors Log errors reading the segment 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);
esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, bool log_errors, 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.
* @brief Non-cryptographically validate app image integrity. On success, length of image is provided to caller.
*
* If the image has a secure boot signature appended, the signature is not checked and this length is not included in the result.
* If the image has a secure boot signature appended, the signature is not checked and this length is not included in the
* output value.
*
* Image validation checks:
* - Magic byte
@ -111,13 +118,17 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const
* - Total image no longer than 16MB
* - 8 bit image checksum is valid
*
* If flash encryption is enabled, the image will be tranpsarently decrypted.
*
* @param src_addr Offset of the start of the image in flash. Must be 4 byte aligned.
* @param allow_decrypt If true and flash encryption is enabled, the image will be transparently decrypted.
* @param log_errors Log errors verifying the image.
* @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);
esp_err_t esp_image_basic_verify(uint32_t src_addr, bool log_errors, uint32_t *length);
typedef struct {

View file

@ -67,9 +67,25 @@ esp_err_t esp_secure_boot_permanently_enable(void);
* @param src_addr Starting offset of the data in flash.
* @param length Length of data in bytes. Signature is appended -after- length bytes.
*
* If flash encryption is enabled, the image will be transparently decrypted while being verified.
*
* @return ESP_OK if signature is valid, ESP_ERR_INVALID_STATE if
* signature fails, ESP_FAIL for other failures (ie can't read flash).
*/
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length);
/** @brief Secure boot verification block, on-flash data format. */
typedef struct {
uint32_t version;
uint8_t signature[64];
} esp_secure_boot_sig_block_t;
#define FLASH_OFFS_SECURE_BOOT_IV_DIGEST 0
/** @brief Secure boot IV+digest header */
typedef struct {
uint8_t iv[128];
uint8_t digest[64];
} esp_secure_boot_iv_digest_t;
#endif

View file

@ -18,6 +18,9 @@
#include <stdbool.h>
#include <stdint.h>
#include <esp_err.h>
#include "esp_spi_flash.h"
#define FLASH_SECTOR_SIZE 0x1000
/* Provide a Flash API for bootloader_support code,
that can be used from bootloader or app code.
@ -56,14 +59,45 @@ void bootloader_munmap(const void *mapping);
/**
* @brief Read data from Flash.
*
* @note Both src and dest have to be 4-byte aligned.
*
* @note All of src, dest and size 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
* @param allow_decrypt If true and flash encryption is enabled, data on flash
* will be decrypted transparently as part of the read.
*
* @return ESP_OK on success, ESP_ERR_FLASH_OP_FAIL on SPI failure,
* ESP_ERR_FLASH_OP_TIMEOUT on SPI timeout.
*/
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt);
/**
* @brief Write data to Flash.
*
* @note All of dest_addr, src and size have to be 4-byte aligned. If write_encrypted is set, dest_addr and size must be 32-byte aligned.
*
* Note: In bootloader, when write_encrypted == true, the src buffer is encrypted in place.
*
* @param dest_addr Destination address to write in Flash.
* @param src Pointer to the data to write to flash
* @param size Length of data in bytes.
* @param write_encrypted If true, data will be written encrypted on flash.
*
* @return ESP_OK on success, ESP_ERR_FLASH_OP_FAIL on SPI failure,
* ESP_ERR_FLASH_OP_TIMEOUT on SPI timeout.
*/
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted);
/**
* @brief Erase the Flash sector.
*
* @param sector Sector number, the count starts at sector 0, 4KB per sector.
*
* @return esp_err_t
*/
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size);
esp_err_t bootloader_flash_erase_sector(size_t sector);
#endif

View file

@ -0,0 +1,25 @@
// Copyright 2010-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.
#pragma once
#include <stddef.h>
/**
* @brief Fill buffer with 'length' random bytes
*
* @param buffer Pointer to buffer
* @param length This many bytes of random data will be copied to buffer
*/
void bootloader_fill_random(void *buffer, size_t length);

View file

@ -46,38 +46,68 @@ void bootloader_munmap(const void *mapping)
map = 0;
}
esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size)
esp_err_t bootloader_flash_read(size_t src, void *dest, size_t size, bool allow_decrypt)
{
return spi_flash_read(src, dest, size);
}
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted)
{
if (write_encrypted) {
return spi_flash_write_encrypted(dest_addr, src, size);
} else {
return spi_flash_write(dest_addr, src, size);
}
}
esp_err_t bootloader_flash_erase_sector(size_t sector)
{
return spi_flash_erase_sector(sector);
}
#else
/* Bootloader version, uses ROM functions only */
#include <soc/dport_reg.h>
#include <rom/spi_flash.h>
#include <rom/cache.h>
static const char *TAG = "bootloader_flash";
/* Use first 50 blocks in MMU for bootloader_mmap,
50th block for bootloader_flash_read
*/
#define MMU_BLOCK0_VADDR 0x3f400000
#define MMU_BLOCK50_VADDR 0x3f720000
#define MMU_FLASH_MASK 0xffff0000
#define MMU_BLOCK_SIZE 0x00010000
static bool mapped;
static uint32_t current_read_mapping = UINT32_MAX;
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 */
}
if (size > 0x320000) {
/* Allow mapping up to 50 of the 51 available MMU blocks (last one used for reads) */
ESP_LOGE(TAG, "bootloader_mmap excess size %x", size);
return NULL;
}
uint32_t src_addr_aligned = src_addr & 0xffff0000;
uint32_t count = (size + (src_addr - src_addr_aligned) + 0xffff) / 0x10000;
uint32_t src_addr_aligned = src_addr & MMU_FLASH_MASK;
uint32_t count = (size + (src_addr - src_addr_aligned) + 0xffff) / MMU_BLOCK_SIZE;
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_flash_mmu_set( 0, 0, MMU_BLOCK0_VADDR, src_addr_aligned, 64, count );
Cache_Read_Enable( 0 );
mapped = true;
return (void *)(0x3f400000 + (src_addr - src_addr_aligned));
return (void *)(MMU_BLOCK0_VADDR + (src_addr - src_addr_aligned));
}
void bootloader_munmap(const void *mapping)
@ -88,25 +118,12 @@ void bootloader_munmap(const void *mapping)
Cache_Flush(0);
mmu_init(0);
mapped = false;
current_read_mapping = UINT32_MAX;
}
}
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size)
static esp_err_t spi_to_esp_err(SpiFlashOpResult r)
{
if(src_addr & 3) {
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 0x%x not 4-byte aligned", (intptr_t)dest);
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;
@ -119,4 +136,101 @@ esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size)
}
}
static esp_err_t bootloader_flash_read_no_decrypt(size_t src_addr, void *dest, size_t size)
{
Cache_Read_Disable(0);
Cache_Flush(0);
SpiFlashOpResult r = SPIRead(src_addr, dest, size);
Cache_Read_Enable(0);
return spi_to_esp_err(r);
}
static esp_err_t bootloader_flash_read_allow_decrypt(size_t src_addr, void *dest, size_t size)
{
uint32_t *dest_words = (uint32_t *)dest;
/* Use the 51st MMU mapping to read from flash in 64KB blocks.
(MMU will transparently decrypt if encryption is enabled.)
*/
for (int word = 0; word < size / 4; word++) {
uint32_t word_src = src_addr + word * 4; /* Read this offset from flash */
uint32_t map_at = word_src & MMU_FLASH_MASK; /* Map this 64KB block from flash */
uint32_t *map_ptr;
if (map_at != current_read_mapping) {
/* Move the 64KB mmu mapping window to fit map_at */
Cache_Read_Disable(0);
Cache_Flush(0);
ESP_LOGD(TAG, "mmu set block paddr=0x%08x (was 0x%08x)", map_at, current_read_mapping);
int e = cache_flash_mmu_set(0, 0, MMU_BLOCK50_VADDR, map_at, 64, 1);
if (e != 0) {
ESP_LOGE(TAG, "cache_flash_mmu_set failed: %d\n", e);
Cache_Read_Enable(0);
return ESP_FAIL;
}
current_read_mapping = map_at;
Cache_Read_Enable(0);
}
map_ptr = (uint32_t *)(MMU_BLOCK50_VADDR + (word_src - map_at));
dest_words[word] = *map_ptr;
}
return ESP_OK;
}
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt)
{
if (src_addr & 3) {
ESP_LOGE(TAG, "bootloader_flash_read src_addr 0x%x not 4-byte aligned", src_addr);
return ESP_FAIL;
}
if (size & 3) {
ESP_LOGE(TAG, "bootloader_flash_read size 0x%x not 4-byte aligned", size);
return ESP_FAIL;
}
if ((intptr_t)dest & 3) {
ESP_LOGE(TAG, "bootloader_flash_read dest 0x%x not 4-byte aligned", (intptr_t)dest);
return ESP_FAIL;
}
if (allow_decrypt) {
return bootloader_flash_read_allow_decrypt(src_addr, dest, size);
} else {
return bootloader_flash_read_no_decrypt(src_addr, dest, size);
}
}
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted)
{
esp_err_t err;
size_t alignment = write_encrypted ? 32 : 4;
if ((dest_addr % alignment) != 0) {
ESP_LOGE(TAG, "bootloader_flash_write dest_addr 0x%x not %d-byte aligned", dest_addr, alignment);
return ESP_FAIL;
}
if ((size % alignment) != 0) {
ESP_LOGE(TAG, "bootloader_flash_write size 0x%x not %d-byte aligned", size, alignment);
return ESP_FAIL;
}
if (((intptr_t)src % 4) != 0) {
ESP_LOGE(TAG, "bootloader_flash_write src 0x%x not 4 byte aligned", (intptr_t)src);
return ESP_FAIL;
}
err = spi_to_esp_err(SPIUnlock());
if (err != ESP_OK) {
return err;
}
if (write_encrypted) {
return spi_to_esp_err(SPI_Encrypt_Write(dest_addr, src, size));
} else {
return spi_to_esp_err(SPIWrite(dest_addr, src, size));
}
}
esp_err_t bootloader_flash_erase_sector(size_t sector)
{
return spi_to_esp_err(SPIEraseSector(sector));
}
#endif

View file

@ -0,0 +1,53 @@
// Copyright 2010-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 "bootloader_random.h"
#include "soc/wdev_reg.h"
#ifndef BOOTLOADER_BUILD
#include "esp_system.h"
#endif
void bootloader_fill_random(void *buffer, size_t length)
{
uint8_t *buffer_bytes = (uint8_t *)buffer;
uint32_t random;
/* TODO: enable HW RNG clock
Until this clock is enabled, this is not secure
*/
for (int i = 0; i < length; i++) {
if (i == 0 || i % 4 == 0) { /* redundant check is for a compiler warning */
#ifdef BOOTLOADER_BUILD
/* HW RNG generates 32 bits entropy per 16 APB cycles,
in bootloader CPU clock == APB clock.
We are being conservative here and waiting at least
that long, as loop shift overhead, etc will add more
cycles.
*/
asm volatile("nop; nop; nop; nop;");
asm volatile("nop; nop; nop; nop;");
asm volatile("nop; nop; nop; nop;");
asm volatile("nop; nop; nop; nop;");
random = REG_READ(WDEV_RND_REG);
#else
random = esp_random();
#endif
}
buffer_bytes[i] = random >> ((i % 4) * 8);
}
}

View file

@ -0,0 +1,47 @@
// 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 "esp_efuse.h"
#define EFUSE_CONF_WRITE 0x5A5A /* efuse_pgm_op_ena, force no rd/wr disable */
#define EFUSE_CONF_READ 0x5AA5 /* efuse_read_op_ena, release force */
#define EFUSE_CMD_PGM 0x02
#define EFUSE_CMD_READ 0x01
void esp_efuse_burn_new_values(void)
{
REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_WRITE);
REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_PGM);
while (REG_READ(EFUSE_CMD_REG) != 0) {
}
REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_READ);
REG_WRITE(EFUSE_CMD_REG, EFUSE_CMD_READ);
while (REG_READ(EFUSE_CMD_REG) != 0) {
}
esp_efuse_reset();
}
void esp_efuse_reset(void)
{
REG_WRITE(EFUSE_CONF_REG, EFUSE_CONF_READ);
const uint32_t block_start[4] = { EFUSE_BLK0_WDATA0_REG, EFUSE_BLK1_WDATA0_REG,
EFUSE_BLK2_WDATA0_REG, EFUSE_BLK3_WDATA0_REG };
const uint32_t block_end[4] = { EFUSE_BLK0_WDATA6_REG, EFUSE_BLK1_WDATA7_REG,
EFUSE_BLK2_WDATA7_REG, EFUSE_BLK3_WDATA7_REG };
for (int i = 0; i < 4; i++) {
for (uint32_t r = block_start[i]; r <= block_end[i]; r+= 4) {
REG_WRITE(r, 0);
}
}
}

View file

@ -22,26 +22,30 @@ static const 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 esp_image_load_header(uint32_t src_addr, bool log_errors, 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));
err = bootloader_flash_read(src_addr, image_header, sizeof(esp_image_header_t), true);
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);
if (log_errors) {
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 (log_errors) {
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);
}
}
}
@ -51,23 +55,27 @@ esp_err_t esp_image_load_header(uint32_t src_addr, esp_image_header_t *image_hea
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 esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const esp_image_header_t *image_header, bool log_errors, 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);
if (log_errors) {
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++) {
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));
err = bootloader_flash_read(next_addr, segment_header, sizeof(esp_image_segment_header_t), true);
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);
if (log_errors) {
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);
@ -85,15 +93,14 @@ esp_err_t esp_image_load_segment_header(uint8_t index, uint32_t src_addr, const
return err;
}
esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length)
esp_err_t esp_image_basic_verify(uint32_t src_addr, bool log_errors, uint32_t *p_length)
{
esp_err_t err;
uint8_t buf[16];
uint8_t buf[128];
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;
@ -101,7 +108,7 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length)
*p_length = 0;
}
err = esp_image_load_header(src_addr, &image_header);
err = esp_image_load_header(src_addr, log_errors, &image_header);
if (err != ESP_OK) {
return err;
}
@ -110,34 +117,38 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length)
/* 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,
err = esp_image_load_segment_header(i, src_addr, &image_header, log_errors,
&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 += sizeof(buf)) {
err = bootloader_flash_read(segment_data_offs + i, buf, sizeof(buf), true);
if (err != ESP_OK) {
return err;
}
for (int j = 0; j < sizeof(buf) && i + j < segment_header.data_len; j++) {
checksum ^= buf[j];
}
}
for(int i = 0; i < segment_header.data_len; i++) {
checksum ^= segment_data[i];
}
bootloader_munmap(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");
if (log_errors) {
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);
if (log_errors) {
ESP_LOGE(TAG, "invalid total length 0x%x", length);
}
return ESP_ERR_IMAGE_INVALID;
}
@ -147,10 +158,12 @@ esp_err_t esp_image_basic_verify(uint32_t src_addr, uint32_t *p_length)
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);
bootloader_flash_read(src_addr + length - 16, buf, 16, true);
if (checksum != buf[15]) {
ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x",
checksum, buf[15]);
if (log_errors) {
ESP_LOGE(TAG, "checksum failed. Calculated 0x%x read 0x%x",
checksum, buf[15]);
}
return ESP_ERR_IMAGE_INVALID;
}

View file

@ -0,0 +1,329 @@
// 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 <strings.h>
#include "bootloader_flash.h"
#include "bootloader_random.h"
#include "esp_image_format.h"
#include "esp_flash_encrypt.h"
#include "esp_flash_partitions.h"
#include "esp_flash_data_types.h"
#include "esp_secure_boot.h"
#include "esp_efuse.h"
#include "esp_log.h"
#include "rom/secure_boot.h"
#include "rom/cache.h"
#include "rom/spi_flash.h" /* TODO: Remove this */
static const char *TAG = "flash_encrypt";
/* Static functions for stages of flash encryption */
static esp_err_t initialise_flash_encryption(void);
static esp_err_t encrypt_flash_contents(uint32_t flash_crypt_cnt, bool flash_crypt_wr_dis);
static esp_err_t encrypt_bootloader();
static esp_err_t encrypt_and_load_partition_table(esp_partition_info_t *partition_table, int *num_partitions);
static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partition);
esp_err_t esp_flash_encrypt_check_and_update(void)
{
uint32_t efuse_blk0 = REG_READ(EFUSE_BLK0_RDATA0_REG);
ESP_LOGV(TAG, "efuse_blk0 raw value %08x", efuse_blk0);
uint32_t flash_crypt_cnt = (efuse_blk0 & EFUSE_RD_FLASH_CRYPT_CNT_M) >> EFUSE_RD_FLASH_CRYPT_CNT_S;
bool flash_crypt_wr_dis = efuse_blk0 & EFUSE_WR_DIS_FLASH_CRYPT_CNT;
ESP_LOGV(TAG, "efuse FLASH_CRYPT_CNT 0x%x WR_DIS_FLASH_CRYPT_CNT 0x%x", flash_crypt_cnt, flash_crypt_wr_dis);
if (__builtin_parity(flash_crypt_cnt) == 1) {
/* Flash is already encrypted */
int left = (7 - __builtin_popcount(flash_crypt_cnt)) / 2;
if (flash_crypt_wr_dis) {
left = 0; /* can't update FLASH_CRYPT_CNT, no more flashes */
}
ESP_LOGI(TAG, "flash encryption is enabled (%d plaintext flashes left)", left);
return ESP_OK;
}
else {
/* Flash is not encrypted, so encrypt it! */
return encrypt_flash_contents(flash_crypt_cnt, flash_crypt_wr_dis);
}
}
static esp_err_t initialise_flash_encryption(void)
{
/* Before first flash encryption pass, need to initialise key & crypto config */
/* Generate key */
uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG);
bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK1;
bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK1;
if (efuse_key_read_protected == false
&& efuse_key_write_protected == false
&& REG_READ(EFUSE_BLK1_RDATA0_REG) == 0
&& REG_READ(EFUSE_BLK1_RDATA1_REG) == 0
&& REG_READ(EFUSE_BLK1_RDATA2_REG) == 0
&& REG_READ(EFUSE_BLK1_RDATA3_REG) == 0
&& REG_READ(EFUSE_BLK1_RDATA4_REG) == 0
&& REG_READ(EFUSE_BLK1_RDATA5_REG) == 0
&& REG_READ(EFUSE_BLK1_RDATA6_REG) == 0
&& REG_READ(EFUSE_BLK1_RDATA7_REG) == 0) {
/* On-device key generation is temporarily disabled, until
* RNG operation during bootloader is qualified.
* See docs/security/flash-encryption.rst for details. */
ESP_LOGE(TAG, "On-device key generation is not yet available.");
return ESP_ERR_NOT_SUPPORTED;
} else {
if(!(efuse_key_read_protected && efuse_key_write_protected)) {
ESP_LOGE(TAG, "Flash encryption key has to be either unset or both read and write protected");
return ESP_ERR_INVALID_STATE;
}
ESP_LOGW(TAG, "Using pre-loaded flash encryption key in EFUSE block 1");
}
/* CRYPT_CONFIG determines which bits of the AES block key are XORed
with bits from the flash address, to provide the key tweak.
CRYPT_CONFIG == 0 is effectively AES ECB mode (NOT SUPPORTED)
For now this is hardcoded to XOR all 256 bits of the key.
If you need to override it, you can pre-burn this efuse to the
desired value and then write-protect it, in which case this
operation does nothing. Please note this is not recommended!
*/
ESP_LOGI(TAG, "Setting CRYPT_CONFIG efuse to 0xF");
REG_WRITE(EFUSE_BLK0_WDATA5_REG, EFUSE_FLASH_CRYPT_CONFIG_M);
esp_efuse_burn_new_values();
uint32_t new_wdata6 = 0;
#ifndef CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_ENCRYPT
ESP_LOGI(TAG, "Disable UART bootloader encryption...");
new_wdata6 |= EFUSE_DISABLE_DL_ENCRYPT;
#else
ESP_LOGW(TAG, "Not disabling UART bootloader encryption");
#endif
#ifndef CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_DECRYPT
ESP_LOGI(TAG, "Disable UART bootloader decryption...");
new_wdata6 |= EFUSE_DISABLE_DL_DECRYPT;
#else
ESP_LOGW(TAG, "Not disabling UART bootloader decryption - SECURITY COMPROMISED");
#endif
#ifndef CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_CACHE
ESP_LOGI(TAG, "Disable UART bootloader MMU cache...");
new_wdata6 |= EFUSE_DISABLE_DL_CACHE;
#else
ESP_LOGW(TAG, "Not disabling UART bootloader MMU cache - SECURITY COMPROMISED");
#endif
#ifndef CONFIG_SECURE_BOOT_ALLOW_JTAG
ESP_LOGI(TAG, "Disable JTAG...");
new_wdata6 |= EFUSE_RD_DISABLE_JTAG;
#else
ESP_LOGW(TAG, "Not disabling JTAG - SECURITY COMPROMISED");
#endif
if (new_wdata6 != 0) {
REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6);
esp_efuse_burn_new_values();
}
return ESP_OK;
}
/* Encrypt all flash data that should be encrypted */
static esp_err_t encrypt_flash_contents(uint32_t flash_crypt_cnt, bool flash_crypt_wr_dis)
{
esp_err_t err;
esp_partition_info_t partition_table[ESP_PARTITION_TABLE_MAX_ENTRIES];
int num_partitions;
/* If the last flash_crypt_cnt bit is burned or write-disabled, the
device can't re-encrypt itself. */
if (flash_crypt_wr_dis || flash_crypt_cnt == 0xFF) {
ESP_LOGE(TAG, "Cannot re-encrypt data (FLASH_CRYPT_CNT 0x%02x write disabled %d", flash_crypt_cnt, flash_crypt_wr_dis);
return ESP_FAIL;
}
if (flash_crypt_cnt == 0) {
/* Very first flash of encrypted data: generate keys, etc. */
err = initialise_flash_encryption();
if (err != ESP_OK) {
return err;
}
}
err = encrypt_bootloader();
if (err != ESP_OK) {
return err;
}
err = encrypt_and_load_partition_table(partition_table, &num_partitions);
if (err != ESP_OK) {
return err;
}
/* Now iterate the just-loaded partition table, looking for entries to encrypt
*/
/* Go through each partition and encrypt if necessary */
for (int i = 0; i < num_partitions; i++) {
err = encrypt_partition(i, &partition_table[i]);
if (err != ESP_OK) {
return err;
}
}
ESP_LOGD(TAG, "All flash regions checked for encryption pass");
/* Set least significant 0-bit in flash_crypt_cnt */
int ffs_inv = __builtin_ffs((~flash_crypt_cnt) & 0xFF);
/* ffs_inv shouldn't be zero, as zero implies flash_crypt_cnt == 0xFF */
uint32_t new_flash_crypt_cnt = flash_crypt_cnt + (1 << (ffs_inv - 1));
ESP_LOGD(TAG, "FLASH_CRYPT_CNT 0x%x -> 0x%x", flash_crypt_cnt, new_flash_crypt_cnt);
REG_SET_FIELD(EFUSE_BLK0_WDATA0_REG, EFUSE_FLASH_CRYPT_CNT, new_flash_crypt_cnt);
esp_efuse_burn_new_values();
ESP_LOGI(TAG, "Flash encryption completed");
return ESP_OK;
}
static esp_err_t encrypt_bootloader()
{
esp_err_t err;
uint32_t image_length;
/* Check for plaintext bootloader */
if (esp_image_basic_verify(ESP_BOOTLOADER_OFFSET, false, &image_length) == ESP_OK) {
ESP_LOGD(TAG, "bootloader is plaintext. Encrypting...");
err = esp_flash_encrypt_region(ESP_BOOTLOADER_OFFSET, image_length);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt bootloader in place: 0x%x", err);
return err;
}
if (esp_secure_boot_enabled()) {
/* If secure boot is enabled and bootloader was plaintext, also
need to encrypt secure boot IV+digest.
*/
ESP_LOGD(TAG, "Encrypting secure bootloader IV & digest...");
err = esp_flash_encrypt_region(FLASH_OFFS_SECURE_BOOT_IV_DIGEST,
FLASH_SECTOR_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt bootloader IV & digest in place: 0x%x", err);
return err;
}
}
}
else {
ESP_LOGW(TAG, "no valid bootloader was found");
}
return ESP_OK;
}
static esp_err_t encrypt_and_load_partition_table(esp_partition_info_t *partition_table, int *num_partitions)
{
esp_err_t err;
/* Check for plaintext partition table */
err = bootloader_flash_read(ESP_PARTITION_TABLE_OFFSET, partition_table, ESP_PARTITION_TABLE_MAX_LEN, false);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to read partition table data");
return err;
}
if (esp_partition_table_basic_verify(partition_table, false, num_partitions) == ESP_OK) {
ESP_LOGD(TAG, "partition table is plaintext. Encrypting...");
esp_err_t err = esp_flash_encrypt_region(ESP_PARTITION_TABLE_OFFSET,
FLASH_SECTOR_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt partition table in place. %x", err);
return err;
}
}
else {
ESP_LOGE(TAG, "Failed to read partition table data - not plaintext?");
return ESP_ERR_INVALID_STATE;
}
/* Valid partition table loded */
return ESP_OK;
}
static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partition)
{
esp_err_t err;
uint32_t image_len = partition->pos.size;
bool should_encrypt = (partition->flags & PART_FLAG_ENCRYPTED);
if (partition->type == PART_TYPE_APP) {
/* check if the partition holds an unencrypted app */
if (esp_image_basic_verify(partition->pos.offset, false, &image_len) == ESP_OK) {
if(image_len > partition->pos.size) {
ESP_LOGE(TAG, "partition entry %d has image longer than partition (%d vs %d)", index, image_len, partition->pos.size);
should_encrypt = false;
} else {
should_encrypt = true;
}
} else {
should_encrypt = false;
}
}
if (!should_encrypt) {
return ESP_OK;
}
else {
/* should_encrypt */
ESP_LOGI(TAG, "Encrypting partition %d at offset 0x%x...", index, partition->pos.offset);
err = esp_flash_encrypt_region(partition->pos.offset, partition->pos.size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt partition %d", index);
}
return err;
}
}
esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length)
{
esp_err_t err;
uint32_t buf[FLASH_SECTOR_SIZE / sizeof(uint32_t)];
if (src_addr % FLASH_SECTOR_SIZE != 0) {
ESP_LOGE(TAG, "esp_flash_encrypt_region bad src_addr 0x%x",src_addr);
return ESP_FAIL;
}
for (size_t i = 0; i < data_length; i += FLASH_SECTOR_SIZE) {
uint32_t sec_start = i + src_addr;
err = bootloader_flash_read(sec_start, buf, FLASH_SECTOR_SIZE, false);
if (err != ESP_OK) {
goto flash_failed;
}
err = bootloader_flash_erase_sector(sec_start / FLASH_SECTOR_SIZE);
if (err != ESP_OK) {
goto flash_failed;
}
err = bootloader_flash_write(sec_start, buf, FLASH_SECTOR_SIZE, true);
if (err != ESP_OK) {
goto flash_failed;
}
}
return ESP_OK;
flash_failed:
ESP_LOGE(TAG, "flash operation failed: 0x%x", err);
return err;
}

View file

@ -0,0 +1,49 @@
// 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 "esp_flash_partitions.h"
#include "esp_log.h"
static const char *TAG = "flash_parts";
esp_err_t esp_partition_table_basic_verify(const esp_partition_info_t *partition_table, bool log_errors, int *num_partitions)
{
int num_parts;
*num_partitions = 0;
for(num_parts = 0; num_parts < ESP_PARTITION_TABLE_MAX_ENTRIES; num_parts++) {
const esp_partition_info_t *part = &partition_table[num_parts];
if(part->magic == 0xFFFF
&& part->type == PART_TYPE_END
&& part->subtype == PART_SUBTYPE_END) {
/* TODO: check md5 */
ESP_LOGD(TAG, "partition table verified, %d entries", num_parts);
*num_partitions = num_parts;
return ESP_OK;
}
if(part->magic != ESP_PARTITION_MAGIC) {
if (log_errors) {
ESP_LOGE(TAG, "partition %d invalid magic number 0x%x", num_parts, part->magic);
}
return ESP_ERR_INVALID_STATE;
}
}
if (log_errors) {
ESP_LOGE(TAG, "partition table has no terminating entry, not valid");
}
return ESP_ERR_INVALID_STATE;
}

View file

@ -20,7 +20,6 @@
#include "rom/cache.h"
#include "rom/ets_sys.h"
#include "rom/spi_flash.h"
#include "rom/secure_boot.h"
#include "soc/dport_reg.h"
@ -31,15 +30,14 @@
#include "sdkconfig.h"
#include "bootloader_flash.h"
#include "bootloader_random.h"
#include "esp_image_format.h"
#include "esp_secure_boot.h"
#include "esp_flash_encrypt.h"
#include "esp_efuse.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 digest (aka "abstract") & iv
@ -47,39 +45,26 @@ static const char* TAG = "secure_boot";
* @inputs: image_len - length of image to calculate digest for
*/
static bool secure_boot_generate(uint32_t image_len){
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;
esp_err_t err;
esp_secure_boot_iv_digest_t digest;
const uint32_t *image;
/* 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;
if (image_len % sizeof(digest.iv) != 0) {
image_len = (image_len / sizeof(digest.iv) + 1) * sizeof(digest.iv);
}
ets_secure_boot_start();
ets_secure_boot_rd_iv(buf);
ets_secure_boot_rd_iv((uint32_t *)digest.iv);
ets_secure_boot_hash(NULL);
Cache_Read_Disable(0);
/* iv stored in sec 0 */
spiRet = SPIEraseSector(0);
if (spiRet != SPI_FLASH_RESULT_OK)
err = bootloader_flash_erase_sector(0);
if (err != ESP_OK)
{
ESP_LOGE(TAG, "SPI erase failed %d", spiRet);
ESP_LOGE(TAG, "SPI erase failed: 0x%x", err);
return false;
}
Cache_Read_Enable(0);
/* write iv to flash, 0x0000, 128 bytes (1024 bits) */
ESP_LOGD(TAG, "write iv to flash.");
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 */
image = bootloader_mmap(0x1000, image_len);
@ -87,22 +72,22 @@ static bool secure_boot_generate(uint32_t image_len){
ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%x) failed", image_len);
return false;
}
for (int i = 0; i < image_len; i+= HASH_BLOCK_SIZE) {
ets_secure_boot_hash(image + i/sizeof(void *));
for (int i = 0; i < image_len; i+= sizeof(digest.iv)) {
ets_secure_boot_hash(&image[i/sizeof(uint32_t)]);
}
bootloader_munmap(image);
ets_secure_boot_obtain();
ets_secure_boot_rd_abstract(buf);
ets_secure_boot_rd_abstract((uint32_t *)digest.digest);
ets_secure_boot_finish();
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);
ESP_LOGD(TAG, "write iv+digest to flash");
err = bootloader_flash_write(FLASH_OFFS_SECURE_BOOT_IV_DIGEST, &digest,
sizeof(digest), esp_flash_encryption_enabled());
if (err != ESP_OK) {
ESP_LOGE(TAG, "SPI write failed: 0x%x", err);
return false;
}
ESP_LOGD(TAG, "write digest to flash.");
Cache_Read_Enable(0);
return true;
}
@ -111,14 +96,9 @@ static bool secure_boot_generate(uint32_t image_len){
static inline void burn_efuses()
{
#ifdef CONFIG_SECURE_BOOT_TEST_MODE
ESP_LOGE(TAG, "SECURE BOOT TEST MODE. Not really burning any efuses!");
ESP_LOGE(TAG, "SECURE BOOT TEST MODE. Not really burning any efuses! NOT SECURE");
#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 */
esp_efuse_burn_new_values();
#endif
}
@ -131,7 +111,7 @@ esp_err_t esp_secure_boot_permanently_enable(void) {
return ESP_OK;
}
err = esp_image_basic_verify(0x1000, &image_len);
err = esp_image_basic_verify(0x1000, true, &image_len);
if (err != ESP_OK) {
ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err);
return err;
@ -150,25 +130,12 @@ esp_err_t esp_secure_boot_permanently_enable(void) {
&& 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_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]);
}
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;
/* On-device key generation is temporarily disabled, until
* RNG operation during bootloader is qualified.
* See docs/security/secure-boot.rst for details. */
ESP_LOGE(TAG, "On-device key generation is not yet available.");
return ESP_ERR_NOT_SUPPORTED;
} else {
ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2");
}
@ -180,6 +147,7 @@ esp_err_t esp_secure_boot_permanently_enable(void) {
}
ESP_LOGI(TAG, "Digest generation complete.");
#ifndef CONFIG_SECURE_BOOT_TEST_MODE
if (!efuse_key_read_protected) {
ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse.");
return ESP_ERR_INVALID_STATE;
@ -188,21 +156,26 @@ esp_err_t esp_secure_boot_permanently_enable(void) {
ESP_LOGE(TAG, "Pre-loaded key is not write protected. Refusing to blow secure boot efuse.");
return ESP_ERR_INVALID_STATE;
}
#endif
ESP_LOGI(TAG, "blowing secure boot efuse...");
ESP_LOGD(TAG, "before updating, EFUSE_BLK0_RDATA6 %x", REG_READ(EFUSE_BLK0_RDATA6_REG));
uint32_t new_wdata6 = EFUSE_RD_ABS_DONE_0;
#ifdef CONFIG_SECURE_BOOT_DISABLE_JTAG
ESP_LOGI(TAG, "disabling JTAG...");
#ifndef CONFIG_SECURE_BOOT_ALLOW_JTAG
ESP_LOGI(TAG, "Disable JTAG...");
new_wdata6 |= EFUSE_RD_DISABLE_JTAG;
#endif
#else
ESP_LOGW(TAG, "Not disabling JTAG - SECURITY COMPROMISED");
#endif
#ifdef CONFIG_SECURE_BOOT_DISABLE_UART_BOOTLOADER
ESP_LOGI(TAG, "disabling UART bootloader...");
new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE_S;
#endif
#ifndef CONFIG_SECURE_BOOT_ALLOW_ROM_BASIC
ESP_LOGI(TAG, "Disable ROM BASIC interpreter fallback...");
new_wdata6 |= EFUSE_RD_CONSOLE_DEBUG_DISABLE;
#else
ESP_LOGW(TAG, "Not disabling ROM BASIC fallback - SECURITY COMPROMISED");
#endif
REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6);
burn_efuses();

View file

@ -27,11 +27,6 @@ typedef SHA_CTX sha_context;
#include "hwcrypto/sha.h"
#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");
@ -47,7 +42,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;
const signature_block_t *sigblock;
const esp_secure_boot_sig_block_t *sigblock;
bool is_valid;
#ifdef BOOTLOADER_BUILD
const uint8_t *digest_data;
@ -56,13 +51,13 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
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));
data = bootloader_mmap(src_addr, length + sizeof(esp_secure_boot_sig_block_t));
if(data == NULL) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(signature_block_t));
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", src_addr, length+sizeof(esp_secure_boot_sig_block_t));
return ESP_FAIL;
}
sigblock = (const signature_block_t *)(data + length);
sigblock = (const esp_secure_boot_sig_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);

View file

@ -47,9 +47,24 @@ typedef struct {
uint8_t subtype;
esp_partition_pos_t pos;
uint8_t label[16];
uint8_t reserved[4];
uint32_t flags;
} esp_partition_info_t;
#define PART_TYPE_APP 0x00
#define PART_SUBTYPE_FACTORY 0x00
#define PART_SUBTYPE_OTA_FLAG 0x10
#define PART_SUBTYPE_OTA_MASK 0x0f
#define PART_SUBTYPE_TEST 0x20
#define PART_TYPE_DATA 0x01
#define PART_SUBTYPE_DATA_OTA 0x00
#define PART_SUBTYPE_DATA_RF 0x01
#define PART_SUBTYPE_DATA_WIFI 0x02
#define PART_TYPE_END 0xff
#define PART_SUBTYPE_END 0xff
#define PART_FLAG_ENCRYPTED (1<<0)
#ifdef __cplusplus
}

View file

@ -490,16 +490,20 @@ SpiFlashOpResult SPI_Prepare_Encrypt_Data(uint32_t flash_addr, uint32_t *data);
void SPI_Write_Encrypt_Disable(void);
/**
* @brief Encrpto writing data to flash, you should Erase it yourself if need.
* Please do not call this function in SDK.
* @brief Write data to flash with transparent encryption.
* @note Sectors to be written should already be erased.
*
* @param uint32_t flash_addr : Address to write, should be 32 bytes aligned.
* @note Please do not call this function in SDK.
*
* @param uint32_t *data : The pointer to data which is to write.
* @param uint32_t flash_addr : Address to write, should be 32 byte aligned.
*
* @param uint32_t *data : The pointer to data to write. Note, this pointer must
* be 32 bit aligned and the content of the data will be
* modified by the encryption function.
*
* @param uint32_t len : Length to write, should be 32 bytes aligned.
*
* @return SPI_FLASH_RESULT_OK : Encrypto write OK.
* @return SPI_FLASH_RESULT_OK : Data written successfully.
* SPI_FLASH_RESULT_ERR : Encrypto write error.
* SPI_FLASH_RESULT_TIMEOUT : Encrypto write timeout.
*/

View file

@ -79,8 +79,8 @@
//set bits of register controlled by mask
#define REG_SET_BITS(_r, _b, _m) (*(volatile uint32_t*)(_r) = (*(volatile uint32_t*)(_r) & ~(_m)) | ((_b) & (_m)))
//get field from register, used when _f is not left shifted by _f##_S
#define REG_GET_FIELD(_r, _f) ((REG_READ(_r) >> (_f##_S)) & (_f))
//get field from register, uses field _S & _V to determine mask
#define REG_GET_FIELD(_r, _f) ((REG_READ(_r) >> (_f##_S)) & (_f##_V))
//set field to register, used when _f is not left shifted by _f##_S
#define REG_SET_FIELD(_r, _f, _v) (REG_WRITE((_r),((REG_READ(_r) & ~((_f) << (_f##_S)))|(((_v) & (_f))<<(_f##_S)))))

View file

@ -14,5 +14,7 @@
#pragma once
#include "soc.h"
/* Hardware random number generator register */
#define WDEV_RND_REG 0x60035144

View file

@ -28,13 +28,13 @@ ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_CO
ESPTOOL_ALL_FLASH_ARGS += $(CONFIG_APP_OFFSET) $(APP_BIN)
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
ifdef CONFIG_SECURE_BOOT_ENABLED
ifndef IS_BOOTLOADER_BUILD
# 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)
$(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $^ # signed in-place
$(APP_BIN): $(APP_BIN_UNSIGNED) $(SECURE_BOOT_SIGNING_KEY)
$(ESPSECUREPY) sign_data --keyfile $(SECURE_BOOT_SIGNING_KEY) -o $@ $<
endif
endif
# non-secure boot (or bootloader), both these files are the same
@ -45,7 +45,7 @@ $(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC)
flash: all_binaries $(ESPTOOLPY_SRC)
@echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(CONFIG_APP_OFFSET))..."
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
ifdef CONFIG_SECURE_BOOT_ENABLED
@echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)"
endif
$(ESPTOOLPY_WRITE_FLASH) $(ESPTOOL_ALL_FLASH_ARGS)

@ -1 +1 @@
Subproject commit 5c6962e894e0a118c9a4b5760876433493449260
Subproject commit adc914b91ac6d2cfd1ace56307b4374eb9439e14

View file

@ -11,6 +11,7 @@
# NB: gen_esp32part.py lives in the sdk/bin/ dir not component dir
GEN_ESP32PART := $(PYTHON) $(COMPONENT_PATH)/gen_esp32part.py -q
# Has a matching value in bootloader_support esp_flash_partitions.h
PARTITION_TABLE_OFFSET := 0x8000
# Path to partition CSV file is relative to project path for custom
@ -20,11 +21,11 @@ PARTITION_TABLE_CSV_PATH := $(call dequote,$(abspath $(PARTITION_TABLE_ROOT)/$(s
PARTITION_TABLE_BIN := $(BUILD_DIR_BASE)/$(notdir $(PARTITION_TABLE_CSV_PATH:.csv=.bin))
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
ifdef CONFIG_SECURE_BOOT_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 $@ $<
$(PARTITION_TABLE_BIN): $(PARTITION_TABLE_BIN_UNSIGNED) $(SDKCONFIG_MAKEFILE) $(SECURE_BOOT_SIGNING_KEY)
$(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)

View file

@ -90,11 +90,11 @@ class PartitionTable(list):
for o in range(0,len(b),32):
data = b[o:o+32]
if len(data) != 32:
raise InputError("Ran out of partition table data before reaching end marker")
raise InputError("Partition table length must be a multiple of 32 bytes")
if data == '\xFF'*32:
break # end of partition table
return result # got end marker
result.append(PartitionDefinition.from_binary(data))
return result
raise InputError("Partition table is missing an end-of-table marker")
def to_binary(self):
result = "".join(e.to_binary() for e in self)
@ -105,7 +105,7 @@ class PartitionTable(list):
def to_csv(self, simple_formatting=False):
rows = [ "# Espressif ESP32 Partition Table",
"# Name, Type, SubType, Offset, Size" ]
"# Name, Type, SubType, Offset, Size, Flags" ]
rows += [ x.to_csv(simple_formatting) for x in self ]
return "\n".join(rows) + "\n"
@ -140,6 +140,12 @@ class PartitionDefinition(object):
DATA_TYPE : 0x04,
}
# dictionary maps flag name (as used in CSV flags list, property name)
# to bit set in flags words in binary format
FLAGS = {
"encrypted" : 1
}
# add subtypes for the 16 OTA slot values ("ota_XXX, etc.")
for ota_slot in range(16):
SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = 0x10 + ota_slot
@ -150,11 +156,12 @@ class PartitionDefinition(object):
self.subtype = None
self.offset = None
self.size = None
self.encrypted = False
@classmethod
def from_csv(cls, line):
""" Parse a line from the CSV """
line_w_defaults = line + ",,," # lazy way to support default fields
line_w_defaults = line + ",,,," # lazy way to support default fields
fields = [ f.strip() for f in line_w_defaults.split(",") ]
res = PartitionDefinition()
@ -165,6 +172,14 @@ class PartitionDefinition(object):
res.size = res.parse_address(fields[4])
if res.size is None:
raise InputError("Size field can't be empty")
flags = fields[5].split(":")
for flag in flags:
if flag in cls.FLAGS:
setattr(res, flag, True)
elif len(flag) > 0:
raise InputError("CSV flag column contains unknown flag '%s'" % (flag))
return res
def __eq__(self, other):
@ -220,22 +235,30 @@ class PartitionDefinition(object):
raise InputError("Partition definition length must be exactly 32 bytes. Got %d bytes." % len(b))
res = cls()
(magic, res.type, res.subtype, res.offset,
res.size, res.name, reserved) = struct.unpack(cls.STRUCT_FORMAT, b)
res.size, res.name, flags) = struct.unpack(cls.STRUCT_FORMAT, b)
if "\x00" in res.name: # strip null byte padding from name string
res.name = res.name[:res.name.index("\x00")]
if magic != cls.MAGIC_BYTES:
raise InputError("Invalid magic bytes (%r) for partition definition" % magic)
if reserved != 0:
critical("WARNING: Partition definition had unexpected reserved value 0x%08x. Newer binary format?" % reserved)
for flag,bit in cls.FLAGS.items():
if flags & (1<<bit):
setattr(res, flag, True)
flags &= ~(1<<bit)
if flags != 0:
critical("WARNING: Partition definition had unknown flag(s) 0x%08x. Newer binary format?" % flags)
return res
def get_flags_list(self):
return [ flag for flag in self.FLAGS.keys() if getattr(self, flag) ]
def to_binary(self):
flags = sum((1 << self.FLAGS[flag]) for flag in self.get_flags_list())
return struct.pack(self.STRUCT_FORMAT,
self.MAGIC_BYTES,
self.type, self.subtype,
self.offset, self.size,
self.name,
0) # reserved
flags)
def to_csv(self, simple_formatting=False):
def addr_format(a, include_sizes):
@ -251,11 +274,16 @@ class PartitionDefinition(object):
return k
return "%d" % t
def generate_text_flags():
""" colon-delimited list of flags """
return ":".join(self.get_flags_list())
return ",".join([ self.name,
lookup_keyword(self.type, self.TYPES),
lookup_keyword(self.subtype, self.SUBTYPES.get(self.type, {})),
addr_format(self.offset, False),
addr_format(self.size, True) ])
addr_format(self.size, True),
generate_text_flags()])
class InputError(RuntimeError):
def __init__(self, e):

View file

@ -1,5 +1,5 @@
# Name, Type, SubType, Offset, Size
# Name, Type, SubType, Offset, Size, Flags
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,

1 # Name, Type, SubType, Offset, Size # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x6000 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M factory, app, factory, 0x10000, 1M,

View file

@ -1,4 +1,4 @@
# Name, Type, SubType, Offset, Size
# Name, Type, SubType, Offset, Size, Flags
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs, data, nvs, 0x9000, 0x4000
otadata, data, ota, 0xd000, 0x2000

1 # Name, Type, SubType, Offset, Size # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x4000 nvs, data, nvs, 0x9000, 0x4000
4 otadata, data, ota, 0xd000, 0x2000 otadata, data, ota, 0xd000, 0x2000

View file

@ -10,8 +10,8 @@ sys.path.append("..")
from gen_esp32part import *
SIMPLE_CSV = """
# Name,Type,SubType,Offset,Size
factory,0,2,65536,1048576
# Name,Type,SubType,Offset,Size,Flags
factory,0,2,65536,1048576,
"""
LONGER_BINARY_TABLE = ""
@ -36,10 +36,14 @@ LONGER_BINARY_TABLE += "\xAA\x50\x10\x00" + \
"\x00\x10\x00\x00" + \
"second" + ("\0"*10) + \
"\x00\x00\x00\x00"
LONGER_BINARY_TABLE += "\xFF" * 32
def _strip_trailing_ffs(binary_table):
while binary_table.endswith("\xFF"):
binary_table = binary_table[0:len(binary_table)-1]
"""
Strip all FFs down to the last 32 bytes (terminating entry)
"""
while binary_table.endswith("\xFF"*64):
binary_table = binary_table[0:len(binary_table)-32]
return binary_table
class CSVParserTests(unittest.TestCase):
@ -161,7 +165,7 @@ first, 0x30, 0xEE, 0x100400, 0x300000
"""
t = PartitionTable.from_csv(csv)
tb = _strip_trailing_ffs(t.to_binary())
self.assertEqual(len(tb), 32)
self.assertEqual(len(tb), 64)
self.assertEqual('\xAA\x50', tb[0:2]) # magic
self.assertEqual('\x30\xee', tb[2:4]) # type, subtype
eo, es = struct.unpack("<LL", tb[4:12])
@ -175,11 +179,23 @@ second,0x31, 0xEF, , 0x100000
"""
t = PartitionTable.from_csv(csv)
tb = _strip_trailing_ffs(t.to_binary())
self.assertEqual(len(tb), 64)
self.assertEqual(len(tb), 96)
self.assertEqual('\xAA\x50', tb[0:2])
self.assertEqual('\xAA\x50', tb[32:34])
def test_encrypted_flag(self):
csv = """
# Name, Type, Subtype, Offset, Size, Flags
first, app, factory,, 1M, encrypted
"""
t = PartitionTable.from_csv(csv)
self.assertTrue(t[0].encrypted)
tb = _strip_trailing_ffs(t.to_binary())
tr = PartitionTable.from_binary(tb)
self.assertTrue(tr[0].encrypted)
class BinaryParserTests(unittest.TestCase):
def test_parse_one_entry(self):
# type 0x30, subtype 0xee,
@ -188,14 +204,15 @@ class BinaryParserTests(unittest.TestCase):
"\x00\x00\x10\x00" + \
"\x00\x00\x20\x00" + \
"0123456789abc\0\0\0" + \
"\x00\x00\x00\x00"
"\x00\x00\x00\x00" + \
"\xFF" * 32
# verify that parsing 32 bytes as a table
# or as a single Definition are the same thing
t = PartitionTable.from_binary(entry)
self.assertEqual(len(t), 1)
t[0].verify()
e = PartitionDefinition.from_binary(entry)
e = PartitionDefinition.from_binary(entry[:32])
self.assertEqual(t[0], e)
e.verify()

View file

@ -218,10 +218,46 @@ esp_err_t IRAM_ATTR spi_flash_write(size_t dest_addr, const void *src, size_t si
}
esp_err_t IRAM_ATTR spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size)
{
if ((dest_addr % 32) != 0) {
return ESP_ERR_INVALID_ARG;
}
if ((size % 32) != 0) {
return ESP_ERR_INVALID_SIZE;
}
if ((uint32_t) src < 0x3ff00000) {
// if source address is in DROM, we won't be able to read it
// from within SPIWrite
// TODO: consider buffering source data using heap and writing it anyway?
return ESP_ERR_INVALID_ARG;
}
COUNTER_START();
spi_flash_disable_interrupts_caches_and_other_cpu();
SpiFlashOpResult rc;
rc = spi_flash_unlock();
if (rc == SPI_FLASH_RESULT_OK) {
/* SPI_Encrypt_Write encrypts data in RAM as it writes,
so copy to a temporary buffer - 32 bytes at a time.
*/
uint32_t encrypt_buf[32/sizeof(uint32_t)];
for (size_t i = 0; i < size; i += 32) {
memcpy(encrypt_buf, ((const uint8_t *)src) + i, 32);
rc = SPI_Encrypt_Write((uint32_t) dest_addr + i, encrypt_buf, 32);
if (rc != SPI_FLASH_RESULT_OK) {
break;
}
}
bzero(encrypt_buf, sizeof(encrypt_buf));
}
COUNTER_ADD_BYTES(write, size);
return spi_flash_translate_rc(rc);
}
esp_err_t IRAM_ATTR spi_flash_read(size_t src_addr, void *dest, size_t size)
{
// TODO: replace this check with code which deals with unaligned destinations
if (((ptrdiff_t) dest) % 4 != 0) {
if (((ptrdiff_t)dest % 4) != 0) {
return ESP_ERR_INVALID_ARG;
}
// Source alignment is also checked in ROM code, but we can give

View file

@ -89,6 +89,10 @@ typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t;
/**
* @brief partition information structure
*
* This is not the format in flash, that format is esp_partition_info_t.
*
* However, this is the format used by this API.
*/
typedef struct {
esp_partition_type_t type; /*!< partition type (app/data) */

View file

@ -16,6 +16,7 @@
#define ESP_SPI_FLASH_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "esp_err.h"
#include "sdkconfig.h"
@ -79,14 +80,33 @@ esp_err_t spi_flash_erase_range(size_t start_address, size_t size);
* @note If source address is in DROM, this function will return
* ESP_ERR_INVALID_ARG.
*
* @param dest destination address in Flash
* @param src pointer to the source buffer
* @param size length of data, in bytes
* @param dest destination address in Flash. Must be a multiple of 4 bytes.
* @param src pointer to the source buffer.
* @param size length of data, in bytes. Must be a multiple of 4 bytes.
*
* @return esp_err_t
*/
esp_err_t spi_flash_write(size_t dest, const void *src, size_t size);
/**
* @brief Write data encrypted to Flash.
*
* @note Flash encryption must be enabled for this function to work.
*
* @note Address in flash, dest, has to be 32-byte aligned.
*
* @note If source address is in DROM, this function will return
* ESP_ERR_INVALID_ARG.
*
* @param dest destination address in Flash. Must be a multiple of 32 bytes.
* @param src pointer to the source buffer.
* @param size length of data, in bytes. Must be a multiple of 32 bytes.
*
* @return esp_err_t
*/
esp_err_t spi_flash_write_encrypted(size_t dest, const void *src, size_t size);
/**
* @brief Read data from Flash.
*

View file

@ -22,6 +22,7 @@
#include "esp_flash_data_types.h"
#include "esp_spi_flash.h"
#include "esp_partition.h"
#include "esp_flash_encrypt.h"
#include "esp_log.h"
@ -164,7 +165,11 @@ static esp_err_t load_partitions()
item->info.size = it->pos.size;
item->info.type = it->type;
item->info.subtype = it->subtype;
item->info.encrypted = false;
item->info.encrypted = it->flags & PART_FLAG_ENCRYPTED;
if (esp_flash_encryption_enabled() && it->type == PART_TYPE_APP) {
/* All app partitions are encrypted if encryption is turned on */
item->info.encrypted = true;
}
// it->label may not be zero-terminated
strncpy(item->info.label, (const char*) it->label, sizeof(it->label));
item->info.label[sizeof(it->label)] = 0;
@ -201,7 +206,24 @@ esp_err_t esp_partition_read(const esp_partition_t* partition,
if (src_offset + size > partition->size) {
return ESP_ERR_INVALID_SIZE;
}
return spi_flash_read(partition->address + src_offset, dst, size);
if (!partition->encrypted) {
return spi_flash_read(partition->address + src_offset, dst, size);
} else {
/* Encrypted partitions need to be read via a cache mapping */
const void *buf;
spi_flash_mmap_handle_t handle;
esp_err_t err;
err = esp_partition_mmap(partition, src_offset, size,
SPI_FLASH_MMAP_DATA, &buf, &handle);
if (err != ESP_OK) {
return err;
}
memcpy(dst, buf, size);
spi_flash_munmap(handle);
return ESP_OK;
}
}
esp_err_t esp_partition_write(const esp_partition_t* partition,
@ -218,7 +240,12 @@ esp_err_t esp_partition_write(const esp_partition_t* partition,
if (dst_offset + size > partition->size) {
return ESP_ERR_INVALID_SIZE;
}
return spi_flash_write(partition->address + dst_offset, src, size);
dst_offset = partition->address + dst_offset;
if (partition->encrypted) {
return spi_flash_write_encrypted(dst_offset, src, size);
} else {
return spi_flash_write(dst_offset, src, size);
}
}
esp_err_t esp_partition_erase_range(const esp_partition_t* partition,

View file

@ -4,7 +4,7 @@ Partition Tables
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.
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 0x8000 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.

View file

@ -0,0 +1,321 @@
Flash Encryption
================
Flash Encryption is a feature for encrypting the contents of the ESP32's attached SPI flash. When flash encryption is enabled, physical readout of the SPI flash is not sufficient to recover most flash contents.
Flash Encryption is separate from the `Secure Boot` feature, and you can use flash encryption without enabling secure boot. However we recommend using both features together for a secure environment.
**IMPORTANT: Enabling flash encryption limits your options for further updates of your ESP32. Make sure to read this document (including `Limitations of Flash Encryption` and understand the implications of enabling flash encryption.**
**IMPORTANT: Flash Encryption feature is currently enabled for development use only, with a key generated on the host. The recommended production configuration, where the flash encryption key is generated by the device on first boot, is currently disabled while final testing is done. This documentation refers to flash encryption keys being generated on first boot, however for now it is necessary to follow the additional steps shown under `Precalculated Flash Encryption Key`.**
Background
----------
- The contents of the flash are encrypted using AES with a 256 bit key. The flash encryption key is stored in efuse internal to the chip, and is (by default) protected from software access.
- Flash access is transparent via the flash cache mapping feature of ESP32 - any flash regions which are mapped to the address space will be transparently decrypted when read.
- Encryption is applied by flashing the ESP32 with plaintext data, and (if encryption is enabled) the bootloader encrypts the data in place on first boot.
- Not all of the flash is encrypted. The following kinds of flash data are encrypted:
- Bootloader
- Secure boot bootloader digest (if secure boot is enabled)
- Partition Table
- All "app" type partitions
- Any partition marked with the "encrypt" flag in the partition table
It may be desirable for some data partitions to remain unencrypted for ease of access, or to use flash-friendly update algorithms that are ineffective if the data is encrypted. "NVS" partitions for non-volatile storage cannot be encrypted.
- The flash encryption key is stored in efuse key block 1, internal to the ESP32 chip. By default, this key is read- and write-protected so software cannot access it or change it.
- The `flash encryption algorithm` is AES-256, where the key is "tweaked" with the offset address of each 32 byte block of flash. This means every 32 byte block (two consecutive 16 byte AES blocks) is encrypted with a unique key derived from the flash encryption key.
- Although software running on the chip can transparently decrypt flash contents, by default it is made possible for the UART bootloader to decrypt (or encrypt) data when flash encryption is enabled.
Flash Encryption Initialisation
-------------------------------
This is the default (and recommended) flash encryption initialisation process. It is possible to customise this process for development or other purposes, see `Flash Encryption Advanced Features` for details.
**IMPORTANT: Once flash encryption is enabled on first boot, the hardware allows a maximum of 3 subsequent flash updates via physical re-flashing. If secure boot is enabled, no physical re-flashes are possible. OTA updates can be used to update flash content without counting towards this limit. When enabling flash encryption in development, use a `precalculated flash encryption key` to allow physically re-flashing an unlimited number of times with pre-encrypted data.**
- The bootloader must be compiled with flash encryption support enabled. In ``make menuconfig``, navigate to "Security Features" and select "Yes" for "Enable flash nencryption on boot".
- If enabling Secure Boot at the same time, you can simultaneously select those options now. See the `Secure Boot` documentation for details.
- Build and flash the bootloader, partition table and factory app image as normal. These partitions are initially written to the flash unencrypted.
- On first boot, the bootloader sees ``FLASH_CRYPT_CNT`` efuse is set to 0 so it generates a flash encryption key using the hardware random number generator. This key is stored in efuse. The key is read and write protected against further software access.
- All of the encrypted partitions are then encrypted in-place by the bootloader. Encrypting in-place can take some time (up to a minute for large partitions.)
**IMPORTANT: Do not interrupt power to the ESP32 while the first boot encryption pass is running. If power is interrupted, the flash contents will be corrupted and require flashing with unencrypted data again. This re-flash will not count towards the flashing limit, as ``FLASH_CRYPT_CNT`` is only updated after this process finishes.**
- Once flashing is complete. efuses are blown (by default) to disable encrypted flash access while the UART bootloader is running.
- If not already write-protected, the ``FLASH_CRYPT_CONFIG`` efuse is also burned to the maximum value (``0xF``) to maximise the number of key bits which are tweaked in the flash algorithm. See `Setting FLASH_CRYPT_CONFIG` for details of this efuse.
- Finally, the ``FLASH_CRYPT_CNT`` efuse is burned with the initial value 1. It is this efuse which activates the transparent flash encryption layer, and limits the number of subsequent reflashes. See the `Updating Encrypted Flash` section for details about ``FLASH_CRYPT_CNT``.
- The bootloader resets itself to reboot from the newly encrypted flash.
Encrypted Flash Access
----------------------
Reading Encrypted Flash
^^^^^^^^^^^^^^^^^^^^^^^
Whenever the ``FLASH_CRYPT_CNT`` efuse is set to a value with an odd number of bits set, all flash content which is accessed via the MMU's flash cache is transparently decrypted. This includes:
- Executable application code in flash (IROM).
- All read-only data stored in flash (DROM).
- Any data accessed via ``esp_spi_flash_mmap``.
- The software bootloader image when it is read by the ROM bootloader.
**IMPORTANT: The MMU flash cache unconditionally decrypts all data. Data which is stored unencrypted in the flash will be "transparently decrypted" via the flash cache and appear to software like random garbage.**
To read data without using a flash cache MMU mapping, we recommend using the partition read function ``esp_partition_read``. When using this function, data will only be decrypted when it is read from an encrypted partition. Other partitions will be read unencrypted. In this way, software can access encrypted and non-encrypted flash in the same way.
Data which is read via other SPI read APIs are not decrypted:
- Data read via ``esp_spi_flash_read`` is not decrypted
- Data read via ROM function ``SPIRead`` is not decrypted (this function is not supported in esp-idf apps).
- Data stored using the Non-Volatile Storage (NVS) API is always stored decrypted.
Writing Encrypted Flash
^^^^^^^^^^^^^^^^^^^^^^^
Where possible, we recommend using the partition write function ``esp_partition_write``. When using this function, data will only be encrypted when writing to encrypted partitions. Data will be written to other partitions unencrypted. In this way, software can access encrypted and non-encrypted flash in the same way.
The ``esp_spi_flash_write`` function will write data when the write_encrypted parameter is set to true. Otherwise, data will be written unencrypted.
The ROM function ``SPI_Encrypt_Write`` will write encrypted data to flash, the ROM function ``SPIWrite`` will write unencrypted to flash. (these function are not supported in esp-idf apps).
The minimum write size for unencrypted data is 4 bytes (and the alignment is 4 bytes). Because data is encrypted in blocks, the minimum write size for encrypted data is 32 bytes (and the alignment is 32 bytes.)
Updating Encrypted Flash
------------------------
OTA Updates
^^^^^^^^^^^
OTA updates to encrypted partitions will automatically write encrypted, as long as the ``esp_partition_write`` function is used.
Serial Flashing
^^^^^^^^^^^^^^^
Provided secure boot is not used, the ``FLASH_CRYPT_CNT`` registers allow the flash to be updated with new plaintext data via serial flashing (or other physical methods), up to 3 additional times. ``FLASH_CRYPT_CNT`` efuse is an 8-bit value, and the flash encryption enables or disables based on the number of bits which are set to "1":
- Even number (0-6) bits are set: Transparent reading of encrypted flash is disabled, any encrypted data cannot be decrypted. If the bootloader was built with "Enable flash encryption on boot" then it will see this situation and immediately re-encrypt the flash wherever it finds unencrypted data. Once done, it sets another bit in the efuse to '1' meaning an odd number of bits are now set.
- Odd number (1-7) bits are set: Transparent reading of encrypted flash is enabled.
- All 8 bits are set (valuye 0: Transparent reading of encrypted flash is disabled, any encrypted data is inaccessible. Bootloader will normally detect this condition and halt. To avoid use of this state to load unauthorised code, secure boot must be used or ``FLASH_CRYPT_CNT`` must be write-protected.
The espefuse.py tool can be used to manually change the number of bits set in ``FLASH_CRYPT_CNT``, via serial bootloader.
Limited Updates
^^^^^^^^^^^^^^^
Only 4 physical flash updates (writing plaintext data which is then encrypted) are possible:
1. On first plaintext boot, bit count has brand new value 0 and bootloader changes to 1 (0x01) following encryption.
2. On next plaintext flash update, bit count is manually updated to 2 (0x03) and bootloader changes to 4 (0x07) following encryption.
3. Then bit count is manually updated to 4 (0x0F) and the bootloader changes efuse bit count to 5 (0x1F).
4. Finally bootloader is manually updated to 6 (0x3F) and bootloader changes efuse bit count to 7 (0x7F).
Cautions With Re-Flashing
^^^^^^^^^^^^^^^^^^^^^^^^^^
- When reflashing via serial, reflash every partition that was previously written with plaintext (including bootloader). It is possible to skip app partitions which are not the "currently selected" OTA partition (these will not be re-encrypted unless a plaintext app image is found there.) However any partition marked with the "encrypt" flag will be unconditionally re-encrypted, meaning that any already encrypted data will be encrypted twice and corrupted.
- If secure boot is enabled, you can't reflash via serial at all unless you used chosen the "Reflashable" option for Secure Boot, pre-generated a key and burned it to the ESP32. In this case you can re-flash a plaintext secure boot digest and bootloader image at offset 0 (see `Secure Boot` documentation.) In production secure boot configuration, the secure boot digest is stored encrypted - so if ``FLASH_CRYPT_CNT`` is set to an even value then the ROM bootloader will read the encrypted digest as-is and therefore will fail to verify any bootloader image as valid.
Re-Flashing Procedure
^^^^^^^^^^^^^^^^^^^^^
The steps to update a device with plaintext via UART bootloader, when flash encryption is enabled are:
- Build the application as usual.
- Burn the ``FLASH_CRYPT_CNT`` efuse by running the command ``espefuse.py burn_efuse FLASH_CRYPT_CNT``. espefuse.py will automatically increment the bit count by 1.
- Flash the device with plaintext data as usual (``make flash`` or ``esptool.py`` commands.) Flash all previously encrypted partitions, including the bootloader. If secure boot is enabled, it must be enabled in "Reflashable" mode and a pre-generated key burned to the ESP32 - flash the bootloader-reflash-digest.bin file at offset 0x0.
- Reset the device and it will re-encrypt plaintext partitions, burn the ``FLASH_CRYPT_CNT`` flag to re-enable encryption.
Disabling Updates
^^^^^^^^^^^^^^^^^
To prevent further plaintext updates via physical access, use espefuse.py to write protect the ``FLASH_CRYPT_CNT`` efuse after flash encryption has been enabled (ie after first boot is complete)::
espefuse.py write_protect_efuse FLASH_CRYPT_CNT
This prevents any further modifications to disable or re-enable flash encryption.
Limitations of Flash Encryption
-------------------------------
Flash Encryption prevents plaintext readout of the encrypted flash, to protect firmware against unauthorised readout and modification. It is important to understand the limitations of the flash encryption system:
- Flash encryption is only as strong as the key. For this reason, we recommend keys are generated on the device during first boot (default behaviour). If generating keys off-device to burn with ``esp_efuse.py burn_key``, ensure they are generated from a quality random number source, kept secure, and never shared between devices.
- Not all data is stored encrypted. If storing data on flash, check if the method you are using (library, API, etc.) supports flash encryption.
- Flash encryption does not prevent an attacker from understanding the high-level layout of the flash. This is because the same AES key is used for every two 16 byte AES blocks. When both adjacent 16 byte blocks contain identical content (such as empty or padding areas), these blocks will encrypt to produce matching pairs of encrypted blocks. This may allow an attacker to make high-level comparisons between encrypted devices (ie to tell if two devices are probably running the same firmware version).
- For the same reason, an attacker can always guess when two adjacent 16 byte blocks (32 byte aligned) contain identical content. Keep this in mind if storing sensitive data on the flash, design your flash storage so this doesn't happen (using a counter byte or some other non-identical value every 16 bytes is sufficient).
- Flash encryption alone may not prevent an attacker from modifying the firmware of the device. Always use flash encryption in combination with Secure Boot.
Flash Encryption Advanced Features
----------------------------------
Encrypted Partition Flag
^^^^^^^^^^^^^^^^^^^^^^^^
In the `partition table` description CSV files, there is a field for flags.
Usually left blank, if you write "encrypted" in this field then the partition will be marked as encrypted in the partition table, and data written here will be treated as encrypted (same as an app partition)::
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000
phy_init, data, phy, 0xf000, 0x1000
factory, app, factory, 0x10000, 1M
secret_data, 0x40, 0x01, 0x20000, 256K, encrypted
- None of the default partition formats have any encrypted data partitions.
- It is not necessary to mark "app" partitions as encrypted, they are always treated as encrypted.
- The "encrypted" flag does nothing if flash encryption is not enabled.
- It is possible to mark the optional ``phy`` partition with ``phy_init`` data as encrypted, if you wish to protect this data from physical access readout or modification.
- It is not possible to mark the ``nvs`` partition as encrypted.
Precalculated Flash Encryption Key
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is possible to pre-generate a flash encryption key on the host computer and burn it into the ESP32 efuse. This allows data to be per-encrypted on the host and flashed to the ESP32 without needing a plaintext flash update.
This is useful for development, because it removes the 4 flash limit and allows reflashing with secure boot enabled.
**IMPORTANT** This method is intended to assist with development only, not for production devices. If pre-generating flash encryption for production, ensure the keys are generated from a high quality random number source and do not share the same flash encryption key across multiple devices.
Obtaining Flash Encryption Key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Flash encryption keys are 32 bytes of random data. You can generate a random key with espsecure.py::
espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin
(The randomness of this data is only as good as the OS and it's Python installation's random data source.)
Alternatively, if you're using `secure boot` and have a secure boot signing key then you can generate a deterministic SHA-256 digest of the secure boot private key to use::
espsecure.py digest_private-key --keyfile secure_boot_signing_key.pem my_flash_encryption_key.bin
The same key is used as the secure boot digest key if you enabled "Reflashable" mode for secure boot.
This means you can always re-calculate the flash encryption key from the secure boot private signing key. This method is **not at all suitable** for production devices.
Burning Flash Encryption Key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once you have generated a flash encryption key, you need to burn it to efuse on the device. This **must be done before first boot**, otherwise the ESP32 will generate a random key that software can't access.
To burn a key to the device (possible one time only)::
espefuse.py burn_key flash_encryption my_flash_encryption_key.bin
First Flash
~~~~~~~~~~~
For the first flash, follow the same steps as for default `Flash Encryption Initialisation` and flash a plaintext image. The bootloader will enable flash encryption using the pre-burned key and encrypt all partitions.
Reflashing
~~~~~~~~~~
To reflash an encrypted image requires an additional manual update step, to encrypt the data you wish to flash.
Suppose that this is the normal flashing non-encrypted flashing step::
esptool.py --port /dev/ttyUSB0 --baud 115200 write_flash -z 0x10000 build/my-app.bin
The data needs to be pre-encrypted with knowledge of the address (0x10000) and the binary file name::
espsecure.py encrypt_flash_data --keyfile my_flash_encryption_key.bin --address 0x10000 -o build/my-app-encrypted.bin build/my-app.bin
This step will encrypt ``my-app.bin`` using the supplied key, and produce an encrypted file ``my-app-encrypted.bin``. Be sure that the address argument matches the address where you plan to flash the binary.
Then, flash the encrypted binary with esptool.py::
esptool.py --port /dev/ttyUSB0 --baud 115200 write_flash -z 0x10000 build/my-app-encrypted.bin
Enabling UART Bootloader Encryption/Decryption
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, on first boot the flash encryption process will burn efuses ``DISABLE_DL_ENCRYPT``, ``DISABLE_DL_DECRYPT`` and ``DISABLE_DL_CACHE``.
- ``DISABLE_DL_ENCRYPT`` disables the flash encryption operations when running in UART bootloader boot mode.
- ``DISABLE_DL_DECRYPT`` disables transparent flash decryption when running in UART bootloader mode, even if ``FLASH_CRYPT_CNT`` is set to enable it in normal operation.
- ``DISABLE_DL_CACHE`` disables the entire MMU flash cache when running in UART bootloader mode.
It is possible to burn only some of these efuses, and write-protect the rest (with unset value 0) before the first boot, in order to preserve them::
espefuse.py burn_efuse DISABLE_DL_DECRYPT
espefuse.py write_protect_efuse DISABLE_DL_ENCRYPT
(Note that all 3 of these efuses are disabled via one write protect bit, so write protecting one will write protect all of them.)
Write protecting these efuses when they are unset (0) is not currently useful, as ``esptool.py`` does not support flash encryption functions.
However, note that write protecting ``DISABLE_DL_DECRYPT`` when it is unset (0) effectively makes flash encryption useless, as an attacker with physical access can use UART bootloader mode to read out the flash.
Technical Details
-----------------
Flash Encryption Algorithm
^^^^^^^^^^^^^^^^^^^^^^^^^^
- AES-256 operates on 16 byte blocks of data. The flash encryption engine encrypts and decrypts data in 32 byte blocks, two AES blocks in series.
- AES algorithm is used inverted in flash encryption, so the flash encryption "encrypt" operation is AES decrypt and the "decrypt" operation is AES encrypt. This is for performance reasons and does not alter the effectiveness of the algorithm.
- The main flash encryption key is stored in efuse (BLK2) and by default is protected from further writes or software readout.
- Each 32 byte block is encrypted with a unique key which is derived from this main flash encryption key XORed with the offset of this block in the flash (a "key tweak").
- The specific tweak depends on the setting of ``FLASH_CRYPT_CONFIG`` efuse. This is a 4 bit efuse, where each bit enables XORing of a particular range of the key bits:
- Bit 1, bits 0-66 of the key are XORed.
- Bit 2, bits 67-131 of the key are XORed.
- Bit 3, bits 132-194 of the key are XORed.
- Bit 4, bits 195-256 of the key are XORed.
It is recommended that ``FLASH_CRYPT_CONFIG`` is always left to set the default value `0xF`, so that all key bits are XORed with the block offset. See `Setting FLASH_CRYPT_CONFIG` for details.
- The high 19 bits of the block offset (bit 5 to bit 23) are XORed with the main flash encryption key. This range is chosen for two reasons: the maximum flash size is 16MB (24 bits), and each block is 32 bytes so the least significant 5 bits are always zero.
- There is a particular mapping from each of the 19 block offset bits to the 256 bits of the flash encryption key, to determine which bit is XORed with which. See the variable _FLASH_ENCRYPTION_TWEAK_PATTERN in espsecure.py for a list of these.
- For the full algorithm implemented in Python, see `_flash_encryption_operation()` in the espsecure.py source code.
Setting FLASH_CRYPT_CONFIG
^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``FLASH_CRYPT_CONFIG`` efuse determines the number of bits in the flash encryption key which are "tweaked" with the block offset. See `Flash Encryption Algorithm` for details.
First boot of the bootloader always sets this value to the maximum `0xF`.
It is possible to write these efuse manually, and write protect it before first boot in order to select different tweak values. This is not recommended.
It is strongly recommended to never write protect ``FLASH_CRYPT_CONFIG`` when it the value is zero. If this efuse is set to zero, no bits in the flash encryption key are tweaked and the flash encryption algorithm is equivalent to AES ECB mode.
.. _Secure Boot: secure-boot.rst
.. _partition table: ../partition-tables.rst

View file

@ -3,9 +3,9 @@ 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 we recommend using both features together for a secure environment.
Secure Boot is separate from the `Flash Encryption` 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.**
**IMPORTANT: Secure Boot feature is currently enabled for development use only, with a key generated on the host. The recommended production configuration, where the secure boot key is generated by the device on first boot, is currently disabled while final testing is done. This documentation refers to "One-Time Flashable" mode (where keys are generated on the device), but for now only the `Re-Flashable Software Bootloader` mode is available.**
Background
----------
@ -175,5 +175,26 @@ Deterministic ECDSA as specified by `RFC6979`.
- 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.
Manual Commands
~~~~~~~~~~~~~~~
Secure boot is integrated into the esp-idf build system, so `make` will automatically sign an app image if secure boot is enabled. `make bootloader` will produce a bootloader digest if menuconfig is configured for it.
However, it is possible to use the `espsecure.py` tool to make standalone signatures and digests.
To sign a binary image::
espsecure.py sign_data --keyfile ./my_signing_key.pem --output ./image_signed.bin image-unsigned.bin
Keyfile is the PEM file containing an ECDSA private signing key.
To generate a bootloader digest::
espsecure.py digest_secure_bootloader --keyfile ./securebootkey.bin --output ./bootloader-digest.bin build/bootloader/bootloader.bin
Keyfile is the 32 byte raw secure boot key for the device. To flash this digest onto the device::
esptool.py write_flash 0x0 bootloader-digest.bin
.. _RFC6979: https://tools.ietf.org/html/rfc6979
.. _Flash Encryption: flash-encryption.rst

View file

@ -139,7 +139,7 @@ export COMPONENT_INCLUDES
include $(IDF_PATH)/make/common.mk
all:
ifdef CONFIG_SECURE_BOOTLOADER_ENABLED
ifdef CONFIG_SECURE_BOOT_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