Enable secure boot only after encrypting flash

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
This commit is contained in:
Anurag Kar 2019-04-04 15:25:22 +05:30
parent a9425cd045
commit 62b0d51c02
4 changed files with 145 additions and 33 deletions

View file

@ -46,6 +46,25 @@ static inline bool esp_secure_boot_enabled(void) {
return REG_READ(EFUSE_BLK0_RDATA6_REG) & EFUSE_RD_ABS_DONE_0; 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. /** @brief Enable secure boot if it is not already enabled.
* *
@ -54,9 +73,13 @@ static inline bool esp_secure_boot_enabled(void) {
* *
* @important This function is intended to be called from bootloader code only. * @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 * If secure boot is not yet enabled for bootloader, this will
* generate the secure boot digest and enable secure boot by blowing * 1) enable R/W protection of secure boot key on EFUSE
* the EFUSE_RD_ABS_DONE_0 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 * This function does not verify secure boot of the bootloader (the
* ROM bootloader does this.) * ROM bootloader does this.)
@ -64,7 +87,6 @@ static inline bool esp_secure_boot_enabled(void) {
* Will fail if efuses have been part-burned in a way that indicates * Will fail if efuses have been part-burned in a way that indicates
* secure boot should not or could not be correctly enabled. * secure boot should not or could not be correctly enabled.
* *
*
* @return ESP_ERR_INVALID_STATE if efuse state doesn't allow * @return ESP_ERR_INVALID_STATE if efuse state doesn't allow
* secure boot to be enabled cleanly. ESP_OK if secure boot * secure boot to be enabled cleanly. ESP_OK if secure boot
* is enabled on this chip from now on. * is enabled on this chip from now on.

View file

@ -478,24 +478,71 @@ void bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_
// Copy loaded segments to RAM, set up caches for mapped segments, and start application. // 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) 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) #if defined(CONFIG_SECURE_BOOT_ENABLED) || defined(CONFIG_FLASH_ENCRYPTION_ENABLED)
esp_err_t err; esp_err_t err;
#endif #endif
#ifdef CONFIG_SECURE_BOOT_ENABLED #ifdef CONFIG_SECURE_BOOT_ENABLED
/* Generate secure digest from this bootloader to protect future /* Steps 1 & 2 (see above for full description):
modifications */ * 1) Generate secure boot EFUSE key
ESP_LOGI(TAG, "Checking secure boot..."); * 2) Compute digest of plaintext bootloader
err = esp_secure_boot_permanently_enable(); */
err = esp_secure_boot_generate_digest();
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); ESP_LOGE(TAG, "Bootloader digest generation for secure boot failed (%d).", err);
/* Allow booting to continue, as the failure is probably return;
due to user-configured EFUSEs for testing...
*/
} }
#endif #endif
#ifdef CONFIG_FLASH_ENCRYPTION_ENABLED #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..."); ESP_LOGI(TAG, "Checking flash encryption...");
bool flash_encryption_enabled = esp_flash_encryption_enabled(); bool flash_encryption_enabled = esp_flash_encryption_enabled();
err = esp_flash_encrypt_check_and_update(); err = esp_flash_encrypt_check_and_update();
@ -503,7 +550,23 @@ static void load_image(const esp_image_metadata_t* image_data)
ESP_LOGE(TAG, "Flash encryption check failed (%d).", err); ESP_LOGE(TAG, "Flash encryption check failed (%d).", err);
return; 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()) { if (!flash_encryption_enabled && esp_flash_encryption_enabled()) {
/* Flash encryption was just enabled for the first time, /* Flash encryption was just enabled for the first time,
so issue a system reset to ensure flash encryption so issue a system reset to ensure flash encryption

View file

@ -224,18 +224,18 @@ static esp_err_t encrypt_bootloader()
return err; return err;
} }
if (esp_secure_boot_enabled()) { #ifdef CONFIG_SECURE_BOOT_ENABLED
/* If secure boot is enabled and bootloader was plaintext, also /* If secure boot is enabled and bootloader was plaintext, also
need to encrypt secure boot IV+digest. * need to encrypt secure boot IV+digest.
*/ */
ESP_LOGD(TAG, "Encrypting secure bootloader IV & digest..."); ESP_LOGD(TAG, "Encrypting secure bootloader IV & digest...");
err = esp_flash_encrypt_region(FLASH_OFFS_SECURE_BOOT_IV_DIGEST, err = esp_flash_encrypt_region(FLASH_OFFS_SECURE_BOOT_IV_DIGEST,
FLASH_SECTOR_SIZE); FLASH_SECTOR_SIZE);
if (err != ESP_OK) { if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to encrypt bootloader IV & digest in place: 0x%x", err); ESP_LOGE(TAG, "Failed to encrypt bootloader IV & digest in place: 0x%x", err);
return err; return err;
}
} }
#endif
} }
else { else {
ESP_LOGW(TAG, "no valid bootloader was found"); ESP_LOGW(TAG, "no valid bootloader was found");

View file

@ -36,6 +36,12 @@
#include "esp_flash_encrypt.h" #include "esp_flash_encrypt.h"
#include "esp_efuse.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"; static const char* TAG = "secure_boot";
/** /**
@ -102,11 +108,12 @@ static inline void burn_efuses()
#endif #endif
} }
esp_err_t esp_secure_boot_permanently_enable(void) { esp_err_t esp_secure_boot_generate_digest(void)
{
esp_err_t err; esp_err_t err;
if (esp_secure_boot_enabled()) if (esp_secure_boot_enabled()) {
{ ESP_LOGI(TAG, "bootloader secure boot is already enabled."
ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing.."); " No need to generate digest. continuing..");
return ESP_OK; return ESP_OK;
} }
@ -124,6 +131,7 @@ esp_err_t esp_secure_boot_permanently_enable(void) {
return err; return err;
} }
/* Generate secure boot key and keep in EFUSE */
uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG); uint32_t dis_reg = REG_READ(EFUSE_BLK0_RDATA0_REG);
bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2; bool efuse_key_read_protected = dis_reg & EFUSE_RD_DIS_BLK2;
bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2; bool efuse_key_write_protected = dis_reg & EFUSE_WR_DIS_BLK2;
@ -140,16 +148,11 @@ esp_err_t esp_secure_boot_permanently_enable(void) {
ESP_LOGI(TAG, "Generating new secure boot key..."); ESP_LOGI(TAG, "Generating new secure boot key...");
esp_efuse_write_random_key(EFUSE_BLK2_WDATA0_REG); esp_efuse_write_random_key(EFUSE_BLK2_WDATA0_REG);
burn_efuses(); 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 { } else {
ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2"); 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..."); ESP_LOGI(TAG, "Generating secure boot digest...");
uint32_t image_len = bootloader_data.image_len; uint32_t image_len = bootloader_data.image_len;
if(bootloader_data.image.hash_appended) { if(bootloader_data.image.hash_appended) {
@ -162,6 +165,28 @@ esp_err_t esp_secure_boot_permanently_enable(void) {
} }
ESP_LOGI(TAG, "Digest generation complete."); 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 #ifndef CONFIG_SECURE_BOOT_TEST_MODE
if (!efuse_key_read_protected) { if (!efuse_key_read_protected) {
ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse."); ESP_LOGE(TAG, "Pre-loaded key is not read protected. Refusing to blow secure boot efuse.");
@ -208,3 +233,5 @@ esp_err_t esp_secure_boot_permanently_enable(void) {
return ESP_ERR_INVALID_STATE; return ESP_ERR_INVALID_STATE;
} }
} }
#endif // #ifdef BOOTLOADER_BUILD