From 62b0d51c0261240b44606594d0b71a38d30377e1 Mon Sep 17 00:00:00 2001 From: Anurag Kar Date: Thu, 4 Apr 2019 15:25:22 +0530 Subject: [PATCH] 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 --- .../include/esp_secure_boot.h | 28 ++++++- .../src/bootloader_utility.c | 81 ++++++++++++++++--- .../bootloader_support/src/flash_encrypt.c | 22 ++--- .../bootloader_support/src/secure_boot.c | 47 ++++++++--- 4 files changed, 145 insertions(+), 33 deletions(-) diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index 370bdd363..17b405e4f 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -46,6 +46,25 @@ static inline bool esp_secure_boot_enabled(void) { 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. * @@ -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 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.) @@ -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 * 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. diff --git a/components/bootloader_support/src/bootloader_utility.c b/components/bootloader_support/src/bootloader_utility.c index a9dbd553d..863eaa098 100644 --- a/components/bootloader_support/src/bootloader_utility.c +++ b/components/bootloader_support/src/bootloader_utility.c @@ -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. 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(); @@ -503,7 +550,23 @@ static void load_image(const esp_image_metadata_t* image_data) 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 diff --git a/components/bootloader_support/src/flash_encrypt.c b/components/bootloader_support/src/flash_encrypt.c index 723441910..6ca918211 100644 --- a/components/bootloader_support/src/flash_encrypt.c +++ b/components/bootloader_support/src/flash_encrypt.c @@ -224,18 +224,18 @@ static esp_err_t encrypt_bootloader() 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"); diff --git a/components/bootloader_support/src/secure_boot.c b/components/bootloader_support/src/secure_boot.c index 1972ebc32..b60a67427 100644 --- a/components/bootloader_support/src/secure_boot.c +++ b/components/bootloader_support/src/secure_boot.c @@ -36,6 +36,12 @@ #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"; /** @@ -102,11 +108,12 @@ static inline void burn_efuses() #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; } @@ -124,6 +131,7 @@ esp_err_t esp_secure_boot_permanently_enable(void) { 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; @@ -140,16 +148,11 @@ esp_err_t esp_secure_boot_permanently_enable(void) { 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) { @@ -162,6 +165,28 @@ esp_err_t esp_secure_boot_permanently_enable(void) { } 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."); @@ -208,3 +233,5 @@ esp_err_t esp_secure_boot_permanently_enable(void) { return ESP_ERR_INVALID_STATE; } } + +#endif // #ifdef BOOTLOADER_BUILD