Improve flash encryption documentation and add Development & Release modes

This MR improves existing flash encryption document to provide simplified steps
Adds two new modes for user: Development & Release
Adds a simple example
Supports encrypted write through make command
This commit is contained in:
Hemal Gujarathi 2019-01-21 22:14:56 +08:00 committed by bot
parent beaefd3359
commit a68c7c21e1
22 changed files with 924 additions and 247 deletions

View file

@ -43,6 +43,9 @@ erase_ota: erase_otadata
all: blank_ota_data
flash: blank_ota_data
ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
encrypted-flash: blank_ota_data
endif
TMP_DEFINES := $(BUILD_DIR_BASE)/app_update/tmp_cppflags.txt
export TMP_DEFINES

View file

@ -409,33 +409,43 @@ menu "Security features"
Note: After first boot, the system will be permanently encrypted. Re-flashing an encrypted
system is complicated and not always possible.
Read https://docs.espressif.com/projects/esp-idf/en/latest/security/flash-encryption.html before enabling.
Read https://docs.espressif.com/projects/esp-idf/en/latest/security/flash-encryption.html
before enabling.
config SECURE_FLASH_ENC_INSECURE
bool "Allow potentially insecure options"
choice SECURE_FLASH_ENCRYPTION_MODE
bool "Enable usage mode"
depends on SECURE_FLASH_ENC_ENABLED
default N
default SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
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.
By default Development mode is enabled which allows UART bootloader to perform flash encryption operations
Only enable these options if you are very sure.
Select Release mode only for production or manufacturing. Once enabled you can not reflash using UART
bootloader
Refer to https://docs.espressif.com/projects/esp-idf/en/latest/security/secure-boot.html and
https://docs.espressif.com/projects/esp-idf/en/latest/security/flash-encryption.html for details.
config SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
bool "Development(NOT SECURE)"
select SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC
config SECURE_FLASH_ENCRYPTION_MODE_RELEASE
bool "Release"
endchoice
menu "Potentially insecure options"
visible if SECURE_FLASH_ENC_INSECURE || SECURE_BOOT_INSECURE
visible if SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT || SECURE_BOOT_INSECURE
# NOTE: Options in this menu NEED to have SECURE_BOOT_INSECURE
# and/or SECURE_FLASH_ENC_INSECURE in "depends on", as the menu
# and/or SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT 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 || SECURE_FLASH_ENC_INSECURE
depends on SECURE_BOOT_INSECURE || SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
default N
help
By default, the BASIC ROM Console starts on reset if no valid bootloader is
@ -449,7 +459,7 @@ menu "Security features"
config SECURE_BOOT_ALLOW_JTAG
bool "Allow JTAG Debugging"
depends on SECURE_BOOT_INSECURE || SECURE_FLASH_ENC_INSECURE
depends on SECURE_BOOT_INSECURE || SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
default N
help
If not set (default), the bootloader will permanently disable JTAG (across entire chip) on first boot
@ -476,7 +486,7 @@ menu "Security features"
config SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC
bool "Leave UART bootloader encryption enabled"
depends on SECURE_FLASH_ENC_INSECURE
depends on SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
default N
help
If not set (default), the bootloader will permanently disable UART bootloader encryption access on
@ -486,7 +496,7 @@ menu "Security features"
config SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC
bool "Leave UART bootloader decryption enabled"
depends on SECURE_FLASH_ENC_INSECURE
depends on SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
default N
help
If not set (default), the bootloader will permanently disable UART bootloader decryption access on
@ -497,7 +507,7 @@ menu "Security features"
config SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE
bool "Leave UART bootloader flash cache enabled"
depends on SECURE_FLASH_ENC_INSECURE
depends on SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
default N
help
If not set (default), the bootloader will permanently disable UART bootloader flash cache access on
@ -507,3 +517,4 @@ menu "Security features"
endmenu # Potentially Insecure
endmenu # Security features

View file

@ -16,7 +16,7 @@ CONFIG_APP_SECURE_VERSION_SIZE_EFUSE_FIELD CONFIG_BOOTLOADER_AP
CONFIG_EFUSE_SECURE_VERSION_EMULATE CONFIG_BOOTLOADER_EFUSE_SECURE_VERSION_EMULATE
CONFIG_FLASH_ENCRYPTION_ENABLED CONFIG_SECURE_FLASH_ENC_ENABLED
CONFIG_FLASH_ENCRYPTION_INSECURE CONFIG_SECURE_FLASH_ENC_INSECURE
CONFIG_FLASH_ENCRYPTION_INSECURE CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_ENCRYPT CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_ENC
CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_DECRYPT CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_DEC
CONFIG_FLASH_ENCRYPTION_UART_BOOTLOADER_ALLOW_CACHE CONFIG_SECURE_FLASH_UART_BOOTLOADER_ALLOW_CACHE

View file

@ -5,6 +5,7 @@ set(srcs
"src/bootloader_random.c"
"src/bootloader_utility.c"
"src/esp_image_format.c"
"src/flash_encrypt.c"
"src/flash_partitions.c"
"src/flash_qio_mode.c")

View file

@ -22,6 +22,14 @@
#endif
#include "soc/efuse_periph.h"
/* @brief Flash encryption mode based on efuse values
*/
typedef enum {
ESP_FLASH_ENC_MODE_DISABLED, // flash encryption is not enabled (flash crypt cnt=0)
ESP_FLASH_ENC_MODE_DEVELOPMENT, // flash encryption is enabled but for Development (reflash over UART allowed)
ESP_FLASH_ENC_MODE_RELEASE // flash encryption is enabled for Release (reflash over UART disabled)
}esp_flash_enc_mode_t;
/**
* @file esp_partition.h
* @brief Support functions for flash encryption features
@ -114,4 +122,13 @@ esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length);
*/
void esp_flash_write_protect_crypt_cnt();
/** @brief Return the flash encryption mode
*
* The API is called during boot process but can also be called by
* application to check the current flash encryption mode of ESP32
*
* @return
*/
esp_flash_enc_mode_t esp_get_flash_encryption_mode();
#endif

View file

@ -27,6 +27,12 @@
#include "esp32/rom/cache.h"
#include "esp32/rom/spi_flash.h" /* TODO: Remove this */
/* This file implements FLASH ENCRYPTION related APIs to perform
* various operations such as programming necessary flash encryption
* eFuses, detect whether flash encryption is enabled (by reading eFuse)
* and if required encrypt the partitions in flash memory
*/
static const char *TAG = "flash_encrypt";
/* Static functions for stages of flash encryption */
@ -203,7 +209,13 @@ static esp_err_t encrypt_flash_contents(uint32_t flash_crypt_cnt, bool flash_cry
/* ffs_inv shouldn't be zero, as zero implies flash_crypt_cnt == EFUSE_RD_FLASH_CRYPT_CNT (0x7F) */
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);
uint32_t wdata0_reg = ((new_flash_crypt_cnt & EFUSE_FLASH_CRYPT_CNT) << EFUSE_FLASH_CRYPT_CNT_S);
#ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE
ESP_LOGI(TAG, "Write protecting FLASH_CRYPT_CNT eFuse");
wdata0_reg |= EFUSE_WR_DIS_FLASH_CRYPT_CNT;
#endif
REG_WRITE(EFUSE_BLK0_WDATA0_REG, wdata0_reg);
esp_efuse_burn_new_values();
ESP_LOGI(TAG, "Flash encryption completed");
@ -337,14 +349,4 @@ esp_err_t esp_flash_encrypt_region(uint32_t src_addr, size_t data_length)
flash_failed:
ESP_LOGE(TAG, "flash operation failed: 0x%x", err);
return err;
}
void esp_flash_write_protect_crypt_cnt()
{
uint32_t efuse_blk0 = REG_READ(EFUSE_BLK0_RDATA0_REG);
bool flash_crypt_wr_dis = efuse_blk0 & EFUSE_WR_DIS_FLASH_CRYPT_CNT;
if(!flash_crypt_wr_dis) {
REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_FLASH_CRYPT_CNT);
esp_efuse_burn_new_values();
}
}
}

View file

@ -0,0 +1,52 @@
// Copyright 2015-2019 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 "esp_efuse.h"
#include "esp_efuse_table.h"
#include "esp_flash_encrypt.h"
void esp_flash_write_protect_crypt_cnt()
{
uint8_t flash_crypt_cnt_wr_dis = 0;
esp_efuse_read_field_blob(ESP_EFUSE_WR_DIS_FLASH_CRYPT_CNT, &flash_crypt_cnt_wr_dis, 1);
if (!flash_crypt_cnt_wr_dis) {
esp_efuse_write_field_cnt(ESP_EFUSE_WR_DIS_FLASH_CRYPT_CNT, 1);
}
}
esp_flash_enc_mode_t esp_get_flash_encryption_mode()
{
uint8_t efuse_flash_crypt_cnt_wr_protected = 0;
uint8_t dis_dl_enc = 0, dis_dl_dec = 0, dis_dl_cache = 0;
esp_flash_enc_mode_t mode = ESP_FLASH_ENC_MODE_DEVELOPMENT;
if (esp_flash_encryption_enabled()) {
/* Check if FLASH CRYPT CNT is write protected */
esp_efuse_read_field_blob(ESP_EFUSE_WR_DIS_FLASH_CRYPT_CNT, &efuse_flash_crypt_cnt_wr_protected, 1);
if (efuse_flash_crypt_cnt_wr_protected) {
esp_efuse_read_field_blob(ESP_EFUSE_DISABLE_DL_CACHE, &dis_dl_cache, 1);
esp_efuse_read_field_blob(ESP_EFUSE_DISABLE_DL_ENCRYPT, &dis_dl_enc, 1);
esp_efuse_read_field_blob(ESP_EFUSE_DISABLE_DL_DECRYPT, &dis_dl_dec, 1);
/* Check if DISABLE_DL_DECRYPT, DISABLE_DL_ENCRYPT & DISABLE_DL_CACHE are set */
if ( dis_dl_cache && dis_dl_enc && dis_dl_dec ) {
mode = ESP_FLASH_ENC_MODE_RELEASE;
}
}
} else {
mode = ESP_FLASH_ENC_MODE_DISABLED;
}
return mode;
}

View file

@ -236,3 +236,4 @@ esp_err_t esp_efuse_update_secure_version(uint32_t secure_version)
#endif
return ESP_OK;
}

View file

@ -62,7 +62,7 @@
#include "esp_core_dump.h"
#include "esp_app_trace.h"
#include "esp_private/dbg_stubs.h"
#include "esp_efuse.h"
#include "esp_flash_encrypt.h"
#include "esp32/spiram.h"
#include "esp_clk_internal.h"
#include "esp_timer.h"
@ -202,6 +202,20 @@ void IRAM_ATTR call_start_cpu0()
abort();
}
ESP_EARLY_LOGI(TAG, "Starting app cpu, entry point is %p", call_start_cpu1);
esp_flash_enc_mode_t mode;
mode = esp_get_flash_encryption_mode();
if (mode == ESP_FLASH_ENC_MODE_DEVELOPMENT) {
#ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_RELEASE
ESP_EARLY_LOGE(TAG, "Flash encryption settings error: mode should be RELEASE but is actually DEVELOPMENT");
ESP_EARLY_LOGE(TAG, "Mismatch found in security options in menuconfig and efuse settings");
#else
ESP_EARLY_LOGW(TAG, "Flash encryption mode is DEVELOPMENT");
#endif
} else if (mode == ESP_FLASH_ENC_MODE_RELEASE) {
ESP_EARLY_LOGI(TAG, "Flash encryption mode is RELEASE");
}
//Flush and enable icache for APP CPU
Cache_Flush(1);
Cache_Read_Enable(1);

View file

@ -20,6 +20,18 @@ file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/flash_project_args.in2
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/flash_project_args
INPUT ${CMAKE_CURRENT_BINARY_DIR}/flash_project_args.in2)
if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)
configure_file(${COMPONENT_DIR}/flash_encrypted_project_args.in
${CMAKE_CURRENT_BINARY_DIR}/flash_encrypted_project_args.in)
file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/flash_encrypted_project_args.in2
INPUT ${CMAKE_CURRENT_BINARY_DIR}/flash_encrypted_project_args.in)
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/flash_encrypted_project_args
INPUT ${CMAKE_CURRENT_BINARY_DIR}/flash_encrypted_project_args.in2)
endif()
configure_file(${COMPONENT_DIR}/flasher_args.json.in
${CMAKE_CURRENT_BINARY_DIR}/flasher_args.json.in)

View file

@ -45,6 +45,10 @@ endif
ESPTOOLPY_WRITE_FLASH=$(ESPTOOLPY_SERIAL) write_flash $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z,-u) $(ESPTOOL_WRITE_FLASH_OPTIONS)
ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
ESPTOOLPY_WRITE_FLASH_ENCRYPT=$(ESPTOOLPY_SERIAL) write_flash --encrypt $(if $(CONFIG_ESPTOOLPY_COMPRESSED),-z,-u) $(ESPTOOL_WRITE_FLASH_OPTIONS)
endif
ESPTOOL_ALL_FLASH_ARGS += $(APP_OFFSET) $(APP_BIN)
ifdef CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES
@ -62,6 +66,19 @@ APP_BIN_UNSIGNED ?= $(APP_BIN)
$(APP_BIN_UNSIGNED): $(APP_ELF) $(ESPTOOLPY_SRC) | check_python_dependencies
$(ESPTOOLPY) elf2image $(ESPTOOL_FLASH_OPTIONS) $(ESPTOOL_ELF2IMAGE_OPTIONS) -o $@ $<
ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
encrypted-flash: all_binaries $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info | check_python_dependencies
@echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(APP_OFFSET))..."
ifdef CONFIG_SECURE_BOOT_ENABLED
@echo "(Secure boot enabled, so bootloader not flashed automatically. See 'make bootloader' output)"
endif
$(ESPTOOLPY_WRITE_FLASH_ENCRYPT) $(ESPTOOL_ALL_FLASH_ARGS)
else
encrypted-flash:
@echo "The command is supported only in FLASH ENCRYPTION DEVELOPMENT MODE"
@exit 1
endif
flash: all_binaries $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info | check_python_dependencies
@echo "Flashing binaries to serial port $(ESPPORT) (app at offset $(APP_OFFSET))..."
ifdef CONFIG_SECURE_BOOT_ENABLED
@ -73,6 +90,16 @@ app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) pa
@echo "Flashing app to serial port $(ESPPORT), offset $(APP_OFFSET)..."
$(ESPTOOLPY_WRITE_FLASH) $(APP_OFFSET) $(APP_BIN)
ifdef CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT
encrypted-app-flash: $(APP_BIN) $(ESPTOOLPY_SRC) $(call prereq_if_explicit,erase_flash) partition_table_get_info | check_python_dependencies
@echo "Flashing encrypted app binary to serial port $(ESPPORT) (app at offset $(APP_OFFSET))..."
$(ESPTOOLPY_WRITE_FLASH_ENCRYPT) $(APP_OFFSET) $(APP_BIN)
else
encrypted-app-flash:
@echo "The command is supported only in FLASH ENCRYPTION DEVELOPMENT MODE"
@exit 1
endif
# Submodules normally added in component.mk, but can be added
# at the project level as long as qualified path
COMPONENT_SUBMODULES += $(COMPONENT_PATH)/esptool

View file

@ -0,0 +1,3 @@
--encrypt ${ESPTOOLPY_FLASH_PROJECT_OPTIONS}
$<JOIN:$<TARGET_PROPERTY:flash_project_args_target,FLASH_PROJECT_ARGS>,
>

View file

@ -144,6 +144,12 @@ esptool_py_custom_target(flash project "app;partition_table;bootloader")
esptool_py_custom_target(app-flash app "app")
esptool_py_custom_target(bootloader-flash bootloader "bootloader")
if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)
esptool_py_custom_target(encrypted-flash encrypted_project "app;partition_table;bootloader")
esptool_py_custom_target(encrypted-app-flash encrypted_app "app")
endif()
add_custom_target(flash_project_args_target)
# esptool_py_flash_project_args

View file

@ -99,4 +99,9 @@ esptool_py_flash_project_args(partition_table ${PARTITION_TABLE_OFFSET}
partition_table_get_partition_info(app_partition_offset "--partition-boot-default" "offset")
esptool_py_flash_project_args(app ${app_partition_offset} ${build_dir}/${PROJECT_BIN} FLASH_IN_PROJECT)
if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/flash_encrypted_app_args.in "--encrypt ${app_partition_offset} ${PROJECT_BIN}")
esptool_py_flash_project_args(encrypted_app ${app_partition_offset} ${build_dir}/${PROJECT_BIN}
FLASH_FILE_TEMPLATE ${CMAKE_CURRENT_BINARY_DIR}/flash_encrypted_app_args.in)
endif()

View file

@ -1,98 +1,505 @@
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.
This document provides introduction to Flash encryption concept on ESP32 and demonstrates how this feature can be used during development as well as production by the user using a sample example. The primary intention of the document is to act as a quick start guide to test and verify flash encryption operations. The details of the flash encryption block can be found in the `ESP32 Technical reference manual`_.
Flash Encryption is separate from the :doc:`Secure Boot <secure-boot>` feature, and you can use flash encryption without enabling secure boot. However, for a secure environment both should be used simultaneously. In absence of secure boot, additional configuration needs to be performed to ensure effectiveness of flash encryption. See :ref:`flash-encryption-without-secure-boot` for more details.
.. _ESP32 Technical Reference Manual: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
.. important::
Enabling flash encryption limits your options for further updates of your ESP32. Make sure to read this document (including :ref:`flash-encryption-limitations`) and understand the implications of enabling flash encryption.
Introduction
------------
Background
----------
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. 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.
- The contents of the flash are encrypted using AES-256. 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:
With flash encryption enabled, following kinds of flash data are encrypted by default:
- Bootloader
- Secure boot bootloader digest (if secure boot is enabled)
- Partition Table
- All "app" type partitions
Other type of flash data are encrypted conditionally:
- Secure boot bootloader digest (if secure boot is enabled)
- Any partition marked with the "encrypted" 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 since NVS library is not directly compatible with flash encryption. Refer to :ref:`NVS Encryption <nvs_encryption>` for more details.
Flash encryption is separate from the :doc:`Secure Boot <secure-boot>` feature, and you can use flash encryption without enabling secure boot. However, for a secure environment both should be used simultaneously. In absence of secure boot, additional configuration needs to be performed to ensure effectiveness of flash encryption. See :ref:`flash-encryption-without-secure-boot` for more details.
- 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.
.. important::
Enabling flash encryption limits the options for further updates of the ESP32. Make sure to read this document (including :ref:`flash-encryption-limitations`) and understand the implications of enabling flash encryption.
- By default, the Efuse Block 1 Coding Scheme is "None" and a 256 bit key is stored in this block. On some ESP32s, the Coding Scheme is set to 3/4 Encoding (CODING_SCHEME efuse has value 1) and a 192 bit key must be stored in this block. See ESP32 Technical Reference Manual section 20.3.1.3 *System Parameter coding_scheme* for more details. The algorithm operates on a 256 bit key in all cases, 192 bit keys are extended by repeating some bits (:ref:`details <flash-encryption-algorithm>`). The coding scheme is shown in the ``Features`` line when ``esptool.py`` connects to the chip, or in the ``espefuse.py summary`` output.
.. _flash-encryption-efuse:
eFuse Used During Flash Encryption Process
-------------------------------------------
The flash encryption operation is controlled by various eFuses available on ESP32. Below is the list of eFuse and their description:
::
eFuse Description Can be locked for Default
reading/writing Value
.. code-block:: none
Coding scheme This 2 bit wide eFuse controls the Yes 0
actual number of bits to be used
from BLOCK1 to derive final 256 bit
AES key. The coding scheme value is
decoded as below:
0: 256 bits
1: 192 bits
2: 128 bits
Final AES key is derived based on the
FLASH_CRYPT_CONFIG value
BLOCK1 256 bit wide eFuse block for storing Yes x
AES key
FLASH_CRYPT_CONFIG 4 bit wide eFuse which controls the Yes 0xF
AES encryption process
download_dis_encrypt When set, disables the flash encryption Yes 0
operation while running in UART
download mode
download_dis_decrypt When set, disables the flash decryption Yes 0
operation while running in UART
download mode
FLASH_CRYPT_CNT 7 bit eFuse which enables/disables Yes 0
encryption at boot time
Even number of bits set (0, 2, 4, 6):
encrypt flash at boot time
Odd number of bits set (1, 3, 5, 7): do
not encrypt flash at boot time
Read and write access to above bits is controlled by appropriate bits in ``efuse_wr_disable`` and ``efuse_rd_disable`` registers. More information about ESP32 eFuse can be found at :doc:`eFuse manager <../api-reference/system/efuse>`.
Flash Encryption Process
------------------------
Assuming the eFuse values are in default state and second stage bootloader is compiled to support flash encryption, the flash encryption process executes as below:
- On first power-on reset, all data in flash is un-encrypted (plaintext). First stage loader (ROM) will load the second stage loader in IRAM.
- Second stage bootloader will read the flash_crypt_cnt (=00000000b) eFuse value and since the value is 0 (even number of bits set) it will configure and enable the flash encryption block. It will also program ``FLASH_CRYPT_CFG`` eFuse to value 0xF.
- The flash encryption block will generate AES-256 bit key and store into BLOCK1 eFuse. This operation is performed in hardware and the key can not be accessed by software.
- Next the flash encryption block will encrypt the flash contents (based on partition table flag value). Encrypting in-place can take some time (up to a minute for large partitions).
- Second stage bootloader then sets the first available bit in flash_crypt_cnt (=00000001b) to mark the flash contents as encrypted (odd number of bits set).
- For :ref:`flash_enc_release_mode` second stage bootloader will program ``download_dis_encrypt``, ``download_dis_decrypt`` & ``download_dis_cache`` eFuse bits to 1 to prevent UART bootloader from decrypting the flash contents. It will also write protect the ``FLASH_CRYPT_CNT`` eFuse bits.
- For :ref:`flash_enc_development_mode` second stage bootloader will program only ``download_dis_decrypt`` & ``download_dis_cache`` eFuse bits to allow UART bootloader reflashing of encrypted binaries. Also ``FLASH_CRYPT_CNT`` eFuse bits will NOT be write protected.
- The second stage bootloader then reboots the device to start executing encrypted image. It will transparently decrypt the flash contents and load into IRAM.
During development stage there is a frequent need to program different plaintext flash images and test the flash encryption process. This requires UART download mode to be able to load new plaintext images as many number of times as required. However during manufacturing or production UART download mode should not be allowed to access flash contents due to security reason. Hence this requires two different ESP32 configurations: one for development and other for production. Following section describes :ref:`flash_enc_development_mode` and :ref:`flash_enc_release_mode` for flash encryption and a step by step process to use them.
.. important::
Development mode as the name suggests should be used ONLY DURING DEVELOPMENT as it does not prevent modification and possible read back of encrypted flash contents.
Steps to Setup Flash Encryption
-------------------------------
.. _flash_enc_development_mode:
Development Mode
^^^^^^^^^^^^^^^^
It is possible to run flash encryption process for development using either ESP32 internally generated key or external host generated keys.
Using ESP32 Generated Flash Encryption Key
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
As mentioned above :ref:`flash_enc_development_mode` allows user to download as many plaintext images using UART download mode. Following steps needs to be done to test flash encryption process:
- Ensure you have a ESP32 device with default flash encryption eFuse settings as shown in :ref:`flash-encryption-efuse`.
- Navigate to flash encryption sample application in ``$IDF_PATH/examples/security/flash_encryption`` folder. This sample application will print the status of flash encryption: enabled or disabled. It will print the ``FLASH_CRYPT_CNT`` eFuse value.
- Enable flash encryption support in second stage bootloader. In make menuconfig, navigate to “Security Features”.
- Select “Enable flash encryption on boot”.
- By default the mode is set for **Development**.
- Select appropriate Bootloader log verbosity under Bootloader config.
- Update to the partition table offset may be required since after enabling flash encryption the size of bootloader is increased. See :ref:`secure-boot-bootloader-size`
- Save the configuration and exit.
Build and flash the complete image including: bootloader, partition table and app. These partitions are initially written to the flash unencrypted.
::
make -j4 flash monitor
Once the flashing is complete device will reset and on next boot second stage bootloader will encrypt the flash app partition and then reset. Now the sample application would get decrypted at runtime and executed. Below is a sample output when ESP32 boots after flash encryption is enabled for the first time.
::
--- idf_monitor on /dev/cu.SLAB_USBtoUART 115200 ---
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
ets Jun 8 2016 00:22:57
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:8452
load:0x40078000,len:13608
load:0x40080400,len:6664
entry 0x40080764
I (28) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader
I (29) boot: compile time 15:37:14
I (30) boot: Enabling RNG early entropy source...
I (35) boot: SPI Speed : 40MHz
I (39) boot: SPI Mode : DIO
I (43) boot: SPI Flash Size : 4MB
I (47) boot: Partition Table:
I (51) boot: ## Label Usage Type ST Offset Length
I (58) boot: 0 nvs WiFi data 01 02 0000a000 00006000
I (66) boot: 1 phy_init RF data 01 01 00010000 00001000
I (73) boot: 2 factory factory app 00 00 00020000 00100000
I (81) boot: End of partition table
I (85) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map
I (105) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) load
I (109) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) load
0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (114) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) load
I (132) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map
0x400d0018: _flash_cache_start at ??:?
I (159) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) load
0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (168) boot: Loaded app from partition at offset 0x20000
I (168) boot: Checking flash encryption...
I (168) flash_encrypt: Generating new flash encryption key...
I (187) flash_encrypt: Read & write protecting new key...
I (187) flash_encrypt: Setting CRYPT_CONFIG efuse to 0xF
W (188) flash_encrypt: Not disabling UART bootloader encryption
I (195) flash_encrypt: Disable UART bootloader decryption...
I (201) flash_encrypt: Disable UART bootloader MMU cache...
I (208) flash_encrypt: Disable JTAG...
I (212) flash_encrypt: Disable ROM BASIC interpreter fallback...
I (219) esp_image: segment 0: paddr=0x00001020 vaddr=0x3fff0018 size=0x00004 ( 4)
I (227) esp_image: segment 1: paddr=0x0000102c vaddr=0x3fff001c size=0x02104 ( 8452)
I (239) esp_image: segment 2: paddr=0x00003138 vaddr=0x40078000 size=0x03528 ( 13608)
I (249) esp_image: segment 3: paddr=0x00006668 vaddr=0x40080400 size=0x01a08 ( 6664)
I (657) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map
I (669) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844)
I (672) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024)
0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (676) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720)
I (692) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map
0x400d0018: _flash_cache_start at ??:?
I (719) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012)
0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (722) flash_encrypt: Encrypting partition 2 at offset 0x20000...
I (13229) flash_encrypt: Flash encryption completed
I (13229) boot: Resetting with flash encryption enabled...
Once the flash encryption is enabled, on subsequent boot the output would mention that flash encryption is already enabled.
::
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:8452
load:0x40078000,len:13652
ho 0 tail 12 room 4
load:0x40080400,len:6664
entry 0x40080764
I (30) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader
I (30) boot: compile time 16:32:53
I (31) boot: Enabling RNG early entropy source...
I (37) boot: SPI Speed : 40MHz
I (41) boot: SPI Mode : DIO
I (45) boot: SPI Flash Size : 4MB
I (49) boot: Partition Table:
I (52) boot: ## Label Usage Type ST Offset Length
I (60) boot: 0 nvs WiFi data 01 02 0000a000 00006000
I (67) boot: 1 phy_init RF data 01 01 00010000 00001000
I (75) boot: 2 factory factory app 00 00 00020000 00100000
I (82) boot: End of partition table
I (86) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map
I (107) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) load
I (111) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) load
0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (116) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) load
I (134) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map
0x400d0018: _flash_cache_start at ??:?
I (162) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) load
0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (171) boot: Loaded app from partition at offset 0x20000
I (171) boot: Checking flash encryption...
I (171) flash_encrypt: flash encryption is enabled (3 plaintext flashes left)
I (178) boot: Disabling RNG early entropy source...
I (184) cpu_start: Pro cpu up.
I (188) cpu_start: Application information:
I (193) cpu_start: Project name: flash-encryption
I (198) cpu_start: App version: v4.0-dev-850-gc4447462d-dirty
I (205) cpu_start: Compile time: Jun 17 2019 16:32:52
I (211) cpu_start: ELF file SHA256: 8770c886bdf561a7...
I (217) cpu_start: ESP-IDF: v4.0-dev-850-gc4447462d-dirty
I (224) cpu_start: Starting app cpu, entry point is 0x40080e4c
0x40080e4c: call_start_cpu1 at esp-idf/esp-idf/components/esp32/cpu_start.c:265
I (0) cpu_start: App cpu up.
I (235) heap_init: Initializing. RAM available for dynamic allocation:
I (241) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (247) heap_init: At 3FFB2EC8 len 0002D138 (180 KiB): DRAM
I (254) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (260) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (266) heap_init: At 40087FF4 len 0001800C (96 KiB): IRAM
I (273) cpu_start: Pro cpu start user code
I (291) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Sample program to check Flash Encryption
This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 1, 4MB external flash
Flash encryption feature is enabled
Flash encryption mode is DEVELOPMENT
Flash in encrypted mode with flash_crypt_cnt = 1
Halting...
At this stage if user wants to update modified plaintext application image to flash in encrypted format it can be done using following command:
::
make -j4 encrypted-app-flash monitor
.. _encrypt_partitions:
Encrypt Multiple Partitions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
If all partitions needs to be updated in encrypted format, it can be done as
::
make -j4 encrypted-flash monitor
.. _pregenerated-flash-encryption-key:
Using Host Generated Flash Encryption Key
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is possible to pregenerate the flash encryption key on the host computer and burn it into the ESP32's eFuse key block. This allows data to be pre-encrypted on the host and flashed to the ESP32 without needing a plaintext flash update. This feature allows encrypted flashing in both :ref:`flash_enc_development_mode` and :ref:`flash_enc_release_mode` modes.
- Ensure you have a ESP32 device with default flash encryption eFuse settings as shown in :ref:`flash-encryption-efuse`.
- Generate a random key with espsecure.py::
espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin
- Burn the key to the device (one time only). **This must be done before first encrypted boot**, otherwise the ESP32 will generate a random key that software can't access or modify::
espefuse.py --port PORT burn_key flash_encryption my_flash_encryption_key.bin
- Enable flash encryption support in second stage bootloader. In make menuconfig, navigate to “Security Features”.
- Select “Enable flash encryption on boot”.
- By default the mode is set for **Development**.
- Select appropriate Bootloader log verbosity under Bootloader config.
- Update to the partition table offset may be required since after enabling flash encryption the size of bootloader is increased. See :ref:`secure-boot-bootloader-size`
- Save the configuration and exit.
Build and flash the complete image including: bootloader, partition table and app. These partitions are initially written to the flash unencrypted
::
make -j4 flash monitor
On next boot second stage bootloader will encrypt the flash app partition and then reset. Now the sample application would get decrypted at runtime and executed.
At this stage if user wants to update new plaintext application image to flash they should issue following command
::
make -j4 encrypted-app-flash monitor
For reprogramming all partitions in encrypted format follow :ref:`encrypt_partitions`.
.. _flash_enc_release_mode:
Release Mode
^^^^^^^^^^^^
In Release mode UART bootloader can not perform flash encryption operations and new plaintext images can be downloaded ONLY using OTA scheme which will encrypt the plaintext image before writing to flash.
- Ensure you have a ESP32 device with default flash encryption eFuse settings as shown in :ref:`flash-encryption-efuse`.
- Enable flash encryption support in second stage bootloader. In make menuconfig, navigate to “Security Features”.
- Select “Enable flash encryption on boot”.
- Select **Release Mode**, by default the mode is set for **Development**. Please note **once the Release mode is selected the ``download_dis_encrypt`` and ``download_dis_decrypt`` eFuse bits will be programmed to disable UART bootloader access to flash contents**.
- Select appropriate Bootloader log verbosity under Bootloader config.
- Update to the partition table offset may be required since after enabling flash encryption the size of bootloader is increased. See :ref:`secure-boot-bootloader-size`
- Save the configuration and exit.
Build and flash the complete image including: bootloader, partition table and app. These partitions are initially written to the flash unencrypted
::
make -j4 flash monitor
On next boot second stage bootloader will encrypt the flash app partition and then reset. Now the sample application should execute correctly.
Once the flash encryption is enabled in Release mode the bootloader will write protect the ``FLASH_CRYPT_CNT`` eFuse.
For subsequent plaintext update in field OTA scheme should be used. Please refer :doc:`OTA <../api-reference/system/ota>` for further details.
Possible Failures
^^^^^^^^^^^^^^^^^
Once the flash encryption is enabled and if the ``FLASH_CRYPT_CNT`` eFuse value has odd number of bits set then all the partitions (which are marked with encryption flag) are expected to contain encrypted ciphertext. Below are three typical failure cases if the ESP32 is loaded with plaintext data:
1. In case the bootloader partition is re-updated with plaintext bootloader image the ROM loader will fail to load the bootloader and following type of failure will be displayed:
::
rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
rst:0x7 (TG0WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
flash read err, 1000
ets_main.c 371
ets Jun 8 2016 00:22:57
2. In case the bootloader is encrypted but partition table is re-updated with plaintext partition table image the bootloader will fail to read the partition table and following type of failure will be displayed:
::
rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:10464
ho 0 tail 12 room 4
load:0x40078000,len:19168
load:0x40080400,len:6664
entry 0x40080764
I (60) boot: ESP-IDF v4.0-dev-763-g2c55fae6c-dirty 2nd stage bootloader
I (60) boot: compile time 19:15:54
I (62) boot: Enabling RNG early entropy source...
I (67) boot: SPI Speed : 40MHz
I (72) boot: SPI Mode : DIO
I (76) boot: SPI Flash Size : 4MB
E (80) flash_parts: partition 0 invalid magic number 0x94f6
E (86) boot: Failed to verify partition table
E (91) boot: load partition table error!
3. In case the bootloader & partition table are encrypted but application is re-updated with plaintext application image the bootloader will fail load the new application and following type of failure will be displayed:
::
rst:0x3 (SW_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:8452
load:0x40078000,len:13616
load:0x40080400,len:6664
entry 0x40080764
I (56) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader
I (56) boot: compile time 15:37:14
I (58) boot: Enabling RNG early entropy source...
I (64) boot: SPI Speed : 40MHz
I (68) boot: SPI Mode : DIO
I (72) boot: SPI Flash Size : 4MB
I (76) boot: Partition Table:
I (79) boot: ## Label Usage Type ST Offset Length
I (87) boot: 0 nvs WiFi data 01 02 0000a000 00006000
I (94) boot: 1 phy_init RF data 01 01 00010000 00001000
I (102) boot: 2 factory factory app 00 00 00020000 00100000
I (109) boot: End of partition table
E (113) esp_image: image at 0x20000 has invalid magic byte
W (120) esp_image: image at 0x20000 has invalid SPI mode 108
W (126) esp_image: image at 0x20000 has invalid SPI size 11
E (132) boot: Factory app partition is not bootable
E (138) boot: No bootable app partitions in the partition table
Key Points About Flash Encryption
---------------------------------
- The contents of the flash are encrypted using AES-256. The flash encryption key is stored in eFuse internal to the chip, and is (by default) protected from software access.
- 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 impossible for the UART bootloader to decrypt (or encrypt) data when flash encryption is enabled.
- 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.
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 since NVS library is not directly compatible with flash encryption. Refer to :ref:`NVS Encryption <nvs_encryption>` for more details.
- If flash encryption may be enabled, the programmer must take certain precautions when writing code that :ref:`uses encrypted flash <using-encrypted-flash>`.
.. _flash-encryption-initialisation:
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 :ref:`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 serial re-flashing. A special procedure (documented in :ref:`updating-encrypted-flash-serial`) must be followed to perform these updates.
- If secure boot is enabled, physical reflashing with plaintext data requires a "Reflashable" secure boot digest (see :ref:`flash-encryption-and-secure-boot`).
- OTA updates can be used to update flash content without counting towards this limit.
- When enabling flash encryption in development, use a `pregenerated flash encryption key` to allow physically re-flashing an unlimited number of times with pre-encrypted data.**
Process to enable flash encryption:
- The bootloader must be compiled with flash encryption support enabled. In ``make menuconfig``, navigate to "Security Features" and select "Yes" for "Enable flash encryption on boot".
- If enabling Secure Boot at the same time, it is best to simultaneously select those options now. Read the :doc:`Secure Boot <secure-boot>` documentation first.
- Build and flash the bootloader, partition table and factory app image as normal. These partitions are initially written to the flash unencrypted.
- If secure boot is enabled, reflashing the bootloader of an encrypted device requires a "Reflashable" secure boot digest (see :ref:`flash-encryption-and-secure-boot`).
.. note:: The bootloader app binary ``bootloader.bin`` may become too large when both secure boot and flash encryption are enabled. See :ref:`secure-boot-bootloader-size`.
- On first boot, the bootloader sees :ref:`FLASH_CRYPT_CNT` is set to 0 (factory default) 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. A reflash like this will not count towards the flashing limit.
- Once flashing is complete. efuses are blown (by default) to disable encrypted flash access while the UART bootloader is running. See :ref:`uart-bootloader-encryption` for advanced details.
- 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 :ref:`setting-flash-crypt-config` for advanced details.
- Finally, the :ref:`FLASH_CRYPT_CNT` 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 :ref:`updating-encrypted-flash` section for details about :ref:`FLASH_CRYPT_CNT`.
- The bootloader resets itself to reboot from the newly encrypted flash.
.. _using-encrypted-flash:
Using Encrypted Flash
---------------------
ESP32 app code can check if flash encryption is currently enabled by calling :func:`esp_flash_encryption_enabled`.
ESP32 app code can check if flash encryption is currently enabled by calling :cpp:func:`esp_flash_encryption_enabled`. Also, device can identify the flash encryption mode by calling :cpp:func:`esp_get_flash_encryption_mode`.
Once flash encryption is enabled, some care needs to be taken when accessing flash contents from code.
Scope of Flash Encryption
^^^^^^^^^^^^^^^^^^^^^^^^^
Whenever the :ref:`FLASH_CRYPT_CNT` 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:
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 :func:`esp_spi_flash_mmap`.
- Any data accessed via :cpp:func:`spi_flash_mmap`.
- The software bootloader image when it is read by the ROM bootloader.
.. important::
@ -100,13 +507,13 @@ Whenever the :ref:`FLASH_CRYPT_CNT` is set to a value with an odd number of bits
Reading Encrypted Flash
^^^^^^^^^^^^^^^^^^^^^^^
To read data without using a flash cache MMU mapping, we recommend using the partition read function :func:`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.
To read data without using a flash cache MMU mapping, we recommend using the partition read function :cpp:func:`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 :func:`esp_spi_flash_read` is not decrypted
- Data read via ROM function :func:`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 and read decrypted from the perspective of Flash Encryption. It is up to the library to provide encryption feature if required. Refer to :ref:`NVS Encryption <nvs_encryption>` for more details.
- Data read via :cpp:func:`spi_flash_read` is not decrypted.
- Data read via ROM function :cpp:func:`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 and read decrypted from the perspective of flash encryption. It is up to the library to provide encryption feature if required. Refer to :ref:`NVS Encryption <nvs_encryption>` for more details.
Writing Encrypted Flash
@ -118,7 +525,7 @@ The ``esp_spi_flash_write`` function will write data when the write_encrypted pa
The ROM function ``esp_rom_spiflash_write_encrypted`` 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 16 bytes (and the alignment is 16 bytes.)
Because data is encrypted in blocks, the minimum write size for encrypted data is 16 bytes (and the alignment is 16 bytes).
.. _updating-encrypted-flash:
@ -134,130 +541,20 @@ OTA updates to encrypted partitions will automatically write encrypted, as long
.. _updating-encrypted-flash-serial:
Serial Flashing
^^^^^^^^^^^^^^^
The :ref:`FLASH_CRYPT_CNT` allows the flash to be updated with new plaintext data via serial flashing (or other physical methods), up to 3 additional times.
The process involves flashing plaintext data, and then bumping the value of :ref:`FLASH_CRYPT_CNT` which causes the bootloader to re-encrypt this data.
Limited Updates
~~~~~~~~~~~~~~~
Only 4 plaintext serial update cycles of this kind are possible, including the initial encrypted flash.
After the fourth time encryption is enabled, :ref:`FLASH_CRYPT_CNT` has the maximum value ``0x7F`` (7 bits set) and encryption is permanently enabled.
Using :ref:`updating-encrypted-flash-ota` or :ref:`pregenerated-flash-encryption-key` allows you to exceed this limit.
Cautions With Serial Flashing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- When reflashing via serial, reflash every partition that was initially written with plaintext data (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.
- Using ``make flash`` should flash all partitions which need to be flashed.
- If secure boot is enabled, you can't reflash plaintext data via serial at all unless you used the "Reflashable" option for Secure Boot. See :ref:`flash-encryption-and-secure-boot`.
Serial Re-Flashing Procedure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Build the application as usual.
- Flash the device with plaintext data as usual (``make flash`` or ``esptool.py`` commands.) Flash all previously encrypted partitions, including the bootloader (see previous section).
- At this point, the device will fail to boot (message is ``flash read err, 1000``) because it expects to see an encrypted bootloader, but the bootloader is plaintext.
- Burn the :ref:`FLASH_CRYPT_CNT` by running the command ``espefuse.py burn_efuse FLASH_CRYPT_CNT``. espefuse.py will automatically increment the bit count by 1, which disables encryption.
- Reset the device and it will re-encrypt plaintext partitions, then burn the :ref:`FLASH_CRYPT_CNT` again to re-enable encryption.
Disabling Serial Updates
~~~~~~~~~~~~~~~~~~~~~~~~
To prevent further plaintext updates via serial, use espefuse.py to write protect the :ref:`FLASH_CRYPT_CNT` after flash encryption has been enabled (ie after first boot is complete)::
espefuse.py --port PORT write_protect_efuse FLASH_CRYPT_CNT
This prevents any further modifications to disable or re-enable flash encryption.
.. _pregenerated-flash-encryption-key:
Reflashing via Pregenerated Flash Encryption Key
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is possible to pregenerate a flash encryption key on the host computer and burn it into the ESP32's efuse key block. This allows data to be pre-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 time reflashing limit. It also allows reflashing the app with secure boot enabled, because the bootloader doesn't need to be reflashed each time.
.. 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.
Pregenerating a 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 :doc:`secure boot <secure-boot>` and have a :ref:`secure boot signing key <secure-boot-generate-key>` then you can generate a deterministic SHA-256 digest of the secure boot private signing key and use this as the flash encryption key::
espsecure.py digest_private_key --keyfile secure_boot_signing_key.pem --keylen 256 my_flash_encryption_key.bin
(The same 32 bytes is used as the secure boot digest key if you enable :ref:`reflashable mode<secure-boot-reflashable>` for secure boot.)
Generating the flash encryption key from the secure boot signing key in this way means that you only need to store one key file. However 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 the ESP32's efuse key block. **This must be done before first encrypted boot**, otherwise the ESP32 will generate a random key that software can't access or modify.
To burn a key to the device (one time only)::
espefuse.py --port PORT burn_key flash_encryption my_flash_encryption_key.bin
First Flash with pregenerated key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After flashing the key, follow the same steps as for default :ref:`flash-encryption-initialisation` and flash a plaintext image for the first boot. The bootloader will enable flash encryption using the pre-burned key and encrypt all partitions.
Reflashing with pregenerated key
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
After encryption is enabled on first boot, reflashing an encrypted image requires an additional manual step. This is where we pre-encrypt the data that we wish to update in flash.
Suppose that this is the normal command used to flash plaintext data::
esptool.py --port /dev/ttyUSB0 --baud 115200 write_flash 0x10000 build/my-app.bin
Binary app image ``build/my-app.bin`` is written to offset ``0x10000``. This file name and offset need to be used to encrypt the data, as follows::
espsecure.py encrypt_flash_data --keyfile my_flash_encryption_key.bin --address 0x10000 -o build/my-app-encrypted.bin build/my-app.bin
This example command will encrypts ``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 0x10000 build/my-app-encrypted.bin
No further steps or efuse manipulation is necessary, because the data is already encrypted when we flash it.
Disabling Flash Encryption
--------------------------
If you've accidentally enabled flash encryption for some reason, the next flash of plaintext data will soft-brick the ESP32 (the device will reboot continously, printing the error ``flash read err, 1000``).
If you've accidentally enabled flash encryption for some reason, the next flash of plaintext data will soft-brick the ESP32 (the device will reboot continuously, printing the error ``flash read err, 1000``).
You can disable flash encryption again by writing :ref:`FLASH_CRYPT_CNT`:
You can disable flash encryption again by writing ``FLASH_CRYPT_CNT`` eFuse (only in Development mode):
- First, run ``make menuconfig`` and uncheck "Enable flash encryption boot" under "Security Features".
- Exit menuconfig and save the new configuration.
- Run ``make menuconfig`` again and double-check you really disabled this option! *If this option is left enabled, the bootloader will immediately re-enable encryption when it boots*.
- Run ``make flash`` to build and flash a new bootloader and app, without flash encryption enabled.
- Run ``espefuse.py`` (in ``components/esptool_py/esptool``) to disable the :ref:`FLASH_CRYPT_CNT`)::
- Run ``espefuse.py`` (in ``components/esptool_py/esptool``) to disable the FLASH_CRYPT_CNT::
espefuse.py burn_efuse FLASH_CRYPT_CNT
Reset the ESP32 and flash encryption should be disabled, the bootloader will boot as normal.
@ -267,13 +564,13 @@ Reset the ESP32 and flash encryption should be disabled, the bootloader will boo
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 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 (see :ref:`pregenerated-flash-encryption-key`), ensure proper procedure is followed.
- 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, ensure proper procedure is followed.
- 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 pair of adjacent 16 byte AES blocks. When these 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).
- 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 pair of adjacent 16 byte AES blocks. When these 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 (i.e. to tell if two devices are probably running the same firmware version).
- For the same reason, an attacker can always tell when a pair of 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).
@ -281,25 +578,21 @@ Flash Encryption prevents plaintext readout of the encrypted flash, to protect f
.. _flash-encryption-and-secure-boot:
Flash Encryption & Secure Boot
------------------------------
Flash Encryption and Secure Boot
---------------------------------
It is recommended to use flash encryption and secure boot together. However, if Secure Boot is enabled then additional restrictions apply to reflashing the device:
- :ref:`updating-encrypted-flash-ota` are not restricted (provided the new app is signed correctly with the Secure Boot signing key).
- :ref:`Plaintext serial flash updates <updating-encrypted-flash-serial>` are only possible if the :ref:`Reflashable <CONFIG_SECURE_BOOTLOADER_MODE>` Secure Boot mode is selected and a Secure Boot key was pre-generated and burned to the ESP32 (refer to :ref:`Secure Boot <secure-boot-reflashable>` docs.). In this configuration, ``make bootloader`` will produce a pre-digested bootloader and secure boot digest file for flashing at offset 0x0. When following the plaintext serial reflashing steps it is necessary to re-flash this file before flashing other plaintext data.
- :ref:`pregenerated-flash-encryption-key` is still possible, provided the bootloader is not reflashed. Reflashing the bootloader requires the same :ref:`Reflashable <CONFIG_SECURE_BOOTLOADER_MODE>` option to be enabled in the Secure Boot config.
- :ref:`Reflashing via Pregenerated Flash Encryption Key <pregenerated-flash-encryption-key>` is still possible, provided the bootloader is not reflashed. Reflashing the bootloader requires the same :ref:`Reflashable <CONFIG_SECURE_BOOTLOADER_MODE>` option to be enabled in the Secure Boot config.
.. _flash-encryption-without-secure-boot:
Using Flash Encryption without Secure Boot
Using Flash Encryption Without Secure Boot
------------------------------------------
If flash encryption is used without secure boot, it is possible to load unauthorised code using serial re-flashing. See :ref:`updating-encrypted-flash-serial` for details. This unauthorised code can then read all encrypted partitions (in decrypted form) making flash-encryption ineffective. This can be avoided by write-protecting :ref:`FLASH_CRYPT_CNT` and thereby disallowing serial re-flashing. :ref:`FLASH_CRYPT_CNT` can be write-protected using command::
espefuse.py --port PORT write_protect_efuse FLASH_CRYPT_CNT
Alternatively, the app can call :func:`esp_flash_write_protect_crypt_cnt` during its startup process.
Even though flash encryption and secure boot can be used independently it is strongly recommended to use flash encryption ALONG with secure boot to achieve strong security.
.. _flash-encryption-advanced-features:
@ -338,21 +631,21 @@ Usually left blank, if you write "encrypted" in this field then the partition wi
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``:
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 :ref:`FLASH_CRYPT_CNT` is set to enable it in normal operation.
- ``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. For example::
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. For example::
espefuse.py --port PORT burn_efuse DISABLE_DL_DECRYPT
espefuse.py --port PORT 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. For this reason, it's necessary to set any bits before write-protecting.)
(Note that all 3 of these eFuses are disabled via one write protect bit, so write protecting one will write protect all of them. For this reason, it's necessary to set any bits before write-protecting.)
.. important::
Write protecting these efuses to keep them unset is not currently very useful, as ``esptool.py`` does not support writing or reading encrypted flash.
Write protecting these eFuses to keep them unset is not currently very useful, as ``esptool.py`` does not support reading encrypted flash.
.. important::
If ``DISABLE_DL_DECRYPT`` is left unset (0) this effectively makes flash encryption useless, as an attacker with physical access can use UART bootloader mode (with custom stub code) to read out the flash contents.
@ -362,13 +655,13 @@ It is possible to burn only some of these efuses, and write-protect the rest (wi
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 :ref:`flash-encryption-algorithm` for details.
The ``FLASH_CRYPT_CONFIG`` eFuse determines the number of bits in the flash encryption key which are "tweaked" with the block offset. See :ref:`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 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.
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.
Technical Details
@ -376,27 +669,6 @@ Technical Details
The following sections provide some reference information about the operation of flash encryption.
.. _FLASH_CRYPT_CNT:
FLASH_CRYPT_CNT efuse
^^^^^^^^^^^^^^^^^^^^^
``FLASH_CRYPT_CNT`` is a 7-bit efuse field which controls flash encryption. Flash encryption enables or disables based on the number of bits in this efuse which are set to "1":
- When an even number of bits (0,2,4,6) are set: Flash encryption 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.
1. On first plaintext boot, bit count has brand new value 0 and bootloader changes it to bit count 1 (value 0x01) following encryption.
2. After next plaintext flash update, bit count is manually updated to 2 (value 0x03). After re-encrypting the bootloader changes efuse bit count to 3 (value 0x07).
3. After next plaintext flash, bit count is manually updated to 4 (value 0x0F). After re-encrypting the bootloader changes efuse bit count to 5 (value 0x1F).
4. After final plaintext flash, bit count is manually updated to 6 (value 0x3F). After re-encrypting the bootloader changes efuse bit count to 7 (value 0x7F).
- When an odd number of bits (1,3,5,7) are set: Transparent reading of encrypted flash is enabled.
- To avoid use of :ref:`FLASH_CRYPT_CNT` state to disable flash encryption, load unauthorised code, then re-enabled flash encryption, secure boot must be used or :ref:`FLASH_CRYPT_CNT` must be write-protected.
.. _flash-encryption-algorithm:
Flash Encryption Algorithm
@ -404,17 +676,17 @@ 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.
- The main flash encryption key is stored in efuse (BLOCK1) and by default is protected from further writes or software readout.
- The main flash encryption key is stored in eFuse (BLOCK1) and by default is protected from further writes or software readout.
- AES-256 key size is 256 bits (32 bytes), read from efuse block 1. The hardware AES engine uses the key in reversed byte order to the order stored in the efuse block.
- If ``CODING_SCHEME`` efuse is set to 0 (default "None" Coding Scheme) then the efuse key block is 256 bits and the key is stored as-is (in reversed byte order).
- If ``CODING_SCHEME`` efuse is set to 1 (3/4 Encoding) then the efuse key block is 192 bits (in reversed byte order), so overall entropy is reduced. The hardware flash encryption still operates on a 256-bit key, after being read (and un-reversed), the key is extended by as ``key = key[0:255] + key[64:127]``.
- AES-256 key size is 256 bits (32 bytes), read from eFuse block 1. The hardware AES engine uses the key in reversed byte order to the order stored in the eFuse block.
- If ``CODING_SCHEME`` eFuse is set to 0 (default "None" Coding Scheme) then the eFuse key block is 256 bits and the key is stored as-is (in reversed byte order).
- If ``CODING_SCHEME`` eFuse is set to 1 (3/4 Encoding) then the eFuse key block is 192 bits (in reversed byte order), so overall entropy is reduced. The hardware flash encryption still operates on a 256-bit key, after being read (and un-reversed), the key is extended by as ``key = key[0:255] + key[64:127]``.
- 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.
- Each 32 byte block (two adjacent 16 byte AES blocks) is encrypted with a unique key. The key is derived from the main flash encryption key in efuse, XORed with the offset of this block in the flash (a "key tweak").
- Each 32 byte block (two adjacent 16 byte AES blocks) is encrypted with a unique key. The key is derived from the main flash encryption key in eFuse, 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:
- 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.

View file

@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(flash_encryption)

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := flash-encryption
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,146 @@
# Flash Encryption
The example checks if the flash encryption feature is enabled/disabled and if enabled prints the flash encryption mode (DEVELOPMENT / RELEASE) and FLASH_CRYPT_CNT eFuse value
## How to use example
### Hardware Required
### Configure the project
```
make menuconfig
```
* Set serial port under Serial Flasher Options.
* Enable the flash encryption mode (Development or Release) under Security Features. Default usage mode is Development (recommended during test and development phase).
* Ensure Bootloader log verbosity is Info under Bootloader config.
* Select Single factory app, no OTA under Partition Table options. Change the partition table offset to 0x9000 from 0x8000 since after enabling flash encryption the size of bootloader is increased.
### Build and Flash
When building the project and flashing it to the board FOR THE FIRST TIME after enabling flash encryption feature in menuconfig, run following command to program ESP32 and monitor the output
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
When reprogramming the device subsequently use following command for encrypted write of new plaintext application
```
make -j4 encrypted-app-flash monitor
```
Please note above command programs only the app partition. In order to reprogram all partitions (bootloader, partition table and application) in encrypted form use
```
make -j4 encrypted-flash monitor
```
## Example Output
When the device boots for the first time after enabling flash encryption in Development mode the output would contain following
```
I (168) boot: Checking flash encryption...
I (168) flash_encrypt: Generating new flash encryption key...
I (187) flash_encrypt: Read & write protecting new key...
I (187) flash_encrypt: Setting CRYPT_CONFIG efuse to 0xF
W (188) flash_encrypt: Not disabling UART bootloader encryption
I (195) flash_encrypt: Disable UART bootloader decryption...
I (201) flash_encrypt: Disable UART bootloader MMU cache...
I (208) flash_encrypt: Disable JTAG...
I (212) flash_encrypt: Disable ROM BASIC interpreter fallback...
....
....
....
I (13229) flash_encrypt: Flash encryption completed
I (13229) boot: Resetting with flash encryption enabled...
```
Once the flash encryption is enabled the device will reset itself. At this stage the flash contents are in encrypted form. The output would be similar to
```
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0018,len:4
load:0x3fff001c,len:8452
load:0x40078000,len:13652
ho 0 tail 12 room 4
load:0x40080400,len:6664
entry 0x40080764
I (30) boot: ESP-IDF v4.0-dev-850-gc4447462d-dirty 2nd stage bootloader
I (30) boot: compile time 16:32:53
I (31) boot: Enabling RNG early entropy source...
I (37) boot: SPI Speed : 40MHz
I (41) boot: SPI Mode : DIO
I (45) boot: SPI Flash Size : 4MB
I (49) boot: Partition Table:
I (52) boot: ## Label Usage Type ST Offset Length
I (60) boot: 0 nvs WiFi data 01 02 0000a000 00006000
I (67) boot: 1 phy_init RF data 01 01 00010000 00001000
I (75) boot: 2 factory factory app 00 00 00020000 00100000
I (82) boot: End of partition table
I (86) esp_image: segment 0: paddr=0x00020020 vaddr=0x3f400020 size=0x0808c ( 32908) map
I (107) esp_image: segment 1: paddr=0x000280b4 vaddr=0x3ffb0000 size=0x01ea4 ( 7844) load
I (111) esp_image: segment 2: paddr=0x00029f60 vaddr=0x40080000 size=0x00400 ( 1024) load
0x40080000: _WindowOverflow4 at esp-idf/esp-idf/components/freertos/xtensa_vectors.S:1778
I (116) esp_image: segment 3: paddr=0x0002a368 vaddr=0x40080400 size=0x05ca8 ( 23720) load
I (134) esp_image: segment 4: paddr=0x00030018 vaddr=0x400d0018 size=0x126a8 ( 75432) map
0x400d0018: _flash_cache_start at ??:?
I (162) esp_image: segment 5: paddr=0x000426c8 vaddr=0x400860a8 size=0x01f4c ( 8012) load
0x400860a8: prvAddNewTaskToReadyList at esp-idf/esp-idf/components/freertos/tasks.c:4561
I (171) boot: Loaded app from partition at offset 0x20000
I (171) boot: Checking flash encryption...
I (171) flash_encrypt: flash encryption is enabled (3 plaintext flashes left)
I (178) boot: Disabling RNG early entropy source...
I (184) cpu_start: Pro cpu up.
I (188) cpu_start: Application information:
I (193) cpu_start: Project name: flash-encryption
I (198) cpu_start: App version: v4.0-dev-850-gc4447462d-dirty
I (205) cpu_start: Compile time: Jun 17 2019 16:32:52
I (211) cpu_start: ELF file SHA256: 8770c886bdf561a7...
I (217) cpu_start: ESP-IDF: v4.0-dev-850-gc4447462d-dirty
I (224) cpu_start: Starting app cpu, entry point is 0x40080e4c
0x40080e4c: call_start_cpu1 at esp-idf/esp-idf/components/esp32/cpu_start.c:265
I (0) cpu_start: App cpu up.
I (235) heap_init: Initializing. RAM available for dynamic allocation:
I (241) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (247) heap_init: At 3FFB2EC8 len 0002D138 (180 KiB): DRAM
I (254) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (260) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (266) heap_init: At 40087FF4 len 0001800C (96 KiB): IRAM
I (273) cpu_start: Pro cpu start user code
I (291) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Sample program to check Flash Encryption
This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 1, 4MB external flash
Flash encryption feature is enabled
Flash encryption mode is DEVELOPMENT
Flash in encrypted mode with flash_crypt_cnt = 1
Halting...
```
## Troubleshooting
It is also possible to use esptool.py utility to read the eFuse values and check if flash encryption is enabled or not
```
python $IDF_PATH/components/esptool_py/esptool/espefuse.py --port /dev/cu.SLAB_USBtoUART summary
```
If FLASH_CRYPT_CNT eFuse value is non-zero flash encryption is enabled

View file

@ -0,0 +1,4 @@
set(COMPONENT_SRCS "flash_encrypt_main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()

View file

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,67 @@
/* Flash encryption Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/efuse_reg.h"
#include "esp_efuse.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_flash_encrypt.h"
#include "esp_efuse_table.h"
void app_main()
{
uint32_t flash_crypt_cnt = 0;
esp_flash_enc_mode_t mode;
printf("\nSample program to check Flash Encryption\n");
/* Print chip information */
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is ESP32 chip with %d CPU cores, WiFi%s%s, ",
chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
printf("silicon revision %d, ", chip_info.revision);
printf("%dMB %s flash\n", spi_flash_get_chip_size() / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
esp_efuse_read_field_blob(ESP_EFUSE_FLASH_CRYPT_CNT, &flash_crypt_cnt, 7);
if (esp_flash_encryption_enabled()) {
printf("Flash encryption feature is enabled\n");
mode = esp_get_flash_encryption_mode();
if (mode == ESP_FLASH_ENC_MODE_DEVELOPMENT) {
printf("Flash encryption mode is DEVELOPMENT\n");
/* If odd bits set flash encryption is enabled else disabled */
if (__builtin_parity(flash_crypt_cnt) == 1) {
printf("Flash in encrypted mode with flash_crypt_cnt = %d\n", flash_crypt_cnt);
}
else {
printf("Flash in plaintext mode with flash_crypt_cnt = %d\n", flash_crypt_cnt);
}
} else {
printf("Flash encryption mode is RELEASE\n");
}
}
else {
printf("FLASH_CRYPT_CNT eFuse value is %d\n", flash_crypt_cnt);
printf("Flash encryption feature is disabled\n\n");
}
printf("Halting...\n");
while(1) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}

View file

@ -347,6 +347,8 @@ def flash(action, ctx, args):
"partition_table-flash": "flash_partition_table_args",
"app-flash": "flash_app_args",
"flash": "flash_project_args",
"encrypted-app-flash": "flash_encrypted_app_args",
"encrypted-flash": "flash_encrypted_project_args",
}[
action
]
@ -1040,6 +1042,18 @@ def init_cli():
"dependencies": ["app"],
"order_dependencies": ["erase_flash"],
},
"encrypted-app-flash": {
"callback": flash,
"help": "Flash the encrypted app only.",
"dependencies": ["app"],
"order_dependencies": ["erase_flash"],
},
"encrypted-flash": {
"callback": flash,
"help": "Flash the encrypted project.",
"dependencies": ["all"],
"order_dependencies": ["erase_flash"],
},
},
}