]> granicus.if.org Git - esp-idf/commitdiff
bootloader: Calculate SHA256 hash of image on every boot
authorAngus Gratton <angus@espressif.com>
Wed, 28 Jun 2017 06:46:34 +0000 (16:46 +1000)
committerAngus Gratton <gus@projectgus.com>
Wed, 19 Jul 2017 08:31:59 +0000 (18:31 +1000)
Makes app image booting more reliable (256-bit rather than 8-bit verification.)

Some measurements, time to boot a 655KB app.bin file and run to app_main() execution.

(All for rev 1 silicon, ie no 340ms spurious WDT delay.)

80MHz QIO mode:
before = 300ms
after = 140ms

40MHz DIO mode:
before = 712ms
after = 577ms

40MHz DIO mode, secure boot enabled
before = 1380ms
after = 934ms

(Secure boot involves two ECC signature verifications (partition table, app) that take approx 300ms each with 80MHz CPU.)

12 files changed:
components/app_update/esp_ota_ops.c
components/bootloader/subproject/main/bootloader_start.c
components/bootloader_support/include/esp_image_format.h
components/bootloader_support/include/esp_secure_boot.h
components/bootloader_support/include_priv/bootloader_sha.h
components/bootloader_support/src/bootloader_sha.c
components/bootloader_support/src/esp_image_format.c
components/bootloader_support/src/secure_boot_signatures.c
components/esptool_py/esptool
components/soc/esp32/include/soc/dport_reg.h
components/spi_flash/flash_mmap.c
tools/unit-test-app/sdkconfig

index a9247bf51ee4390b7089142bcec0933aaa4b0cd0..534fbb110a509d40571bc4f1086f61e69c9d02d5 100644 (file)
@@ -241,7 +241,7 @@ esp_err_t esp_ota_end(esp_ota_handle_t handle)
     }
 
 #ifdef CONFIG_SECURE_BOOT_ENABLED
-    ret = esp_secure_boot_verify_signature(it->part->address, data.image_length);
+    ret = esp_secure_boot_verify_signature(it->part->address, data.image_len);
     if (ret != ESP_OK) {
         ret = ESP_ERR_OTA_VALIDATE_FAILED;
         goto cleanup;
@@ -385,7 +385,7 @@ esp_err_t esp_ota_set_boot_partition(const esp_partition_t *partition)
     }
 
 #ifdef CONFIG_SECURE_BOOT_ENABLED
-    esp_err_t ret = esp_secure_boot_verify_signature(partition->address, data.image_length);
+    esp_err_t ret = esp_secure_boot_verify_signature(partition->address, data.image_len);
     if (ret != ESP_OK) {
         return ESP_ERR_OTA_VALIDATE_FAILED;
     }
index 75f481ba27465c070df8e1cbde2730e12de23329..dd2d6a88d6bce08c52ddab655e18f10c0dd49ec7 100644 (file)
@@ -87,11 +87,15 @@ void call_start_cpu0()
     cpu_configure_region_protection();
 
     /* Sanity check that static RAM is after the stack */
-    int *sp = get_sp();
-    assert(&_bss_start <= &_bss_end);
-    assert(&_data_start <= &_data_end);
-    assert(sp < &_bss_start);
-    assert(sp < &_data_start);
+#ifndef NDEBUG
+    {
+        int *sp = get_sp();
+        assert(&_bss_start <= &_bss_end);
+        assert(&_data_start <= &_data_end);
+        assert(sp < &_bss_start);
+        assert(sp < &_data_start);
+    }
+#endif
 
     //Clear bss
     memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start));
@@ -425,24 +429,13 @@ static void unpack_load_app(const esp_partition_pos_t* partition)
     esp_image_metadata_t data;
 
     /* TODO: load the app image as part of OTA boot decision, so can fallback if loading fails */
+    /* Loading the image here also includes secure boot verification */
     err = esp_image_load(ESP_IMAGE_LOAD, partition, &data);
     if (err != ESP_OK) {
         ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err);
         return;
     }
 
-#ifdef CONFIG_SECURE_BOOT_ENABLED
-    if (esp_secure_boot_enabled()) {
-        ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, data.image_length);
-        err = esp_secure_boot_verify_signature(partition->offset, data.image_length);
-        if (err != ESP_OK) {
-            ESP_LOGE(TAG, "App image @ 0x%x failed signature verification (%d)", partition->offset, err);
-            return;
-        }
-        ESP_LOGD(TAG, "App signature is valid");
-    }
-#endif
-
     uint32_t drom_addr = 0;
     uint32_t drom_load_addr = 0;
     uint32_t drom_size = 0;
@@ -497,6 +490,14 @@ static void set_cache_and_start_app(
     ESP_LOGD(TAG, "configure drom and irom and start");
     Cache_Read_Disable( 0 );
     Cache_Flush( 0 );
+
+    /* Clear the MMU entries that are already set up,
+       so the new app only has the mappings it creates.
+    */
+    for (int i = 0; i < DPORT_FLASH_MMU_TABLE_SIZE; i++) {
+        DPORT_PRO_FLASH_MMU_TABLE[i] = DPORT_FLASH_MMU_TABLE_INVALID_VAL;
+    }
+
     uint32_t drom_page_count = (drom_size + 64*1024 - 1) / (64*1024); // round up to 64k
     ESP_LOGV(TAG, "d mmu set paddr=%08x vaddr=%08x size=%d n=%d", drom_addr & 0xffff0000, drom_load_addr & 0xffff0000, drom_size, drom_page_count );
     int rc = cache_flash_mmu_set( 0, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count );
index 0334e5e40ad9bd8b9571ef002f0ec9b8eadcd241..25d81d83c7f1da2ee02060a5f1e67570950f1600 100644 (file)
@@ -59,12 +59,27 @@ typedef enum {
 typedef struct {
     uint8_t magic;
     uint8_t segment_count;
-    uint8_t spi_mode;      /* flash read mode (esp_image_spi_mode_t as uint8_t) */
-    uint8_t spi_speed: 4;  /* flash frequency (esp_image_spi_freq_t as uint8_t) */
-    uint8_t spi_size: 4;   /* flash chip size (esp_image_flash_size_t as uint8_t) */
+    /* flash read mode (esp_image_spi_mode_t as uint8_t) */
+    uint8_t spi_mode;
+    /* flash frequency (esp_image_spi_freq_t as uint8_t) */
+    uint8_t spi_speed: 4;
+    /* flash chip size (esp_image_flash_size_t as uint8_t) */
+    uint8_t spi_size: 4;
     uint32_t entry_addr;
-    uint8_t extra_header[16]; /* ESP32 additional header, unused by second bootloader */
-}  esp_image_header_t;
+    /* WP pin when SPI pins set via efuse (read by ROM bootloader, the IDF bootloader uses software to configure the WP
+     * pin and sets this field to 0xEE=disabled) */
+    uint8_t wp_pin;
+    /* Drive settings for the SPI flash pins (read by ROM bootloader) */
+    uint8_t spi_pin_drv[3];
+    /* Reserved bytes in ESP32 additional header space, currently unused */
+    uint8_t reserved[11];
+    /* If 1, a SHA256 digest "simple hash" (of the entire image) is appended after the checksum. Included in image length. This digest
+     * is separate to secure boot and only used for detecting corruption. For secure boot signed images, the signature
+     * is appended after this (and the simple hash is included in the signed data). */
+    uint8_t hash_appended;
+} __attribute__((packed))  esp_image_header_t;
+
+_Static_assert(sizeof(esp_image_header_t) == 24, "binary image header should be 24 bytes");
 
 /* Header of binary image segment */
 typedef struct {
@@ -97,18 +112,23 @@ typedef enum {
  *
  * If encryption is enabled, data will be transparently decrypted.
  *
+ * @param mode Mode of operation (verify, silent verify, or load).
  * @param part Partition to load the app from.
- * @param[out] data Pointer to the metadata structure to 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.
- * @param log_errors Log errors reading the image.
+ * @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.
  *
  * Image validation checks:
- * - Magic byte
- * - Partition smaller than 16MB
- * - All segments & image fit in partition
- * - 8 bit image checksum is valid
- * - (Signature) if signature verification is enabled
+ * - Magic byte.
+ * - Partition smaller than 16MB.
+ * - All segments & image fit in partition.
+ * - 8 bit image checksum is valid.
+ * - SHA-256 of image is valid (if image has this appended).
+ * - (Signature) if signature verification is enabled.
  *
- * @return ESP_OK if image metadata was loaded successfully, 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 data pointer is invalid.
+ * @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 esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data);
 
index 003328557d3b8a9e3751812cbd45f800b7c4d22a..6aa4b6285e24351cb803c0e61f395d54cc4b22d0 100644 (file)
@@ -77,12 +77,22 @@ esp_err_t esp_secure_boot_permanently_enable(void);
  */
 esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length);
 
+/** @brief Verify the secure boot signature block (deterministic ECDSA w/ SHA256) based on the SHA256 hash of some data.
+ *
+ * Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
+ * @param sig_block Pointer to signature block data
+ * @param image_digest Pointer to 32 byte buffer holding SHA-256 hash.
+ *
+ */
+
 /** @brief Secure boot verification block, on-flash data format. */
 typedef struct {
     uint32_t version;
     uint8_t signature[64];
 } esp_secure_boot_sig_block_t;
 
+esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest);
+
 #define FLASH_OFFS_SECURE_BOOT_IV_DIGEST 0
 
 /** @brief Secure boot IV+digest header */
index 0434000d08096ed529fbd983c92bdd78d11f4541..38bd080485dccd21433959cd0254e94220ac387d 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
index dc029d9f26b4666622ff4b75d71350b2fcb17025..079093ef8affe28d8826703c0fc884409e5d3864 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+// Copyright 2017 Espressif Systems (Shanghai) PTE LTD
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -43,7 +43,11 @@ void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest
 {
     assert(handle != NULL);
     mbedtls_sha256_context *ctx = (mbedtls_sha256_context *)handle;
-    mbedtls_sha256_finish(ctx, digest);
+    if (digest != NULL) {
+        mbedtls_sha256_finish(ctx, digest);
+    }
+    mbedtls_sha256_free(ctx);
+    free(handle);
 }
 
 #else // Bootloader version
@@ -58,6 +62,8 @@ static uint32_t words_hashed;
 
 // Words per SHA256 block
 static const size_t BLOCK_WORDS = (64/sizeof(uint32_t));
+// Words in final SHA256 digest
+static const size_t DIGEST_WORDS = (32/sizeof(uint32_t));
 
 bootloader_sha256_handle_t bootloader_sha256_start()
 {
@@ -116,22 +122,28 @@ void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest
 {
     assert(handle != NULL);
 
+    if (digest == NULL) {
+        return; // We'd free resources here, but there are none to free
+    }
+
     uint32_t data_words = words_hashed;
-    ets_printf("Padding from %d bytes\n", data_words * 4);
 
-    // Pad to a 60 byte long block loaded in the engine
-    // (normally end of block is a 64-bit length, but we know
-    // the upper 32 bits will be zeroes.)
+    // Pad to a 55 byte long block loaded in the engine
+    // (leaving 1 byte 0x80 plus variable padding plus 8 bytes of length,
+    // to fill a 64 byte block.)
     int block_bytes = (words_hashed % BLOCK_WORDS) * 4;
-    int pad_bytes = 60 - block_bytes;
+    int pad_bytes = 55 - block_bytes;
     if (pad_bytes < 0) {
         pad_bytes += 64;
     }
     static const uint8_t padding[64] = { 0x80, 0, };
 
+    pad_bytes += 5; // 1 byte for 0x80 plus first 4 bytes of the 64-bit length
+    assert(pad_bytes % 4 == 0); // should be, as (block_bytes % 4 == 0)
+
     bootloader_sha256_data(handle, padding, pad_bytes);
 
-    assert(words_hashed % BLOCK_WORDS == 56/4);
+    assert(words_hashed % BLOCK_WORDS == 60/4); // 32-bits left in block
 
     // Calculate 32-bit length for final 32 bits of data
     uint32_t bit_count = __builtin_bswap32( data_words * 32 );
@@ -139,15 +151,13 @@ void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest
 
     assert(words_hashed % BLOCK_WORDS == 0);
 
-    ets_printf("Padded to %d bytes\n", words_hashed * 4);
-
     while(REG_READ(SHA_256_BUSY_REG) == 1) { }
     REG_WRITE(SHA_256_LOAD_REG, 1);
     while(REG_READ(SHA_256_BUSY_REG) == 1) { }
 
     uint32_t *digest_words = (uint32_t *)digest;
     uint32_t *sha_text_reg = (uint32_t *)(SHA_TEXT_BASE);
-    for (int i = 0; i < BLOCK_WORDS; i++) {
+    for (int i = 0; i < DIGEST_WORDS; i++) {
         digest_words[i] = __builtin_bswap32(sha_text_reg[i]);
     }
     asm volatile ("memw");
index aac9275a6823f73b7a36e5fa057b3b069d97d977..2eb31da5297055203de313f76fce093f700287f6 100644 (file)
@@ -17,6 +17,7 @@
 #include <rom/rtc.h>
 #include <soc/cpu.h>
 #include <esp_image_format.h>
+#include <esp_secure_boot.h>
 #include <esp_log.h>
 #include <bootloader_flash.h>
 #include <bootloader_random.h>
 
 static const char *TAG = "esp_image";
 
+#define HASH_LEN 32 /* SHA-256 digest length */
+
 #define SIXTEEN_MB 0x1000000
 #define ESP_ROM_CHECKSUM_INITIAL 0xEF
 
 /* Headroom to ensure between stack SP (at time of checking) and data loaded from flash */
-#define STACK_LOAD_HEADROOM 4096
+#define STACK_LOAD_HEADROOM 32768
 
 #ifdef BOOTLOADER_BUILD
 /* 64 bits of random data to obfuscate loaded RAM with, until verification is complete
@@ -60,6 +63,11 @@ static esp_err_t verify_segment_header(int index, const esp_image_segment_header
     }                                               \
     while(0)
 
+static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data);
+
+static esp_err_t __attribute__((unused)) verify_secure_boot(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
+static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
+
 esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
 {
 #ifdef BOOTLOADER_BUILD
@@ -72,7 +80,6 @@ esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *
     // 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;
     bootloader_sha256_handle_t sha_handle = NULL;
-    uint8_t image_digest[32] = { 0 };
 
     if (data == NULL || part == NULL) {
         return ESP_ERR_INVALID_ARG;
@@ -86,17 +93,24 @@ esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *
     bzero(data, sizeof(esp_image_metadata_t));
     data->start_addr = part->offset;
 
-    sha_handle = bootloader_sha256_start();
-    if (sha_handle == NULL) {
-        return ESP_ERR_NO_MEM;
-    }
-
     ESP_LOGD(TAG, "reading image header @ 0x%x", data->start_addr);
     err = bootloader_flash_read(data->start_addr, &data->image, sizeof(esp_image_header_t), true);
     if (err != ESP_OK) {
         goto err;
     }
-    bootloader_sha256_data(sha_handle, &data->image, sizeof(esp_image_header_t));
+
+    // Calculate SHA-256 of image if secure boot is on, or if image has a hash appended
+#ifdef CONFIG_SECURE_BOOT_ENABLED
+    if (1) {
+#else
+    if (data->image.hash_appended) {
+#endif
+        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));
+    }
 
     ESP_LOGD(TAG, "image header: 0x%02x 0x%02x 0x%02x 0x%02x %08x",
              data->image.magic,
@@ -134,48 +148,32 @@ goto err;
         FAIL_LOAD("image offset has wrapped");
     }
 
-    uint32_t unpadded_length = end_addr - data->start_addr;
-    uint32_t length = unpadded_length + 1; // Add a byte for the checksum
-    length = (length + 15) & ~15; // Pad to next full 16 byte block
-    if (length > part->size) {
-        FAIL_LOAD("Image length %d doesn't fit in partition length %d", length, part->size);
+    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);
+    err = verify_checksum(sha_handle, checksum_word, data);
+    if (err != ESP_OK) {
+        goto err;
     }
 
-    // Verify checksum
-    uint32_t buf[16/sizeof(uint32_t)];
-    err = bootloader_flash_read(end_addr, buf, length - unpadded_length, true);
-    uint8_t calc = ((uint8_t *)buf)[length - unpadded_length - 1];
-    uint8_t checksum = (checksum_word >> 24)
-        ^ (checksum_word >> 16)
-        ^ (checksum_word >> 8)
-        ^ (checksum_word >> 0);
-    if (err != ESP_OK || checksum != calc) {
-        FAIL_LOAD("Checksum failed. Calculated 0x%x read 0x%x",
-                  checksum, calc);
+    if (data->image_len > part->size) {
+        FAIL_LOAD("Image length %d doesn't fit in partition length %d", data->image_len, part->size);
     }
 
-    bootloader_sha256_data(sha_handle, buf, length - unpadded_length);
-    bootloader_sha256_finish(sha_handle, image_digest);
-
-#if BOOT_LOG_LEVEL >= LOG_LEVEL_DEBUG
-    char digest_print[sizeof(image_digest)*2 + 1];
-    digest_print[sizeof(image_digest)*2] = 0;
-    for (int i = 0; i < sizeof(image_digest); i++) {
-        for (int shift = 0; shift < 2; shift++) {
-            uint8_t nibble = (image_digest[i] >> (shift ? 0 : 4)) & 0x0F;
-            if (nibble < 10) {
-                digest_print[i*2+shift] = '0' + nibble;
-            } else {
-                digest_print[i*2+shift] = 'a' + nibble - 10;
-            }
+#ifdef CONFIG_SECURE_BOOT_ENABLED
+    err = verify_secure_boot(sha_handle, data);
+    sha_handle = NULL;
+    if (err != ESP_OK) {
+        goto err;
+    }
+#else // No secure boot, but SHA-256 can be appended for basic corruption detection
+    if (sha_handle != NULL) {
+        err = verify_simple_hash(sha_handle, data);
+        sha_handle = NULL;
+        if (err != ESP_OK) {
+            goto err;
         }
     }
-    ESP_LOGD(TAG, "Total image length %d bytes (unpagged %d)", length, unpadded_length);
-    ESP_LOGD(TAG, "Image SHA256 digest: %s", digest_print);
 #endif
-    // Verify digest here
-
-    data->image_length = length;
 
 #ifdef BOOTLOADER_BUILD
     if (do_load) { // Need to deobfuscate RAM
@@ -199,8 +197,8 @@ goto err;
       err = ESP_ERR_IMAGE_INVALID;
     }
     if (sha_handle != NULL) {
-        // Need to finish the digest process to free the handle
-        bootloader_sha256_finish(sha_handle, image_digest);
+        // Need to finish the hash process to free the handle
+        bootloader_sha256_finish(sha_handle, NULL);
     }
     // Prevent invalid/incomplete data leaking out
     bzero(data, sizeof(esp_image_metadata_t));
@@ -238,9 +236,12 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme
     /* read segment header */
     err = bootloader_flash_read(flash_addr, header, sizeof(esp_image_segment_header_t), true);
     if (err != ESP_OK) {
+        ESP_LOGE(TAG, "bootloader_flash_read failed at 0x%08x", flash_addr);
         return err;
     }
-    bootloader_sha256_data(sha_handle, header, sizeof(esp_image_segment_header_t));
+    if (sha_handle != NULL) {
+        bootloader_sha256_data(sha_handle, header, sizeof(esp_image_segment_header_t));
+    }
 
     intptr_t load_addr = header->load_addr;
     uint32_t data_len = header->data_len;
@@ -311,7 +312,7 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme
         // counter-intuitive, but it's ~3ms better than using the
         // SHA256 block size.
         const size_t SHA_CHUNK = 1024;
-        if (i % SHA_CHUNK == 0) {
+        if (sha_handle != NULL && i % SHA_CHUNK == 0) {
             bootloader_sha256_data(sha_handle, &src[w_i],
                                    MIN(SHA_CHUNK, data_len - i));
         }
@@ -349,7 +350,7 @@ static esp_err_t verify_segment_header(int index, const esp_image_segment_header
     if (map_segment
         && ((segment_data_offs % SPI_FLASH_MMU_PAGE_SIZE) != (load_addr % SPI_FLASH_MMU_PAGE_SIZE))) {
         if (!silent) {
-            ESP_LOGE(TAG, "Segment %d has load address 0x%08x, doesn't match segment data at 0x%08x",
+            ESP_LOGE(TAG, "Segment %d load address 0x%08x, doesn't match data 0x%08x",
                      index, load_addr, segment_data_offs);
         }
         return ESP_ERR_IMAGE_INVALID;
@@ -411,3 +412,120 @@ esp_err_t esp_image_verify_bootloader(uint32_t *length)
     }
     return err;
 }
+
+static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data)
+{
+    uint32_t unpadded_length = data->image_len;
+    uint32_t length = unpadded_length + 1; // Add a byte for the checksum
+    length = (length + 15) & ~15; // Pad to next full 16 byte block
+
+    // Verify checksum
+    uint8_t buf[16];
+    esp_err_t err = bootloader_flash_read(data->start_addr + unpadded_length, buf, length - unpadded_length, true);
+    uint8_t calc = buf[length - unpadded_length - 1];
+    uint8_t checksum = (checksum_word >> 24)
+        ^ (checksum_word >> 16)
+        ^ (checksum_word >> 8)
+        ^ (checksum_word >> 0);
+    if (err != ESP_OK || checksum != calc) {
+        ESP_LOGE(TAG, "Checksum failed. Calculated 0x%x read 0x%x", checksum, calc);
+        return ESP_ERR_IMAGE_INVALID;
+    }
+    if (sha_handle != NULL) {
+        bootloader_sha256_data(sha_handle, buf, length - unpadded_length);
+    }
+
+    if (data->image.hash_appended) {
+        // Account for the hash in the total image length
+        length += HASH_LEN;
+    }
+    data->image_len = length;
+
+    return ESP_OK;
+}
+
+static void debug_log_hash(const uint8_t *image_hash, const char *caption);
+
+static esp_err_t verify_secure_boot(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data)
+{
+    uint8_t image_hash[HASH_LEN] = { 0 };
+
+    // For secure boot, we calculate the signature hash over the whole file, which includes any "simple" hash
+    // appended to the image for corruption detection
+    if (data->image.hash_appended) {
+        const void *simple_hash = bootloader_mmap(data->start_addr + data->image_len - HASH_LEN, HASH_LEN);
+        bootloader_sha256_data(sha_handle, simple_hash, HASH_LEN);
+        bootloader_munmap(simple_hash);
+    }
+
+    bootloader_sha256_finish(sha_handle, image_hash);
+
+    // Log the hash for debugging
+    debug_log_hash(image_hash, "Calculated secure boot hash");
+
+    // Use hash to verify signature block
+    const esp_secure_boot_sig_block_t *sig_block = bootloader_mmap(data->start_addr + data->image_len, sizeof(esp_secure_boot_sig_block_t));
+    esp_err_t err = esp_secure_boot_verify_signature_block(sig_block, image_hash);
+    bootloader_munmap(sig_block);
+    if (err != ESP_OK) {
+        ESP_LOGE(TAG, "Secure boot signature verification failed");
+
+        // Go back and check if the simple hash matches or not (we're off the fast path so we can re-hash the whole image now)
+        ESP_LOGI(TAG, "Calculating simple hash to check for corruption...");
+        const void *whole_image = bootloader_mmap(data->start_addr, data->image_len - HASH_LEN);
+        if (whole_image != NULL) {
+            sha_handle = bootloader_sha256_start();
+            bootloader_sha256_data(sha_handle, whole_image, data->image_len - HASH_LEN);
+            bootloader_munmap(whole_image);
+            if (verify_simple_hash(sha_handle, data) != ESP_OK) {
+                ESP_LOGW(TAG, "image corrupted on flash");
+            } else {
+                ESP_LOGW(TAG, "image valid, signature bad");
+            }
+        }
+        return ESP_ERR_IMAGE_INVALID;
+    }
+
+    return ESP_OK;
+}
+
+static esp_err_t verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data)
+{
+    uint8_t image_hash[HASH_LEN] = { 0 };
+    bootloader_sha256_finish(sha_handle, image_hash);
+
+    // Log the hash for debugging
+    debug_log_hash(image_hash, "Calculated hash");
+
+    // Simple hash for verification only
+    const void *hash = bootloader_mmap(data->start_addr + data->image_len - HASH_LEN, HASH_LEN);
+    if (memcmp(hash, image_hash, HASH_LEN) != 0) {
+        ESP_LOGE(TAG, "Image hash failed - image is corrupt");
+        debug_log_hash(hash, "Expected hash");
+        bootloader_munmap(hash);
+        return ESP_ERR_IMAGE_INVALID;
+    }
+
+    bootloader_munmap(hash);
+    return ESP_OK;
+}
+
+// Log a hash as a hex string
+static void debug_log_hash(const uint8_t *image_hash, const char *label)
+{
+#if BOOT_LOG_LEVEL >= LOG_LEVEL_DEBUG
+        char hash_print[sizeof(image_hash)*2 + 1];
+        hash_print[sizeof(image_hash)*2] = 0;
+        for (int i = 0; i < sizeof(image_hash); i++) {
+            for (int shift = 0; shift < 2; shift++) {
+                uint8_t nibble = (image_hash[i] >> (shift ? 0 : 4)) & 0x0F;
+                if (nibble < 10) {
+                    hash_print[i*2+shift] = '0' + nibble;
+                } else {
+                    hash_print[i*2+shift] = 'a' + nibble - 10;
+                }
+            }
+        }
+        ESP_LOGD(TAG, "%s: %s", label, hash_print);
+#endif
+}
index 3ca2f2e9197b828b8d3da3f383522a38ba31e94f..988ab7935f2c45e14a9127f64be9b2608716726a 100644 (file)
@@ -14,6 +14,7 @@
 #include "sdkconfig.h"
 
 #include "bootloader_flash.h"
+#include "bootloader_sha.h"
 #include "esp_log.h"
 #include "esp_image_format.h"
 #include "esp_secure_boot.h"
@@ -34,20 +35,13 @@ extern const uint8_t signature_verification_key_end[] asm("_binary_signature_ver
 
 #define SIGNATURE_VERIFICATION_KEYLEN 64
 
+#define DIGEST_LEN 32
+
 esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
 {
-#ifdef BOOTLOADER_BUILD
-    SHA_CTX sha;
-#endif
-    uint8_t digest[32];
-    ptrdiff_t keylen;
+    uint8_t digest[DIGEST_LEN];
     const uint8_t *data;
     const esp_secure_boot_sig_block_t *sigblock;
-    bool is_valid;
-#ifdef BOOTLOADER_BUILD
-    const uint8_t *digest_data;
-    uint32_t digest_len;
-#endif
 
     ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
 
@@ -57,46 +51,43 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
         return ESP_FAIL;
     }
 
-    sigblock = (const esp_secure_boot_sig_block_t *)(data + length);
-
-    if (sigblock->version != 0) {
-        ESP_LOGE(TAG, "src 0x%x has invalid signature version field 0x%08x", src_addr, sigblock->version);
-        goto unmap_and_fail;
-    }
-
+    // Calculate digest of main image
 #ifdef BOOTLOADER_BUILD
-    /* Use ROM SHA functions directly */
-    ets_sha_enable();
-    ets_sha_init(&sha);
-    digest_len = length * 8;
-    digest_data = data;
-    while (digest_len > 0) {
-        uint32_t chunk_len = (digest_len > 64) ? 64 : digest_len;
-        ets_sha_update(&sha, SHA2_256, digest_data, chunk_len);
-        digest_len -= chunk_len;
-        digest_data += chunk_len / 8;
-    }
-    ets_sha_finish(&sha, SHA2_256, digest);
-    ets_sha_disable();
+    bootloader_sha256_handle_t handle = bootloader_sha256_start();
+    bootloader_sha256_data(handle, data, length);
+    bootloader_sha256_finish(handle, digest);
 #else
     /* Use thread-safe esp-idf SHA function */
     esp_sha(SHA2_256, data, length, digest);
 #endif
 
+    // Map the signature block and verify the signature
+    sigblock = (const esp_secure_boot_sig_block_t *)(data + length);
+    esp_err_t err = esp_secure_boot_verify_signature_block(sigblock, digest);
+    bootloader_munmap(data);
+    return err;
+}
+
+esp_err_t esp_secure_boot_verify_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest)
+{
+    ptrdiff_t keylen;
+    bool is_valid;
+
     keylen = signature_verification_key_end - signature_verification_key_start;
     if(keylen != SIGNATURE_VERIFICATION_KEYLEN) {
         ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen);
-        goto unmap_and_fail;
+        return ESP_FAIL;
     }
 
-    is_valid = uECC_verify(signature_verification_key_start,
-                           digest, sizeof(digest), sigblock->signature,
-                           uECC_secp256r1());
+    if (sig_block->version != 0) {
+        ESP_LOGE(TAG, "image has invalid signature version field 0x%08x", sig_block->version);
+        return ESP_FAIL;
+    }
 
-    bootloader_munmap(data);
+    is_valid = uECC_verify(signature_verification_key_start,
+                                image_digest,
+                                DIGEST_LEN,
+                                sig_block->signature,
+                                uECC_secp256r1());
     return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID;
-
- unmap_and_fail:
-    bootloader_munmap(data);
-    return ESP_FAIL;
 }
index 325f01637b667af02cc6390965b09d50e6a31dac..a4207741eca1fb8e5e3670e498ed058320bbcb5a 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 325f01637b667af02cc6390965b09d50e6a31dac
+Subproject commit a4207741eca1fb8e5e3670e498ed058320bbcb5a
index 4e17363bcfebe04107d1d7ef0f03198b0940bb39..852967f1c19928e20e547b0881333999ed2764a2 100644 (file)
 /* Flash MMU table for APP CPU */
 #define DPORT_APP_FLASH_MMU_TABLE ((volatile uint32_t*) 0x3FF12000)
 
+#define DPORT_FLASH_MMU_TABLE_SIZE 0x100
 
+#define DPORT_FLASH_MMU_TABLE_INVALID_VAL 0x100
 
 #endif /*_SOC_DPORT_REG_H_ */
 
index 080532f86224bedc0cc80f7cba54fae21b90913a..2db0c2437f9de1434ad183352e1f35d734bd1274 100644 (file)
@@ -83,14 +83,14 @@ static void IRAM_ATTR spi_flash_mmap_init()
         uint32_t entry_app = DPORT_APP_FLASH_MMU_TABLE[i];
         if (entry_pro != entry_app) {
             // clean up entries used by boot loader
-            entry_pro = INVALID_ENTRY_VAL;
-            DPORT_PRO_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL;
+            entry_pro = DPORT_FLASH_MMU_TABLE_INVALID_VAL;
+            DPORT_PRO_FLASH_MMU_TABLE[i] = DPORT_FLASH_MMU_TABLE_INVALID_VAL;
         }
         if ((entry_pro & INVALID_ENTRY_VAL) == 0 && (i == 0 || i == PRO_IRAM0_FIRST_USABLE_PAGE || entry_pro != 0)) {
             s_mmap_page_refcnt[i] = 1;
         } else {
-            DPORT_PRO_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL;
-            DPORT_APP_FLASH_MMU_TABLE[i] = INVALID_ENTRY_VAL;
+            DPORT_PRO_FLASH_MMU_TABLE[i] = DPORT_FLASH_MMU_TABLE_INVALID_VAL;
+            DPORT_APP_FLASH_MMU_TABLE[i] = DPORT_FLASH_MMU_TABLE_INVALID_VAL;
         }
     }
 }
index c3500943cf048d13b8a2ce9b0bcb18b2ff10c038..29be2173dddf5b72e3312707b00ff2d8878e97f6 100644 (file)
@@ -19,6 +19,7 @@ CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y
 # CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set
 # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set
 CONFIG_LOG_BOOTLOADER_LEVEL=2
+# CONFIG_BOOTLOADER_LTO is not set
 
 #
 # Security features