Security Features Enablement Workflows
Introduction
When enabling security features on ESP32 SoCs, it is recommended that power supply be uninterrupted. Power failures during this process could cause issues that are hard to debug and, in some cases, may cause permanent boot-up failures.
This guide describes a set of workflows to enable security features on the device with the assistance of an external host machine. These workflows are broken down into various stages, with each stage generating signing/encryption keys on the host machine. This allows for greater chances of recovery in case of power or other failures. Furthermore, these workflows expedites the overall provisioning process via the use of the host machine (e.g., encrypting firmware on the host is quicker than on the device).
Goals
Simplify the traditional workflow for enabling security features with stepwise instructions.
Design a more flexible workflow when compared to the traditional firmware-based workflow.
Improve reliability by dividing the workflow into small operations.
Eliminate dependency on Second Stage Bootloader.
Prerequisites
esptool
: Please make sure theesptool
has been installed. It can be installed by running:
pip install esptool
Scope
Security Features Enablement
Enable Flash Encryption and Secure Boot v2 Externally
Important
It is recommended to enable both Flash Encryption and Secure Boot v2 for a production use case.
When enabling the Flash Encryption and Secure Boot v2 together, they need to enable them in the following order:
Enable the Flash Encryption feature by following the steps listed in Enable Flash Encryption Externally.
Enable the Secure Boot v2 feature by following the steps listed in Enable Secure Boot v2 Externally.
The reason this particular ordering is that when enabling Secure Boot (SB) v2, it is necessary to keep the SB v2 key readable. To protect the key's readability, the write protection for RD_DIS
(ESP_EFUSE_WR_DIS_RD_DIS
) is applied. However, this action poses a challenge when attempting to enable Flash Encryption, as the Flash Encryption (FE) key needs to remain unreadable. This conflict arises because the RD_DIS
is already write-protected, making it impossible to read protect the FE key.
Enable Flash Encryption Externally
In this case all the eFuses related to Flash Encryption are written with help of the espefuse tool. More details about Flash Encryption can process can be found in Flash Encryption.
Ensure that you have an ESP32-C2 device with default Flash Encryption eFuse settings as shown in Relevant eFuses
See how to check ESP32-C2 Flash Encryption Status.
At this point, the Flash Encryption must not be already enabled on the chip. Additionally, the flash on the chip needs to be erased, which can be done by running:
esptool.py --port PORT erase_flash
Generate a Flash Encryption key
A random Flash Encryption key can be generated by running:
If Size of generated AES-XTS key is AES-128 (256-bit key):
espsecure.py generate_flash_encryption_key my_flash_encryption_key.bin
else if Size of generated AES-XTS key is AES-128 key derived from 128 bits (SHA256(128 bits)):
espsecure.py generate_flash_encryption_key --keylen 128 my_flash_encryption_key.bin
Burn the Flash Encryption key into eFuse
Warning
This action cannot be reverted.
It can be done by running:
For AES-128 (256-bit key) -
XTS_AES_128_KEY
(theXTS_KEY_LENGTH_256
eFuse will be burn to 1):espefuse.py --port PORT burn_key BLOCK_KEY0 flash_encryption_key256.bin XTS_AES_128_KEY
For AES-128 key derived from SHA256(128 eFuse bits) -
XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS
. The FE key will be written in the lower part of eFuse BLOCK_KEY0. The upper 128 bits are not used and will remain available for reading by software. Using the special mode of the espefuse tool, shown in theFor burning both keys together
section below, the user can write their data to it using any espefuse commands.espefuse.py --port PORT burn_key BLOCK_KEY0 flash_encryption_key128.bin XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS
For burning both keys together (Secure Boot and Flash Encryption):
espefuse.py --port PORT --chip esp32c2 burn_key_digest secure_boot_signing_key.pem \ burn_key BLOCK_KEY0 flash_encryption_key128.bin XTS_AES_128_KEY_DERIVED_FROM_128_EFUSE_BITS
Burn the
SPI_BOOT_CRYPT_CNT
eFuseIf you only want to enable Flash Encryption in Development mode and want to keep the ability to disable it in the future, Update the SPI_BOOT_CRYPT_CNT value in the below command from 7 to 0x1 (not recommended for production).
espefuse.py --port PORT --chip esp32c2 burn_efuse SPI_BOOT_CRYPT_CNT 7
Burn Flash Encryption-related security eFuses as listed below
Burn security eFuses
Important
For production use cases, it is highly recommended to burn all the eFuses listed below.
DIS_DOWNLOAD_ICACHE
: Disable UART cacheDIS_DIRECT_BOOT
: Disable direct boot (legacy SPI boot mode)DIS_PAD_JTAG
: Disable JTAG permanentlyDIS_DOWNLOAD_MANUAL_ENCRYPT
: Disable UART bootloader encryption access
The respective eFuses can be burned by running:
espefuse.py burn_efuse --port PORT EFUSE_NAME 0x1
Note
Please update the
EFUSE_NAME
with the eFuse that you need to burn. Multiple eFuses can be burned at the same time by appending them to the above command (e.g.,EFUSE_NAME VAL EFUSE_NAME2 VAL2
). More documentation about espefuse.py can be found here.Configure the project
The bootloader and the application binaries for the project must be built with Flash Encryption release mode with default configurations.
Flash Encryption release mode can be set in the menuconfig as follows:
Select release mode (Note that once release mode is selected, the
EFUSE_DIS_DOWNLOAD_MANUAL_ENCRYPT
eFuse bit will be burned to disable Flash Encryption hardware in ROM download mode).Select UART ROM download mode (permanently switch to Secure mode (recommended)). This is the default option, and is recommended. It is also possible to change this configuration setting to permanently disable UART ROM download mode, if this mode is not needed.
Save the configuration and exit.
Build, Encrypt and Flash the binaries
The binaries can be encrypted on the host machine by running:
espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0x0 --output bootloader-enc.bin build/bootloader/bootloader.bin espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0x8000 --output partition-table-enc.bin build/partition_table/partition-table.bin espsecure.py encrypt_flash_data --aes_xts --keyfile my_flash_encryption_key.bin --address 0x10000 --output my-app-enc.bin build/my-app.bin
In the above command, the offsets are used for a sample firmware, and the actual offset for your firmware can be obtained by checking the partition table entry or by running idf.py partition-table. Please note that not all the binaries need to be encrypted, the encryption applies only to those generated from the partitions which are marked as
encrypted
in the partition table definition file. Other binaries are flashed unencrypted, i.e., as a plain output of the build process.The above files can then be flashed to their respective offset using
esptool.py
. To see all of the command line options recommended foresptool.py
, see the output printed whenidf.py build
succeeds.When the application contains the following partition:
otadata
andnvs_encryption_keys
, they need to be encrypted as well. Please refer to Encrypted Partitions for more details about encrypted partitions.Note
If the flashed ciphertext file is not recognized by the ESP32-C2 when it boots, check that the keys match and that the command line arguments match exactly, including the correct offset. It is important to provide the correct offset as the ciphertext changes when the offset changes.
The command
espsecure.py decrypt_flash_data
can be used with the same options (and different input or output files), to decrypt ciphertext flash contents or a previously encrypted file.Secure the ROM download mode
Warning
Please perform the following step at the very end. After this eFuse is burned, the espefuse tool can no longer be used to burn additional eFuses.
Enable security download mode:
ENABLE_SECURITY_DOWNLOAD
: Enable secure ROM download mode
The eFuse can be burned by running:
espefuse.py --port PORT burn_efuse ENABLE_SECURITY_DOWNLOAD
Important
Delete Flash Encryption key on host
Once the Flash Encryption has been enabled for the device, the key must be deleted immediately. This ensures that the host can't produce encrypted binaries for the same device going forward. This step is important to reduce the vulnerability of the Flash Encryption key.
Flash Encryption Guidelines
It is recommended to generate a unique Flash Encryption key for each device for production use-cases.
It is recommended to ensure that the RNG used by host machine to generate the Flash Encryption key has good entropy.
See Limitations of Flash Encryption for more details.
Enable Secure Boot v2 Externally
In this workflow we shall use espsecure
tool to generate signing keys and use the espefuse
tool to burn the relevant eFuses. The details about the Secure Boot v2 process can be found at Secure Boot v2.
Generate Secure Boot v2 Signing Private Key
The Secure Boot v2 signing key for ECDSA scheme can be generated by running:
espsecure.py generate_signing_key --version 2 --scheme ecdsa256 secure_boot_signing_key.pem
The scheme in the above command can be changed to
ecdsa192
to generate ecdsa192 private key.Generate Public Key Digest
The public key digest for the private key generated in the previous step can be generated by running:
espsecure.py digest_sbv2_public_key --keyfile secure_boot_signing_key.pem --output digest.bin
Burn the key digest in eFuse
The public key digest can be burned in the eFuse by running:
espefuse.py --port PORT --chip esp32c2 burn_key KEY_BLOCK0 digest.bin SECURE_BOOT_DIGEST
Enable Secure Boot v2
Secure Boot v2 eFuse can be enabled by running:
espefuse.py --port PORT --chip esp32c2 burn_efuse SECURE_BOOT_EN
Burn relevant eFuses
Burn security eFuses
Important
For production use cases, it is highly recommended to burn all the eFuses listed below.
DIS_DIRECT_BOOT
: Disable direct boot (legacy SPI boot mode).DIS_PAD_JTAG
: Disable JTAG permanently.
The respective eFuses can be burned by running:
espefuse.py burn_efuse --port PORT EFUSE_NAME 0x1
Note
Please update the EFUSE_NAME with the eFuse that you need to burn. Multiple eFuses can be burned at the same time by appending them to the above command (e.g., EFUSE_NAME VAL EFUSE_NAME2 VAL2). More documentation about espefuse.py can be found here
Secure Boot v2-related eFuses
Disable the read-protection option:
The Secure Boot digest burned in the eFuse must be kept readable otherwise the Secure Boot operation would result in a failure. To prevent the accidental enabling of read protection for this key block, the following eFuse needs to be burned:
espefuse.py -p $ESPPORT write_protect_efuse RD_DIS
Important
After burning above-mentioned eFuse, the read protection can't be enabled for any key. For example, if Flash Encryption which requires read protection for its key is not enabled at this point, then it can't be enabled afterwards. Please ensure that no eFuse keys are going to need read protection after completing this step.
Configure the project
By default, the first stage (ROM) bootloader would only verify the Second Stage Bootloader. The second stage bootloader would verify the app partition only when the CONFIG_SECURE_BOOT option is enabled (and CONFIG_SECURE_BOOT_VERSION is set to
SECURE_BOOT_V2_ENABLED
) while building the bootloader.Open the Editing the Configuration, in
Security features
setEnable hardware Secure Boot in bootloader
to enable Secure Boot.
The
Secure Boot v2
option will be selected and theApp Signing Scheme
will be set to ECDSA (V2) by default.Disable the option CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES for the project in the Editing the Configuration. This shall make sure that all the generated binaries are secure padded and unsigned. This step is done to avoid generating signed binaries as we are going to manually sign the binaries using
espsecure
tool.
Build, Sign and Flash the binaries
After the above configurations, the bootloader and application binaries can be built with
idf.py build
command.The Secure Boot v2 workflow only verifies the
bootloader
andapplication
binaries, hence only those binaries need to be signed. The other binaries (e.g.,partition-table.bin
) can be flashed as they are generated in the build stage.The
bootloader.bin
andapp.bin
binaries can be signed by running:espsecure.py sign_data --version 2 --keyfile secure_boot_signing_key.pem --output bootloader-signed.bin build/bootloader/bootloader.bin espsecure.py sign_data --version 2 --keyfile secure_boot_signing_key.pem --output my-app-signed.bin build/my-app.bin
The signatures attached to a binary can be checked by running:
espsecure.py signature_info_v2 bootloader-signed.bin
The above files along with other binaries (e.g., partition table) can then be flashed to their respective offset using
esptool.py
. To see all of the command line options recommended foresptool.py
, see the output printed whenidf.py build
succeeds. The flash offset for your firmware can be obtained by checking the partition table entry or by runningidf.py partition-table
.Secure the ROM download mode
Warning
Please perform the following step at the very end. After this eFuse is burned, the espefuse tool can no longer be used to burn additional eFuses.
Enable security download mode:
ENABLE_SECURITY_DOWNLOAD
: Enable secure ROM download mode
The eFuse can be burned by running:
espefuse.py --port PORT burn_efuse ENABLE_SECURITY_DOWNLOAD
Secure Boot v2 Guidelines
It is recommended to store the Secure Boot key in a highly secure place. A physical or a cloud HSM may be used for secure storage of the Secure Boot private key. Please take a look at Remote Signing of Images for more details.
Enable NVS Encryption Externally
The details about NVS encryption and related schemes can be found at NVS Encryption.
Enable NVS Encryption Based on Flash Encryption
In this case we generate NVS Encryption keys on a host. This key is then flashed on the chip and protected with the help of Flash Encryption features.
Generate the NVS encryption key
For generation of respective keys, we shall use NVS partition generator utility. We shall generate the encryption key on host and this key shall be stored on the flash of ESP32-C2 in encrypted state.
The key can be generated with the nvs_flash/nvs_partition_generator/nvs_partition_gen.py script with the help of the following command:
python3 nvs_partition_gen.py generate-key --keyfile nvs_encr_key.bin
This shall generate the respective key in the
keys
folder.Generate the encrypted NVS partition
We shall generate the actual encrypted NVS partition on host. More details about generating the encrypted NVS partition can be found at Generate Encrypted NVS Partition.For this, the contents of the NVS file shall be available in a CSV file. Please refer to CSV File Format for more details.
The encrypted NVS partition can be generated with following command:
python3 nvs_partition_gen.py encrypt sample_singlepage_blob.csv nvs_encr_partition.bin 0x3000 --inputkey keys/nvs_encr_key.bin
Some command arguments are explained below:
CSV file name - In this case sample_singlepage_blob.csv is the CSV file which contains the NVS data. Please replace it with the file you wish to choose.
NVS partition offset - This is the offset at which the NVS partition shall be stored in the flash of ESP32-C2. The offset of your NVS partition can be found by executing
idf.py partition-table
in the projtect directory. Please update the sample value of0x3000
in the above-provided command to the correct offset.
Configure the project
Enable NVS Encryption by enabling CONFIG_NVS_ENCRYPTION.
Set NVS to use Flash Encryption based scheme by setting CONFIG_NVS_SEC_KEY_PROTECTION_SCHEME to
CONFIG_NVS_SEC_KEY_PROTECT_USING_FLASH_ENC
.
Flash NVS partition and NVS encryption keys
The NVS partition (
nvs_encr_partition.bin
) and NVS encryption key (nvs_encr_key.bin
) can then be flashed to their respective offset usingesptool.py
. To see all of the command line options recommended foresptool.py
, check the output print whenidf.py build
succeeds.If Flash Encryption is enabled for the chip, then please encrypt the partition first before flashing. You may refer the flashing related steps of Flash Encryption workflow.