From 99af5e9a719398028e13d4a42246acb58f5a37f7 Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Tue, 27 Apr 2021 18:40:52 +0800 Subject: [PATCH 1/4] bootloader: Fixed a case when signed OTA updates fail when debugger is attached due to the wrong image_len. And it fixed another case for bootloader_common_get_sha256_of_partition() when CHECK_SIGNATURE is on - If RSA signature check is on in Kconfig then sha256 was 0xFFFFF... because image_load gave image_len which pointed to the end of sign blocks. And image_digest was filled from a wrong position. Closes https://github.com/espressif/esp-idf/issues/6873 --- .../bootloader_support/src/esp_image_format.c | 333 +++++++++--------- 1 file changed, 173 insertions(+), 160 deletions(-) diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 436dbb4902..afe2eb4ad6 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -47,10 +47,14 @@ #ifdef BOOTLOADER_BUILD #ifdef CONFIG_SECURE_SIGNED_ON_BOOT #define SECURE_BOOT_CHECK_SIGNATURE 1 +#else +#define SECURE_BOOT_CHECK_SIGNATURE 0 #endif #else /* !BOOTLOADER_BUILD */ #ifdef CONFIG_SECURE_SIGNED_ON_UPDATE #define SECURE_BOOT_CHECK_SIGNATURE 1 +#else +#define SECURE_BOOT_CHECK_SIGNATURE 0 #endif #endif @@ -77,6 +81,7 @@ static bool should_load(uint32_t load_addr); /* Return true if load_addr is an address the bootloader should map via flash cache */ static bool should_map(uint32_t load_addr); +static esp_err_t process_segments(esp_image_metadata_t *data, bool silent, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum); /* Load or verify a segment */ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segment_header_t *header, bool silent, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum); @@ -98,7 +103,16 @@ 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); +#define CHECK_ERR(func) do { \ + if ((err = func) != ESP_OK) { \ + goto err; \ + } \ + } \ + while(0) + +static esp_err_t process_image_header(esp_image_metadata_t *data, uint32_t part_offset, bootloader_sha256_handle_t *sha_handle, bool do_verify, bool silent); +static esp_err_t process_appended_hash(esp_image_metadata_t *data, uint32_t part_len, bool do_verify, bool silent); +static esp_err_t process_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data, bool silent, bool skip_calc_checksum); static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest); static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data); @@ -116,9 +130,9 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ 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; + uint32_t *checksum = (do_verify) ? &checksum_word : NULL; bootloader_sha256_handle_t sha_handle = NULL; -#if SECURE_BOOT_CHECK_SIGNATURE +#if (SECURE_BOOT_CHECK_SIGNATURE == 1) /* used for anti-FI checks */ uint8_t image_digest[HASH_LEN] = { [ 0 ... 31] = 0xEE }; uint8_t verified_digest[HASH_LEN] = { [ 0 ... 31 ] = 0x01 }; @@ -133,107 +147,37 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ FAIL_LOAD("partition size 0x%x invalid, larger than 16MB", part->size); } - bzero(data, sizeof(esp_image_metadata_t)); - data->start_addr = part->offset; - - 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_handle_t *p_sha_handle = &sha_handle; + CHECK_ERR(process_image_header(data, part->offset, p_sha_handle, do_verify, silent)); + CHECK_ERR(process_segments(data, silent, do_load, sha_handle, checksum)); + bool skip_calc_checksum = !do_verify || esp_cpu_in_ocd_debug_mode(); + CHECK_ERR(process_checksum(sha_handle, checksum_word, data, silent, skip_calc_checksum)); + CHECK_ERR(process_appended_hash(data, part->size, do_verify, silent)); 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) { -#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, - 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; - } - - 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); - if (err != ESP_OK) { - goto err; - } - next_addr += sizeof(esp_image_segment_header_t); - data->segment_data[i] = next_addr; - next_addr += header->data_len; - } - - 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 (NULL != checksum && !esp_cpu_in_ocd_debug_mode()) { - err = verify_checksum(sha_handle, checksum_word, data); - if (err != ESP_OK) { - goto err; - } - } - /* For secure boot V1 on ESP32, we don't calculate SHA or verify signature on bootloaders. For Secure Boot V2, we do verify signature on bootloader which includes the SHA calculation. (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.) */ - bool verify_sha; #if CONFIG_SECURE_BOOT_V2_ENABLED - verify_sha = true; + bool verify_sha = true; #else // Secure boot not enabled - verify_sha = (data->start_addr != ESP_BOOTLOADER_OFFSET); + bool verify_sha = (data->start_addr != ESP_BOOTLOADER_OFFSET); #endif if (verify_sha) { - if (data->image_len > part->size) { - FAIL_LOAD("Image length %d doesn't fit in partition length %d", data->image_len, part->size); - } - -#ifdef SECURE_BOOT_CHECK_SIGNATURE +#if (SECURE_BOOT_CHECK_SIGNATURE == 1) // secure boot images have a signature appended #if defined(BOOTLOADER_BUILD) && !defined(CONFIG_SECURE_BOOT) // If secure boot is not enabled in hardware, then // skip the signature check in bootloader when the debugger is attached. // This is done to allow for breakpoints in Flash. - if (!esp_cpu_in_ocd_debug_mode()) { + bool do_verify_sig = !esp_cpu_in_ocd_debug_mode(); #else // CONFIG_SECURE_BOOT - if (true) { + bool do_verify_sig = true; #endif // end checking for JTAG + if (do_verify_sig) { err = verify_secure_boot_signature(sha_handle, data, image_digest, verified_digest); sha_handle = NULL; // verify_secure_boot_signature finishes sha_handle } @@ -241,42 +185,25 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ // 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); -#ifdef CONFIG_IDF_ENV_FPGA - if (err != ESP_OK) { - ESP_LOGW(TAG, "Ignoring invalid SHA-256 as running on FPGA"); - err = ESP_OK; - } -#endif sha_handle = NULL; // calling verify_simple_hash finishes sha_handle } #endif // SECURE_BOOT_CHECK_SIGNATURE - } else { // verify_sha - // bootloader may still have a sha256 digest handle open - if (sha_handle != NULL) { - bootloader_sha256_finish(sha_handle, NULL); - } - sha_handle = NULL; - } //verify_sha - - // Separately, if there's a hash appended to the image then copy it out to the data->image_digest field - 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); - } + } // verify_sha } // do_verify + // bootloader may still have a sha256 digest handle open + if (sha_handle != NULL) { + bootloader_sha256_finish(sha_handle, NULL); + sha_handle = NULL; + } + if (err != ESP_OK) { goto err; } #ifdef BOOTLOADER_BUILD -#ifdef SECURE_BOOT_CHECK_SIGNATURE +#if (SECURE_BOOT_CHECK_SIGNATURE == 1) /* If signature was checked in bootloader build, verified_digest should equal image_digest This is to detect any fault injection that caused signature verification to not complete normally. @@ -307,7 +234,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ } } } -#endif +#endif // BOOTLOADER_BUILD // Success! return ESP_OK; @@ -404,11 +331,15 @@ static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t { esp_err_t err = ESP_OK; + ESP_LOGD(TAG, "image header: 0x%02x 0x%02x 0x%02x 0x%02x %08x", + image->magic, + image->segment_count, + image->spi_mode, + image->spi_size, + image->entry_addr); + if (image->magic != ESP_IMAGE_HEADER_MAGIC) { - if (!silent) { - ESP_LOGE(TAG, "image at 0x%x has invalid magic byte", src_addr); - } - err = ESP_ERR_IMAGE_INVALID; + FAIL_LOAD("image at 0x%x has invalid magic byte (nothing flashed here?)", src_addr); } if (!silent) { if (image->spi_mode > ESP_IMAGE_SPI_MODE_SLOW_READ) { @@ -422,15 +353,19 @@ static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t } } - if (err == ESP_OK) { - // Checking the chip revision header *will* print a bunch of other info - // regardless of silent setting as this may be important, but don't bother checking it - // if it looks like the app partition is erased or otherwise garbage - if (bootloader_common_check_chip_validity(image, ESP_IMAGE_APPLICATION) != ESP_OK) { - err = ESP_ERR_IMAGE_INVALID; - } - } + // Checking the chip revision header *will* print a bunch of other info + // regardless of silent setting as this may be important, but don't bother checking it + // if it looks like the app partition is erased or otherwise garbage + CHECK_ERR(bootloader_common_check_chip_validity(image, ESP_IMAGE_APPLICATION)); + if (image->segment_count > ESP_IMAGE_MAX_SEGMENTS) { + FAIL_LOAD("image at 0x%x segment count %d exceeds max %d", src_addr, image->segment_count, ESP_IMAGE_MAX_SEGMENTS); + } + return err; +err: + if (err == ESP_OK) { + err = ESP_ERR_IMAGE_INVALID; + } return err; } @@ -555,6 +490,63 @@ static bool verify_load_addresses(int segment_index, intptr_t load_addr, intptr_ } #endif // BOOTLOADER_BUILD +static esp_err_t process_image_header(esp_image_metadata_t *data, uint32_t part_offset, bootloader_sha256_handle_t *sha_handle, bool do_verify, bool silent) +{ + esp_err_t err; + bzero(data, sizeof(esp_image_metadata_t)); + data->start_addr = part_offset; + + ESP_LOGD(TAG, "reading image header @ 0x%x", data->start_addr); + CHECK_ERR(bootloader_flash_read(data->start_addr, &data->image, sizeof(esp_image_header_t), true)); + + if (do_verify) { + // Calculate SHA-256 of image if secure boot is on, or if image has a hash appended + if (SECURE_BOOT_CHECK_SIGNATURE || data->image.hash_appended) { + if (sha_handle != NULL) { + *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)); + } + } + CHECK_ERR(verify_image_header(data->start_addr, &data->image, silent)); + } + data->image_len = sizeof(esp_image_header_t); + return ESP_OK; +err: + return err; +} + +static esp_err_t process_segments(esp_image_metadata_t *data, bool silent, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum) +{ + esp_err_t err = ESP_OK; + uint32_t start_segments = data->start_addr + data->image_len; + uint32_t next_addr = start_segments; + 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); + CHECK_ERR(process_segment(i, next_addr, header, silent, do_load, sha_handle, checksum)); + next_addr += sizeof(esp_image_segment_header_t); + data->segment_data[i] = next_addr; + 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"); + } + + data->image_len += end_addr - start_segments; + ESP_LOGV(TAG, "image start 0x%08x end of last section 0x%08x", data->start_addr, end_addr); + return err; +err: + if (err == ESP_OK) { + err = ESP_ERR_IMAGE_INVALID; + } + return err; +} + static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segment_header_t *header, bool silent, bool do_load, bootloader_sha256_handle_t sha_handle, uint32_t *checksum) { esp_err_t err; @@ -575,10 +567,7 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme ESP_LOGV(TAG, "segment data length 0x%x data starts 0x%x", data_len, data_addr); - err = verify_segment_header(index, header, data_addr, silent); - if (err != ESP_OK) { - return err; - } + CHECK_ERR(verify_segment_header(index, header, data_addr, silent)); if (data_len % 4 != 0) { FAIL_LOAD("unaligned segment length 0x%x", data_len); @@ -609,17 +598,14 @@ static esp_err_t process_segment(int index, uint32_t flash_addr, esp_image_segme uint32_t data_len_remain = data_len; while (data_len_remain > 0) { -#if SECURE_BOOT_CHECK_SIGNATURE && defined(BOOTLOADER_BUILD) +#if (SECURE_BOOT_CHECK_SIGNATURE == 1) && defined(BOOTLOADER_BUILD) /* Double check the address verification done above */ ESP_FAULT_ASSERT(!do_load || verify_load_addresses(0, load_addr, load_addr + data_len_remain, false, false)); #endif uint32_t offset_page = ((data_addr & MMAP_ALIGNED_MASK) != 0) ? 1 : 0; /* Data we could map in case we are not aligned to PAGE boundary is one page size lesser. */ data_len = MIN(data_len_remain, ((free_page_count - offset_page) * SPI_FLASH_MMU_PAGE_SIZE)); - err = process_segment_data(load_addr, data_addr, data_len, do_load, sha_handle, checksum); - if (err != ESP_OK) { - return err; - } + CHECK_ERR(process_segment_data(load_addr, data_addr, data_len, do_load, sha_handle, checksum)); data_addr += data_len; data_len_remain -= data_len; } @@ -794,41 +780,67 @@ esp_err_t esp_image_verify_bootloader_data(esp_image_metadata_t *data) data); } - -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 process_appended_hash(esp_image_metadata_t *data, uint32_t part_len, bool do_verify, bool silent) { + esp_err_t err = ESP_OK; + if (data->image.hash_appended) { + // Account for the hash in the total image length + if (do_verify) { + CHECK_ERR(bootloader_flash_read(data->start_addr + data->image_len, &data->image_digest, HASH_LEN, true)); + } + data->image_len += HASH_LEN; + } + + if (data->image_len > part_len) { + FAIL_LOAD("Image length %d doesn't fit in partition length %d", data->image_len, part_len); + } + return err; +err: + if (err == ESP_OK) { + err = ESP_ERR_IMAGE_INVALID; + } + + return err; +} + +static esp_err_t process_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data, bool silent, bool skip_calc_checksum) +{ + esp_err_t err = ESP_OK; 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 + length = length - unpadded_length; - // Verify checksum - WORD_ALIGNED_ATTR 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 (!skip_calc_checksum) { + // Verify checksum + WORD_ALIGNED_ATTR uint8_t buf[16]; + CHECK_ERR(bootloader_flash_read(data->start_addr + unpadded_length, buf, length, true)); + uint8_t calc = buf[length - 1]; + uint8_t checksum = (checksum_word >> 24) + ^ (checksum_word >> 16) + ^ (checksum_word >> 8) + ^ (checksum_word >> 0); + if (checksum != calc) { + FAIL_LOAD("Checksum failed. Calculated 0x%x read 0x%x", checksum, calc); + } + if (sha_handle != NULL) { + bootloader_sha256_data(sha_handle, buf, length); + } } - if (sha_handle != NULL) { - bootloader_sha256_data(sha_handle, buf, length - unpadded_length); + data->image_len += length; + + return err; +err: + if (err == ESP_OK) { + err = ESP_ERR_IMAGE_INVALID; } - if (data->image.hash_appended) { - // Account for the hash in the total image length - length += HASH_LEN; - } - data->image_len = length; - - return ESP_OK; + return err; } static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest) { -#ifdef SECURE_BOOT_CHECK_SIGNATURE +#if (SECURE_BOOT_CHECK_SIGNATURE == 1) uint32_t end = data->start_addr + data->image_len; ESP_LOGI(TAG, "Verifying image signature..."); @@ -909,15 +921,16 @@ static esp_err_t verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_i bootloader_debug_buffer(image_hash, HASH_LEN, "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) { + if (memcmp(data->image_digest, image_hash, HASH_LEN) != 0) { ESP_LOGE(TAG, "Image hash failed - image is corrupt"); - bootloader_debug_buffer(hash, HASH_LEN, "Expected hash"); - bootloader_munmap(hash); + bootloader_debug_buffer(data->image_digest, HASH_LEN, "Expected hash"); +#ifdef CONFIG_IDF_ENV_FPGA + ESP_LOGW(TAG, "Ignoring invalid SHA-256 as running on FPGA"); + return ESP_OK; +#endif return ESP_ERR_IMAGE_INVALID; } - bootloader_munmap(hash); return ESP_OK; } From 54ae758b771963f895e6e6ebc0baaa7e4cbc1e0c Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Tue, 27 Apr 2021 18:49:37 +0800 Subject: [PATCH 2/4] bootloader_support: Used esp_image_get_metadata() instead of esp_image_verify() - bootloader_common_get_sha256_of_partition will not do any unnecessery verifies. - Used esp_image_get_metadata() instead of esp_image_verify(). --- .../src/bootloader_common.c | 4 +- .../bootloader_support/src/esp_image_format.c | 133 +++++++----------- 2 files changed, 54 insertions(+), 83 deletions(-) diff --git a/components/bootloader_support/src/bootloader_common.c b/components/bootloader_support/src/bootloader_common.c index 2bf2074988..0a081679b9 100644 --- a/components/bootloader_support/src/bootloader_common.c +++ b/components/bootloader_support/src/bootloader_common.c @@ -163,9 +163,7 @@ esp_err_t bootloader_common_get_sha256_of_partition (uint32_t address, uint32_t .size = size, }; esp_image_metadata_t data; - // Function esp_image_verify() verifies and fills the structure data. - // here important to get: image_digest, image_len, hash_appended. - if (esp_image_verify(ESP_IMAGE_VERIFY_SILENT, &partition_pos, &data) != ESP_OK) { + if (esp_image_get_metadata(&partition_pos, &data) != ESP_OK) { return ESP_ERR_IMAGE_INVALID; } if (data.image.hash_appended) { diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index afe2eb4ad6..707b64fdbc 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -112,7 +112,7 @@ static esp_err_t verify_segment_header(int index, const esp_image_segment_header static esp_err_t process_image_header(esp_image_metadata_t *data, uint32_t part_offset, bootloader_sha256_handle_t *sha_handle, bool do_verify, bool silent); static esp_err_t process_appended_hash(esp_image_metadata_t *data, uint32_t part_len, bool do_verify, bool silent); -static esp_err_t process_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data, bool silent, bool skip_calc_checksum); +static esp_err_t process_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data, bool silent, bool skip_check_checksum); static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest); static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data); @@ -137,6 +137,15 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ uint8_t image_digest[HASH_LEN] = { [ 0 ... 31] = 0xEE }; uint8_t verified_digest[HASH_LEN] = { [ 0 ... 31 ] = 0x01 }; #endif +#if CONFIG_SECURE_BOOT_V2_ENABLED + // For Secure Boot V2, we do verify signature on bootloader which includes the SHA calculation. + bool verify_sha = do_verify; +#else // Secure boot not enabled + // For secure boot V1 on ESP32, we don't calculate SHA or 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.) + bool verify_sha = (data->start_addr != ESP_BOOTLOADER_OFFSET) && do_verify; +#endif if (data == NULL || part == NULL) { return ESP_ERR_INVALID_ARG; @@ -148,48 +157,34 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ } bootloader_sha256_handle_t *p_sha_handle = &sha_handle; - CHECK_ERR(process_image_header(data, part->offset, p_sha_handle, do_verify, silent)); + CHECK_ERR(process_image_header(data, part->offset, (verify_sha) ? p_sha_handle : NULL, do_verify, silent)); CHECK_ERR(process_segments(data, silent, do_load, sha_handle, checksum)); - bool skip_calc_checksum = !do_verify || esp_cpu_in_ocd_debug_mode(); - CHECK_ERR(process_checksum(sha_handle, checksum_word, data, silent, skip_calc_checksum)); + bool skip_check_checksum = !do_verify || esp_cpu_in_ocd_debug_mode(); + CHECK_ERR(process_checksum(sha_handle, checksum_word, data, silent, skip_check_checksum)); CHECK_ERR(process_appended_hash(data, part->size, do_verify, silent)); - if (do_verify) { - /* For secure boot V1 on ESP32, we don't calculate SHA or verify signature on bootloaders. - For Secure Boot V2, we do verify signature on bootloader which includes the SHA calculation. - - (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 CONFIG_SECURE_BOOT_V2_ENABLED - bool verify_sha = true; -#else // Secure boot not enabled - bool verify_sha = (data->start_addr != ESP_BOOTLOADER_OFFSET); -#endif - - if (verify_sha) { + if (verify_sha) { #if (SECURE_BOOT_CHECK_SIGNATURE == 1) - // secure boot images have a signature appended + // secure boot images have a signature appended #if defined(BOOTLOADER_BUILD) && !defined(CONFIG_SECURE_BOOT) - // If secure boot is not enabled in hardware, then - // skip the signature check in bootloader when the debugger is attached. - // This is done to allow for breakpoints in Flash. - bool do_verify_sig = !esp_cpu_in_ocd_debug_mode(); + // If secure boot is not enabled in hardware, then + // skip the signature check in bootloader when the debugger is attached. + // This is done to allow for breakpoints in Flash. + bool do_verify_sig = !esp_cpu_in_ocd_debug_mode(); #else // CONFIG_SECURE_BOOT - bool do_verify_sig = true; + bool do_verify_sig = true; #endif // end checking for JTAG - if (do_verify_sig) { - err = verify_secure_boot_signature(sha_handle, data, image_digest, verified_digest); - sha_handle = NULL; // verify_secure_boot_signature finishes sha_handle - } + if (do_verify_sig) { + err = verify_secure_boot_signature(sha_handle, data, image_digest, verified_digest); + sha_handle = NULL; // verify_secure_boot_signature finishes sha_handle + } #else // SECURE_BOOT_CHECK_SIGNATURE - // 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); - sha_handle = NULL; // calling verify_simple_hash finishes sha_handle - } + // 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); + sha_handle = NULL; // calling verify_simple_hash finishes sha_handle + } #endif // SECURE_BOOT_CHECK_SIGNATURE - } // verify_sha - } // do_verify + } // verify_sha // bootloader may still have a sha256 digest handle open if (sha_handle != NULL) { @@ -290,41 +285,22 @@ esp_err_t esp_image_verify(esp_image_load_mode_t mode, const esp_partition_pos_t esp_err_t esp_image_get_metadata(const esp_partition_pos_t *part, esp_image_metadata_t *metadata) { + esp_err_t err; if (metadata == NULL || part == NULL || part->size > SIXTEEN_MB) { return ESP_ERR_INVALID_ARG; } - memset(metadata, 0, sizeof(esp_image_metadata_t)); - metadata->start_addr = part->offset; - - esp_err_t err = bootloader_flash_read(metadata->start_addr, &metadata->image, sizeof(esp_image_header_t), true); - if (err != ESP_OK) { - return err; - } - uint32_t next_addr = metadata->start_addr + sizeof(esp_image_header_t); - for (int i = 0; i < metadata->image.segment_count; i++) { - esp_image_segment_header_t *header = &metadata->segments[i]; - err = process_segment(i, next_addr, header, true, false, NULL, NULL); - if (err != ESP_OK) { - return err; - } - next_addr += sizeof(esp_image_segment_header_t); - metadata->segment_data[i] = next_addr; - next_addr += header->data_len; - } - metadata->image_len = next_addr - metadata->start_addr; - - // checksum - uint32_t unpadded_length = metadata->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 - if (metadata->image.hash_appended) { - // Account for the hash in the total image length - length += HASH_LEN; - } - metadata->image_len = length; - + bool silent = true; + bool do_verify = false; + bool do_load = false; + CHECK_ERR(process_image_header(metadata, part->offset, NULL, do_verify, silent)); + CHECK_ERR(process_segments(metadata, silent, do_load, NULL, NULL)); + bool skip_check_checksum = true; + CHECK_ERR(process_checksum(NULL, 0, metadata, silent, skip_check_checksum)); + CHECK_ERR(process_appended_hash(metadata, part->size, true, silent)); return ESP_OK; +err: + return err; } static esp_err_t verify_image_header(uint32_t src_addr, const esp_image_header_t *image, bool silent) @@ -803,7 +779,7 @@ err: return err; } -static esp_err_t process_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data, bool silent, bool skip_calc_checksum) +static esp_err_t process_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data, bool silent, bool skip_check_checksum) { esp_err_t err = ESP_OK; uint32_t unpadded_length = data->image_len; @@ -811,21 +787,18 @@ static esp_err_t process_checksum(bootloader_sha256_handle_t sha_handle, uint32_ length = (length + 15) & ~15; // Pad to next full 16 byte block length = length - unpadded_length; - if (!skip_calc_checksum) { - // Verify checksum - WORD_ALIGNED_ATTR uint8_t buf[16]; + // Verify checksum + WORD_ALIGNED_ATTR uint8_t buf[16]; + if (!skip_check_checksum || sha_handle != NULL) { CHECK_ERR(bootloader_flash_read(data->start_addr + unpadded_length, buf, length, true)); - uint8_t calc = buf[length - 1]; - uint8_t checksum = (checksum_word >> 24) - ^ (checksum_word >> 16) - ^ (checksum_word >> 8) - ^ (checksum_word >> 0); - if (checksum != calc) { - FAIL_LOAD("Checksum failed. Calculated 0x%x read 0x%x", checksum, calc); - } - if (sha_handle != NULL) { - bootloader_sha256_data(sha_handle, buf, length); - } + } + uint8_t read_checksum = buf[length - 1]; + uint8_t calc_checksum = (checksum_word >> 24) ^ (checksum_word >> 16) ^ (checksum_word >> 8) ^ (checksum_word >> 0); + if (!skip_check_checksum && calc_checksum != read_checksum) { + FAIL_LOAD("Checksum failed. Calculated 0x%x read 0x%x", calc_checksum, read_checksum); + } + if (sha_handle != NULL) { + bootloader_sha256_data(sha_handle, buf, length); } data->image_len += length; From 2d439ba001e4d24206198c4aeedf1a180f13e295 Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Tue, 27 Apr 2021 18:56:02 +0800 Subject: [PATCH 3/4] simple_ota_example: Adds sha256 check for app images --- .../ota/simple_ota_example/example_test.py | 34 ++++++++++++++++++- .../main/simple_ota_example.c | 31 +++++++++++++++++ tools/ci/python_packages/ttfw_idf/IDFDUT.py | 27 +++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/examples/system/ota/simple_ota_example/example_test.py b/examples/system/ota/simple_ota_example/example_test.py index a2666e44bd..223572f595 100644 --- a/examples/system/ota/simple_ota_example/example_test.py +++ b/examples/system/ota/simple_ota_example/example_test.py @@ -6,7 +6,7 @@ import ssl from threading import Thread import ttfw_idf -from tiny_test_fw import DUT +from tiny_test_fw import DUT, Utility server_cert = '-----BEGIN CERTIFICATE-----\n' \ 'MIIDXTCCAkWgAwIBAgIJAP4LF7E72HakMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n'\ @@ -93,6 +93,29 @@ def start_https_server(ota_image_dir, server_ip, server_port): httpd.serve_forever() +def check_sha256(sha256_expected, sha256_reported): + Utility.console_log('sha256_expected: %s' % (sha256_expected)) + Utility.console_log('sha256_reported: %s' % (sha256_reported)) + if sha256_reported not in sha256_expected: + raise ValueError('SHA256 mismatch') + else: + Utility.console_log('SHA256 expected and reported are the same') + + +def calc_all_sha256(dut): + bootloader_path = os.path.join(dut.app.binary_path, 'bootloader', 'bootloader.bin') + output = dut.image_info(bootloader_path) + sha256_bootloader = re.search(r'Validation Hash:\s+([a-f0-9]+)', output).group(1) + Utility.console_log('bootloader SHA256: %s' % sha256_bootloader) + + app_path = os.path.join(dut.app.binary_path, 'simple_ota.bin') + output = dut.image_info(app_path) + sha256_app = re.search(r'Validation Hash:\s+([a-f0-9]+)', output).group(1) + Utility.console_log('app SHA256: %s' % sha256_app) + + return sha256_bootloader, sha256_app + + @ttfw_idf.idf_example_test(env_tag='Example_WIFI') def test_examples_protocol_simple_ota_example(env, extra_data): """ @@ -106,6 +129,7 @@ def test_examples_protocol_simple_ota_example(env, extra_data): binary_file = os.path.join(dut1.app.binary_path, 'simple_ota.bin') bin_size = os.path.getsize(binary_file) ttfw_idf.log_performance('simple_ota_bin_size', '{}KB'.format(bin_size // 1024)) + sha256_bootloader, sha256_app = calc_all_sha256(dut1) # start test host_ip = get_my_ip() thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, 8000)) @@ -113,6 +137,8 @@ def test_examples_protocol_simple_ota_example(env, extra_data): thread1.start() dut1.start_app() dut1.expect('Loaded app from partition at offset 0x10000', timeout=30) + check_sha256(sha256_bootloader, dut1.expect(re.compile(r'SHA-256 for bootloader:\s+([a-f0-9]+)'))[0]) + check_sha256(sha256_app, dut1.expect(re.compile(r'SHA-256 for current firmware:\s+([a-f0-9]+)'))[0]) try: ip_address = dut1.expect(re.compile(r' sta ip: ([^,]+),'), timeout=30) print('Connected to AP with IP: {}'.format(ip_address)) @@ -214,6 +240,7 @@ def test_examples_protocol_simple_ota_example_with_verify_app_signature_on_updat binary_file = os.path.join(dut1.app.binary_path, 'simple_ota.bin') bin_size = os.path.getsize(binary_file) ttfw_idf.log_performance('simple_ota_bin_size', '{}KB'.format(bin_size // 1024)) + sha256_bootloader, sha256_app = calc_all_sha256(dut1) # start test host_ip = get_my_ip() thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, 8000)) @@ -221,6 +248,8 @@ def test_examples_protocol_simple_ota_example_with_verify_app_signature_on_updat thread1.start() dut1.start_app() dut1.expect('Loaded app from partition at offset 0x20000', timeout=30) + check_sha256(sha256_bootloader, dut1.expect(re.compile(r'SHA-256 for bootloader:\s+([a-f0-9]+)'))[0]) + check_sha256(sha256_app, dut1.expect(re.compile(r'SHA-256 for current firmware:\s+([a-f0-9]+)'))[0]) try: ip_address = dut1.expect(re.compile(r' eth ip: ([^,]+),'), timeout=30) print('Connected to AP with IP: {}'.format(ip_address)) @@ -252,6 +281,7 @@ def test_examples_protocol_simple_ota_example_with_verify_app_signature_on_updat binary_file = os.path.join(dut1.app.binary_path, 'simple_ota.bin') bin_size = os.path.getsize(binary_file) ttfw_idf.log_performance('simple_ota_bin_size', '{}KB'.format(bin_size // 1024)) + sha256_bootloader, sha256_app = calc_all_sha256(dut1) # start test host_ip = get_my_ip() thread1 = Thread(target=start_https_server, args=(dut1.app.binary_path, host_ip, 8000)) @@ -259,6 +289,8 @@ def test_examples_protocol_simple_ota_example_with_verify_app_signature_on_updat thread1.start() dut1.start_app() dut1.expect('Loaded app from partition at offset 0x20000', timeout=30) + check_sha256(sha256_bootloader, dut1.expect(re.compile(r'SHA-256 for bootloader:\s+([a-f0-9]+)'))[0]) + check_sha256(sha256_app, dut1.expect(re.compile(r'SHA-256 for current firmware:\s+([a-f0-9]+)'))[0]) try: ip_address = dut1.expect(re.compile(r' eth ip: ([^,]+),'), timeout=30) print('Connected to AP with IP: {}'.format(ip_address)) diff --git a/examples/system/ota/simple_ota_example/main/simple_ota_example.c b/examples/system/ota/simple_ota_example/main/simple_ota_example.c index 1f73b4d832..3e443d4fc9 100644 --- a/examples/system/ota/simple_ota_example/main/simple_ota_example.c +++ b/examples/system/ota/simple_ota_example/main/simple_ota_example.c @@ -25,6 +25,8 @@ #include "esp_wifi.h" #endif +#define HASH_LEN 32 + static const char *TAG = "simple_ota_example"; extern const uint8_t server_cert_pem_start[] asm("_binary_ca_cert_pem_start"); extern const uint8_t server_cert_pem_end[] asm("_binary_ca_cert_pem_end"); @@ -99,6 +101,33 @@ void simple_ota_example_task(void *pvParameter) } } +static void print_sha256(const uint8_t *image_hash, const char *label) +{ + char hash_print[HASH_LEN * 2 + 1]; + hash_print[HASH_LEN * 2] = 0; + for (int i = 0; i < HASH_LEN; ++i) { + sprintf(&hash_print[i * 2], "%02x", image_hash[i]); + } + ESP_LOGI(TAG, "%s %s", label, hash_print); +} + +static void get_sha256_of_partitions(void) +{ + uint8_t sha_256[HASH_LEN] = { 0 }; + esp_partition_t partition; + + // get sha256 digest for bootloader + partition.address = ESP_BOOTLOADER_OFFSET; + partition.size = ESP_PARTITION_TABLE_OFFSET; + partition.type = ESP_PARTITION_TYPE_APP; + esp_partition_get_sha256(&partition, sha_256); + print_sha256(sha_256, "SHA-256 for bootloader: "); + + // get sha256 digest for running partition + esp_partition_get_sha256(esp_ota_get_running_partition(), sha_256); + print_sha256(sha_256, "SHA-256 for current firmware: "); +} + void app_main(void) { // Initialize NVS. @@ -113,6 +142,8 @@ void app_main(void) } ESP_ERROR_CHECK(err); + get_sha256_of_partitions(); + ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); diff --git a/tools/ci/python_packages/ttfw_idf/IDFDUT.py b/tools/ci/python_packages/ttfw_idf/IDFDUT.py index c4fbe61e49..eaafdbde6f 100644 --- a/tools/ci/python_packages/ttfw_idf/IDFDUT.py +++ b/tools/ci/python_packages/ttfw_idf/IDFDUT.py @@ -14,6 +14,7 @@ """ DUT for IDF applications """ import functools +import io import os import os.path import re @@ -308,6 +309,32 @@ class IDFDUT(DUT.SerialDUT): else: raise last_error + def image_info(self, path_to_file): + """ + get hash256 of app + + :param: path: path to file + :return: sha256 appended to app + """ + + old_stdout = sys.stdout + new_stdout = io.StringIO() + sys.stdout = new_stdout + + class Args(object): + def __init__(self, attributes): + for key, value in attributes.items(): + self.__setattr__(key, value) + + args = Args({ + 'chip': self.TARGET, + 'filename': path_to_file, + }) + esptool.image_info(args) + output = new_stdout.getvalue() + sys.stdout = old_stdout + return output + @_uses_esptool def reset(self, esp): """ From 6a501972464b1f8826b89efd81bba98a3d2c5f8c Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Wed, 26 May 2021 17:00:24 +0800 Subject: [PATCH 4/4] bootloader: Fix a wrong offset in image_load after refactoring --- components/bootloader_support/src/esp_image_format.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/bootloader_support/src/esp_image_format.c b/components/bootloader_support/src/esp_image_format.c index 707b64fdbc..1dc2e030fc 100644 --- a/components/bootloader_support/src/esp_image_format.c +++ b/components/bootloader_support/src/esp_image_format.c @@ -144,7 +144,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ // For secure boot V1 on ESP32, we don't calculate SHA or 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.) - bool verify_sha = (data->start_addr != ESP_BOOTLOADER_OFFSET) && do_verify; + bool verify_sha = (part->offset != ESP_BOOTLOADER_OFFSET) && do_verify; #endif if (data == NULL || part == NULL) {