ESP32-S3 Secure boot+Flash Encryption - Correct procedure?

Crinst
Posts: 1
Joined: Fri May 17, 2024 7:11 am

ESP32-S3 Secure boot+Flash Encryption - Correct procedure?

Postby Crinst » Fri May 17, 2024 9:00 am

Hi guys,

I have tried to enable Secure boot and Flash Encryption on ESP32-S3 for the past few days with limited success.

In the past, we have used ESP32 (ESP32-WROVER-E N16R8) in our product, where we used conbination of secure boot and flash encryption and it worked really well. We have implemented whole procedure per guide: https://docs.espressif.com/projects/esp ... flows.html and it worked as expected. But guide for ESP32-S3 (https://docs.espressif.com/projects/esp ... flows.html seems to be incomplete or faulty, because no matter what I did, it does not work as I was expecting.

For ESP32-S3, we have choosen ESP32-S3-WROOM-1 N16R8. All test were done on WROOM modules in programmer board such as https://www.aliexpress.com/item/1005006825819098.html.

Regarding ESP-IDF, I am using ESP-IDF "release\v4.4" as it's validated and working oka-ish for my use-case. On top of that I have did some fixes so moving to newer version is not priority right now.

My partition table for ESP32-S3 is below. I let partition tool calculate all offsets, I have only set first offset at 0x11000, partition table itself is located at offset 0x10000.

Code: Select all

# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs,		data,	nvs,		0x11000,	128K,
nvs_key,	data,	nvs_keys,	0x31000,	4K,
otadata,	data,	ota,		0x32000,	8K,
phy_init,	data,	phy,		0x34000,	4K,
factory,	app,	factory,	0x40000,	4M,
ota_0,		app,	ota_0,		0x440000,	4M,
ota_1,		app,	ota_1,		0x840000,	4M,
storage,	data,	spiffs,		0xc40000,	2880K,
fw_nvs_data,data,	nvs,		0xf10000,	256K,
Current state:
My workflow is as this:
1) Flash Encryption key
I flash host-generated AES-256 (512 bit) key to "BLOCK_KEY_0" with function "XTS_AES_256_KEY", which flashes to "BLOCK_KEY0" and "BLOCK_KEY1" AES-256 key.
I do this with

Code: Select all

 command = ["espefuse", "--port", COMport, "--chip", "esp32s3", "burn_key", "BLOCK_KEY0", EncryptionKeyPath, "XTS_AES_256_KEY"]
.

2) Flash public key digest for Secure boot.
I flash host-generated RSA3070 key digest to "BLOCK_KEY2" with function "SECURE_BOOT_DIGEST0".
I do this with

Code: Select all

command = ["espefuse", "--port", COMport, "--chip", "esp32s3", "burn_key", "BLOCK_KEY2", SignDigestKeyPath, "SECURE_BOOT_DIGEST0"]
3) I burn eFuses.
I have set of eFuses, which are burned in sequence as written.

Code: Select all

	*** Efuse ***					*** Val ***
	"SPI_BOOT_CRYPT_CNT"    		: "7",
	"DIS_DOWNLOAD_ICACHE"   		: "0x1",
	"DIS_DOWNLOAD_DCACHE"   		: "0x1",
	"DIS_PAD_JTAG"          		: "0x1",
	"SOFT_DIS_JTAG"         		: "0x1",
	"DIS_DIRECT_BOOT"				: "0x1",
	"DIS_USB_JTAG"   				: "0x1",
	"DIS_DOWNLOAD_MANUAL_ENCRYPT"   : "0x1",

command = ["espefuse", "--port", COMport, "--chip", "esp32s3", "burn_efuse", Efuse, Val]
subprocess.run(command, input = str("BURN").encode())
4) After these eFuses are burned, I enable secure boot, secure download mode and disable read for BLOCK4-BLOCK10 (keys).

*** Efuse *** *** Val ***
"SECURE_BOOT_EN" : "True",
"RD_DIS" : "3",
"ENABLE_SECURITY_DOWNLOAD" : "True",

For burning I send these command, in sequence as written from top to buttom.

Code: Select all

if Efuse == "ENABLE_SECURITY_DOWNLOAD":
        command = ["espefuse", "--port", COMport, "--chip", "esp32s3", "burn_efuse", "ENABLE_SECURITY_DOWNLOAD"]
    elif Efuse == "SECURE_BOOT_EN":
        command = ["espefuse", "--port", COMport, "--chip", "esp32s3", "burn_efuse", "SECURE_BOOT_EN"]
    elif Efuse == "RD_DIS":
        command = ["espefuse", "--port", COMport, "write_protect_efuse", "RD_DIS"]
    else:
        continue
        
    subprocess.run(command, input = str("BURN").encode())
NOTE: Once, I have enabled security download, I can no longer read/write any eFuse. Thats allright and expected behaviour.

5) I build binaries.

I use standard command "idf.py build" in Visual Studio code.

6) Erase whole flash memory
As security download is enabled, I was unable to use standard command "idf.py erase_flash" is ROM did not allow it. So I came up with variant that i generated fixed size bin files fulled with 0xFF values (=empty data) and write these to all relevant offsets.
This way whole address block from 0x0 up to end of factory_partition is erased.

Code: Select all

	adress = ["0x0", "0x40000"]
	binaries = ["512k_0xff_file.bin", "4096k_0xff_file.bin"]

	for i in range(0, len(binaries)):
        bins = [addresses[i], folderPath + "\\" + binaries[i]]

        command = ["esptool", "-p", COMport, "-b", "460800", "--no-stub", "--before", "default_reset", "--after", "no_reset"]
        command = command + ["--chip", "esp32s3", "write_flash", "--flash_mode", "dio"]
        command = command + ["--flash_size", "16MB", "--flash_freq", "80m"]
        command = command + bins + ["--force"]
    
        subprocess.run(command)
7) Sign binaries.

I sign all binaries (bootloader, partition table, ota_init, factory fw).

Code: Select all

command = ["espsecure", "sign_data", "--version", "2", "--keyfile", SignKeyPath, "--output", OutputBinPath, InputBinPath]
	subprocess.run(command)
8) Encryption signed binaries.

I enrypt all binaries (bootloader, partition table, ota_init, factory fw).

Code: Select all

command = ["espsecure", "encrypt_flash_data", "--aes_xts", "--keyfile", EncryptKeyPath, "--address", BinAdsress, "--output", OutputBinPath, InputBinPath]
	subprocess.run(command)
9) Flash signed and encrypted binaries.

Code: Select all

	adress = ["0x0", "0x10000", "0x40000", "0x32000"]
	binaries = ["bootloader_signed_encrypted.bin", "partition-table_signed_encrypted.bin", "esp32_evse_signed_encrypted.bin", "ota_data_initial_signed_encrypted.bin"]
	
	for i in range(0, len(binaries)):
        bins = [addresses[i], binaries[i]]

        command = ["esptool", "-p", COMport, "-b", "460800", "--no-stub", "--before", "default_reset", "--after", "no_reset"]
        command = command + ["--chip", "esp32s3", "write_flash", "--flash_mode", "dio"]
        command = command + ["--flash_size", "16MB", "--flash_freq", "80m"]
        command = command + bins + ["--force"]
    
        subprocess.run(command)
10) Controller is in bootloader and waiting for reset.
I open VSCode console and controller runs for the first time and everything works as expected. It boot in bootloader, 2nd stage bootloader and then validates successfully factory partition and runs FW as it should.
But once I reset controller, It's unable to validate factory partition checksum and start to restart in loop.

I have tried to do OTA update for this first run if it will finish and it restart with several critical erros.
I have attached below my sdkconfig and log file, where I captured correct and incorrect run for first and second boot respectively.

I have tried to validate firmware image for chechsum with command

Code: Select all

esptool image_info esp32_evse_signed.bin --version 2
It returned correct value for plaintext and signed firmware binary. For encrypted file it returned error, but this should be correct behaviour. I have tried this validation for old ESP32 flow and it produced same results.
NOTE: checksum values in log and image_info are not same as I have tried to build FW several times and captured only handful of outputs to console.

Here is output to console:

Code: Select all

esptool.py v4.6.2
File size: 1314816 (bytes)
Detected image type: ESP32-S3

ESP32-S3 image header
=====================
Image version: 1
Entry point: 0x40375594
Segments: 8
Flash size: 16MB
Flash freq: 80m
Flash mode: DIO

ESP32-S3 extended image header
==============================
WP pin: 0xee (disabled)
Flash pins drive settings: clk_drv: 0x0, q_drv: 0x0, d_drv: 0x0, cs0_drv: 0x0, hd_drv: 0x0, wp_drv: 0x0
Chip ID: 9 (ESP32-S3)
Minimal chip revision: v0.0, (legacy min_rev = 0)
Maximal chip revision: v0.0

Segments information
====================
Segment   Length   Load addr   File offs  Memory types
-------  -------  ----------  ----------  ------------
      1  0x3e574  0x3c0f0020  0x00000018  DROM
      2  0x01a7c  0x3fc975b0  0x0003e594  BYTE_ACCESSIBLE, MEM_INTERNAL, DRAM
      3  0xe1d80  0x42000020  0x00040018  IROM
      4  0x035b8  0x3fc9902c  0x00121da0  BYTE_ACCESSIBLE, MEM_INTERNAL, DRAM
      5  0x135a4  0x40374000  0x00125360  MEM_INTERNAL, IRAM
      6  0x00010  0x50000000  0x0013890c  RTC_DATA
      7  0x00028  0x600fe000  0x00138924  RTC_DRAM, RTC_IRAM
      8  0x07674  0x00000000  0x00138954  PADDING

ESP32-S3 image footer
=====================
Checksum: 0xaa (valid)
Validation hash: 554343f3cc6ca7eb9e2c6ec0926d67cac7f57ed19de081623cb64367f7d8097f (valid)

Application information
=======================
Project name: esp32_evse
App version: V1.2.2D-Litva-57-g80a89a3-dirty
Compile time: May 16 2024      18:13:31
ELF file SHA256: db3c3ef25804e68ebc8bfcd0be494376bc03e09d5dd5de0f27d6f08c547e5225
ESP-IDF: v4.4-dev-3703-gddc44956bf-dirty
Secure version: 0
Do you have any idea what I'm missing out or what is wrong? I'm slowly going crazy as I have been debugging this issue for the past week. :/ Any help appreciated.
Attachments
forth_write_console_log_3.txt
(14.41 KiB) Downloaded 114 times
sdkconfig.txt
(49.91 KiB) Downloaded 102 times

Who is online

Users browsing this forum: Baidu [Spider], Bing [Bot] and 80 guests