Sending encrypted flash content via esptool.py

davdav
Posts: 208
Joined: Thu Nov 17, 2016 2:33 pm

Sending encrypted flash content via esptool.py

Postby davdav » Tue Sep 18, 2018 3:39 pm

Hi,

Is it possible to send pre-encrypted program to flash using esptool.py. This will speed up production process without waiting esp32 encrpyt by itself the content with possible side effect to brick the device if power is removed as state in online doc:
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.


My process would be (it is a summary of what I have understood from docs
https://docs.espressif.com/projects/esp ... yption-key):

-generate a key with

Code: Select all

espsecure.py generate_flash_encryption_key My_encryption_key.bin
-fuse the key into eFuse with

Code: Select all

espefuse.py --port PORT burn_key flash_encryption My_encryption_key.bin
-encrypt my program with the key

Code: Select all

espsecure.py encrypt_flash_data --keyfile My_encryption_key.bin --address 0x10000 -o build/program-encrypted.bin build/program.bin
- use esptool.py to send encrypted content to esp32 esptool.py --port PORT --baud 460800 write_flash 0x10000 build/program-encrypted.bin


I didn't understand if I can do this the very time I program the esp32 or only after an esptool.py session with plaintext firmware.

Thanks

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: Sending encrypted flash content via esptool.py

Postby ESP_Angus » Tue Sep 18, 2018 11:45 pm

Yes, this is possible. A few comments:
davdav wrote:possible side effect to brick the device if power is removed as state in online doc:
If power is removed early it will temporarily "brick" the device (ie it will need reflashing), but no efuses are burned until this process is completed so it's fully reversible.

(There is a small window of time early after reset, if secure boot & flash encryption are both enabled, when the secure boot digest is being encrypted, where the device may become bricked. This will be fixed soon.)
davdav wrote: My process would be (it is a summary of what I have understood from docs
https://docs.espressif.com/projects/esp ... yption-key):

-generate a key with

Code: Select all

espsecure.py generate_flash_encryption_key My_encryption_key.bin
-fuse the key into eFuse with

Code: Select all

espefuse.py --port PORT burn_key flash_encryption My_encryption_key.bin
-encrypt my program with the key

Code: Select all

espsecure.py encrypt_flash_data --keyfile My_encryption_key.bin --address 0x10000 -o build/program-encrypted.bin build/program.bin
- use esptool.py to send encrypted content to esp32 esptool.py --port PORT --baud 460800 write_flash 0x10000 build/program-encrypted.bin
That's correct. But there are a few additional steps as well, if flashing an unencrypted device for the first time:
  • You need to also encrypt bootloader.bin for offset 0x1000 and flash this.
  • You need to also encrypt partition_table.bin for offset 0x8000 (or custom offset, if set) and flash this.
  • Enable flash encryption (so the ESP32 can boot the encrypted binaries) via "espefuse.py burn_efuse FLASH_CRYPT_CNT". You only need to do this the first time you flash, after this flash encryption will be enabled.

davdav wrote: I didn't understand if I can do this the very time I program the esp32 or only after an esptool.py session with plaintext firmware.
If you leave flash encryption enabled and you have the flash encryption key then you can re-flash encrypted firmware as many times as you like. The process is described here: https://docs.espressif.com/projects/esp ... erated-key

Some things to keep in mind if using this in production:
  • This removes the need to keep each device powered for ~1 minute after first boot. However, it means that you can't use a bulk SPI flash programmer to program the flash chips - espefuse.py is needed to flash efuses, so serial programming is required. For a lot of factory workflows, it may be easier to bulk SPI flash and have a slightly longer first boot burn-in/test period.
  • Recommended production configuration is to use a unique flash encryption key on each device. The simplest way to achieve this is to allow each device to generate its own on first boot, although it's also possible to pre-generate them all. Sharing the same key across all devices is also possible, but keep in mind that theoretical compromise of a single device will affect all devices in this case.

davdav
Posts: 208
Joined: Thu Nov 17, 2016 2:33 pm

Re: Sending encrypted flash content via esptool.py

Postby davdav » Wed Sep 19, 2018 7:29 am

Thanks @ESP_Angus.
You need to also encrypt bootloader.bin for offset 0x1000 and flash this.
You need to also encrypt partition_table.bin for offset 0x8000 (or custom offset, if set) and flash this.
I have also a factory APP so I guess I need to encrypt it also.
Enable flash encryption (so the ESP32 can boot the encrypted binaries) via "espefuse.py burn_efuse FLASH_CRYPT_CNT". You only need to do this the first time you flash, after this flash encryption will be enabled.
Is it possible to send more "commands" to a single espefuse.py? For example

Code: Select all

espefuse.py --port COM1 burn_key flash_encryption My_encryption_key.bin  burn_efuse FLASH_CRYPT_CNT
This will save time because in a single session of espefuse.py I will do the two operation (encrypt key AND flash_Crypt_cnt fuse). I have tryed but I got error

Code: Select all

usage: espefuse [-h] [--baud BAUD] [--port PORT]
                [--before {default_reset,no_reset,esp32r1,no_reset_no_sync}]
                [--do-not-confirm]
                {dump,summary,burn_efuse,read_protect_efuse,write_protect_efuse,burn_key,set_flash_voltage,adc_info}
                ...
espefuse: error: unrecognized arguments: burn_efuse FLASH_CRYPT_CNT
What is the correct syntax?

Recommended production configuration is to use a unique flash encryption key on each device. The simplest way to achieve this is to allow each device to generate its own on first boot, although it's also possible to pre-generate them all. Sharing the same key across all devices is also possible, but keep in mind that theoretical compromise of a single device will affect all devices in this case.
Yes I'm aware of that, but the device we produce have the same firrmware. Therefore, from a "probability point of view" having a pre-generated key or let esp32 to burn its key it doesn't matter because, choosen one device, once you have find the key and you got the firmware of the "product". For sure if we are going to have another product we will pre-generate another key.

Thanks.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: Sending encrypted flash content via esptool.py

Postby ESP_Angus » Wed Sep 19, 2018 7:38 am

davdav wrote: Is it possible to send more "commands" to a single espefuse.py?
This is not currently possible. I've logged it as a feature request: https://github.com/espressif/esptool/issues/360
davdav wrote: Yes I'm aware of that, but the device we produce have the same firrmware. Therefore, from a "probability point of view" having a pre-generated key or let esp32 to burn its key it doesn't matter because, choosen one device, once you have find the key and you got the firmware of the "product". For sure if we are going to have another product we will pre-generate another key.
OK. If your main concern is not leaking the firmware binary, and you don't use secure boot, then this makes sense.

davdav
Posts: 208
Joined: Thu Nov 17, 2016 2:33 pm

[ANSWERED]Re: Sending encrypted flash content via esptool.py

Postby davdav » Wed Sep 19, 2018 8:04 am

OK @ESP_Angus. Thanks for the enhancement request. I look forward to have it in next esptool version.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: Sending encrypted flash content via esptool.py

Postby ESP_Angus » Mon Sep 24, 2018 2:02 am

Hi davdav,

I realised a couple of other things about pre-encrypting with a common key:

- One more step is required for the encryption config on the device to match the (default) used by espsecure.py, and needs to be burned before an initial boot can succeed ("flash read err 1000" will be printed if these don't match, as the flash contents can't be decrypted):

Code: Select all

espefuse.py burn_efuse FLASH_CRYPT_CONFIG 0xF
(It's possible to leave this value at zero and pass the --flash-crypt-conf option to espsecure.py instead, but this is very strongly discouraged - see docs).

- Some additional efuses need to be burned to ensure a secure device: DISABLE_DL_ENCRYPT, DISABLE_DL_ENCRYPT, DISABLE_DL_CACHE, DISABLE_JTAG and CONSOLE_DEBUG_DISABLE. These are burned by default in the default flash encryption workflow, but need manually burning if you pre-generate a key.

CONSOLE_DEBUG_DISABLE should be burned by default on first boot, unless default config is changed. To save factory time, I recommend burning the other 4 from the app rather than using espefuse.py. Something like this, called from app_main(), should work:

Code: Select all

#include "esp_efuse.h"

void burn_protection_efuses()
{
    esp_efuse_reset();

    uint32_t new_wdata6 = EFUSE_DISABLE_DL_ENCRYPT
        | EFUSE_DISABLE_DL_DECRYPT
        | EFUSE_DISABLE_DL_CACHE
        | EFUSE_RD_DISABLE_JTAG
        | EFUSE_RD_CONSOLE_DEBUG_DISABLE;

    if ((new_wdata6 & REG_READ(EFUSE_BLK0_RDATA6_REG)) != new_wdata6) {
        ESP_LOGI(TAG, "Burning protection efuses...");
        REG_WRITE(EFUSE_BLK0_WDATA6_REG, new_wdata6);
        esp_efuse_burn_new_values();
    }
}

davdav
Posts: 208
Joined: Thu Nov 17, 2016 2:33 pm

Re: Sending encrypted flash content via esptool.py

Postby davdav » Mon Sep 24, 2018 2:15 pm

Thank you @ESP_Angus,

I have update my workflow and factory application to account for the efuse to enhance security with pre-generated key
My efuse summary after those modification is:

Code: Select all

Security fuses:
FLASH_CRYPT_CNT        Flash encryption mode counter                     = 1 R/W (0x1)
FLASH_CRYPT_CONFIG     Flash encryption config (key tweak bits)          = 15 R/W (0xf)
CONSOLE_DEBUG_DISABLE  Disable ROM BASIC interpreter fallback            = 1 R/W (0x1)
ABS_DONE_0             secure boot enabled for bootloader                = 0 R/W (0x0)
ABS_DONE_1             secure boot abstract 1 locked                     = 0 R/W (0x0)
JTAG_DISABLE           Disable JTAG                                      = 1 R/W (0x1)
DISABLE_DL_ENCRYPT     Disable flash encryption in UART bootloader       = 1 R/W (0x1)
DISABLE_DL_DECRYPT     Disable flash decryption in UART bootloader       = 1 R/W (0x1)
DISABLE_DL_CACHE       Disable flash cache in UART bootloader            = 1 R/W (0x1)
BLK1                   Flash encryption key
  = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 -/-
BLK2                   Secure boot key
  = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W
BLK3                   Variable Block 3
  = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 R/W

Efuse fuses:
WR_DIS                 Efuse write disable mask                          = 128 R/W (0x80)
RD_DIS                 Efuse read disablemask                            = 1 R/W (0x1)
CODING_SCHEME          Efuse variable block length scheme                = 0 R/W (0x0)
KEY_STATUS             Usage of efuse block 3 (reserved)                 = 0 R/W (0x0)

Config fuses:
XPD_SDIO_FORCE         Ignore MTDI pin (GPIO12) for VDD_SDIO on reset    = 0 R/W (0x0)
XPD_SDIO_REG           If XPD_SDIO_FORCE, enable VDD_SDIO reg on reset   = 0 R/W (0x0)
XPD_SDIO_TIEH          If XPD_SDIO_FORCE & XPD_SDIO_REG, 1=3.3V 0=1.8V   = 0 R/W (0x0)
SPI_PAD_CONFIG_CLK     Override SD_CLK pad (GPIO6/SPICLK)                = 0 R/W (0x0)
SPI_PAD_CONFIG_Q       Override SD_DATA_0 pad (GPIO7/SPIQ)               = 0 R/W (0x0)
SPI_PAD_CONFIG_D       Override SD_DATA_1 pad (GPIO8/SPID)               = 0 R/W (0x0)
SPI_PAD_CONFIG_HD      Override SD_DATA_2 pad (GPIO9/SPIHD)              = 0 R/W (0x0)
SPI_PAD_CONFIG_CS0     Override SD_CMD pad (GPIO11/SPICS0)               = 0 R/W (0x0)
DISABLE_SDIO_HOST      Disable SDIO host                                 = 0 R/W (0x0)

Identity fuses:
MAC                    MAC Address
  = 30:ae:a4:0b:3c:a4 (CRC b4 OK) R/W
CHIP_VER_REV1          Silicon Revision 1                                = 1 -/W (0x1)
CHIP_VERSION           Reserved for future chip versions                 = 0 -/W (0x0)
CHIP_PACKAGE           Chip package identifier                           = 0 -/W (0x0)

Calibration fuses:
BLK3_PART_RESERVE      BLOCK3 partially served for ADC calibration data  = 0 -/W (0x0)
ADC_VREF               Voltage reference calibration                     = 1100 -/W (0x0)

Flash voltage (VDD_SDIO) determined by GPIO12 on reset (High for 1.8V, Low/NC for 3.3V).
CONSOLE_DEBUG_DISABLE has been automatically fused by device, the others from application code.It seems OK.

Thanks.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: Sending encrypted flash content via esptool.py

Postby ESP_Angus » Mon Sep 24, 2018 11:36 pm

davdav wrote:It seems OK.
This looks correct to me as well.

Andy1234
Posts: 3
Joined: Mon Feb 10, 2020 1:48 pm

Re: Sending encrypted flash content via esptool.py

Postby Andy1234 » Tue Feb 25, 2020 10:48 am

Hi!

Code: Select all

Security fuses:
FLASH_CRYPT_CNT        Flash encryption mode counter                     = 1 R/- (0x1)
FLASH_CRYPT_CONFIG     Flash encryption config (key tweak bits)          = 15 R/W (0xf)
CONSOLE_DEBUG_DISABLE  Disable ROM BASIC interpreter fallback            = 1 R/W (0x1)
ABS_DONE_0             secure boot enabled for bootloader                = 1 R/W (0x1)
ABS_DONE_1             secure boot abstract 1 locked                     = 0 R/W (0x0)
JTAG_DISABLE           Disable JTAG                                      = 1 R/W (0x1)
DISABLE_DL_ENCRYPT     Disable flash encryption in UART bootloader       = 1 R/W (0x1)
DISABLE_DL_DECRYPT     Disable flash decryption in UART bootloader       = 1 R/W (0x1)
DISABLE_DL_CACHE       Disable flash cache in UART bootloader            = 1 R/W (0x1)
BLK1                   Flash encryption key                              
  = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/- 
BLK2                   Secure boot key                                   
  = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/- 
 

1)
I thought that with such fuses reading and writing encrypted firmware via UART would be prohibited.
But reading and writing encrypted firmware via UART can still occur.
Is it dangerous to give my customers a device in this state?
The device is fully protected?

2)
Do I need to protect some fuses from writing?

Thanks.

ESP_Angus
Posts: 2344
Joined: Sun May 08, 2016 4:11 am

Re: Sending encrypted flash content via esptool.py

Postby ESP_Angus » Wed Feb 26, 2020 7:23 am

Andy1234 wrote:
Tue Feb 25, 2020 10:48 am
Hi!

Code: Select all

Security fuses:
FLASH_CRYPT_CNT        Flash encryption mode counter                     = 1 R/- (0x1)
FLASH_CRYPT_CONFIG     Flash encryption config (key tweak bits)          = 15 R/W (0xf)
CONSOLE_DEBUG_DISABLE  Disable ROM BASIC interpreter fallback            = 1 R/W (0x1)
ABS_DONE_0             secure boot enabled for bootloader                = 1 R/W (0x1)
ABS_DONE_1             secure boot abstract 1 locked                     = 0 R/W (0x0)
JTAG_DISABLE           Disable JTAG                                      = 1 R/W (0x1)
DISABLE_DL_ENCRYPT     Disable flash encryption in UART bootloader       = 1 R/W (0x1)
DISABLE_DL_DECRYPT     Disable flash decryption in UART bootloader       = 1 R/W (0x1)
DISABLE_DL_CACHE       Disable flash cache in UART bootloader            = 1 R/W (0x1)
BLK1                   Flash encryption key                              
  = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/- 
BLK2                   Secure boot key                                   
  = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? -/- 

1)
I thought that with such fuses reading and writing encrypted firmware via UART would be prohibited.
But reading and writing encrypted firmware via UART can still occur.
Is it dangerous to give my customers a device in this state?
The device is fully protected?
Yes, this is by design. Even though the UART loader interface is still available, it should not allow anyone to bypass the security features.

There is a more complex issue: On ESP32 revision 2 and earlier, it is possible for an attacker with physical access and the correct knowledge/equipment to bypass the efuse protections. See here for more details:
https://www.espressif.com/en/news/Secur ... rotections
https://www.espressif.com/en/news/ESP32_FIA_Analysis

The ESP32 ECO3 adds protection against this. ECO3 also adds a method to disable the UART download mode (unrelated to the vulnerability mentioned in these links), although we don't expose this in ESP-IDF yet and it's not necessary to disable the UART download mode in order to have a secure chip.
Andy1234 wrote:
Tue Feb 25, 2020 10:48 am
2)
Do I need to protect some fuses from writing?

Thanks.
If you haven't enabled any of the submenu of "potentially insecure options" in the project Security config then the required write protections are all applied automatically when the security features are enabled. Looking at the summary you have posted, it appears this has already happened. (Where it says R/- or -/-, the efuse is write protected. and efuse bits can only be written 0 to 1, so any single bit efuse which is already 1 cannot be changed.)

Who is online

Users browsing this forum: Baidu [Spider] and 75 guests