]> granicus.if.org Git - esp-idf/commitdiff
Enable secure boot only after encrypting flash
authorAnurag Kar <anurag.kar@espressif.com>
Thu, 4 Apr 2019 09:55:22 +0000 (15:25 +0530)
committerAnurag Kar <anurag.kar@espressif.com>
Mon, 6 May 2019 06:04:12 +0000 (11:34 +0530)
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

components/bootloader_support/include/esp_secure_boot.h
components/bootloader_support/src/bootloader_utility.c
components/bootloader_support/src/flash_encrypt.c
components/bootloader_support/src/secure_boot.c

index 370bdd363328fd2c6ee362ccf3193b60373dddcb..17b405e4fabd844a563cf54338de70a429580ed4 100644 (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;
 }
 
+/** @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.
index 720294fa48a259ce66e853f61c6d1933a28c8f27..9aa0b9c8677a6b77364a27319e9d61eba9094f6b 100644 (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.
 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
index 8d5955eff74a5f715cfdd0e0877fb6bbd8e5b589..f9d7bfdb3a5f9df74c67187dd70f35192f2f6d88 100644 (file)
@@ -225,18 +225,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");
index 64bf07180368ef086361199f9a8eae978f6de0d9..d0cf9a1415e29c11751fe1c3aaed82de8353a152 100644 (file)
 #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