]> granicus.if.org Git - esp-idf/commitdiff
bootloader: Support for skipping validation upon wake from deep sleep
authorTim Nordell <tim.nordell@nimbelink.com>
Fri, 12 Apr 2019 20:32:47 +0000 (15:32 -0500)
committerbot <bot@espressif.com>
Wed, 21 Aug 2019 11:44:37 +0000 (11:44 +0000)
This saves time when waking up from deep sleep, but potentially decreases
the security of the system.  If the application able to modify itself
(especially areas that are loaded into RAM) in flash while running
without crashing or is modifies the cached bits of information about
what was last booted from the bootloader, this could cause security
issues if the user does a "deep sleep reset" since the full validation
is skipped.

Signed-off-by: Tim Nordell <tim.nordell@nimbelink.com>
components/bootloader/Kconfig.projbuild
components/bootloader_support/include/esp_image_format.h
components/bootloader_support/src/bootloader_utility.c
components/bootloader_support/src/esp_image_format.c
components/esp32/ld/esp32.ld

index a2c2df6444c6f94a82884e019325d4b0a3e57b14..ba08b72385ddedcec597f054510bdd089540da38 100644 (file)
@@ -219,6 +219,31 @@ menu "Bootloader config"
             It allow to test anti-rollback implemention without permanent write eFuse bits.
             In partition table should be exist this partition `emul_efuse, data, 5, , 0x2000`.
 
+    config BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP
+        bool "Skip image validation when exiting deep sleep"
+        depends on (SECURE_BOOT_ENABLED && SECURE_BOOT_INSECURE) || !SECURE_BOOT_ENABLED
+        default n
+        help
+            This option disables the normal validation of an image coming out of
+            deep sleep (checksums, SHA256, and signature). This is a trade-off
+            between wakeup performance from deep sleep, and image integrity checks.
+
+            Only enable this if you know what you are doing. It should not be used
+            in conjunction with using deep_sleep() entry and changing the active OTA
+            partition as this would skip the validation upon first load of the new
+            OTA partition.
+
+    config BOOTLOADER_RESERVE_RTC_SIZE
+        hex
+        default 0x10 if BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP
+        default 0
+        help
+            Reserve RTC FAST memory for Skip image validation. This option in bytes.
+            This option reserves an area in the RTC FAST memory (access only PRO_CPU).
+            Used to save the addresses of the selected application.
+            When a wakeup occurs (from Deep sleep), the bootloader retrieves it and
+            loads the application without validation.
+
 endmenu  # Bootloader
 
 
index d0a9469d66834ccfebfbb041494b1cf4e7048435..09d4bda3da239a9656939dbaa1e2dc17d6f70058 100644 (file)
@@ -42,12 +42,12 @@ typedef struct {
   uint8_t image_digest[32]; /* appended SHA-256 digest */
 } esp_image_metadata_t;
 
-/* Mode selection for esp_image_load() */
 typedef enum {
-    ESP_IMAGE_VERIFY,        /* Verify image contents, load metadata. Print errors. */
-    ESP_IMAGE_VERIFY_SILENT, /* Verify image contents, load metadata. Don't print errors. */
+    ESP_IMAGE_VERIFY,            /* Verify image contents, not load to memory, load metadata. Print errors. */
+    ESP_IMAGE_VERIFY_SILENT,     /* Verify image contents, not load to memory, load metadata. Don't print errors. */
 #ifdef BOOTLOADER_BUILD
-    ESP_IMAGE_LOAD,          /* Verify image contents, load to memory. Print errors. */
+    ESP_IMAGE_LOAD,              /* Verify image contents, load to memory, load metadata. Print errors. */
+    ESP_IMAGE_LOAD_NO_VALIDATE,  /* Not verify image contents, load to memory, load metadata. Print errors. */
 #endif
 } esp_image_load_mode_t;
 
@@ -134,6 +134,24 @@ esp_err_t esp_image_verify(esp_image_load_mode_t mode, const esp_partition_pos_t
  */
 esp_err_t bootloader_load_image(const esp_partition_pos_t *part, esp_image_metadata_t *data);
 
+/**
+ * @brief Load an app image without verification (available only in space of bootloader).
+ *
+ * If encryption is enabled, data will be transparently decrypted.
+ *
+ * @param part Partition to load the app from.
+ * @param[inout] data Pointer to the image metadata structure which is be filled in by this function.
+ *                    'start_addr' member should be set (to the start address of the image.)
+ *                    Other fields will all be initialised by this function.
+ *
+ * @return
+ * - ESP_OK if verify or load was successful
+ * - ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs
+ * - ESP_ERR_IMAGE_INVALID if the image appears invalid.
+ * - ESP_ERR_INVALID_ARG if the partition or data pointers are invalid.
+ */
+esp_err_t bootloader_load_image_no_verify(const esp_partition_pos_t *part, esp_image_metadata_t *data);
+
 /**
  * @brief Verify the bootloader image.
  *
index 3f9d9fe8a768df14c5f0681a58246adf7d2dbb5b..7d5f67ac7e123e04dd97b02a30bb5058500bcb98 100644 (file)
@@ -28,6 +28,7 @@
 #include "esp32/rom/uart.h"
 #include "esp32/rom/gpio.h"
 #include "esp32/rom/secure_boot.h"
+#include "esp32/rom/rtc.h"
 
 #include "soc/soc.h"
 #include "soc/cpu.h"
@@ -416,8 +417,31 @@ static void set_actual_ota_seq(const bootloader_state_t *bs, int index)
         update_anti_rollback(&bs->ota[index]);
 #endif
     }
+#if defined( CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP )
+    esp_partition_pos_t partition = index_to_partition(bs, index);
+    bootloader_common_update_rtc_retain_mem(&partition, true);
+#endif
 }
 
+#ifdef CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP
+void bootloader_utility_load_boot_image_from_deep_sleep(void)
+{
+    if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) {
+        esp_partition_pos_t* partition = bootloader_common_get_rtc_retain_mem_partition();
+        if (partition != NULL) {
+            esp_image_metadata_t image_data;
+            if (bootloader_load_image_no_verify(partition, &image_data) == ESP_OK) {
+                ESP_LOGI(TAG, "Fast booting app from partition at offset 0x%x", partition->offset);
+                bootloader_common_update_rtc_retain_mem(NULL, true);
+                load_image(&image_data);
+            }
+        }
+        ESP_LOGE(TAG, "Fast booting is not successful");
+        ESP_LOGI(TAG, "Try to load an app as usual with all validations");
+    }
+}
+#endif
+
 #define TRY_LOG_FORMAT "Trying partition index %d offs 0x%x size 0x%x"
 
 void bootloader_utility_load_boot_image(const bootloader_state_t *bs, int start_index)
index 4bda87af19a46a952a5bc6010088c4feefa28dbf..b0db9b398426f49bd7125051073eb543a353588c 100644 (file)
@@ -97,14 +97,17 @@ static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_ha
 static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
 {
 #ifdef BOOTLOADER_BUILD
-    bool do_load = (mode == ESP_IMAGE_LOAD);
+    bool do_load   = (mode == ESP_IMAGE_LOAD) || (mode == ESP_IMAGE_LOAD_NO_VALIDATE);
+    bool do_verify = (mode == ESP_IMAGE_LOAD) || (mode == ESP_IMAGE_VERIFY) || (mode == ESP_IMAGE_VERIFY_SILENT);
 #else
-    bool do_load = false; // Can't load the image in app mode
+    bool do_load   = false; // Can't load the image in app mode
+    bool do_verify = true;     // In app mode is avalible only verify mode
 #endif
-    bool silent = (mode == ESP_IMAGE_VERIFY_SILENT);
+    bool silent    = (mode == ESP_IMAGE_VERIFY_SILENT);
     esp_err_t err = ESP_OK;
     // checksum the image a word at a time. This shaves 30-40ms per MB of image size
     uint32_t checksum_word = ESP_ROM_CHECKSUM_INITIAL;
+    uint32_t *checksum = NULL;
     bootloader_sha256_handle_t sha_handle = NULL;
 
     if (data == NULL || part == NULL) {
@@ -125,41 +128,45 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
         goto err;
     }
 
-    // Calculate SHA-256 of image if secure boot is on, or if image has a hash appended
+    if (do_verify) {
+        checksum = &checksum_word;
+
+        // Calculate SHA-256 of image if secure boot is on, or if image has a hash appended
 #ifdef SECURE_BOOT_CHECK_SIGNATURE
-    if (1) {
+        if (1) {
 #else
-    if (data->image.hash_appended) {
+        if (data->image.hash_appended) {
 #endif
-        sha_handle = bootloader_sha256_start();
-        if (sha_handle == NULL) {
-            return ESP_ERR_NO_MEM;
+            sha_handle = bootloader_sha256_start();
+            if (sha_handle == NULL) {
+                return ESP_ERR_NO_MEM;
+            }
+            bootloader_sha256_data(sha_handle, &data->image, sizeof(esp_image_header_t));
         }
-        bootloader_sha256_data(sha_handle, &data->image, sizeof(esp_image_header_t));
-    }
 
-    ESP_LOGD(TAG, "image header: 0x%02x 0x%02x 0x%02x 0x%02x %08x",
-             data->image.magic,
-             data->image.segment_count,
-             data->image.spi_mode,
-             data->image.spi_size,
-             data->image.entry_addr);
+        ESP_LOGD(TAG, "image header: 0x%02x 0x%02x 0x%02x 0x%02x %08x",
+                 data->image.magic,
+                 data->image.segment_count,
+                 data->image.spi_mode,
+                 data->image.spi_size,
+                 data->image.entry_addr);
 
-    err = verify_image_header(data->start_addr, &data->image, silent);
-    if (err != ESP_OK) {
-        goto err;
-    }
+        err = verify_image_header(data->start_addr, &data->image, silent);
+        if (err != ESP_OK) {
+            goto err;
+        }
 
-    if (data->image.segment_count > ESP_IMAGE_MAX_SEGMENTS) {
-        FAIL_LOAD("image at 0x%x segment count %d exceeds max %d",
-                  data->start_addr, data->image.segment_count, ESP_IMAGE_MAX_SEGMENTS);
-    }
+        if (data->image.segment_count > ESP_IMAGE_MAX_SEGMENTS) {
+            FAIL_LOAD("image at 0x%x segment count %d exceeds max %d",
+                      data->start_addr, data->image.segment_count, ESP_IMAGE_MAX_SEGMENTS);
+        }
+    } // if (do_verify)
 
     uint32_t next_addr = data->start_addr + sizeof(esp_image_header_t);
     for(int i = 0; i < data->image.segment_count; i++) {
         esp_image_segment_header_t *header = &data->segments[i];
         ESP_LOGV(TAG, "loading segment header %d at offset 0x%x", i, next_addr);
-        err = process_segment(i, next_addr, header, silent, do_load, sha_handle, &checksum_word);
+        err = process_segment(i, next_addr, header, silent, do_load, sha_handle, checksum);
         if (err != ESP_OK) {
             goto err;
         }
@@ -168,58 +175,61 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
         next_addr += header->data_len;
     }
 
-    // Segments all loaded, verify length
-    uint32_t end_addr = next_addr;
-    if (end_addr < data->start_addr) {
-        FAIL_LOAD("image offset has wrapped");
-    }
+    if (do_verify) {
+        // Segments all loaded, verify length
+        uint32_t end_addr = next_addr;
+        if (end_addr < data->start_addr) {
+            FAIL_LOAD("image offset has wrapped");
+        }
 
-    data->image_len = end_addr - data->start_addr;
-    ESP_LOGV(TAG, "image start 0x%08x end of last section 0x%08x", data->start_addr, end_addr);
-    if (!esp_cpu_in_ocd_debug_mode()) {
-        err = verify_checksum(sha_handle, checksum_word, data);
-        if (err != ESP_OK) {
-            goto err;
+        data->image_len = end_addr - data->start_addr;
+        ESP_LOGV(TAG, "image start 0x%08x end of last section 0x%08x", data->start_addr, end_addr);
+        if (NULL != checksum && !esp_cpu_in_ocd_debug_mode()) {
+            err = verify_checksum(sha_handle, checksum_word, data);
+            if (err != ESP_OK) {
+                goto err;
+            }
         }
-    }
-    if (data->image_len > part->size) {
-        FAIL_LOAD("Image length %d doesn't fit in partition length %d", data->image_len, part->size);
-    }
 
-    bool is_bootloader = (data->start_addr == ESP_BOOTLOADER_OFFSET);
-    /* For secure boot, we don't verify signature on bootloaders.
+        if (data->image_len > part->size) {
+            FAIL_LOAD("Image length %d doesn't fit in partition length %d", data->image_len, part->size);
+        }
 
-       For non-secure boot, we don't verify any SHA-256 hash appended to the bootloader because esptool.py may have
-       rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead.
-    */
-    if (!is_bootloader) {
+        bool is_bootloader = (data->start_addr == ESP_BOOTLOADER_OFFSET);
+        /* For secure boot, we don't verify signature on bootloaders.
+
+           For non-secure boot, we don't verify any SHA-256 hash appended to the bootloader because esptool.py may have
+           rewritten the header - rely on esptool.py having verified the bootloader at flashing time, instead.
+        */
+        if (!is_bootloader) {
 #ifdef SECURE_BOOT_CHECK_SIGNATURE
-        // secure boot images have a signature appended
-        err = verify_secure_boot_signature(sha_handle, data);
+            // secure boot images have a signature appended
+            err = verify_secure_boot_signature(sha_handle, data);
 #else
-        // No secure boot, but SHA-256 can be appended for basic corruption detection
-        if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) {
-            err = verify_simple_hash(sha_handle, data);
-        }
+            // No secure boot, but SHA-256 can be appended for basic corruption detection
+            if (sha_handle != NULL && !esp_cpu_in_ocd_debug_mode()) {
+                err = verify_simple_hash(sha_handle, data);
+            }
 #endif // SECURE_BOOT_CHECK_SIGNATURE
-    } else { // is_bootloader
-        // bootloader may still have a sha256 digest handle open
-        if (sha_handle != NULL) {
-            bootloader_sha256_finish(sha_handle, NULL);
+        } else { // is_bootloader
+            // bootloader may still have a sha256 digest handle open
+            if (sha_handle != NULL) {
+                bootloader_sha256_finish(sha_handle, NULL);
+            }
         }
-    }
 
-    if (data->image.hash_appended) {
-        const void *hash = bootloader_mmap(data->start_addr + data->image_len - HASH_LEN, HASH_LEN);
-        if (hash == NULL) {
-            err = ESP_FAIL;
-            goto err;
+        if (data->image.hash_appended) {
+            const void *hash = bootloader_mmap(data->start_addr + data->image_len - HASH_LEN, HASH_LEN);
+            if (hash == NULL) {
+                err = ESP_FAIL;
+                goto err;
+            }
+            memcpy(data->image_digest, hash, HASH_LEN);
+            bootloader_munmap(hash);
         }
-        memcpy(data->image_digest, hash, HASH_LEN);
-        bootloader_munmap(hash);
-    }
+        sha_handle = NULL;
+    } // if (do_verify)
 
-    sha_handle = NULL;
     if (err != ESP_OK) {
         goto err;
     }
@@ -263,6 +273,15 @@ esp_err_t bootloader_load_image(const esp_partition_pos_t *part, esp_image_metad
 #endif
 }
 
+esp_err_t bootloader_load_image_no_verify(const esp_partition_pos_t *part, esp_image_metadata_t *data)
+{
+#ifdef BOOTLOADER_BUILD
+    return image_load(ESP_IMAGE_LOAD_NO_VALIDATE, part, data);
+#else
+    return ESP_FAIL;
+#endif
+}
+
 esp_err_t esp_image_verify(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
 {
     return image_load(mode, part, data);
@@ -396,6 +415,13 @@ err:
 
 static esp_err_t process_segment_data(intptr_t load_addr, uint32_t data_addr, uint32_t data_len, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum)
 {
+    // If we are not loading, and the checksum is empty, skip processing this
+    // segment for data
+    if(!do_load && checksum == NULL) {
+        ESP_LOGD(TAG, "skipping checksum for segment");
+        return ESP_OK;
+    }
+
     const uint32_t *data = (const uint32_t *)bootloader_mmap(data_addr, data_len);
     if(!data) {
         ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed",
@@ -422,7 +448,9 @@ static esp_err_t process_segment_data(intptr_t load_addr, uint32_t data_addr, ui
     for (int i = 0; i < data_len; i += 4) {
         int w_i = i/4; // Word index
         uint32_t w = src[w_i];
-        *checksum ^= w;
+        if (checksum != NULL) {
+            *checksum ^= w;
+        }
 #ifdef BOOTLOADER_BUILD
         if (do_load) {
             dest[w_i] = w ^ ((w_i & 1) ? ram_obfs_value[0] : ram_obfs_value[1]);
index d88cdc0342fcb2ad4cc4dcbba006fb1c60bcfd9b..514b9ebd820a2019a8e33011c286ecce410408eb 100644 (file)
 #define CONFIG_BT_RESERVE_DRAM 0
 #endif
 
+#if defined(CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP)
+#define ESP_BOOTLOADER_RESERVE_RTC (CONFIG_BOOTLOADER_RESERVE_RTC_SIZE)
+#else
+#define ESP_BOOTLOADER_RESERVE_RTC 0
+#endif
+
 #if defined(CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE)
 
 ASSERT((CONFIG_ESP32_FIXED_STATIC_RAM_SIZE <= 0x2c200),
@@ -74,7 +80,7 @@ MEMORY
   rtc_iram_seg(RWX) :                org = 0x400C0000, len = 0x2000
 
   /* RTC fast memory (same block as above), viewed from data bus */
-  rtc_data_seg(RW) :                 org = 0x3ff80000, len = 0x2000
+  rtc_data_seg(RW) :                 org = 0x3ff80000, len = 0x2000 - ESP_BOOTLOADER_RESERVE_RTC
 
   /* RTC slow memory (data accessible). Persists over deep sleep.