Flash Encryption

[中文]

This is a quick start guide to ESP32-S2’s flash encryption feature. Using an application code example, it demonstrates how to test and verify flash encryption operations during development and production.

Introduction

Flash encryption is intended for encrypting the contents of the ESP32-S2’s off-chip flash memory. Once this feature is enabled, firmware is flashed as plaintext, and then the data is encrypted in place on the first boot. As a result, physical readout of flash will not be sufficient to recover most flash contents.

With flash encryption enabled, the following types of data are encrypted by default:

  • Firmware bootloader

  • Partition Table

  • All “app” type partitions

Other types of data can be encrypted conditionally:

  • Any partition marked with the encrypted flag in the partition table. For details, see Encrypted Partition Flag.

  • Secure Boot bootloader digest if Secure Boot is enabled (see below).

Important

For production use, flash encryption should be enabled in the “Release” mode only.

Important

Enabling flash encryption limits the options for further updates of ESP32-S2. Before using this feature, read the document and make sure to understand the implications.

Relevant eFuses

The flash encryption operation is controlled by various eFuses available on ESP32-S2. The list of eFuses and their descriptions is given in the table below.

eFuses Used in Flash Encryption

eFuse

Description

Bit Depth

Locking for Reading/Writing Available

Default Value

KEYN

AES key storage. N is between 0 and 5.

256

Yes

x

EFUSE_KEY_PURPOSE_N

Controls the purpose of eFuse block KEYN, where N is between 0 and 5. Possible values: 2 for XTS_AES_256_KEY_1 , 3 for XTS_AES_256_KEY_2, and 4 for XTS_AES_128_KEY. Final AES key is derived based on the value of one or two of these purpose eFuses. For a detailed description of the possible combinations see ESP32-S2 Technical Reference Manual, chapter Flash Encryption.

4

Yes

0

EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT

If set, disables flash encryption when in download bootmodes.

1

Yes

0

EFUSE_SPI_BOOT_CRYPT_CNT

Enables encryption and decryption, when an SPI boot mode is set. Feature is enabled if 1 or 3 bits are set in the eFuse, disabled otherwise.

3

Yes

0

Read and write access to eFuse bits is controlled by appropriate fields in the registers EFUSE_WR_DIS and EFUSE_RD_DIS. For more information on ESP32-S2 eFuses, see eFuse manager.

Flash Encryption Process

Assuming that the eFuse values are in their default states and the firmware bootloader is compiled to support flash encryption, the flash encryption process executes as shown below:

  1. On the first power-on reset, all data in flash is un-encrypted (plaintext). The ROM bootloader loads the firmware bootloader.

  2. Firmware bootloader reads the EFUSE_SPI_BOOT_CRYPT_CNT eFuse value (0b00000000). Since the value is 0 (even number of bits set), it configures and enables the flash encryption block. For more information on the flash encryption block, see ESP32-S2 Technical Reference Manual.

  3. Flash encryption block generates an 256 bit or 512 bit key, depending on the value of Size of generated AES-XTS key, and writes it into respectively one or two KEYN eFuses. The software also updates the EFUSE_KEY_PURPOSE_N for the blocks where the keys where stored. This operation is done entirely by hardware, and the key cannot be accessed via software.

  4. Flash encryption block encrypts the flash contents - partitions encrypted by default and the ones marked as encrypted. Encrypting in-place can take time, up to a minute for large partitions.

  5. Firmware bootloader sets the first available bit in EFUSE_SPI_BOOT_CRYPT_CNT (0b00000001) to mark the flash contents as encrypted. Odd number of bits is set.

  6. For Development Mode, the firmware bootloader allows the UART bootloader to re-flash encrypted binaries. Also, the EFUSE_SPI_BOOT_CRYPT_CNT eFuse bits are NOT write-protected.

  7. For Release Mode, the firmware bootloader sets the eFuse bits EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT, EFUSE_DIS_BOOT_REMAP, EFUSE_DIS_DOWNLOAD_ICACHE and EFUSE_DIS_DOWNLOAD_DCACHE. It also write-protects the EFUSE_SPI_BOOT_CRYPT_CNT eFuse bits. To modify this behavior, see Enabling UART Bootloader Encryption/Decryption.

  8. The device is then rebooted to start executing the encrypted image. The firmware bootloader calls the flash decryption block to decrypt the flash contents and then loads the decrypted contents into IRAM.

During the development stage, there is a frequent need to program different plaintext flash images and test the flash encryption process. This requires that Firmware Download mode is able to load new plaintext images as many times as it might be needed. However, during manufacturing or production stages, Firmware Download mode should not be allowed to access flash contents for security reasons.

Hence, two different flash encryption configurations were created: for development and for production. For details on these configurations, see Section Flash Encryption Configuration.

Flash Encryption Configuration

The following flash encryption modes are available:

  • Development Mode - recommended for use ONLY DURING DEVELOPMENT, as it does not prevent modification and possible readout of encrypted flash contents.

  • Release Mode - recommended for manufacturing and production to prevent physical readout of encrypted flash contents.

This section provides information on the mentioned flash encryption modes and step by step instructions on how to use them.

Development Mode

During development, you can encrypt flash using either an ESP32-S2 generated key or external host-generated key.

Using ESP32-S2 Generated Key

Development mode allows you to download multiple plaintext images using Firmware Download mode.

To test flash encryption process, take the following steps:

  1. Ensure that you have an ESP32-S2 device with default flash encryption eFuse settings as shown in Relevant eFuses.

  1. In Project Configuration Menu, do the following:

  1. Run the command given below to build and flash the complete image.

idf.py flash monitor

The image will include the firmware bootloader, partition table, application, and other partitions marked by the user as encrypted. These binaries will be written to flash memory unencrypted. Once the flashing is complete, your device will reset. On the next boot, the firmware bootloader encrypts the flash application partition and then resets. After that, the sample application is decrypted at runtime and executed.

A sample output of the first ESP32-S2 boot after enabling flash encryption is given below:

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

A sample output of subsequent ESP32-S2 boots just mentions 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/esp32s2/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 you need to update and re-flash binaries, see Re-flashing Updated Partitions.

Using Host Generated Key

It is possible to pre-generate a flash encryption key on the host computer and burn it into the eFuse. This allows you to pre-encrypt data on the host and flash already encrypted data without needing a plaintext flash update. This feature can be used in both Development Mode and Release Mode. Without a pre-generated key, data is flashed in plaintext and then ESP32-S2 encrypts the data in-place.

Note

This option is not recommended for production, unless a separate key is generated for each individual device.

To use a host generated key, take the following steps:

  1. Ensure that you have an ESP32-S2 device with default flash encryption eFuse settings as shown in Relevant eFuses.

  1. Generate a random key by running:

espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin
  1. Before the first encrypted boot, burn the key into your device’s BLOCK1 eFuse using the command below. This action can be done only once.

espefuse.py --port PORT burn_key flash_encryption my_flash_encryption_key.bin

If the key is not burned and the device is started after enabling flash encryption, the ESP32-S2 will generate a random key that software cannot access or modify.

  1. In Project Configuration Menu, do the following:

  1. Run the command given below to build and flash the complete.

idf.py flash monitor

The image will include the firmware bootloader, partition table, application, and other partitions marked by the user as encrypted. These binaries will be written to flash memory unencrypted. Once the flashing is complete, your device will reset. On the next boot, the firmware bootloader encrypts the flash application partition and then resets. After that, the sample application is decrypted at runtime and executed.

At this stage, if you need to update and re-flash binaries, see Re-flashing Updated Partitions.

Re-flashing Updated Partitions

If you update your application code (done in plaintext) and want to re-flash it, you will need to encrypt it before flashing. To encrypt the application and flash it in one step, run:

idf.py encrypted-app-flash monitor

If all partitions needs to be updated in encrypted format, run:

idf.py encrypted-flash monitor

Release Mode

In Release mode, UART bootloader cannot perform flash encryption operations. New plaintext images can ONLY be downloaded using the over-the-air (OTA) scheme which will encrypt the plaintext image before writing to flash.

To use this mode, take the following steps:

  1. Ensure that you have an ESP32-S2 device with default flash encryption eFuse settings as shown in Relevant eFuses.

  1. In Project Configuration Menu, do the following:

  1. Run the command given below to build and flash the complete image.

idf.py flash monitor

The image will include the firmware bootloader, partition table, application, and other partitions marked by the user as encrypted. These binaries will be written to flash memory unencrypted. Once the flashing is complete, your device will reset. On the next boot, the firmware bootloader encrypts the flash application partition and then resets. After that, the sample application is decrypted at runtime and executed.

Once the flash encryption is enabled in Release mode, the bootloader will write-protect the EFUSE_SPI_BOOT_CRYPT_CNT eFuse.

For subsequent plaintext field updates, use OTA scheme.

Possible Failures

Once flash encryption is enabled, the EFUSE_SPI_BOOT_CRYPT_CNT eFuse value will have an odd number of bits set. It means that all the partitions marked with the encryption flag are expected to contain encrypted ciphertext. Below are the three typical failure cases if the ESP32-S2 is erroneously loaded with plaintext data:

  1. If the bootloader partition is re-flashed with a plaintext firmware bootloader image, the ROM bootloader will fail to load the firmware bootloader resulting in the following failure:

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

Note

This error also appears if the flash contents are erased or corrupted.

  1. If the firmware bootloader is encrypted, but the partition table is re-flashed with a plaintext partition table image, the bootloader will fail to read the partition table resulting in the following failure:

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!
  1. If the bootloader and partition table are encrypted, but the application is re-flashed with a plaintext application image, the bootloader will fail to load the application resulting in the following failure:

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

ESP32-S2 Flash Encryption Status

  1. Ensure that you have an ESP32-S2 device with default flash encryption eFuse settings as shown in Relevant eFuses.

To check if flash encryption on your ESP32-S2 device is enabled, do one of the following:

  • flash the application example security/flash_encryption onto your device. This application prints the EFUSE_SPI_BOOT_CRYPT_CNT eFuse value and if flash encryption is enabled or disabled.

  • Find the serial port name under which your ESP32-S2 device is connected, replace PORT with your port name in the following command, and run it:

    espefuse.py -p PORT summary
    

Reading and Writing Data in Encrypted Flash

ESP32-S2 application code can check if flash encryption is currently enabled by calling esp_flash_encryption_enabled(). Also, a device can identify the flash encryption mode by calling esp_get_flash_encryption_mode().

Once flash encryption is enabled, be more careful with accessing flash contents from code.

Scope of Flash Encryption

Whenever the EFUSE_SPI_BOOT_CRYPT_CNT eFuse is set to a value with an odd number of bits, all flash content accessed via the MMU’s flash cache is transparently decrypted. It includes:

  • Executable application code in flash (IROM).

  • All read-only data stored in flash (DROM).

  • Any data accessed via spi_flash_mmap().

  • The firmware bootloader image when it is read by the ROM bootloader.

Important

The MMU flash cache unconditionally decrypts all existing data. Data which is stored unencrypted in flash memory will also be “transparently decrypted” via the flash cache and will appear to software as random garbage.

Reading from Encrypted Flash

To read data without using a flash cache MMU mapping, you can use the partition read function esp_partition_read(). This function will only decrypt data when it is read from an encrypted partition. Data read from unencrypted partitions will not be decrypted. In this way, software can access encrypted and non-encrypted flash in the same way.

You can also use the following SPI flash API functions:

The ROM function SPIRead() can read data without decryption, however, this function is not supported in esp-idf applications.

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 NVS Encryption for more details.

Writing to Encrypted Flash

It is recommended to use the partition write function esp_partition_write(). This function will only encrypt data when it is written to an encrypted partition. Data written to unencrypted partitions will not be encrypted. In this way, software can access encrypted and non-encrypted flash in the same way.

You can also pre-encrypt and write data using the function esp_flash_write_encrypted()

Also, the following ROM function exist but not supported in esp-idf applications:

  • esp_rom_spiflash_write_encrypted pre-encrypts and writes data to flash

  • SPIWrite writes unencrypted data to flash

Since data is encrypted in blocks, the minimum write size for encrypted data is 16 bytes and the alignment is also 16 bytes.

Updating Encrypted Flash

OTA Updates

OTA updates to encrypted partitions will automatically write encrypted data if the function esp_partition_write() is used.

Before building the application image for OTA updating of an already encrypted device, enable the option Enable flash encryption on boot in project configuration menu.

For general information about ESP-IDF OTA updates, please refer to OTA

Disabling Flash Encryption

If flash encryption was enabled accidentally, flashing of plaintext data will soft-brick the ESP32-S2. The device will reboot continuously, printing the error flash read err, 1000.

For flash encryption in Development mode, encryption can be disabled by burning the EFUSE_SPI_BOOT_CRYPT_CNT eFuse. It can only be done one time per chip by taking the following steps:

  1. In Project Configuration Menu, disable Enable flash encryption on boot, then save and exit.

  2. Open project configuration menu again and double-check that you have disabled this option! If this option is left enabled, the bootloader will immediately re-enable encryption when it boots.

  3. With flash encryption disabled, build and flash the new bootloader and application by running idf.py flash.

  4. Use espefuse.py (in components/esptool_py/esptool) to disable the EFUSE_SPI_BOOT_CRYPT_CNT by running:

espefuse.py burn_efuse EFUSE_SPI_BOOT_CRYPT_CNT

Reset the ESP32-S2. Flash encryption will be disabled, and the bootloader will boot as usual.

Key Points About Flash Encryption

  • Flash memory contents are encrypted using XTS-AES-128 or XTS-AES-256. The flash encryption key is 256 bits and 512 bits respectively and stored one or two KEYN eFuses internal to the chip and, by default, is protected from software access.

  • Flash access is transparent via the flash cache mapping feature of ESP32-S2 - any flash regions which are mapped to the address space will be transparently decrypted when read.

    Some data partitions might need to remain unencrypted for ease of access or might require the use of flash-friendly update algorithms which are ineffective if the data is encrypted. NVS partitions for non-volatile storage cannot be encrypted since the NVS library is not directly compatible with flash encryption. For details, refer to NVS Encryption.

  • If flash encryption might be used in future, the programmer must keep it in mind and take certain precautions when writing code that uses encrypted flash.

  • If secure boot is enabled, re-flashing the bootloader of an encrypted device requires a “Re-flashable” secure boot digest (see Flash Encryption and Secure Boot).

    Important

    Do not interrupt power to the ESP32-S2 while the first boot encryption pass is running. If power is interrupted, the flash contents will be corrupted and will require flashing with unencrypted data again. In this case, re-flashing will not count towards the flashing limit.

Limitations of Flash Encryption

Flash encryption protects firmware against unauthorised readout and modification. It is important to understand the limitations of the flash encryption feature:

  • Flash encryption is only as strong as the key. It is recommended to generate keys on the device during first boot (default behaviour). If generating keys on a host computer, ensure to follow a proper procedure and do not use the same key for produced devices.

  • Not all data is stored encrypted. If storing data in flash memory, make sure that the method you are using (library, API, etc.) supports flash encryption.

Flash Encryption and Secure Boot

It is recommended to use flash encryption in combination with Secure Boot. However, if Secure Boot is enabled, additional restrictions apply to device re-flashing:

  • OTA Updates are not restricted, provided that the new app is signed correctly with the Secure Boot signing key.

Advanced Features

The following section covers advanced features of flash encryption.

Encrypted Partition Flag

Some partitions are encrypted by default. Other partitions can be marked in the partition table description as requiring encryption by adding the flag encrypted to the partitions’ flag field. As a result, data in these marked partitions will be treated as encrypted in the same manner 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

For details on partition table description, see partition table.

Further information about encryption of partitions:

  • Default partition tables do not include any encrypted data partitions.

  • With flash encryption enabled, the app partition is always treated as encrypted and does not require marking.

  • If flash encryption is not enabled, the flag “encrypted” has no effect.

  • You can also consider protecting phy_init data from physical access, readout, or modification, by marking the optional phy partition with the flag encrypted.

  • The nvs partition cannot be encrypted, because the NVS library is not directly compatible with flash encryption.

Enabling UART Bootloader Encryption/Decryption

On the first boot, the flash encryption process burns by default the following eFuses:

  • EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT flash encryption operation when running in UART bootloader boot mode.

However, before the first boot you can choose to keep any of these features enabled by burning only selected eFuses and write-protect the rest of eFuses with unset value 0. For example:

JTAG Debugging

By default, when Flash Encryption is enabled (in either Development or Release mode) then JTAG debugging is disabled via eFuse. The bootloader does this on first boot, at the same time it enables flash encryption.

See JTAG with Flash Encryption or Secure Boot for more information about using JTAG Debugging with Flash Encryption.

Technical Details

The following sections provide some reference information about the operation of flash encryption.

Flash Encryption Algorithm

  • ESP32-S2 use the XTS-AES block chiper mode with 256 bit or 512 bit key size for flash encryption.

  • XTS-AES is a block chiper mode specifically designed for disc encryption and addresses the weaknesses other potential modes (e.g. AES-CTR) have for this use case. A detailed description of the XTS-AES algorithm can be found in IEEE Std 1619-2007.

  • The flash encryption key is stored in one or two KEYN eFuses and, by default, is protected from further writes or software readout.

  • To see the full flash encryption algorithm implemented in Python, refer to the _flash_encryption_operation() function in the espsecure.py source code.