Merge branch 'bugfix/esp_partition_get_sha256' into 'master'

bootloader_support: Fix bootloader_common_get_sha256_of_partition when CHECK_SIGNATURE is on

Closes IDFGH-5089

See merge request espressif/esp-idf!12795
This commit is contained in:
Angus Gratton 2021-05-07 23:34:56 +00:00
commit 84f2f2932d
5 changed files with 292 additions and 218 deletions

View File

@ -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) {

View File

@ -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 = (data->start_addr != 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,63 +285,52 @@ 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 (nothing flashed here?)", src_addr);
}
FAIL_LOAD("image at 0x%x has invalid magic byte (nothing flashed here?)", src_addr);
}
// 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;
}
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;
}
}
return err;
}
@ -544,6 +455,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;
@ -564,10 +532,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);
@ -598,17 +563,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;
}
@ -783,41 +745,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...");
@ -898,15 +883,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;
}

View File

@ -7,7 +7,7 @@ import sys
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'\
@ -92,6 +92,29 @@ def start_https_server(ota_image_dir, server_ip, server_port, server_file=None,
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_OTA')
def test_examples_protocol_simple_ota_example(env, extra_data):
"""
@ -105,6 +128,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))
@ -112,6 +136,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))
@ -248,6 +274,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))
@ -255,6 +282,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))
@ -286,6 +315,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))
@ -293,6 +323,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))

View File

@ -25,6 +25,8 @@
#include "esp_wifi.h"
#endif
#define HASH_LEN 32
#ifdef CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF
/* The interface name value can refer to if_desc in esp_netif_defaults.h */
#if CONFIG_EXAMPLE_FIRMWARE_UPGRADE_BIND_IF_ETH
@ -120,6 +122,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.
@ -134,6 +163,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());

View File

@ -14,6 +14,7 @@
""" DUT for IDF applications """
import functools
import io
import os
import os.path
import re
@ -309,6 +310,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):
"""