From: Angus Gratton Date: Wed, 28 Jun 2017 06:46:34 +0000 (+1000) Subject: bootloader: Calculate SHA256 hash of image on every boot X-Git-Tag: v3.1-dev~454^2~2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=17adb40ca8d9159e28c3666cf46085eb16ddc7f6;p=esp-idf bootloader: Calculate SHA256 hash of image on every boot 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.) --- diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index a9247bf51e..534fbb110a 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -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; } diff --git a/components/bootloader/subproject/main/bootloader_start.c b/components/bootloader/subproject/main/bootloader_start.c index 75f481ba27..dd2d6a88d6 100644 --- a/components/bootloader/subproject/main/bootloader_start.c +++ b/components/bootloader/subproject/main/bootloader_start.c @@ -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 ); diff --git a/components/bootloader_support/include/esp_image_format.h b/components/bootloader_support/include/esp_image_format.h index 0334e5e40a..25d81d83c7 100644 --- a/components/bootloader_support/include/esp_image_format.h +++ b/components/bootloader_support/include/esp_image_format.h @@ -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); diff --git a/components/bootloader_support/include/esp_secure_boot.h b/components/bootloader_support/include/esp_secure_boot.h index 003328557d..6aa4b6285e 100644 --- a/components/bootloader_support/include/esp_secure_boot.h +++ b/components/bootloader_support/include/esp_secure_boot.h @@ -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 */ diff --git a/components/bootloader_support/include_priv/bootloader_sha.h b/components/bootloader_support/include_priv/bootloader_sha.h index 0434000d08..38bd080485 100644 --- a/components/bootloader_support/include_priv/bootloader_sha.h +++ b/components/bootloader_support/include_priv/bootloader_sha.h @@ -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. diff --git a/components/bootloader_support/src/bootloader_sha.c b/components/bootloader_support/src/bootloader_sha.c index dc029d9f26..079093ef8a 100644 --- a/components/bootloader_support/src/bootloader_sha.c +++ b/components/bootloader_support/src/bootloader_sha.c @@ -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"); diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index aac9275a68..2eb31da529 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -24,11 +25,13 @@ 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 +} diff --git a/components/bootloader_support/src/secure_boot_signatures.c b/components/bootloader_support/src/secure_boot_signatures.c index 3ca2f2e919..988ab7935f 100644 --- a/components/bootloader_support/src/secure_boot_signatures.c +++ b/components/bootloader_support/src/secure_boot_signatures.c @@ -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; } diff --git a/components/esptool_py/esptool b/components/esptool_py/esptool index 325f01637b..a4207741ec 160000 --- a/components/esptool_py/esptool +++ b/components/esptool_py/esptool @@ -1 +1 @@ -Subproject commit 325f01637b667af02cc6390965b09d50e6a31dac +Subproject commit a4207741eca1fb8e5e3670e498ed058320bbcb5a diff --git a/components/soc/esp32/include/soc/dport_reg.h b/components/soc/esp32/include/soc/dport_reg.h index 4e17363bcf..852967f1c1 100644 --- a/components/soc/esp32/include/soc/dport_reg.h +++ b/components/soc/esp32/include/soc/dport_reg.h @@ -4261,7 +4261,9 @@ /* 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_ */ diff --git a/components/spi_flash/flash_mmap.c b/components/spi_flash/flash_mmap.c index 080532f862..2db0c2437f 100644 --- a/components/spi_flash/flash_mmap.c +++ b/components/spi_flash/flash_mmap.c @@ -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; } } } diff --git a/tools/unit-test-app/sdkconfig b/tools/unit-test-app/sdkconfig index c3500943cf..29be2173dd 100644 --- a/tools/unit-test-app/sdkconfig +++ b/tools/unit-test-app/sdkconfig @@ -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