mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-01 19:54:32 +02:00
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:
@@ -235,7 +235,7 @@ esp_err_t esp_ota_end(esp_ota_handle_t handle)
|
|||||||
.size = it->part->size,
|
.size = it->part->size,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (esp_image_load(ESP_IMAGE_VERIFY, &part_pos, &data) != ESP_OK) {
|
if (esp_image_verify(ESP_IMAGE_VERIFY, &part_pos, &data) != ESP_OK) {
|
||||||
ret = ESP_ERR_OTA_VALIDATE_FAILED;
|
ret = ESP_ERR_OTA_VALIDATE_FAILED;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@@ -372,7 +372,7 @@ esp_err_t esp_ota_set_boot_partition(const esp_partition_t *partition)
|
|||||||
.offset = partition->address,
|
.offset = partition->address,
|
||||||
.size = partition->size,
|
.size = partition->size,
|
||||||
};
|
};
|
||||||
if (esp_image_load(ESP_IMAGE_VERIFY, &part_pos, &data) != ESP_OK) {
|
if (esp_image_verify(ESP_IMAGE_VERIFY, &part_pos, &data) != ESP_OK) {
|
||||||
return ESP_ERR_OTA_VALIDATE_FAILED;
|
return ESP_ERR_OTA_VALIDATE_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -133,7 +133,7 @@ esp_err_t esp_ota_set_boot_partition(const esp_partition_t* partition);
|
|||||||
* If the OTA data partition is not present or not valid then the result is the first app partition found in the
|
* If the OTA data partition is not present or not valid then the result is the first app partition found in the
|
||||||
* partition table. In priority order, this means: the factory app, the first OTA app slot, or the test app partition.
|
* partition table. In priority order, this means: the factory app, the first OTA app slot, or the test app partition.
|
||||||
*
|
*
|
||||||
* Note that there is no guarantee the returned partition is a valid app. Use esp_image_load(ESP_IMAGE_VERIFY, ...) to verify if the
|
* Note that there is no guarantee the returned partition is a valid app. Use esp_image_verify(ESP_IMAGE_VERIFY, ...) to verify if the
|
||||||
* returned partition contains a bootable image.
|
* returned partition contains a bootable image.
|
||||||
*
|
*
|
||||||
* @return Pointer to info for partition structure, or NULL if partition table is invalid or a flash read operation failed. Any returned pointer is valid for the lifetime of the application.
|
* @return Pointer to info for partition structure, or NULL if partition table is invalid or a flash read operation failed. Any returned pointer is valid for the lifetime of the application.
|
||||||
|
@@ -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.
|
* @return Returns true if the list contains the label, false otherwise.
|
||||||
*/
|
*/
|
||||||
bool bootloader_common_label_search(const char *list, char *label);
|
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);
|
||||||
|
@@ -96,11 +96,12 @@ typedef struct {
|
|||||||
esp_image_segment_header_t segments[ESP_IMAGE_MAX_SEGMENTS]; /* Per-segment header data */
|
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 segment_data[ESP_IMAGE_MAX_SEGMENTS]; /* Data offsets for each segment */
|
||||||
uint32_t image_len; /* Length of image on flash, in bytes */
|
uint32_t image_len; /* Length of image on flash, in bytes */
|
||||||
|
uint8_t image_digest[32]; /* appended SHA-256 digest */
|
||||||
} esp_image_metadata_t;
|
} esp_image_metadata_t;
|
||||||
|
|
||||||
/* Mode selection for esp_image_load() */
|
/* Mode selection for esp_image_load() */
|
||||||
typedef enum {
|
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. */
|
ESP_IMAGE_VERIFY_SILENT, /* Verify image contents, load metadata. Don't print errors. */
|
||||||
#ifdef BOOTLOADER_BUILD
|
#ifdef BOOTLOADER_BUILD
|
||||||
ESP_IMAGE_LOAD, /* Verify image contents, load to memory. Print errors. */
|
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.
|
* @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.
|
* If encryption is enabled, data will be transparently decrypted.
|
||||||
*
|
*
|
||||||
* @param mode Mode of operation (verify, silent verify, or load).
|
* @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_IMAGE_INVALID if the image appears invalid.
|
||||||
* - ESP_ERR_INVALID_ARG if the partition or data pointers are 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.
|
* @brief Verify the bootloader image.
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
typedef void *bootloader_sha256_handle_t;
|
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_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);
|
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);
|
||||||
|
@@ -27,6 +27,10 @@
|
|||||||
#include "bootloader_flash.h"
|
#include "bootloader_flash.h"
|
||||||
#include "bootloader_common.h"
|
#include "bootloader_common.h"
|
||||||
#include "soc/gpio_periph.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";
|
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;
|
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;
|
||||||
|
}
|
||||||
|
@@ -169,3 +169,21 @@ void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#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;
|
||||||
|
}
|
||||||
|
@@ -50,6 +50,7 @@
|
|||||||
#include "bootloader_config.h"
|
#include "bootloader_config.h"
|
||||||
#include "bootloader_common.h"
|
#include "bootloader_common.h"
|
||||||
#include "bootloader_utility.h"
|
#include "bootloader_utility.h"
|
||||||
|
#include "bootloader_sha.h"
|
||||||
|
|
||||||
static const char* TAG = "boot";
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
#ifdef BOOTLOADER_BUILD
|
#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",
|
ESP_LOGI(TAG, "Loaded app from partition at offset 0x%x",
|
||||||
partition->offset);
|
partition->offset);
|
||||||
return true;
|
return true;
|
||||||
|
@@ -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_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);
|
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
|
#ifdef BOOTLOADER_BUILD
|
||||||
bool do_load = (mode == ESP_IMAGE_LOAD);
|
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);
|
err = verify_image_header(data->start_addr, &data->image, silent);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data->image.segment_count > ESP_IMAGE_MAX_SEGMENTS) {
|
if (data->image.segment_count > ESP_IMAGE_MAX_SEGMENTS) {
|
||||||
@@ -189,6 +189,17 @@ goto err;
|
|||||||
bootloader_sha256_finish(sha_handle, NULL);
|
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;
|
sha_handle = NULL;
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
goto err;
|
goto err;
|
||||||
@@ -224,6 +235,22 @@ goto err;
|
|||||||
return 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)
|
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_err_t err = ESP_OK;
|
||||||
@@ -446,7 +473,7 @@ esp_err_t esp_image_verify_bootloader(uint32_t *length)
|
|||||||
.offset = ESP_BOOTLOADER_OFFSET,
|
.offset = ESP_BOOTLOADER_OFFSET,
|
||||||
.size = ESP_PARTITION_TABLE_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,
|
&bootloader_part,
|
||||||
&data);
|
&data);
|
||||||
if (length != NULL) {
|
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)
|
static void debug_log_hash(const uint8_t *image_hash, const char *label)
|
||||||
{
|
{
|
||||||
#if BOOT_LOG_LEVEL >= LOG_LEVEL_DEBUG
|
#if BOOT_LOG_LEVEL >= LOG_LEVEL_DEBUG
|
||||||
char hash_print[sizeof(image_hash)*2 + 1];
|
char hash_print[HASH_LEN * 2 + 1];
|
||||||
hash_print[sizeof(image_hash)*2] = 0;
|
hash_print[HASH_LEN * 2] = 0;
|
||||||
for (int i = 0; i < sizeof(image_hash); i++) {
|
bootloader_sha256_hex_to_str(hash_print, image_hash, HASH_LEN);
|
||||||
for (int shift = 0; shift < 2; shift++) {
|
ESP_LOGD(TAG, "%s: %s", label, hash_print);
|
||||||
uint8_t nibble = (image_hash[i] >> (shift ? 0 : 4)) & 0x0F;
|
|
||||||
if (nibble < 10) {
|
|
||||||
hash_print[i*2+shift] = '0' + nibble;
|
|
||||||
} else {
|
|
||||||
hash_print[i*2+shift] = 'a' + nibble - 10;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_LOGD(TAG, "%s: %s", label, hash_print);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@@ -281,7 +281,7 @@ static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partit
|
|||||||
if (partition->type == PART_TYPE_APP) {
|
if (partition->type == PART_TYPE_APP) {
|
||||||
/* check if the partition holds a valid unencrypted app */
|
/* check if the partition holds a valid unencrypted app */
|
||||||
esp_image_metadata_t data_ignored;
|
esp_image_metadata_t data_ignored;
|
||||||
err = esp_image_load(ESP_IMAGE_VERIFY,
|
err = esp_image_verify(ESP_IMAGE_VERIFY,
|
||||||
&partition->pos,
|
&partition->pos,
|
||||||
&data_ignored);
|
&data_ignored);
|
||||||
should_encrypt = (err == ESP_OK);
|
should_encrypt = (err == ESP_OK);
|
||||||
|
@@ -25,7 +25,7 @@ TEST_CASE("Verify bootloader image in flash", "[bootloader_support]")
|
|||||||
.size = ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET,
|
.size = ESP_PARTITION_TABLE_OFFSET - ESP_BOOTLOADER_OFFSET,
|
||||||
};
|
};
|
||||||
esp_image_metadata_t data = { 0 };
|
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);
|
TEST_ASSERT_NOT_EQUAL(0, data.image_len);
|
||||||
|
|
||||||
uint32_t bootloader_length = 0;
|
uint32_t bootloader_length = 0;
|
||||||
@@ -43,7 +43,7 @@ TEST_CASE("Verify unit test app image", "[bootloader_support]")
|
|||||||
.size = running->size,
|
.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_NOT_EQUAL(0, data.image_len);
|
||||||
TEST_ASSERT_TRUE(data.image_len <= running->size);
|
TEST_ASSERT_TRUE(data.image_len <= running->size);
|
||||||
}
|
}
|
||||||
|
@@ -286,6 +286,37 @@ esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset,
|
|||||||
spi_flash_mmap_memory_t memory,
|
spi_flash_mmap_memory_t memory,
|
||||||
const void** out_ptr, spi_flash_mmap_handle_t* out_handle);
|
const void** out_ptr, spi_flash_mmap_handle_t* out_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get SHA-256 digest for required partition.
|
||||||
|
*
|
||||||
|
* 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] partition Pointer to info for partition containing app or data. (fields: address, size and type, are required to be filled).
|
||||||
|
* @param[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 esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sha_256);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check for the identity of two partitions by SHA-256 digest.
|
||||||
|
*
|
||||||
|
* @param[in] partition_1 Pointer to info for partition 1 containing app or data. (fields: address, size and type, are required to be filled).
|
||||||
|
* @param[in] partition_2 Pointer to info for partition 2 containing app or data. (fields: address, size and type, are required to be filled).
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - True: In case of the two firmware is equal.
|
||||||
|
* - False: Otherwise
|
||||||
|
*/
|
||||||
|
bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_partition_t *partition_2);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,9 @@
|
|||||||
#include "esp_partition.h"
|
#include "esp_partition.h"
|
||||||
#include "esp_flash_encrypt.h"
|
#include "esp_flash_encrypt.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "bootloader_common.h"
|
||||||
|
|
||||||
|
#define HASH_LEN 32 /* SHA-256 digest length */
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// Enable built-in checks in queue.h in debug builds
|
// Enable built-in checks in queue.h in debug builds
|
||||||
@@ -322,3 +324,23 @@ esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset,
|
|||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_partition_get_sha256(const esp_partition_t *partition, uint8_t *sha_256)
|
||||||
|
{
|
||||||
|
return bootloader_common_get_sha256_of_partition(partition->address, partition->size, partition->type, sha_256);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool esp_partition_check_identity(const esp_partition_t *partition_1, const esp_partition_t *partition_2)
|
||||||
|
{
|
||||||
|
uint8_t sha_256[2][HASH_LEN] = { 0 };
|
||||||
|
|
||||||
|
if (esp_partition_get_sha256(partition_1, sha_256[0]) == ESP_OK &&
|
||||||
|
esp_partition_get_sha256(partition_2, sha_256[1]) == ESP_OK) {
|
||||||
|
|
||||||
|
if (memcmp(sha_256[0], sha_256[1], HASH_LEN) == 0) {
|
||||||
|
// The partitions are identity
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@@ -3,7 +3,8 @@ SOURCE_FILES := \
|
|||||||
log/log.c \
|
log/log.c \
|
||||||
newlib/lock.c \
|
newlib/lock.c \
|
||||||
esp32/crc.cpp \
|
esp32/crc.cpp \
|
||||||
esp32/esp_random.c
|
esp32/esp_random.c \
|
||||||
|
bootloader_support/src/bootloader_common.c
|
||||||
|
|
||||||
INCLUDE_DIRS := \
|
INCLUDE_DIRS := \
|
||||||
../include \
|
../include \
|
||||||
|
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
esp_err_t bootloader_common_get_sha256_of_partition(uint32_t address, uint32_t size, int type, uint8_t *out_sha_256);
|
@@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
esp_err_t bootloader_common_get_sha256_of_partition (uint32_t address, uint32_t size, int type, uint8_t *out_sha_256)
|
||||||
|
{
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
@@ -16,6 +16,8 @@
|
|||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_ota_ops.h"
|
#include "esp_ota_ops.h"
|
||||||
#include "esp_http_client.h"
|
#include "esp_http_client.h"
|
||||||
|
#include "esp_flash_partitions.h"
|
||||||
|
#include "esp_partition.h"
|
||||||
|
|
||||||
#include "nvs.h"
|
#include "nvs.h"
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
@@ -24,6 +26,7 @@
|
|||||||
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
|
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD
|
||||||
#define EXAMPLE_SERVER_URL CONFIG_FIRMWARE_UPG_URL
|
#define EXAMPLE_SERVER_URL CONFIG_FIRMWARE_UPG_URL
|
||||||
#define BUFFSIZE 1024
|
#define BUFFSIZE 1024
|
||||||
|
#define HASH_LEN 32 /* SHA-256 digest length */
|
||||||
|
|
||||||
static const char *TAG = "native_ota_example";
|
static const char *TAG = "native_ota_example";
|
||||||
/*an ota data write buffer ready to write to the flash*/
|
/*an ota data write buffer ready to write to the flash*/
|
||||||
@@ -96,6 +99,16 @@ static void __attribute__((noreturn)) task_fatal_error()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 ota_example_task(void *pvParameter)
|
static void ota_example_task(void *pvParameter)
|
||||||
{
|
{
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
@@ -181,6 +194,17 @@ static void ota_example_task(void *pvParameter)
|
|||||||
http_cleanup(client);
|
http_cleanup(client);
|
||||||
task_fatal_error();
|
task_fatal_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (esp_partition_check_identity(esp_ota_get_running_partition(), update_partition) == true) {
|
||||||
|
ESP_LOGI(TAG, "The current running firmware is same as the firmware just downloaded");
|
||||||
|
int i = 0;
|
||||||
|
ESP_LOGI(TAG, "When a new firmware is available on the server, press the reset button to download it");
|
||||||
|
while(1) {
|
||||||
|
ESP_LOGI(TAG, "Waiting for a new firmware ... %d", ++i);
|
||||||
|
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
err = esp_ota_set_boot_partition(update_partition);
|
err = esp_ota_set_boot_partition(update_partition);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
|
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
|
||||||
@@ -194,6 +218,27 @@ static void ota_example_task(void *pvParameter)
|
|||||||
|
|
||||||
void app_main()
|
void app_main()
|
||||||
{
|
{
|
||||||
|
uint8_t sha_256[HASH_LEN] = { 0 };
|
||||||
|
esp_partition_t partition;
|
||||||
|
|
||||||
|
// get sha256 digest for the partition table
|
||||||
|
partition.address = ESP_PARTITION_TABLE_OFFSET;
|
||||||
|
partition.size = ESP_PARTITION_TABLE_MAX_LEN;
|
||||||
|
partition.type = ESP_PARTITION_TYPE_DATA;
|
||||||
|
esp_partition_get_sha256(&partition, sha_256);
|
||||||
|
print_sha256(sha_256, "SHA-256 for the partition table: ");
|
||||||
|
|
||||||
|
// 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: ");
|
||||||
|
|
||||||
// Initialize NVS.
|
// Initialize NVS.
|
||||||
esp_err_t err = nvs_flash_init();
|
esp_err_t err = nvs_flash_init();
|
||||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||||
|
Reference in New Issue
Block a user