app_update: Add API for getting sha256_of_partition

Added bootloader_common_get_sha256_of_partition() and esp_partition_get_sha256() - get or calculate SHA-256
digest for app and data partitions.
Added bootloader_sha256_hex_to_str() - helps to print SHA-256 digest
Added esp_partition_check_identity() - compares two partitions by SHA-256 digest

Refactoring a function esp_image_load() in bootloader space to esp_image_verify() and
bootloader_load_image(). Old name function esp_image_load is deprecated
and will remove in V4.0 version.

spi_flash/sim: Fix error test_host. Add stub for bootloader_common_get_sha256_of_partition in sim/stubs
This commit is contained in:
Konstantin Kondrashov
2018-05-30 14:08:00 +05:00
parent 886bf4b88e
commit 117c79eae5
17 changed files with 354 additions and 26 deletions

View File

@@ -68,3 +68,26 @@ bool bootloader_common_erase_part_type_data(const char *list_erase, bool ota_dat
* @return Returns true if the list contains the label, false otherwise.
*/
bool bootloader_common_label_search(const char *list, char *label);
/**
* @brief Calculates a sha-256 for a given partition or returns a appended digest.
*
* This function can be used to return the SHA-256 digest of application, bootloader and data partitions.
* For apps with SHA-256 appended to the app image, the result is the appended SHA-256 value for the app image content.
* The hash is verified before returning, if app content is invalid then the function returns ESP_ERR_IMAGE_INVALID.
* For apps without SHA-256 appended to the image, the result is the SHA-256 of all bytes in the app image.
* For other partition types, the result is the SHA-256 of the entire partition.
*
* @param[in] address Address of partition.
* @param[in] size Size of partition.
* @param[in] type Type of partition. For applications the type is 0, otherwise type is data.
* @param[out] out_sha_256 Returned SHA-256 digest for a given partition.
*
* @return
* - ESP_OK: In case of successful operation.
* - ESP_ERR_INVALID_ARG: The size was 0 or the sha_256 was NULL.
* - ESP_ERR_NO_MEM: Cannot allocate memory for sha256 operation.
* - ESP_ERR_IMAGE_INVALID: App partition doesn't contain a valid app image.
* - ESP_FAIL: An allocation error occurred.
*/
esp_err_t bootloader_common_get_sha256_of_partition(uint32_t address, uint32_t size, int type, uint8_t *out_sha_256);

View File

@@ -96,11 +96,12 @@ typedef struct {
esp_image_segment_header_t segments[ESP_IMAGE_MAX_SEGMENTS]; /* Per-segment header data */
uint32_t segment_data[ESP_IMAGE_MAX_SEGMENTS]; /* Data offsets for each segment */
uint32_t image_len; /* Length of image on flash, in bytes */
uint8_t image_digest[32]; /* appended SHA-256 digest */
} esp_image_metadata_t;
/* Mode selection for esp_image_load() */
typedef enum {
ESP_IMAGE_VERIFY, /* Verify image contents, load metadata. Print errorsors. */
ESP_IMAGE_VERIFY, /* Verify image contents, load metadata. Print errors. */
ESP_IMAGE_VERIFY_SILENT, /* Verify image contents, load metadata. Don't print errors. */
#ifdef BOOTLOADER_BUILD
ESP_IMAGE_LOAD, /* Verify image contents, load to memory. Print errors. */
@@ -110,6 +111,11 @@ typedef enum {
/**
* @brief Verify and (optionally, in bootloader mode) load an app image.
*
* This name is deprecated and is included for compatibility with the ESP-IDF v3.x API.
* It will be removed in V4.0 version.
* Function has been renamed to esp_image_verify().
* Use function esp_image_verify() to verify a image. And use function bootloader_load_image() to load image from a bootloader space.
*
* If encryption is enabled, data will be transparently decrypted.
*
* @param mode Mode of operation (verify, silent verify, or load).
@@ -130,7 +136,60 @@ typedef enum {
* - ESP_ERR_IMAGE_INVALID if the image appears invalid.
* - ESP_ERR_INVALID_ARG if the partition or data pointers are invalid.
*/
esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data);
esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data) __attribute__((deprecated));
/**
* @brief Verify an app image.
*
* If encryption is enabled, data will be transparently decrypted.
*
* @param mode Mode of operation (verify, silent verify, or load).
* @param part Partition to load the app from.
* @param[inout] data Pointer to the image metadata structure which is be filled in by this function.
* 'start_addr' member should be set (to the start address of the image.)
* Other fields will all be initialised by this function.
*
* Image validation checks:
* - Magic byte.
* - Partition smaller than 16MB.
* - All segments & image fit in partition.
* - 8 bit image checksum is valid.
* - SHA-256 of image is valid (if image has this appended).
* - (Signature) if signature verification is enabled.
*
* @return
* - ESP_OK if verify or load was successful
* - ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs
* - ESP_ERR_IMAGE_INVALID if the image appears invalid.
* - ESP_ERR_INVALID_ARG if the partition or data pointers are invalid.
*/
esp_err_t esp_image_verify(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data);
/**
* @brief Verify and load an app image (available only in space of bootloader).
*
* If encryption is enabled, data will be transparently decrypted.
*
* @param part Partition to load the app from.
* @param[inout] data Pointer to the image metadata structure which is be filled in by this function.
* 'start_addr' member should be set (to the start address of the image.)
* Other fields will all be initialised by this function.
*
* Image validation checks:
* - Magic byte.
* - Partition smaller than 16MB.
* - All segments & image fit in partition.
* - 8 bit image checksum is valid.
* - SHA-256 of image is valid (if image has this appended).
* - (Signature) if signature verification is enabled.
*
* @return
* - ESP_OK if verify or load was successful
* - ESP_ERR_IMAGE_FLASH_FAIL if a SPI flash error occurs
* - ESP_ERR_IMAGE_INVALID if the image appears invalid.
* - ESP_ERR_INVALID_ARG if the partition or data pointers are invalid.
*/
esp_err_t bootloader_load_image(const esp_partition_pos_t *part, esp_image_metadata_t *data);
/**
* @brief Verify the bootloader image.

View File

@@ -22,6 +22,7 @@
#include <stdint.h>
#include <stdlib.h>
#include "esp_err.h"
typedef void *bootloader_sha256_handle_t;
@@ -30,3 +31,26 @@ bootloader_sha256_handle_t bootloader_sha256_start();
void bootloader_sha256_data(bootloader_sha256_handle_t handle, const void *data, size_t data_len);
void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest);
/**
* @brief Converts an array to a printable string.
*
* This function is useful for printing SHA-256 digest.
* \code{c}
* // Example of using. image_hash will be printed
* #define HASH_LEN 32 // SHA-256 digest length
* ...
* char hash_print[HASH_LEN * 2 + 1];
* hash_print[HASH_LEN * 2] = 0;
* bootloader_sha256_hex_to_str(hash_print, image_hash, HASH_LEN);
* ESP_LOGI(TAG, %s", hash_print);
* \endcode
* @param[out] out_str Output string
* @param[in] in_array_hex Pointer to input array
* @param[in] len Length of input array
*
* @return ESP_OK: Successful
* ESP_ERR_INVALID_ARG: Error in the passed arguments
*/
esp_err_t bootloader_sha256_hex_to_str(char *out_str, const uint8_t *in_array_hex, size_t len);

View File

@@ -27,6 +27,10 @@
#include "bootloader_flash.h"
#include "bootloader_common.h"
#include "soc/gpio_periph.h"
#include "esp_image_format.h"
#include "bootloader_sha.h"
#define ESP_PARTITION_HASH_LEN 32 /* SHA-256 digest length */
static const char* TAG = "boot_comm";
@@ -145,3 +149,46 @@ bool bootloader_common_erase_part_type_data(const char *list_erase, bool ota_dat
return ret;
}
esp_err_t bootloader_common_get_sha256_of_partition (uint32_t address, uint32_t size, int type, uint8_t *out_sha_256)
{
if (out_sha_256 == NULL || size == 0) {
return ESP_ERR_INVALID_ARG;
}
if (type == PART_TYPE_APP) {
const esp_partition_pos_t partition_pos = {
.offset = address,
.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) {
return ESP_ERR_IMAGE_INVALID;
}
if (data.image.hash_appended) {
memcpy(out_sha_256, data.image_digest, ESP_PARTITION_HASH_LEN);
return ESP_OK;
}
// If image doesn't have a appended hash then hash calculates for entire image.
size = data.image_len;
}
// If image is type by data then hash is calculated for entire image.
const void *partition_bin = bootloader_mmap(address, size);
if (partition_bin == NULL) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", address, size);
return ESP_FAIL;
}
bootloader_sha256_handle_t sha_handle = bootloader_sha256_start();
if (sha_handle == NULL) {
bootloader_munmap(partition_bin);
return ESP_ERR_NO_MEM;
}
bootloader_sha256_data(sha_handle, partition_bin, size);
bootloader_sha256_finish(sha_handle, out_sha_256);
bootloader_munmap(partition_bin);
return ESP_OK;
}

View File

@@ -169,3 +169,21 @@ void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest
}
#endif
esp_err_t bootloader_sha256_hex_to_str(char *out_str, const uint8_t *in_array_hex, size_t len)
{
if (out_str == NULL || in_array_hex == NULL || len == 0) {
return ESP_ERR_INVALID_ARG;
}
for (int i = 0; i < len; i++) {
for (int shift = 0; shift < 2; shift++) {
uint8_t nibble = (in_array_hex[i] >> (shift ? 0 : 4)) & 0x0F;
if (nibble < 10) {
out_str[i*2+shift] = '0' + nibble;
} else {
out_str[i*2+shift] = 'a' + nibble - 10;
}
}
}
return ESP_OK;
}

View File

@@ -50,6 +50,7 @@
#include "bootloader_config.h"
#include "bootloader_common.h"
#include "bootloader_utility.h"
#include "bootloader_sha.h"
static const char* TAG = "boot";
@@ -265,7 +266,7 @@ static bool try_load_partition(const esp_partition_pos_t *partition, esp_image_m
return false;
}
#ifdef BOOTLOADER_BUILD
if (esp_image_load(ESP_IMAGE_LOAD, partition, data) == ESP_OK) {
if (bootloader_load_image(partition, data) == ESP_OK) {
ESP_LOGI(TAG, "Loaded app from partition at offset 0x%x",
partition->offset);
return true;

View File

@@ -75,7 +75,7 @@ static esp_err_t verify_checksum(bootloader_sha256_handle_t sha_handle, uint32_t
static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
{
#ifdef BOOTLOADER_BUILD
bool do_load = (mode == ESP_IMAGE_LOAD);
@@ -128,7 +128,7 @@ esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *
err = verify_image_header(data->start_addr, &data->image, silent);
if (err != ESP_OK) {
goto err;
goto err;
}
if (data->image.segment_count > ESP_IMAGE_MAX_SEGMENTS) {
@@ -189,6 +189,17 @@ goto err;
bootloader_sha256_finish(sha_handle, NULL);
}
}
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);
}
sha_handle = NULL;
if (err != ESP_OK) {
goto err;
@@ -224,6 +235,22 @@ goto err;
return err;
}
esp_err_t bootloader_load_image(const esp_partition_pos_t *part, esp_image_metadata_t *data)
{
#ifdef BOOTLOADER_BUILD
return image_load(ESP_IMAGE_LOAD, part, data);
#else
return ESP_FAIL;
#endif
}
esp_err_t esp_image_verify(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data)
{
return image_load(mode, part, data);
}
esp_err_t esp_image_load(esp_image_load_mode_t mode, const esp_partition_pos_t *part, esp_image_metadata_t *data) __attribute__((alias("esp_image_verify")));
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;
@@ -446,7 +473,7 @@ esp_err_t esp_image_verify_bootloader(uint32_t *length)
.offset = ESP_BOOTLOADER_OFFSET,
.size = ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET,
};
esp_err_t err = esp_image_load(ESP_IMAGE_VERIFY,
esp_err_t err = esp_image_verify(ESP_IMAGE_VERIFY,
&bootloader_part,
&data);
if (length != NULL) {
@@ -556,18 +583,9 @@ static esp_err_t verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_i
static void debug_log_hash(const uint8_t *image_hash, const char *label)
{
#if BOOT_LOG_LEVEL >= LOG_LEVEL_DEBUG
char hash_print[sizeof(image_hash)*2 + 1];
hash_print[sizeof(image_hash)*2] = 0;
for (int i = 0; i < sizeof(image_hash); i++) {
for (int shift = 0; shift < 2; shift++) {
uint8_t nibble = (image_hash[i] >> (shift ? 0 : 4)) & 0x0F;
if (nibble < 10) {
hash_print[i*2+shift] = '0' + nibble;
} else {
hash_print[i*2+shift] = 'a' + nibble - 10;
}
}
}
ESP_LOGD(TAG, "%s: %s", label, hash_print);
char hash_print[HASH_LEN * 2 + 1];
hash_print[HASH_LEN * 2] = 0;
bootloader_sha256_hex_to_str(hash_print, image_hash, HASH_LEN);
ESP_LOGD(TAG, "%s: %s", label, hash_print);
#endif
}

View File

@@ -281,7 +281,7 @@ static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partit
if (partition->type == PART_TYPE_APP) {
/* check if the partition holds a valid unencrypted app */
esp_image_metadata_t data_ignored;
err = esp_image_load(ESP_IMAGE_VERIFY,
err = esp_image_verify(ESP_IMAGE_VERIFY,
&partition->pos,
&data_ignored);
should_encrypt = (err == ESP_OK);

View File

@@ -25,7 +25,7 @@ TEST_CASE("Verify bootloader image in flash", "[bootloader_support]")
.size = ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET,
};
esp_image_metadata_t data = { 0 };
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_load(ESP_IMAGE_VERIFY, &fake_bootloader_partition, &data));
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_verify(ESP_IMAGE_VERIFY, &fake_bootloader_partition, &data));
TEST_ASSERT_NOT_EQUAL(0, data.image_len);
uint32_t bootloader_length = 0;
@@ -43,7 +43,7 @@ TEST_CASE("Verify unit test app image", "[bootloader_support]")
.size = running->size,
};
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_load(ESP_IMAGE_VERIFY, &running_pos, &data));
TEST_ASSERT_EQUAL_HEX(ESP_OK, esp_image_verify(ESP_IMAGE_VERIFY, &running_pos, &data));
TEST_ASSERT_NOT_EQUAL(0, data.image_len);
TEST_ASSERT_TRUE(data.image_len <= running->size);
}