The MCUboot Espressif port bootloader is built using the toolchain and tools provided by ESP-IDF. Additional configuration related to MCUboot features and slot partitioning may be made using the `port/<TARGET>/bootloader.conf` file or passing a custom config file using the `-DMCUBOOT_CONFIG_FILE` argument on the first step below.
*This is the basic signing needed for adding MCUboot headers and trailers.
For signing with a crypto key and guarantee the authenticity of the image being booted, see the section [MCUboot image signature verification](#mcuboot-image-signature-verification) below.*
# [Security Chain on Espressif port](#security-chain-on-espressif-port)
[MCUboot encrypted images](encrypted_images.md) do not provide full code confidentiality when only external storage is available (see [Threat model](encrypted_images.md#threat-model)) since by MCUboot design the image in Primary Slot, from where the image is executed, is stored plaintext.
Espressif chips have off-chip flash memory, so to ensure a security chain along with MCUboot image signature verification, the hardware-assisted Secure Boot and Flash Encryption were made available on the MCUboot Espressif port.
The image that MCUboot is booting can be signed with 4 types of keys: RSA-2048, RSA-3072, EC256 and ED25519. In order to enable the feature, the **bootloader** must be compiled with the following configurations:
If signing a Zephyr image, the `--pad-header` is not needed, as it already have the padding for MCUboot header.
## [Secure Boot](#secure-boot)
The Secure Boot implementation is based on [IDF's Secure Boot V2](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/secure-boot-v2.html), is hardware-assisted and RSA based, and has the role for ensuring that only authorized code will be executed on the device. This is done through bootloader signature checking by the ROM bootloader. \
***Note***: ROM bootloader is the First Stage Bootloader, while the Espressif MCUboot port is the Second Stage Bootloader.
### [Building bootloader with Secure Boot](#building-bootloader-with-secure-boot)
In order to build the bootloader with the feature on, the following configurations must be enabled:
```
CONFIG_SECURE_BOOT=1
CONFIG_SECURE_BOOT_V2_ENABLED=1
CONFIG_SECURE_SIGNED_ON_BOOT=1
CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=1
CONFIG_SECURE_BOOT_SUPPORTS_RSA=1
```
---
:warning: ***ATTENTION***
*On development phase is recommended add the following configuration in order to keep the debugging enabled and also to avoid any unrecoverable/permanent state change:*
*You can disable UART Download Mode by adding the following configuration:*
```
CONFIG_SECURE_DISABLE_ROM_DL_MODE=1
```
*This may be suitable for **production** builds. **After disabling UART Download Mode you will not be able to flash other images through UART.***
*Otherwise, you can switch the UART ROM Download Mode to the Secure Download Mode. It will limit the use of Download Mode functions to simple flash read, write and erase operations.*
```
CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE=1
```
*Once the device makes its first full boot, these configurations cannot be reverted*
*Once the bootloader is flashed and the device resets, the **first boot will enable Secure Boot** and the bootloader and key **no longer can be modified**. So **ENSURE** that both bootloader and key are correct and you did not forget anything before flashing.*
---
Flash the bootloader as following, with `--after no_reset` flag, so you can reset the device only when assured:
Secure boot uses a signature block appended to the bootloader image in order to verify the authenticity. The signature block contains the RSA-3072 signature of that image and the RSA-3072 public key.
On its **first boot** the Secure Boot is not enabled on the device eFuses yet, neither the key nor digests. So the first boot will have the following process:
1. On startup, since it is the first boot, the ROM bootloader will not verify the bootloader image (the Secure Boot bit in the eFuse is disabled) yet, so it proceeds to execute it (our MCUboot bootloader port).
2. Bootloader calculates the SHA-256 hash digest of the public key and writes the result to eFuse.
3. Bootloader validates the application images and prepare the booting process (MCUboot phase).
4. Bootloader burns eFuse to enable Secure Boot V2.
5. Bootloader proceeds to load the Primary image.
After that the Secure Boot feature is permanently enabled and on every next boot the ROM bootloader will verify the MCUboot bootloader image.
The process of an usual boot:
1. On startup, the ROM bootloader checks the Secure Boot enable bit in the eFuse. If it is enabled, the boot will proceed as following.
2. ROM bootloader verifies the bootloader's signature block integrity (magic number and CRC). Interrupt boot if it fails.
3. ROM bootloader verifies the bootloader image, interrupt boot if any step fails.: \
3.1. Compare the SHA-256 hash digest of the public key embedded in the bootloader’s signature block with the digest saved in the eFuses. \
3.2. Generate the application image digest and match it with the image digest in the signature block. \
3.3. Use the public key to verify the signature of the bootloader image, using RSA-PSS with the image digest calculated from previous step for comparison.
4. ROM bootloader executes the bootloader image.
5. Bootloader does the usual verification (MCUboot phase).
6. Proceeds to boot the Primary image.
## [Flash Encryption](#flash-encryption)
The Espressif Flash Encryption is hardware-assisted, transparent to the MCUboot process and is an additional security measure beyond MCUboot existent features.
The Flash Encryption implementation is also based on [IDF](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/security/flash-encryption.html) and is intended for encrypting off-chip flash memory contents, so it is protected against physical reading.
When enabling the Flash Encryption, the user can encrypt the content either using a **device generated key** (remains unknown and unreadable) or a **host generated key** (owner is responsible for keeping the key private and safe). After the flash encryption gets enabled through eFuse burning on the device, all read and write operations are decrypted/encrypted in runtime.
### [Building bootloader with Flash Encryption](#building-bootloader-with-flash-encryption)
In order to build the bootloader with the feature on, the following configurations must be enabled:
*On development phase is strongly recommended adding the following configuration in order to keep the debugging enabled and also to avoid any unrecoverable/permanent state change:*
*Unless the recommended flags for **DEVELOPMENT MODE** were enabled, the actions made by Flash Encryption process are **PERMANENT**.* \
*Once the bootloader is flashed and the device resets, the **first boot will enable Flash Encryption, encrypt the flash content including bootloader and image slots, burn the eFuses that no longer can be modified** and if device generated the key **it will not be recoverable**.* \
*When on **RELEASE MODE**, **ENSURE** that the application with an update agent is flashed before reset the device.*
*In the same way as Secure Boot feature, you can disable UART Download Mode by adding the following configuration:*
```
CONFIG_SECURE_DISABLE_ROM_DL_MODE=1
```
*This may be suitable for **production** builds. **After disabling UART Download Mode you will not be able to flash other images through UART.***
*Otherwise, you can switch the UART Download Mode to the Secure Download Mode. It will limit the use of Download Mode functions to simple flash read, write and erase operations.*
```
CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE=1
```
*These configurations cannot be reverted after the device's first boot*
First ensure that the application image is able to perform encrypted read and write operations to the SPI Flash. Also ensure that the **UART ROM Download Mode is not disabled** - or that the **Secure Download Mode is enabled**.
*eFuse emulation in Flash configuration options do not have any effect, so if the key burning command below is used, it will actually burn the physical eFuse.*
espefuse.py --port PORT burn_key BLOCK <FLASH_ENCRYPTION_KEY.bin><KEYPURPOSE>
```
BLOCK is a free keyblock between BLOCK_KEY0 and BLOCK_KEY5. And KEYPURPOSE is either XTS_AES_128_KEY, XTS_AES_256_KEY_1, XTS_AES_256_KEY_2 (AES XTS 256 is available only in ESP32S2).
Now, similar as the Device generated key, the bootloader and application can be flashed plaintext. The **first boot** will encrypt the flash content using the host key burned in the eFuse instead of generate a new one.
OTA updates are required to be sent plaintext. The reason is that, as said before, after the Flash Encryption is enabled all read/write operations are decrypted/encrypted in runtime, so as e.g. if pre-encrypted data is sent for an OTA update, it would be wrongly double-encrypted when the update agent writes to the flash.
For updating with an image encrypted on the host, flash it through serial using `esptool.py` as above. **UART ROM Download Mode must not be disabled**.
Using the 3 features, Secure Boot, Image signature verification and Flash Encryption, a Security Chain can be established so only trusted code is executed, and also the code and content residing in the off-chip flash are protected against undesirable reading.
The overall final process when all features are enabled:
1. ROM bootloader validates the MCUboot bootloader using RSA signature verification.
2. MCUboot bootloader validates the image using the chosen algorithm EC256/RSA/ED25519. It also validates an upcoming image when updating.
3. Flash Encryption guarantees that code and data are not exposed.
When all 3 features are enable at same time, the bootloader size may exceed the fixed limit for the ROM bootloader checking on the Espressif chips **depending on which algorithm** was chosen for MCUboot image signing. The issue https://github.com/mcu-tools/mcuboot/issues/1262 was created to track this limitation.
## [Multi image](#multi-image)
The multi image feature (currently limited to 2 images) allows the images to be updated separately (each one has its own primary and secondary slot) by MCUboot.
The Espressif port bootloader handles the boot in two different approaches:
### [Host OS boots second image](#host-os-boots-second-image)
Host OS from the *first image* is responsible for booting the *second image*, therefore the bootloader is aware of the second image regions and can update it, however it does not load neither boots it.
Configuration example (`bootloader.conf`):
```
CONFIG_ESP_BOOTLOADER_SIZE=0xF000
CONFIG_ESP_MCUBOOT_WDT_ENABLE=y
# Enables multi image, if it is not defined, its assumed
# only one updatable image
CONFIG_ESP_IMAGE_NUMBER=2
# Example of values to be used when multi image is enabled
# Notice that the OS layer and update agent must be aware
In the multi boot approach the bootloader is responsible for booting two different images in two different CPUs, firstly the *second image* on the APP CPU and then the *first image* on the PRO CPU (current CPU), it is also responsible for update both images as well. Thus multi boot will be only supported by Espressif multi core chips - currently only ESP32 is implemented.
---
***Note***
*The host OSes in each CPU must handle how the resources are divided/controlled between then.*
---
Configuration example:
```
CONFIG_ESP_BOOTLOADER_SIZE=0xF000
CONFIG_ESP_MCUBOOT_WDT_ENABLE=y
# Enables multi image, if it is not defined, its assumed
# only one updatable image
CONFIG_ESP_IMAGE_NUMBER=2
# Enables multi image boot on independent processors
# (main host OS is not responsible for booting the second image)
# Use only with CONFIG_ESP_IMAGE_NUMBER=2
CONFIG_ESP_MULTI_PROCESSOR_BOOT=y
# Example of values to be used when multi image is enabled
# Notice that the OS layer and update agent must be aware
### [Image version dependency](#image-version-dependency)
MCUboot allows version dependency check between the images when updating them. As `imgtool.py` allows a version assigment when signing an image, it is also possible to add the version dependency constraint:
Serial recovery mode allows management through MCUMGR (more information and how to install it: https://github.com/apache/mynewt-mcumgr-cli) for communicating and uploading a firmware to the device.
# Enables the MCUboot Serial Recovery, that allows the use of
# MCUMGR to upload a firmware through the serial port
CONFIG_ESP_MCUBOOT_SERIAL=y
# GPIO used to boot on Serial Recovery
CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=32
# GPIO input type (0 for Pull-down, 1 for Pull-up)
CONFIG_ESP_SERIAL_BOOT_GPIO_INPUT_TYPE=0
# GPIO signal value
CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL=1
# Delay time for identify the GPIO signal
CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S=5
# UART port used for serial communication
CONFIG_ESP_SERIAL_BOOT_UART_NUM=1
# GPIO for Serial RX signal
CONFIG_ESP_SERIAL_BOOT_GPIO_RX=25
# GPIO for Serial TX signal
CONFIG_ESP_SERIAL_BOOT_GPIO_TX=26
```
When enabled, the bootloader checks the if the GPIO `<CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT>` configured has the signal value `<CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT_VAL>` for approximately `<CONFIG_ESP_SERIAL_BOOT_DETECT_DELAY_S>` seconds for entering the Serial recovery mode. Example: a button configured on GPIO 32 pressed for 5 seconds.
Serial mode then uses the UART port configured for communication (`<CONFIG_ESP_SERIAL_BOOT_UART_NUM>`, pins `<CONFIG_ESP_SERIAL_BOOT_GPIO_RX>`, `<CONFIG_ESP_SERIAL_BOOT_GPIO_RX>`).
### [Serial Recovery through USB JTAG Serial port](#serial-recovery-through-usb-jtag-serial-port)
Some chips, like ESP32-C3, have an integrated USB JTAG Serial Controller that implements a serial port (CDC) that can also be used for handling MCUboot Serial Recovery.
More information about the USB pins and hardware configuration on ESP32-C3: https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-guides/usb-serial-jtag-console.html.
Configuration example:
```
# Use Serial through USB JTAG Serial port for Serial Recovery
CONFIG_ESP_MCUBOOT_SERIAL_USB_SERIAL_JTAG=y
# Use sector erasing (recommended) instead of entire image size
# erasing when uploading through Serial Recovery
CONFIG_ESP_MCUBOOT_ERASE_PROGRESSIVELY=y
# GPIO used to boot on Serial Recovery
CONFIG_ESP_SERIAL_BOOT_GPIO_DETECT=5
# GPIO input type (0 for Pull-down, 1 for Pull-up)
Upload the image (the process may take some time):
```
mcumgr -c esp image upload <IMAGE_BIN>
```
Reset the device:
```
mcumgr -c esp reset
```
---
:warning: ***ATTENTION***
*Serial recovery mode uploads the image to the PRIMARY_SLOT, therefore if the upload process gets interrupted the image may be corrupted and unable to boot*