This prevents a device from being bricked in case when both secure boot & flash encryption are enabled and encryption gets interrupted during first boot. After interruption, all partitions on the device need to be reflashed (including the bootloader).
List of changes:
* Secure boot key generation and bootloader digest generation logic, implemented inside function esp_secure_boot_permanently_enable(), has been pulled out into new API esp_secure_boot_generate_digest(). The enabling of R/W protection of secure boot key on EFUSE still happens inside esp_secure_boot_permanently_enable()
* Now esp_secure_boot_permanently_enable() is called only after flash encryption process completes
* esp_secure_boot_generate_digest() is called before flash encryption process starts
return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0;
}
+/** @brief Generate secure digest from bootloader image
+ *
+ * @important This function is intended to be called from bootloader code only.
+ *
+ * If secure boot is not yet enabled for bootloader, this will:
+ * 1) generate the secure boot key and burn it on EFUSE
+ * (without enabling R/W protection)
+ * 2) generate the digest from bootloader and save it
+ * to flash address 0x0
+ *
+ * If first boot gets interrupted after calling this function
+ * but before esp_secure_boot_permanently_enable() is called, then
+ * the key burned on EFUSE will not be regenerated, unless manually
+ * done using espefuse.py tool
+ *
+ * @return ESP_OK if secure boot digest is generated
+ * successfully or found to be already present
+ */
+esp_err_t esp_secure_boot_generate_digest(void);
/** @brief Enable secure boot if it is not already enabled.
*
*
* @important This function is intended to be called from bootloader code only.
*
+ * @important This will enable r/w protection of secure boot key on EFUSE,
+ * therefore it is to be ensured that esp_secure_boot_generate_digest()
+ * is called before this
+ *
* If secure boot is not yet enabled for bootloader, this will
- * generate the secure boot digest and enable secure boot by blowing
- * the EFUSE_RD_ABS_DONE_0 efuse.
+ * 1) enable R/W protection of secure boot key on EFUSE
+ * 2) enable secure boot by blowing the EFUSE_RD_ABS_DONE_0 efuse.
*
* This function does not verify secure boot of the bootloader (the
* ROM bootloader does this.)
* Will fail if efuses have been part-burned in a way that indicates
* secure boot should not or could not be correctly enabled.
*
- *
* @return ESP_ERR_INVALID_STATE if efuse state doesn't allow
* secure boot to be enabled cleanly. ESP_OK if secure boot
* is enabled on this chip from now on.
// Copy loaded segments to RAM, set up caches for mapped segments, and start application.
static void load_image(const esp_image_metadata_t* image_data)
{
+ /**
+ * Rough steps for a first boot, when encryption and secure boot are both disabled:
+ * 1) Generate secure boot key and write to EFUSE.
+ * 2) Write plaintext digest based on plaintext bootloader
+ * 3) Generate flash encryption key and write to EFUSE.
+ * 4) Encrypt flash in-place including bootloader, then digest,
+ * then app partitions and other encrypted partitions
+ * 5) Burn EFUSE to enable flash encryption (FLASH_CRYPT_CNT)
+ * 6) Burn EFUSE to enable secure boot (ABS_DONE_0)
+ *
+ * If power failure happens during Step 1, probably the next boot will continue from Step 2.
+ * There is some small chance that EFUSEs will be part-way through being written so will be
+ * somehow corrupted here. Thankfully this window of time is very small, but if that's the
+ * case, one has to use the espefuse tool to manually set the remaining bits and enable R/W
+ * protection. Once the relevant EFUSE bits are set and R/W protected, Step 1 will be skipped
+ * successfully on further reboots.
+ *
+ * If power failure happens during Step 2, Step 1 will be skipped and Step 2 repeated:
+ * the digest will get re-written on the next boot.
+ *
+ * If power failure happens during Step 3, it's possible that EFUSE was partially written
+ * with the generated flash encryption key, though the time window for that would again
+ * be very small. On reboot, Step 1 will be skipped and Step 2 repeated, though, Step 3
+ * may fail due to the above mentioned reason, in which case, one has to use the espefuse
+ * tool to manually set the remaining bits and enable R/W protection. Once the relevant EFUSE
+ * bits are set and R/W protected, Step 3 will be skipped successfully on further reboots.
+ *
+ * If power failure happens after start of 4 and before end of 5, the next boot will fail
+ * (bootloader header is encrypted and flash encryption isn't enabled yet, so it looks like
+ * noise to the ROM bootloader). The check in the ROM is pretty basic so if the first byte of
+ * ciphertext happens to be the magic byte E9 then it may try to boot, but it will definitely
+ * crash (no chance that the remaining ciphertext will look like a valid bootloader image).
+ * Only solution is to reflash with all plaintext and the whole process starts again: skips
+ * Step 1, repeats Step 2, skips Step 3, etc.
+ *
+ * If power failure happens after 5 but before 6, the device will reboot with flash
+ * encryption on and will regenerate an encrypted digest in Step 2. This should still
+ * be valid as the input data for the digest is read via flash cache (so will be decrypted)
+ * and the code in secure_boot_generate() tells bootloader_flash_write() to encrypt the data
+ * on write if flash encryption is enabled. Steps 3 - 5 are skipped (encryption already on),
+ * then Step 6 enables secure boot.
+ */
+
#if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_FLASH_ENCRYPTION_ENABLED)
esp_err_t err;
#endif
+
#ifdef CONFIG_SECURE_BOOT_ENABLED
- /* Generate secure digest from this bootloader to protect future
- modifications */
- ESP_LOGI(TAG, "Checking secure boot...");
- err = esp_secure_boot_permanently_enable();
+ /* Steps 1 & 2 (see above for full description):
+ * 1) Generate secure boot EFUSE key
+ * 2) Compute digest of plaintext bootloader
+ */
+ err = esp_secure_boot_generate_digest();
if (err != ESP_OK) {
- ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err);
- /* Allow booting to continue, as the failure is probably
- due to user-configured EFUSEs for testing...
- */
+ ESP_LOGE(TAG, "Bootloader digest generation for secure boot failed (%d).", err);
+ return;
}
#endif
#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED
- /* encrypt flash */
+ /* Steps 3, 4 & 5 (see above for full description):
+ * 3) Generate flash encryption EFUSE key
+ * 4) Encrypt flash contents
+ * 5) Burn EFUSE to enable flash encryption
+ */
ESP_LOGI(TAG, "Checking flash encryption...");
bool flash_encryption_enabled = esp_flash_encryption_enabled();
err = esp_flash_encrypt_check_and_update();
ESP_LOGE(TAG, "Flash encryption check failed (%d).", err);
return;
}
+#endif
+
+#ifdef CONFIG_SECURE_BOOT_ENABLED
+ /* Step 6 (see above for full description):
+ * 6) Burn EFUSE to enable secure boot
+ */
+ ESP_LOGI(TAG, "Checking secure boot...");
+ err = esp_secure_boot_permanently_enable();
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "FAILED TO ENABLE SECURE BOOT (%d).", err);
+ /* Allow booting to continue, as the failure is probably
+ due to user-configured EFUSEs for testing...
+ */
+ }
+#endif
+#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED
if (!flash_encryption_enabled && esp_flash_encryption_enabled()) {
/* Flash encryption was just enabled for the first time,
so issue a system reset to ensure flash encryption
return err;
}
- if (esp_secure_boot_enabled()) {
- /* If secure boot is enabled and bootloader was plaintext, also
- need to encrypt secure boot IV+digest.
- */
- ESP_LOGD(TAG, "Encrypting secure bootloader IV & digest...");
- err = esp_flash_encrypt_region(FLASH_OFFS_SECURE_BOOT_IV_DIGEST,
- FLASH_SECTOR_SIZE);
- if (err != ESP_OK) {
- ESP_LOGE(TAG, "Failed to encrypt bootloader IV & digest in place: 0x%x", err);
- return err;
- }
+#ifdef CONFIG_SECURE_BOOT_ENABLED
+ /* If secure boot is enabled and bootloader was plaintext, also
+ * need to encrypt secure boot IV+digest.
+ */
+ ESP_LOGD(TAG, "Encrypting secure bootloader IV & digest...");
+ err = esp_flash_encrypt_region(FLASH_OFFS_SECURE_BOOT_IV_DIGEST,
+ FLASH_SECTOR_SIZE);
+ if (err != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to encrypt bootloader IV & digest in place: 0x%x", err);
+ return err;
}
+#endif
}
else {
ESP_LOGW(TAG, "no valid bootloader was found");
#include "esp_flash_encrypt.h"
#include "esp_efuse.h"
+/* The following API implementations are used only when called
+ * from the bootloader code.
+ */
+
+#ifdef BOOTLOADER_BUILD
+
static const char* TAG = "secure_boot";
/**
#endif
}
-esp_err_t esp_secure_boot_permanently_enable(void) {
+esp_err_t esp_secure_boot_generate_digest(void)
+{
esp_err_t err;
- if (esp_secure_boot_enabled())
- {
- ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing..");
+ if (esp_secure_boot_enabled()) {
+ ESP_LOGI(TAG, "bootloader secure boot is already enabled."
+ " No need to generate digest. continuing..");
return ESP_OK;
}
return err;
}
+ /* Generate secure boot key and keep in EFUSE */
uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG);
bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2;
bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2;
ESP_LOGI(TAG, "Generating new secure boot key...");
esp_efuse_write_random_key(EFUSE_BLK2_WDATA0_REG);
burn_efuses();
- ESP_LOGI(TAG, "Read & write protecting new key...");
- REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2);
- burn_efuses();
- efuse_key_read_protected = true;
- efuse_key_write_protected = true;
-
} else {
ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2");
}
+ /* Generate secure boot digest using programmed key in EFUSE */
ESP_LOGI(TAG, "Generating secure boot digest...");
uint32_t image_len = bootloader_data.image_len;
if(bootloader_data.image.hash_appended) {
}
ESP_LOGI(TAG, "Digest generation complete.");
+ return ESP_OK;
+}
+
+esp_err_t esp_secure_boot_permanently_enable(void)
+{
+ if (esp_secure_boot_enabled()) {
+ ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing..");
+ return ESP_OK;
+ }
+
+ uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG);
+ bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2;
+ bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2;
+ if (efuse_key_read_protected == false
+ && efuse_key_write_protected == false) {
+ ESP_LOGI(TAG, "Read & write protecting new key...");
+ REG_WRITE(EFUSE_BLK0_WDATA0_REG, EFUSE_WR_DIS_BLK2 | EFUSE_RD_DIS_BLK2);
+ burn_efuses();
+ efuse_key_read_protected = true;
+ efuse_key_write_protected = true;
+ }
+
#ifndef CONFIG_SECURE_BOOT_TEST_MODE
if (!efuse_key_read_protected) {
ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse.");
return ESP_ERR_INVALID_STATE;
}
}
+
+#endif // #ifdef BOOTLOADER_BUILD