Secure Boot V1 and its implementation in ESP-IDF

03/09/2021

Secure Boot is a method of securing the boot process for ESP32 microcontrollers against untrusted firmware and bootloader, which can be physically loaded into flash memory via the UART interface or in an update. Secure Boot combines the method of verifying the digital signature of the firmware in the boot process and also in the process of remote OTA (Over-The-Air) firmware update.

This verification is performed by the software bootloader with a public key that is inserted into it during the compilation process. The firmware is signed with a private key. This key is generated for the NIST256p elliptic curve and is 256 bits long. The ECDSA signature scheme is used to sign the firmware itself. At the same time, Secure Boot is enhanced by verifying the software bootloader (Second-Stage bootloader) before booting the firmware. This functionality is handled by a hardware bootloader (First-Stage bootloader) stored in ROM memory.

In principle, this involves calculating the fingerprint of the software bootloader written in flash memory at offset 0x1000 and comparing it with the reference from offset 0x0. An AES key (used only for encryption operation) with a length of 256 bits is used to calculate the fingerprint. The AES key is stored in the one-time programmable memory of the eFuse BLK2 ESP32 microcontroller with a size of 256 bits, from where it cannot be read / overwritten by software.

The purpose of the fingerprint calculation and subsequent verification is to verify that the software bootloader in the flash memory is trusted and matches the fingerprint that is stored in the flash memory on the predefined offset 0x0 as a reference. It guarantees that the software bootloader is from a trusted publisher who has also been able to create an fingerprint for this bootloader using the reference AES encryption key (the same is written in eFuse BLK2), which is available locally on its machine where it develops firmware.

If Secure Boot is not implemented, it could result in an unauthenticated firmware that could be tampered with by an attacker, along with a modified bootloader that would ignore the firmware's digital signature. Secure Boot can be enabled via Menuconfig in the ESP-IDF console application in the "Security Features" submenu. Failed bootloader authentication can also be caused by a missing fingerprint on the expected offset 0x0 in flash memory.

The whole process of this method uses the principle of the Chain of Trust. If the software bootloader is not verified, neither the firmware boot nor the digital signature is verified. The ESP32 microcontroller restarts in an infinite loop and prints a cyclic dump of the failed bootloader authentication each time ESP32 is started. Because the bootloader has not been validated, no attempt will be made to boot the firmware from the available partitions. The prerequisite for the possibility of access to the next phase of the boot process for booting firmware from application partitions is not fulfilled (see the figure below).

From the point of view of Secure Boot operation, it is possible to choose one of its two supported implementations in the ESP-IDF environment - "Re-flashable", which is suitable for application development and allows you to load AES encryption key multiple times into a certain part of flash memory. uses. This mode is not suitable for production (final) applications, as the attacker can obtain the AES key after reading the flash memory because the key used for encryption is not protected in eFuse, where only the hardware bootloader can access the key. The second implementation of the method that can be used is "One-Time Flash". This type is suitable for production applications with a one-time entry of the AES encryption key into the eFuse BLK2.

At the same time, the use of this functionality is permanent without the possibility of turning it off in the future. The following subchapter explains the implementation in the ESP-IDF environment for the "One-Time Flash" implementation of Secure Boot. The figure below shows the basic settings of the "Security Features" submenu in Menuconfig for the production version of Secure Boot using "One-Time Flash". As can be seen from the configuration menu, the firmware is not signed during the compilation process, as you can choose to automatically insert the public key into the software bootloader (used to verify the firmware digital signature) during the compilation process, or sign the firmware, not both.

For this reason, the firmware signing is performed manually in the ESP-IDF console application using the Python tool espsecure.py. However, enabling this functionality via Menuconfig in the "Security Features" section is not yet fully active, as it must be enabled manually by writing the AES encryption key to the eFuse BLK2 and writing a bit to the acknowledgment 1-bit eFuse ABS_DONE_0, which will permanently enable this method. I used Secure Boot in version V1, whose functionality is described in this chapter.

This hardware functionality also exists in version V2, which, however, is tied to more modern ESP32 platform models from production in recent years (eg ESP32-S2, ESP32-C6), which use eFuse ABS_DONE_1 to run Secure Boot V2. The ESP32-WROOM-32 microcontroller used does not support this method of the second version, even though eFuse has ABS_DONE_1 available.


Encryption key generation in ESP-IDF

The AES encryption key for Secure Boot must first be generated. The cryptographic tool espsecure.py can be used to generate the key. Using the espsecure.py generate_ generate flash_encryption_key secure-bootloader-key-256.bin command, I generated a 256-bit key using the randomness of the operating system. The os.random () function in Python is used, which increases the entropy - the randomness of the key, as it uses more factors of the operating system on which the ESP-IDF console application is running.

The key generated in this way must be written to the one-time programmable memory eFuse BLK2 with a size of 256 bits, which is intended for this key. To work with eFuses, there is a tool espefuse.py in ESP-IDF, which is also called eFuse manager. It allows you to work with all eFuses that are available in ESP32. Said one-time programmable eFuse BLK2 is a special system eFuse with a size of 256 bits into which the AES encryption key for Secure Boot functionality is written.

Using espefuse.py, I wrote the AES encryption key to eFuse BLK2 using the command espefuse.py burn_key secure_boot secure-bootloader-key-256.bin. Each eFuse write command must be confirmed by writing the value (text) "BURN" to the ESP-IDF console application to write. From now on, the eFuse BLK2 is protected from software writing and reading. To permanently enable Secure Boot, it is also necessary to write the confirmation bit to eFuse ABS_DONE_0.

I wrote with the command espefuse.py burn_efuse ABS_DONE_0. A potential attacker cannot retrieve the key stored in this eFuse because it does not have access to any software tools. Only the hardware or the Secure Boot hardware functionality can access the eFuse BLK2 in question via the hardware bootloader.

Subsequently, the functionality ensures the verification of the software bootloader of the ESP32 microcontroller written in the flash memory on the predefined offset 0x1000 when attempting to boot. In order to verify the software bootloader and thus ensure the firmware boot process, it is necessary to generate a digest, which is the output of the Secure Bootloader Digest Algorithm (SBDA) and write it to flash memory at offset 0x0, where Secure Boot expects it and uses it. as a reference for verification.


SBDA algorithm

The SBDA algorithm is run by a hardware bootloader. Performs reading of the AES encryption key from the eFuse BLK2 in reverse bit representation and the image of the software bootloader from the flash memory from offset 0x1000. The bootloader image is preceded by a 128-byte generated initialization vector. The algorithm aligns the bootloader image modulo 128 and adds 0xFF (hexadecimal value) to the image representation to reach a length of 128 bytes.

For every 16 bytes of open text of the bootloader image, the AES256 block cipher is applied in ECB mode using the 256-bit AES encryption key from the eFuse BLK2. The resulting encrypted text has a reverse bit representation. The algorithm exchanges the byte of each 4-byte word of the encrypted text, calculates the SHA-512 hash value of the resulting encrypted text. The output is a 192-byte string that consists of a 128-byte initialization vector and a 64-byte SHA-512 hash value from the encrypted text.

The fingerprint can also be generated locally by the SBDA algorithm using the espsecure.py tool. The principle of the SBDA algorithm is identical, we will use the locally available generated AES encryption key "securebootloader-key-256.bin", which we entered into eFuse in the previous step. The espsecure.py digest_secure_bootloader --keyfile secure-bootloader-key-256.bin --output ./bootloader-digest.bin build / bootloader / bootloader.bin command generates a "bootloader-digest.bin" fingerprint that can be written to flash memory to a predefined offset 0x0.

The command has an input - AES encryption key "secure-bootloader-key-256.bin" with a length of 256 bits in binary format (the same was written to eFuse BLK2) and a bootloader image "bootloader.bin" from the build folder in the Native OTA project (with support for remote firmware updates). To generate a fingerprint, the developer must have an encryption key and is responsible for archiving and protecting it. When writing a fingerprint to flash memory, it is necessary to select the target offset to 0x0, where it is expected by the hardware bootloader as a reference for comparing the calculated fingerprint of the software bootloader.

I wrote the fingerprint to the flash memory using the command: esptool.py write_flash 0x0 bootloaderdigest.bin. After writing the fingerprint to the flash memory and successfully verifying the bootloader fingerprint, it is possible to proceed to the next phase of the boot process - booting the firmware. When the microcontroller is started, a hardware fingerprint calculation is performed, when the hardware (ROM bootloader) reads the contents of the eFuse BLK2 and its image is taken from the known offset where the software bootloader (0x1000) is written.

The SBDA algorithm is executed and the resulting fingerprint is compared by the hardware bootloader with the fingerprint stored in flash memory at offset 0x0, which serves as a reference. If both fingerprints are identical, the hardware bootloader will allow the software bootloader to start and it will boot the firmware (with verification of its digital signature at boot, but also at remote firmware update...). If the fingerprints are different, booting is prohibited by the Secure Boot hardware functionality.

This method of protection is effective if an attacker gains physical access to the microcontroller and tries to run his program on the microcontroller. In the process of uploading firmware via the USB-UART interface, the software bootloader, the partition table based on the configuration of the environment from which the firmware upload is performed, is also overwritten. The calculated fingerprint will not match the fingerprint written on offset 0x0 (provided it is not overwritten) when uploading the firmware.

If an attacker could only write firmware to a specific offset and also overwrite the flag in the OTA_DATA partitions, the firmware would not be booted because it is not digitally signed with a key that the attacker does not have. Effectively, Secure Boot is complemented by Flash Encryption functionality in production, which allows you to encrypt all application partitions, the reference fingerprint, and the software bootloader image so that this flash memory content is not obtained by potential attackers with the ability to parse the AES encryption key.

© 2021 Martin Chlebovec - Arduino Blog - . All rights reserved
Powered by Webnode Cookies
Create your website for free! This website was made with Webnode. Create your own for free today! Get started