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 436dbb4902..1dc2e030fc 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_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); @@ -116,13 +130,22 @@ 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 }; #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 = (part->offset != ESP_BOOTLOADER_OFFSET) && do_verify; +#endif if (data == NULL || part == NULL) { return ESP_ERR_INVALID_ARG; @@ -133,142 +156,41 @@ 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; - } - - 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; -#else // Secure boot not enabled - 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 - // secure boot images have a signature appended + bootloader_sha256_handle_t *p_sha_handle = &sha_handle; + 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_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 (verify_sha) { +#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()) { + // 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 - if (true) { + bool do_verify_sig = true; #endif // end checking for JTAG - 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); -#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); + 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 } - } // do_verify +#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 + } +#endif // SECURE_BOOT_CHECK_SIGNATURE + } // verify_sha + + // 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; @@ -276,7 +198,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_ #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 +229,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; @@ -363,52 +285,37 @@ 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) { 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 +329,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 +466,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 +543,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 +574,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 +756,64 @@ 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_check_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_check_checksum || sha_handle != NULL) { + CHECK_ERR(bootloader_flash_read(data->start_addr + unpadded_length, buf, length, true)); + } + 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 - unpadded_length); + bootloader_sha256_data(sha_handle, buf, 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 +894,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; } 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): """