mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
feat(esp_image_verify): split image verification out of bootloader_support
This commit is contained in:
committed by
Mahavir Jain
parent
b912691fcd
commit
e7af5c69d9
287
components/esp_image_verify/src/bootloader_sha.c
Normal file
287
components/esp_image_verify/src/bootloader_sha.c
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "bootloader_sha.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "rom/sha.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if NON_OS_BUILD || CONFIG_APP_BUILD_TYPE_RAM
|
||||
#if !CONFIG_IDF_TARGET_ESP32
|
||||
static SHA_CTX ctx;
|
||||
|
||||
bootloader_sha256_handle_t bootloader_sha256_start()
|
||||
{
|
||||
// Enable SHA hardware
|
||||
ets_sha_enable();
|
||||
ets_sha_init(&ctx, SHA2_256);
|
||||
return &ctx; // Meaningless non-NULL value
|
||||
}
|
||||
|
||||
void bootloader_sha256_data(bootloader_sha256_handle_t handle, const void *data, size_t data_len)
|
||||
{
|
||||
ets_sha_update(&ctx, data, data_len, false);
|
||||
}
|
||||
|
||||
void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest)
|
||||
{
|
||||
if (digest == NULL) {
|
||||
bzero(&ctx, sizeof(ctx));
|
||||
return;
|
||||
}
|
||||
ets_sha_finish(&ctx, digest);
|
||||
}
|
||||
|
||||
#if SOC_SHA_SUPPORT_SHA512
|
||||
bootloader_sha_handle_t bootloader_sha512_start(bool is384)
|
||||
{
|
||||
// Enable SHA hardware
|
||||
ets_sha_enable();
|
||||
ets_sha_init(&ctx, is384 ? SHA2_384 : SHA2_512);
|
||||
return &ctx; // Meaningless non-NULL value
|
||||
}
|
||||
|
||||
void bootloader_sha512_data(bootloader_sha_handle_t handle, const void *data, size_t data_len)
|
||||
{
|
||||
ets_sha_update(&ctx, data, data_len, false);
|
||||
}
|
||||
|
||||
void bootloader_sha512_finish(bootloader_sha_handle_t handle, uint8_t *digest)
|
||||
{
|
||||
if (digest == NULL) {
|
||||
bzero(&ctx, sizeof(ctx));
|
||||
return;
|
||||
}
|
||||
ets_sha_finish(&ctx, digest);
|
||||
}
|
||||
#endif /* SOC_SHA_SUPPORT_SHA512 */
|
||||
#else /* !CONFIG_IDF_TARGET_ESP32 */
|
||||
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/hwcrypto_periph.h"
|
||||
|
||||
static uint32_t words_hashed;
|
||||
|
||||
// Words per SHA256 block
|
||||
static const size_t BLOCK_WORDS = (64 / sizeof(uint32_t));
|
||||
// Words in final SHA256 digest
|
||||
static const size_t DIGEST_WORDS = (32 / sizeof(uint32_t));
|
||||
|
||||
bootloader_sha256_handle_t bootloader_sha256_start(void)
|
||||
{
|
||||
// Enable SHA hardware
|
||||
ets_sha_enable();
|
||||
words_hashed = 0;
|
||||
return (bootloader_sha256_handle_t)&words_hashed; // Meaningless non-NULL value
|
||||
}
|
||||
|
||||
void bootloader_sha256_data(bootloader_sha256_handle_t handle, const void *data, size_t data_len)
|
||||
{
|
||||
assert(data_len % 4 == 0);
|
||||
|
||||
const uint32_t *w = (const uint32_t *)data;
|
||||
size_t word_len = data_len / 4;
|
||||
uint32_t *sha_text_reg = (uint32_t *)(SHA_TEXT_BASE);
|
||||
|
||||
while (word_len > 0) {
|
||||
size_t block_count = words_hashed % BLOCK_WORDS;
|
||||
size_t copy_words = (BLOCK_WORDS - block_count);
|
||||
|
||||
copy_words = MIN(word_len, copy_words);
|
||||
|
||||
// Wait for SHA engine idle
|
||||
while (_DPORT_REG_READ(SHA_256_BUSY_REG) != 0) { }
|
||||
|
||||
// Copy to memory block
|
||||
for (size_t i = 0; i < copy_words; i++) {
|
||||
sha_text_reg[block_count + i] = __builtin_bswap32(w[i]);
|
||||
}
|
||||
asm volatile("memw");
|
||||
|
||||
// Update counters
|
||||
words_hashed += copy_words;
|
||||
block_count += copy_words;
|
||||
word_len -= copy_words;
|
||||
w += copy_words;
|
||||
|
||||
// If we loaded a full block, run the SHA engine
|
||||
if (block_count == BLOCK_WORDS) {
|
||||
if (words_hashed == BLOCK_WORDS) {
|
||||
_DPORT_REG_WRITE(SHA_256_START_REG, 1);
|
||||
} else {
|
||||
_DPORT_REG_WRITE(SHA_256_CONTINUE_REG, 1);
|
||||
}
|
||||
block_count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest)
|
||||
{
|
||||
assert(handle != NULL);
|
||||
|
||||
if (digest == NULL) {
|
||||
return; // We'd free resources here, but there are none to free
|
||||
}
|
||||
|
||||
uint32_t data_words = words_hashed;
|
||||
|
||||
// Pad to a 55 byte long block loaded in the engine
|
||||
// (leaving 1 byte 0x80 plus variable padding plus 8 bytes of length,
|
||||
// to fill a 64 byte block.)
|
||||
int block_bytes = (words_hashed % BLOCK_WORDS) * 4;
|
||||
int pad_bytes = 55 - block_bytes;
|
||||
if (pad_bytes < 0) {
|
||||
pad_bytes += 64;
|
||||
}
|
||||
static const uint8_t padding[64] = { 0x80, 0, };
|
||||
|
||||
pad_bytes += 5; // 1 byte for 0x80 plus first 4 bytes of the 64-bit length
|
||||
assert(pad_bytes % 4 == 0); // should be, as (block_bytes % 4 == 0)
|
||||
|
||||
bootloader_sha256_data(handle, padding, pad_bytes);
|
||||
|
||||
assert(words_hashed % BLOCK_WORDS == 60 / 4); // 32-bits left in block
|
||||
|
||||
// Calculate 32-bit length for final 32 bits of data
|
||||
uint32_t bit_count = __builtin_bswap32(data_words * 32);
|
||||
bootloader_sha256_data(handle, &bit_count, sizeof(bit_count));
|
||||
|
||||
assert(words_hashed % BLOCK_WORDS == 0);
|
||||
|
||||
while (_DPORT_REG_READ(SHA_256_BUSY_REG) == 1) { }
|
||||
_DPORT_REG_WRITE(SHA_256_LOAD_REG, 1);
|
||||
while (_DPORT_REG_READ(SHA_256_BUSY_REG) == 1) { }
|
||||
|
||||
uint32_t *digest_words = (uint32_t *)digest;
|
||||
uint32_t *sha_text_reg = (uint32_t *)(SHA_TEXT_BASE);
|
||||
for (size_t i = 0; i < DIGEST_WORDS; i++) {
|
||||
digest_words[i] = __builtin_bswap32(sha_text_reg[i]);
|
||||
}
|
||||
asm volatile("memw");
|
||||
}
|
||||
#endif /* CONFIG_IDF_TARGET_ESP32 */
|
||||
#else /* NON_OS_BUILD || CONFIG_APP_BUILD_TYPE_RAM */
|
||||
|
||||
/* App-side SHA implementation backed by PSA/mbedtls. */
|
||||
#include "psa/crypto.h"
|
||||
|
||||
bootloader_sha256_handle_t bootloader_sha256_start(void)
|
||||
{
|
||||
psa_hash_operation_t *op = (psa_hash_operation_t *)malloc(sizeof(psa_hash_operation_t));
|
||||
if (!op) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*op = psa_hash_operation_init();
|
||||
psa_status_t status = psa_hash_setup(op, PSA_ALG_SHA_256);
|
||||
if (status != PSA_SUCCESS) {
|
||||
free(op);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (bootloader_sha256_handle_t)op;
|
||||
}
|
||||
|
||||
void bootloader_sha256_data(bootloader_sha256_handle_t handle, const void *data, size_t data_len)
|
||||
{
|
||||
assert(handle != NULL);
|
||||
psa_hash_operation_t *op = (psa_hash_operation_t *)handle;
|
||||
|
||||
psa_status_t status = psa_hash_update(op, data, data_len);
|
||||
assert(status == PSA_SUCCESS);
|
||||
(void)status; // Suppress unused variable warning in release builds
|
||||
}
|
||||
|
||||
void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest)
|
||||
{
|
||||
assert(handle != NULL);
|
||||
psa_hash_operation_t *op = (psa_hash_operation_t *)handle;
|
||||
|
||||
if (digest != NULL) {
|
||||
size_t hash_len;
|
||||
psa_status_t status = psa_hash_finish(op, digest, PSA_HASH_LENGTH(PSA_ALG_SHA_256), &hash_len);
|
||||
assert(status == PSA_SUCCESS);
|
||||
assert(hash_len == PSA_HASH_LENGTH(PSA_ALG_SHA_256));
|
||||
(void)status; // Suppress unused variable warning in release builds
|
||||
(void)hash_len; // Suppress unused variable warning in release builds
|
||||
} else {
|
||||
psa_hash_abort(op);
|
||||
}
|
||||
|
||||
free(handle);
|
||||
}
|
||||
|
||||
#if SOC_SHA_SUPPORT_SHA512
|
||||
|
||||
typedef struct {
|
||||
psa_hash_operation_t *hash_op;
|
||||
psa_algorithm_t psa_alg;
|
||||
} bootloader_psa_sha_handle_t;
|
||||
|
||||
bootloader_sha_handle_t bootloader_sha512_start(bool is384)
|
||||
{
|
||||
psa_status_t status;
|
||||
bootloader_psa_sha_handle_t *op = (bootloader_psa_sha_handle_t *)malloc(sizeof(bootloader_psa_sha_handle_t));
|
||||
if (!op) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
op->hash_op = (psa_hash_operation_t *)malloc(sizeof(psa_hash_operation_t));
|
||||
if (!op->hash_op) {
|
||||
free(op);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
op->psa_alg = is384 ? PSA_ALG_SHA_384 : PSA_ALG_SHA_512;
|
||||
|
||||
*op->hash_op = psa_hash_operation_init();
|
||||
status = psa_hash_setup(op->hash_op, op->psa_alg);
|
||||
if (status != PSA_SUCCESS) {
|
||||
free(op->hash_op);
|
||||
free(op);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (bootloader_psa_sha_handle_t *)op;
|
||||
}
|
||||
|
||||
void bootloader_sha512_data(bootloader_sha_handle_t handle, const void *data, size_t data_len)
|
||||
{
|
||||
assert(handle != NULL);
|
||||
bootloader_psa_sha_handle_t *op = (bootloader_psa_sha_handle_t *)handle;
|
||||
|
||||
psa_status_t status = psa_hash_update(op->hash_op, data, data_len);
|
||||
assert(status == PSA_SUCCESS);
|
||||
(void)status; // Suppress unused variable warning in release builds
|
||||
}
|
||||
|
||||
void bootloader_sha512_finish(bootloader_sha_handle_t handle, uint8_t *digest)
|
||||
{
|
||||
assert(handle != NULL);
|
||||
bootloader_psa_sha_handle_t *op = (bootloader_psa_sha_handle_t *)handle;
|
||||
|
||||
if (digest != NULL) {
|
||||
size_t hash_len;
|
||||
psa_status_t status = psa_hash_finish(op->hash_op, digest, PSA_HASH_LENGTH(op->psa_alg), &hash_len);
|
||||
assert(status == PSA_SUCCESS);
|
||||
assert(hash_len == PSA_HASH_LENGTH(op->psa_alg));
|
||||
(void)status; // Suppress unused variable warning in release builds
|
||||
(void)hash_len; // Suppress unused variable warning in release builds
|
||||
} else {
|
||||
psa_hash_abort(op->hash_op);
|
||||
}
|
||||
|
||||
free(op->hash_op);
|
||||
free(op);
|
||||
}
|
||||
#endif /* SOC_SHA_SUPPORT_SHA512 */
|
||||
#endif /* !(NON_OS_BUILD || CONFIG_APP_BUILD_TYPE_RAM) */
|
||||
114
components/esp_image_verify/src/bootloader_sha_flash.c
Normal file
114
components/esp_image_verify/src/bootloader_sha_flash.c
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include "esp_log.h"
|
||||
#include "bootloader_flash_priv.h"
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_sha_flash.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "hal/sha_types.h"
|
||||
#include "rom/sha.h"
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "boot_sha");
|
||||
|
||||
static esp_err_t bootloader_sha_flash_contents(esp_sha_type type, uint32_t flash_offset, uint32_t len, uint8_t *digest)
|
||||
{
|
||||
if (digest == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Handling firmware images larger than MMU capacity */
|
||||
uint32_t mmu_free_pages_count = bootloader_mmap_get_free_pages();
|
||||
bootloader_sha_handle_t sha_handle = NULL;
|
||||
|
||||
if (type == SHA2_256) {
|
||||
sha_handle = bootloader_sha256_start();
|
||||
} else
|
||||
#if SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
if (type == SHA2_384) {
|
||||
sha_handle = bootloader_sha512_start(true);
|
||||
} else
|
||||
#endif /* SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384 */
|
||||
{
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (sha_handle == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
while (len > 0) {
|
||||
uint32_t mmu_page_offset = ((flash_offset & MMAP_ALIGNED_MASK) != 0) ? 1 : 0; /* Skip 1st MMU Page if it is already populated */
|
||||
uint32_t max_pages = (mmu_free_pages_count > mmu_page_offset) ? (mmu_free_pages_count - mmu_page_offset) : 0;
|
||||
if (max_pages == 0) {
|
||||
ESP_LOGE(TAG, "No free MMU pages are available");
|
||||
if (type == SHA2_256) {
|
||||
bootloader_sha256_finish(sha_handle, NULL);
|
||||
}
|
||||
#if SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
else if (type == SHA2_384) {
|
||||
bootloader_sha512_finish(sha_handle, NULL);
|
||||
}
|
||||
#endif /* SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384 */
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
uint32_t max_image_len;
|
||||
if (__builtin_mul_overflow(max_pages, SPI_FLASH_MMU_PAGE_SIZE, &max_image_len)) {
|
||||
max_image_len = UINT32_MAX;
|
||||
}
|
||||
uint32_t partial_image_len = MIN(len, max_image_len); /* Read the image that fits in the free MMU pages */
|
||||
|
||||
const void * image = bootloader_mmap(flash_offset, partial_image_len);
|
||||
if (image == NULL) {
|
||||
if (type == SHA2_256) {
|
||||
bootloader_sha256_finish(sha_handle, NULL);
|
||||
}
|
||||
#if SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
else if (type == SHA2_384) {
|
||||
bootloader_sha512_finish(sha_handle, NULL);
|
||||
}
|
||||
#endif /* SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384 */
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (type == SHA2_256) {
|
||||
bootloader_sha256_data(sha_handle, image, partial_image_len);
|
||||
}
|
||||
#if SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
else if (type == SHA2_384) {
|
||||
bootloader_sha512_data(sha_handle, image, partial_image_len);
|
||||
}
|
||||
#endif /* SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384 */
|
||||
|
||||
bootloader_munmap(image);
|
||||
|
||||
flash_offset += partial_image_len;
|
||||
len -= partial_image_len;
|
||||
}
|
||||
|
||||
if (type == SHA2_256) {
|
||||
bootloader_sha256_finish(sha_handle, digest);
|
||||
}
|
||||
#if SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
else if (type == SHA2_384) {
|
||||
bootloader_sha512_finish(sha_handle, digest);
|
||||
}
|
||||
#endif /* SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384 */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t bootloader_sha256_flash_contents(uint32_t flash_offset, uint32_t len, uint8_t *digest)
|
||||
{
|
||||
return bootloader_sha_flash_contents(SHA2_256, flash_offset, len, digest);
|
||||
}
|
||||
|
||||
#if SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
esp_err_t bootloader_sha384_flash_contents(uint32_t flash_offset, uint32_t len, uint8_t *digest)
|
||||
{
|
||||
return bootloader_sha_flash_contents(SHA2_384, flash_offset, len, digest);
|
||||
}
|
||||
#endif
|
||||
1263
components/esp_image_verify/src/esp_image_format.c
Normal file
1263
components/esp_image_verify/src/esp_image_format.c
Normal file
File diff suppressed because it is too large
Load Diff
33
components/esp_image_verify/src/secure_boot.c
Normal file
33
components/esp_image_verify/src/secure_boot.c
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_secure_boot.h"
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "secure_boot");
|
||||
|
||||
#if (CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME) && CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
|
||||
|
||||
void esp_secure_boot_check_signature_on_update(void)
|
||||
{
|
||||
/* We rely on the keys used to sign this app to verify the next app on OTA, so make sure
|
||||
there is at least one to avoid a stuck firmware. */
|
||||
esp_image_sig_public_key_digests_t digests = { 0 };
|
||||
esp_err_t err = esp_secure_boot_get_signature_blocks_for_running_app(false, &digests);
|
||||
|
||||
if (err != ESP_OK || digests.num_digests == 0) {
|
||||
ESP_LOGE(TAG, "This app is not signed, but check signature on update is enabled in config. It won't be possible to verify any update.");
|
||||
abort();
|
||||
}
|
||||
#if SECURE_BOOT_NUM_BLOCKS > 1
|
||||
if (digests.num_digests > 1) {
|
||||
ESP_LOGW(TAG, "App has %d signatures. Only the first position of signature blocks is used to verify any update", digests.num_digests);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* (CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME) && CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT */
|
||||
191
components/esp_image_verify/src/secure_boot_v1/secure_boot.c
Normal file
191
components/esp_image_verify/src/secure_boot_v1/secure_boot.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_attr.h"
|
||||
#include "esp_types.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp32/rom/cache.h"
|
||||
#include "esp32/rom/secure_boot.h"
|
||||
|
||||
#include "soc/rtc_periph.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "bootloader_flash_priv.h"
|
||||
#include "bootloader_random.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_secure_boot.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_efuse_table.h"
|
||||
|
||||
/* The following API implementations are used only when called
|
||||
* from the bootloader code.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
ESP_LOG_ATTR_TAG(TAG, "secure_boot_v1");
|
||||
/**
|
||||
* @function : secure_boot_generate
|
||||
* @description: generate boot digest (aka "abstract") & iv
|
||||
*
|
||||
* @inputs: image_len - length of image to calculate digest for
|
||||
*/
|
||||
static bool secure_boot_generate(uint32_t image_len)
|
||||
{
|
||||
esp_err_t err;
|
||||
esp_secure_boot_iv_digest_t digest;
|
||||
const uint32_t *image;
|
||||
|
||||
/* hardware secure boot engine only takes full blocks, so round up the
|
||||
image length. The additional data should all be 0xFF (or the appended SHA, if it falls in the same block).
|
||||
*/
|
||||
if (image_len % sizeof(digest.iv) != 0) {
|
||||
image_len = (image_len / sizeof(digest.iv) + 1) * sizeof(digest.iv);
|
||||
}
|
||||
ets_secure_boot_start();
|
||||
ets_secure_boot_rd_iv((uint32_t *)digest.iv);
|
||||
ets_secure_boot_hash(NULL);
|
||||
/* iv stored in sec 0 */
|
||||
err = bootloader_flash_erase_sector(0);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SPI erase failed: 0x%x", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* generate digest from image contents */
|
||||
image = bootloader_mmap(ESP_BOOTLOADER_OFFSET, image_len);
|
||||
if (!image) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x1000, 0x%" PRIx32 ") failed", image_len);
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < image_len; i += sizeof(digest.iv)) {
|
||||
ets_secure_boot_hash(&image[i / sizeof(uint32_t)]);
|
||||
}
|
||||
bootloader_munmap(image);
|
||||
|
||||
ets_secure_boot_obtain();
|
||||
ets_secure_boot_rd_abstract((uint32_t *)digest.digest);
|
||||
ets_secure_boot_finish();
|
||||
|
||||
ESP_LOGD(TAG, "write iv+digest to flash");
|
||||
err = bootloader_flash_write(FLASH_OFFS_SECURE_BOOT_IV_DIGEST, &digest,
|
||||
sizeof(digest), esp_efuse_is_flash_encryption_enabled());
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "SPI write failed: 0x%x", err);
|
||||
return false;
|
||||
}
|
||||
Cache_Read_Enable(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_generate_digest(void)
|
||||
{
|
||||
esp_err_t err;
|
||||
if (esp_secure_boot_enabled()) {
|
||||
ESP_LOGI(TAG, "bootloader secure boot is already enabled."
|
||||
" No need to generate digest. continuing..");
|
||||
return ESP_OK;
|
||||
}
|
||||
#if CONFIG_SECURE_BOOT_REQUIRE_ALREADY_ENABLED
|
||||
else {
|
||||
ESP_LOGE(TAG, "secure boot is not enabled, and SECURE_BOOT_REQUIRE_ALREADY_ENABLED is set, refusing to boot.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
#endif // CONFIG_SECURE_BOOT_REQUIRE_ALREADY_ENABLED
|
||||
|
||||
esp_efuse_coding_scheme_t coding_scheme = esp_efuse_get_coding_scheme(EFUSE_BLK_SECURE_BOOT);
|
||||
if (coding_scheme != EFUSE_CODING_SCHEME_NONE && coding_scheme != EFUSE_CODING_SCHEME_3_4) {
|
||||
ESP_LOGE(TAG, "Unknown/unsupported CODING_SCHEME value 0x%x", coding_scheme);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/* Verify the bootloader */
|
||||
esp_image_metadata_t bootloader_data = { 0 };
|
||||
err = esp_image_verify_bootloader_data(&bootloader_data);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "bootloader image appears invalid! error %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
bool dis_write = esp_efuse_read_field_bit(ESP_EFUSE_WR_DIS_BLK2);
|
||||
bool dis_read = esp_efuse_read_field_bit(ESP_EFUSE_RD_DIS_BLK2);
|
||||
bool key_is_empty = esp_efuse_block_is_empty(EFUSE_BLK_SECURE_BOOT);
|
||||
|
||||
/* Generate secure boot key and keep in EFUSE */
|
||||
if (!dis_read && !dis_write && key_is_empty) {
|
||||
ESP_LOGI(TAG, "Generating new secure boot key...");
|
||||
uint32_t key[8];
|
||||
size_t key_size = 256;
|
||||
if (coding_scheme == EFUSE_CODING_SCHEME_3_4) {
|
||||
key_size = 192;
|
||||
}
|
||||
bootloader_fill_random(key, key_size / 8);
|
||||
esp_efuse_write_block(EFUSE_BLK_SECURE_BOOT, key, 0, key_size);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Using pre-loaded secure boot key in EFUSE block 2");
|
||||
}
|
||||
|
||||
/* Generate secure boot digest using programmed key in EFUSE */
|
||||
ESP_LOGI(TAG, "Generating secure boot digest...");
|
||||
uint32_t image_len = bootloader_data.image_len;
|
||||
if (bootloader_data.image.hash_appended) {
|
||||
/* Secure boot digest doesn't cover the hash */
|
||||
image_len -= ESP_IMAGE_HASH_LEN;
|
||||
}
|
||||
if (false == secure_boot_generate(image_len)) {
|
||||
ESP_LOGE(TAG, "secure boot generation failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "Digest generation complete.");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_permanently_enable(void)
|
||||
{
|
||||
if (esp_secure_boot_enabled()) {
|
||||
ESP_LOGI(TAG, "bootloader secure boot is already enabled, continuing..");
|
||||
return ESP_OK;
|
||||
}
|
||||
#if CONFIG_SECURE_BOOT_REQUIRE_ALREADY_ENABLED
|
||||
else {
|
||||
ESP_LOGE(TAG, "secure boot is not enabled, and SECURE_BOOT_REQUIRE_ALREADY_ENABLED is set, refusing to boot.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
#endif // CONFIG_SECURE_BOOT_REQUIRE_ALREADY_ENABLED
|
||||
|
||||
bool dis_read = esp_efuse_read_field_bit(ESP_EFUSE_RD_DIS_BLK2);
|
||||
bool dis_write = esp_efuse_read_field_bit(ESP_EFUSE_WR_DIS_BLK2);
|
||||
if (dis_read != dis_write) {
|
||||
ESP_LOGE(TAG, "Pre-loaded key is not %s %s protected. Refusing to blow secure boot efuse.",
|
||||
(!dis_read) ? "read," : " ",
|
||||
(!dis_read) ? "write" : " ");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
esp_efuse_batch_write_begin(); /* Batch all efuse writes at the end of this function */
|
||||
|
||||
ESP_LOGI(TAG, "blowing secure boot efuse...");
|
||||
esp_err_t err = esp_secure_boot_enable_secure_features();
|
||||
if (err != ESP_OK) {
|
||||
esp_efuse_batch_write_cancel();
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_efuse_batch_write_commit();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error programming security eFuses (err=0x%x).", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
assert(esp_secure_boot_enabled());
|
||||
|
||||
ESP_LOGI(TAG, "secure boot is now enabled for bootloader image");
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif // CONFIG_SECURE_BOOT_V1_ENABLED
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "bootloader_flash_priv.h"
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_sha_flash.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_secure_boot.h"
|
||||
#include "psa/crypto.h"
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include "mbedtls/pk.h"
|
||||
|
||||
#ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
ESP_LOG_ATTR_TAG(TAG, "secure_boot_v1");
|
||||
|
||||
extern const uint8_t signature_verification_key_start[] asm("_binary_signature_verification_key_bin_start");
|
||||
extern const uint8_t signature_verification_key_end[] asm("_binary_signature_verification_key_bin_end");
|
||||
|
||||
#define SIGNATURE_VERIFICATION_KEYLEN 64
|
||||
#define PSA_ECDSA_PUB_KEY_SIZE_BITS 256
|
||||
#define UNCOMPRESSED_SECP256R1_KEY_SIZE 65 // Size for uncompressed SECP256R1 (1 + 32 + 32)
|
||||
#define ECC_UNCOMPRESSED_POINT_FORMAT_INDICATOR 0x04
|
||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||
{
|
||||
uint8_t digest[ESP_SECURE_BOOT_DIGEST_LEN];
|
||||
uint8_t verified_digest[ESP_SECURE_BOOT_DIGEST_LEN];
|
||||
const esp_secure_boot_sig_block_t *sigblock;
|
||||
|
||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%"PRIx32" length 0x%"PRIx32, src_addr, length);
|
||||
|
||||
esp_err_t err = bootloader_sha256_flash_contents(src_addr, length, digest);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Digest calculation failed 0x%"PRIx32", 0x%"PRIx32, src_addr, length);
|
||||
return err;
|
||||
}
|
||||
|
||||
// Map the signature block and verify the signature
|
||||
sigblock = (const esp_secure_boot_sig_block_t *)bootloader_mmap(src_addr + length, sizeof(esp_secure_boot_sig_block_t));
|
||||
if (sigblock == NULL) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x%"PRIx32", 0x%x) failed", src_addr + length, sizeof(esp_secure_boot_sig_block_t));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
err = esp_secure_boot_verify_ecdsa_signature_block(sigblock, digest, verified_digest);
|
||||
bootloader_munmap(sigblock);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_verify_ecdsa_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
#if !(defined(CONFIG_MBEDTLS_ECDSA_C) && defined(CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED))
|
||||
ESP_LOGE(TAG, "Signature verification requires ECDSA & SECP256R1 curve enabled");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
ptrdiff_t keylen;
|
||||
|
||||
/* Note: in IDF app image verification we don't add any fault injection resistance, boot-time checks only */
|
||||
memset(verified_digest, 0, ESP_SECURE_BOOT_DIGEST_LEN);
|
||||
|
||||
keylen = signature_verification_key_end - signature_verification_key_start;
|
||||
if (keylen != SIGNATURE_VERIFICATION_KEYLEN) {
|
||||
ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (sig_block->version != ESP_SECURE_BOOT_SCHEME) {
|
||||
ESP_LOGE(TAG, "image has invalid signature version field 0x%08"PRIx32" (image without a signature?)", sig_block->version);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Verifying secure boot signature");
|
||||
psa_status_t status;
|
||||
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_handle;
|
||||
|
||||
// Format the public key for PSA import
|
||||
uint8_t formatted_key[UNCOMPRESSED_SECP256R1_KEY_SIZE];
|
||||
formatted_key[0] = ECC_UNCOMPRESSED_POINT_FORMAT_INDICATOR;
|
||||
|
||||
// Copy X and Y coordinates
|
||||
if (keylen == 64) { // Raw coordinates without format byte
|
||||
memcpy(&formatted_key[1], signature_verification_key_start, 64);
|
||||
} else if (keylen == UNCOMPRESSED_SECP256R1_KEY_SIZE && signature_verification_key_start[0] == ECC_UNCOMPRESSED_POINT_FORMAT_INDICATOR) {
|
||||
// Key is already in correct format
|
||||
memcpy(formatted_key, signature_verification_key_start, UNCOMPRESSED_SECP256R1_KEY_SIZE);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid key format or length");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Set key attributes
|
||||
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_HASH);
|
||||
psa_set_key_algorithm(&key_attributes, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
|
||||
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
|
||||
psa_set_key_bits(&key_attributes, PSA_ECDSA_PUB_KEY_SIZE_BITS);
|
||||
|
||||
// Import the properly formatted public key
|
||||
status = psa_import_key(&key_attributes, formatted_key, sizeof(formatted_key), &key_handle);
|
||||
if (status != PSA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Failed to import key, status:%d", status);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Verify the signature
|
||||
status = psa_verify_hash(key_handle, PSA_ALG_ECDSA(PSA_ALG_SHA_256), image_digest, ESP_SECURE_BOOT_DIGEST_LEN, sig_block->signature, SIGNATURE_VERIFICATION_KEYLEN);
|
||||
ESP_LOGI(TAG, "Verification result %d", status);
|
||||
|
||||
// Destroy the key handle
|
||||
psa_destroy_key(key_handle);
|
||||
psa_reset_key_attributes(&key_attributes);
|
||||
|
||||
return status == PSA_SUCCESS ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||
#endif // CONFIG_MBEDTLS_ECDSA_C && CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED
|
||||
}
|
||||
#endif // CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "bootloader_flash_priv.h"
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_sha_flash.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_secure_boot.h"
|
||||
#include "esp_fault.h"
|
||||
#include "esp32/rom/sha.h"
|
||||
#include "uECC_verify_antifault.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "secure_boot");
|
||||
|
||||
#ifdef CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
extern const uint8_t signature_verification_key_start[] asm("_binary_signature_verification_key_bin_start");
|
||||
extern const uint8_t signature_verification_key_end[] asm("_binary_signature_verification_key_bin_end");
|
||||
|
||||
#define SIGNATURE_VERIFICATION_KEYLEN 64
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||
{
|
||||
uint8_t digest[ESP_SECURE_BOOT_DIGEST_LEN];
|
||||
uint8_t verified_digest[ESP_SECURE_BOOT_DIGEST_LEN] = { 0 }; /* ignored in this function */
|
||||
const esp_secure_boot_sig_block_t *sigblock;
|
||||
|
||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%" PRIx32 " length 0x%" PRIx32, src_addr, length);
|
||||
|
||||
esp_err_t err = bootloader_sha256_flash_contents(src_addr, length, digest);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// Map the signature block
|
||||
sigblock = (const esp_secure_boot_sig_block_t *) bootloader_mmap(src_addr + length, sizeof(esp_secure_boot_sig_block_t));
|
||||
if (!sigblock) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x%" PRIx32 ", 0x%x) failed", src_addr + length, sizeof(esp_secure_boot_sig_block_t));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
// Verify the signature
|
||||
err = esp_secure_boot_verify_ecdsa_signature_block(sigblock, digest, verified_digest);
|
||||
// Unmap
|
||||
bootloader_munmap(sigblock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_verify_ecdsa_signature_block(const esp_secure_boot_sig_block_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
ptrdiff_t keylen;
|
||||
|
||||
keylen = signature_verification_key_end - signature_verification_key_start;
|
||||
if (keylen != SIGNATURE_VERIFICATION_KEYLEN) {
|
||||
ESP_LOGE(TAG, "Embedded public verification key has wrong length %d", keylen);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (sig_block->version != ESP_SECURE_BOOT_SCHEME) {
|
||||
ESP_LOGE(TAG, "image has invalid signature version field 0x%08" PRIx32 " (image without a signature?)", sig_block->version);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Verifying secure boot signature");
|
||||
|
||||
bool is_valid;
|
||||
is_valid = uECC_verify_antifault(signature_verification_key_start,
|
||||
image_digest,
|
||||
ESP_SECURE_BOOT_DIGEST_LEN,
|
||||
sig_block->signature,
|
||||
uECC_secp256r1(),
|
||||
verified_digest);
|
||||
ESP_LOGD(TAG, "Verification result %d", is_valid);
|
||||
|
||||
return is_valid ? ESP_OK : ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
|
||||
#endif // CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME
|
||||
410
components/esp_image_verify/src/secure_boot_v2/secure_boot.c
Normal file
410
components/esp_image_verify/src/secure_boot_v2/secure_boot.c
Normal file
@@ -0,0 +1,410 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_secure_boot.h"
|
||||
#include "bootloader_flash_priv.h"
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_sha_flash.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_efuse_table.h"
|
||||
#include "secure_boot_signature_priv.h"
|
||||
#include "esp_macros.h"
|
||||
|
||||
/* The following API implementations are used only when called
|
||||
* from the bootloader code.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
extern esp_image_metadata_t tee_data;
|
||||
#endif
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "secure_boot_v2");
|
||||
|
||||
/* A signature block is valid when it has correct magic byte, crc and image digest. */
|
||||
static esp_err_t validate_signature_block(const ets_secure_boot_sig_block_t *block, int block_num, const uint8_t *image_digest)
|
||||
{
|
||||
if (block->magic_byte != ETS_SECURE_BOOT_V2_SIGNATURE_MAGIC) {
|
||||
// All signature blocks have been parsed, no new signature block present.
|
||||
ESP_LOGD(TAG, "Signature block(%d) invalid/absent.", block_num);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (block->block_crc != esp_rom_crc32_le(0, (uint8_t *)block, CRC_SIGN_BLOCK_LEN)) {
|
||||
ESP_LOGE(TAG, "Magic byte correct but incorrect crc.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (memcmp(image_digest, block->image_digest, ESP_SECURE_BOOT_DIGEST_LEN)) {
|
||||
ESP_LOGE(TAG, "Magic byte & CRC correct but incorrect image digest.");
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "valid signature block(%d) found", block_num);
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Generates the public key digests of the valid public keys in an image's
|
||||
signature block, verifies each signature, and stores the key digests in the
|
||||
public_key_digests structure.
|
||||
|
||||
@param flash_offset Image offset in flash
|
||||
@param flash_size Image size in flash (not including signature block)
|
||||
@param[out] public_key_digests Pointer to structure to hold the key digests for valid sig blocks
|
||||
|
||||
|
||||
Note that this function doesn't read any eFuses, so it doesn't know if the
|
||||
keys are ultimately trusted by the hardware or not
|
||||
|
||||
@return - ESP_OK if no signatures failed to verify, or if no valid signature blocks are found at all.
|
||||
- ESP_FAIL if there's a valid signature block that doesn't verify using the included public key (unexpected!)
|
||||
*/
|
||||
static esp_err_t s_calculate_image_public_key_digests(uint32_t flash_offset, uint32_t flash_size, esp_image_sig_public_key_digests_t *public_key_digests)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
uint8_t image_digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0};
|
||||
uint8_t __attribute__((aligned(4))) key_digest[ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN] = {0};
|
||||
size_t sig_block_addr = flash_offset + ESP_ALIGN_UP(flash_size, FLASH_SECTOR_SIZE);
|
||||
|
||||
ESP_LOGD(TAG, "calculating public key digests for sig blocks of image offset 0x%" PRIx32 " (sig block offset 0x%x)", flash_offset, sig_block_addr);
|
||||
|
||||
bzero(public_key_digests, sizeof(esp_image_sig_public_key_digests_t));
|
||||
|
||||
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
|
||||
ret = bootloader_sha384_flash_contents(flash_offset, sig_block_addr - flash_offset, image_digest);
|
||||
#else
|
||||
ret = bootloader_sha256_flash_contents(flash_offset, sig_block_addr - flash_offset, image_digest);
|
||||
#endif
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "error generating image digest, %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "reading signature(s)");
|
||||
const ets_secure_boot_signature_t *signatures = bootloader_mmap(sig_block_addr, sizeof(ets_secure_boot_signature_t));
|
||||
if (signatures == NULL) {
|
||||
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", sig_block_addr, sizeof(ets_secure_boot_signature_t));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Validating Signature block */
|
||||
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
const ets_secure_boot_sig_block_t *block = &signatures->block[i];
|
||||
|
||||
ret = validate_signature_block(block, i, image_digest);
|
||||
if (ret != ESP_OK) {
|
||||
ret = ESP_OK; // past the last valid signature block
|
||||
break;
|
||||
}
|
||||
|
||||
/* Generating the SHA of the public key components in the signature block */
|
||||
bootloader_sha256_handle_t sig_block_sha;
|
||||
sig_block_sha = bootloader_sha256_start();
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
bootloader_sha256_data(sig_block_sha, &block->key, sizeof(block->key));
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
bootloader_sha256_data(sig_block_sha, &block->ecdsa.key, sizeof(block->ecdsa.key));
|
||||
#endif
|
||||
bootloader_sha256_finish(sig_block_sha, key_digest);
|
||||
|
||||
// Check we can verify the image using this signature and this key
|
||||
uint8_t temp_verified_digest[ESP_SECURE_BOOT_DIGEST_LEN];
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
bool verified = ets_rsa_pss_verify(&block->key, block->signature, image_digest, temp_verified_digest);
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
bool verified = ets_ecdsa_verify(&block->ecdsa.key.point[0], block->ecdsa.signature, block->ecdsa.key.curve_id, image_digest, temp_verified_digest);
|
||||
#endif
|
||||
|
||||
if (!verified) {
|
||||
/* We don't expect this: the signature blocks before we enable secure boot should all be verifiable or invalid,
|
||||
so this is a fatal error
|
||||
*/
|
||||
ret = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "Secure boot key (%d) verification failed.", i);
|
||||
break;
|
||||
}
|
||||
ESP_LOGD(TAG, "Signature block (%d) is verified", i);
|
||||
/* Copy the key digest to the buffer provided by the caller */
|
||||
memcpy((void *)public_key_digests->key_digests[i], key_digest, ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN);
|
||||
public_key_digests->num_digests++;
|
||||
}
|
||||
|
||||
if (ret == ESP_OK && public_key_digests->num_digests > 0) {
|
||||
ESP_LOGI(TAG, "Digests successfully calculated, %d valid signatures (image offset 0x%" PRIx32 ")",
|
||||
public_key_digests->num_digests, flash_offset);
|
||||
}
|
||||
|
||||
bootloader_munmap(signatures);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t check_and_generate_secure_boot_keys(const esp_image_metadata_t *image_data)
|
||||
{
|
||||
esp_err_t ret;
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
esp_efuse_coding_scheme_t coding_scheme = esp_efuse_get_coding_scheme(EFUSE_BLK_SECURE_BOOT);
|
||||
if (coding_scheme != EFUSE_CODING_SCHEME_NONE) {
|
||||
ESP_LOGE(TAG, "No coding schemes are supported in secure boot v2.(Detected scheme: 0x%x)", coding_scheme);
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
#endif // CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
esp_efuse_purpose_t secure_boot_key_purpose[SECURE_BOOT_NUM_BLOCKS] = {
|
||||
#if SECURE_BOOT_NUM_BLOCKS == 1
|
||||
ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_V2,
|
||||
#else
|
||||
ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0,
|
||||
ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1,
|
||||
ESP_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2,
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Verify the bootloader */
|
||||
esp_image_metadata_t bootloader_data = { 0 };
|
||||
ret = esp_image_verify_bootloader_data(&bootloader_data);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "bootloader image appears invalid! error %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize all efuse block entries to invalid (max) value */
|
||||
esp_efuse_block_t blocks[SECURE_BOOT_NUM_BLOCKS] = {[0 ... SECURE_BOOT_NUM_BLOCKS - 1] = EFUSE_BLK_KEY_MAX};
|
||||
/* Check if secure boot digests are present */
|
||||
bool has_secure_boot_digest = false;
|
||||
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
bool tmp_has_key = esp_efuse_find_purpose(secure_boot_key_purpose[i], &blocks[i]);
|
||||
if (tmp_has_key) { // For ESP32: esp_efuse_find_purpose() always returns True, need to check whether the key block is used or not.
|
||||
tmp_has_key &= !esp_efuse_key_block_unused(blocks[i]);
|
||||
}
|
||||
has_secure_boot_digest |= tmp_has_key;
|
||||
}
|
||||
|
||||
esp_image_sig_public_key_digests_t boot_key_digests = {0};
|
||||
esp_image_sig_public_key_digests_t app_key_digests = {0};
|
||||
ESP_LOGI(TAG, "Secure boot digests %s", has_secure_boot_digest ? "already present" : "absent, generating..");
|
||||
|
||||
if (!has_secure_boot_digest) {
|
||||
/* Generate the bootloader public key digests */
|
||||
ret = s_calculate_image_public_key_digests(bootloader_data.start_addr, bootloader_data.image_len - SIG_BLOCK_PADDING, &boot_key_digests);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Bootloader signature block is invalid");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (boot_key_digests.num_digests == 0) {
|
||||
ESP_LOGE(TAG, "No valid bootloader signature blocks found.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGI(TAG, "%d signature block(s) found appended to the bootloader.", boot_key_digests.num_digests);
|
||||
|
||||
ESP_LOGI(TAG, "Burning public key hash to eFuse");
|
||||
ret = esp_efuse_write_keys(secure_boot_key_purpose, boot_key_digests.key_digests, boot_key_digests.num_digests);
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_ERR_NOT_ENOUGH_UNUSED_KEY_BLOCKS) {
|
||||
ESP_LOGE(TAG, "Bootloader signatures(%d) more than available key slots.", boot_key_digests.num_digests);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to write efuse block with purpose (err=0x%x). Can't continue.", ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
/* Check if corresponding digest slot is used or not */
|
||||
if (blocks[i] == EFUSE_BLK_KEY_MAX) {
|
||||
ESP_LOGD(TAG, "SECURE_BOOT_DIGEST%d slot is not used", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS
|
||||
if (esp_efuse_get_digest_revoke(i)) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
#ifndef CONFIG_SOC_EFUSE_CONSISTS_OF_ONE_KEY_BLOCK
|
||||
if (esp_efuse_get_key_dis_read(blocks[i])) {
|
||||
ESP_LOGE(TAG, "Key digest (BLK%d) read protected, aborting...", blocks[i]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
if (esp_efuse_block_is_empty(blocks[i])) {
|
||||
ESP_LOGE(TAG, "%d eFuse block is empty, aborting...", blocks[i]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_efuse_set_key_dis_write(blocks[i]);
|
||||
#ifdef CONFIG_SOC_EFUSE_CONSISTS_OF_ONE_KEY_BLOCK
|
||||
size_t offset = 128;
|
||||
#else
|
||||
size_t offset = 0;
|
||||
#endif
|
||||
ret = esp_efuse_read_block(blocks[i], boot_key_digests.key_digests[boot_key_digests.num_digests], offset,
|
||||
ESP_SECURE_BOOT_KEY_DIGEST_LEN * 8);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "Error during reading %d eFuse block (err=0x%x)", blocks[i], ret);
|
||||
return ret;
|
||||
}
|
||||
boot_key_digests.num_digests++;
|
||||
}
|
||||
if (boot_key_digests.num_digests == 0) {
|
||||
ESP_LOGE(TAG, "No valid pre-loaded public key digest in eFuse");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
ESP_LOGW(TAG, "Using pre-loaded public key digest in eFuse");
|
||||
}
|
||||
|
||||
/* Generate the application public key digests */
|
||||
ret = s_calculate_image_public_key_digests(image_data->start_addr, image_data->image_len - SIG_BLOCK_PADDING, &app_key_digests);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Application signature block is invalid.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (app_key_digests.num_digests == 0) {
|
||||
ESP_LOGE(TAG, "No valid applications signature blocks found.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "%d signature block(s) found appended to the app.", app_key_digests.num_digests);
|
||||
if (app_key_digests.num_digests > boot_key_digests.num_digests) {
|
||||
ESP_LOGW(TAG, "App has %d signature blocks but bootloader only has %d. Some keys missing from bootloader?", app_key_digests.num_digests, boot_key_digests.num_digests);
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
/* Generate the TEE public key digests */
|
||||
bool tee_match = false;
|
||||
esp_image_sig_public_key_digests_t tee_key_digests = {0};
|
||||
|
||||
ret = s_calculate_image_public_key_digests(tee_data.start_addr, tee_data.image_len - SIG_BLOCK_PADDING, &tee_key_digests);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "TEE signature block is invalid.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (tee_key_digests.num_digests == 0) {
|
||||
ESP_LOGE(TAG, "No valid TEE signature blocks found.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "%d signature block(s) found appended to the tee.", tee_key_digests.num_digests);
|
||||
if (tee_key_digests.num_digests > boot_key_digests.num_digests) {
|
||||
ESP_LOGW(TAG, "TEE has %d signature blocks but bootloader only has %d. Some keys missing from bootloader?", tee_key_digests.num_digests, boot_key_digests.num_digests);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Confirm if at least one public key from the application matches a public key in the bootloader
|
||||
(Also, ensure if that public revoke bit is not set for the matched key) */
|
||||
bool match = false;
|
||||
|
||||
for (unsigned i = 0; i < boot_key_digests.num_digests; i++) {
|
||||
#if SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS
|
||||
if (esp_efuse_get_digest_revoke(i)) {
|
||||
ESP_LOGI(TAG, "Key block(%d) has been revoked.", i);
|
||||
continue; // skip if the key block is revoked
|
||||
}
|
||||
#endif // SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS
|
||||
for (unsigned j = 0; j < app_key_digests.num_digests; j++) {
|
||||
if (!memcmp(boot_key_digests.key_digests[i], app_key_digests.key_digests[j], ESP_SECURE_BOOT_KEY_DIGEST_LEN)) {
|
||||
ESP_LOGI(TAG, "Application key(%d) matches with bootloader key(%d).", j, i);
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < tee_key_digests.num_digests; j++) {
|
||||
if (!memcmp(boot_key_digests.key_digests[i], tee_key_digests.key_digests[j], ESP_SECURE_BOOT_KEY_DIGEST_LEN)) {
|
||||
ESP_LOGI(TAG, "TEE key(%d) matches with bootloader key(%d).", j, i);
|
||||
tee_match = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (match == false) {
|
||||
ESP_LOGE(TAG, "No application key digest matches the bootloader key digest.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
if (tee_match == false) {
|
||||
ESP_LOGE(TAG, "No TEE key digest matches the bootloader key digest.");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS
|
||||
/* Revoke the empty signature blocks */
|
||||
if (boot_key_digests.num_digests < SECURE_BOOT_NUM_BLOCKS) {
|
||||
/* The revocation index can be 0, 1, 2. Bootloader count can be 1,2,3. */
|
||||
for (unsigned i = boot_key_digests.num_digests; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
#ifndef CONFIG_SECURE_BOOT_ALLOW_UNUSED_DIGEST_SLOTS
|
||||
ESP_LOGI(TAG, "Revoking empty key digest slot (%d)...", i);
|
||||
esp_efuse_set_digest_revoke(i);
|
||||
#else
|
||||
ESP_LOGW(TAG, "Unused key digest slot (%d) left un-revoked due to the config SECURE_BOOT_ALLOW_UNUSED_DIGEST_SLOTS", i);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif // SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_v2_permanently_enable(const esp_image_metadata_t *image_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "enabling secure boot v2...");
|
||||
|
||||
if (esp_secure_boot_enabled()) {
|
||||
ESP_LOGI(TAG, "secure boot v2 is already enabled, continuing..");
|
||||
return ESP_OK;
|
||||
}
|
||||
#if CONFIG_SECURE_BOOT_REQUIRE_ALREADY_ENABLED
|
||||
else {
|
||||
ESP_LOGE(TAG, "secure boot is not enabled, and SECURE_BOOT_REQUIRE_ALREADY_ENABLED is set, refusing to boot.");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
#endif // CONFIG_SECURE_BOOT_REQUIRE_ALREADY_ENABLED
|
||||
|
||||
esp_efuse_batch_write_begin(); /* Batch all efuse writes at the end of this function */
|
||||
|
||||
esp_err_t err;
|
||||
err = check_and_generate_secure_boot_keys(image_data);
|
||||
if (err != ESP_OK) {
|
||||
esp_efuse_batch_write_cancel();
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "blowing secure boot efuse...");
|
||||
err = esp_secure_boot_enable_secure_features();
|
||||
if (err != ESP_OK) {
|
||||
esp_efuse_batch_write_cancel();
|
||||
return err;
|
||||
}
|
||||
|
||||
err = esp_efuse_batch_write_commit();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error programming security eFuses (err=0x%x).", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SECURE_BOOT_ENABLE_AGGRESSIVE_KEY_REVOKE
|
||||
assert(esp_efuse_read_field_bit(ESP_EFUSE_SECURE_BOOT_AGGRESSIVE_REVOKE));
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SECURE_BOOT_FLASH_ENC_KEYS_BURN_TOGETHER
|
||||
assert(esp_secure_boot_enabled());
|
||||
ESP_LOGI(TAG, "Secure boot permanently enabled");
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#endif // CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "esp_secure_boot.h"
|
||||
#include "rom/ecdsa.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "psa/crypto.h"
|
||||
|
||||
#include "secure_boot_signature_priv.h"
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "secure_boot_v2_ecdsa");
|
||||
|
||||
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
|
||||
#define ECDSA_INTEGER_LEN 48
|
||||
#else
|
||||
#define ECDSA_INTEGER_LEN 32
|
||||
#endif
|
||||
|
||||
esp_err_t verify_ecdsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, const ets_secure_boot_sig_block_t *trusted_block)
|
||||
{
|
||||
if (!sig_block || !image_digest || !trusted_block) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
psa_status_t status;
|
||||
|
||||
/* Prepare public key for verification */
|
||||
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_handle = 0;
|
||||
|
||||
/* Set key attributes according to the curve */
|
||||
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_HASH);
|
||||
|
||||
uint8_t key_size = 0;
|
||||
psa_ecc_family_t curve_family;
|
||||
|
||||
switch (trusted_block->ecdsa.key.curve_id) {
|
||||
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_256_BITS
|
||||
case ECDSA_CURVE_P256:
|
||||
key_size = 32;
|
||||
curve_family = PSA_ECC_FAMILY_SECP_R1;
|
||||
psa_set_key_bits(&key_attributes, PSA_BYTES_TO_BITS(key_size));
|
||||
break;
|
||||
#endif /* CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_256_BITS */
|
||||
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
|
||||
case ECDSA_CURVE_P384:
|
||||
key_size = 48;
|
||||
curve_family = PSA_ECC_FAMILY_SECP_R1;
|
||||
psa_set_key_bits(&key_attributes, PSA_BYTES_TO_BITS(key_size));
|
||||
break;
|
||||
#endif /* CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS */
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid curve ID");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
|
||||
const psa_algorithm_t alg = PSA_ALG_ECDSA(PSA_ALG_SHA_384);
|
||||
#else
|
||||
const psa_algorithm_t alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256);
|
||||
#endif
|
||||
psa_set_key_algorithm(&key_attributes, alg);
|
||||
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(curve_family));
|
||||
|
||||
/* Prepare the public key data from X and Y coordinates */
|
||||
uint8_t public_key[(2 * ECDSA_INTEGER_LEN) + 1];
|
||||
uint8_t x_point[ECDSA_INTEGER_LEN] = {0};
|
||||
uint8_t y_point[ECDSA_INTEGER_LEN] = {0};
|
||||
|
||||
/* Convert key points from little-endian to big-endian format */
|
||||
for (int i = 0; i < key_size; i++) {
|
||||
x_point[i] = trusted_block->ecdsa.key.point[key_size - i - 1];
|
||||
|
||||
y_point[i] = trusted_block->ecdsa.key.point[2 * key_size - i - 1];
|
||||
}
|
||||
|
||||
public_key[0] = 0x04; /* Uncompressed point format */
|
||||
|
||||
/* Combine X and Y into a single public key buffer */
|
||||
memcpy(public_key + 1, x_point, key_size);
|
||||
memcpy(public_key + 1 + key_size, y_point, key_size);
|
||||
|
||||
/* Import the public key */
|
||||
status = psa_import_key(&key_attributes, public_key, (2 * key_size) + 1, &key_handle);
|
||||
if (status != PSA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Failed to import key, err:%d", status);
|
||||
ret = ESP_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Convert signature from little-endian to big-endian format */
|
||||
uint8_t signature[2 * ECDSA_INTEGER_LEN] = {0};
|
||||
for (int i = 0; i < key_size; i++) {
|
||||
signature[i] = trusted_block->ecdsa.signature[key_size - i - 1];
|
||||
signature[key_size + i] = trusted_block->ecdsa.signature[2 * key_size - i - 1];
|
||||
}
|
||||
|
||||
/* Verify the signature */
|
||||
status = psa_verify_hash(key_handle, alg,
|
||||
image_digest, ESP_SECURE_BOOT_DIGEST_LEN,
|
||||
signature, 2 * key_size);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Signature verification failed, err:%d", status);
|
||||
ret = ESP_FAIL;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
/* Clean up resources */
|
||||
if (key_handle) {
|
||||
psa_destroy_key(key_handle);
|
||||
}
|
||||
psa_reset_key_attributes(&key_attributes);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_log.h"
|
||||
#include "esp_secure_boot.h"
|
||||
#include "psa/crypto.h"
|
||||
#include "mbedtls/asn1.h"
|
||||
#include "mbedtls/asn1write.h"
|
||||
#include "mbedtls/x509.h"
|
||||
|
||||
#include "secure_boot_signature_priv.h"
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "secure_boot_v2_rsa");
|
||||
|
||||
/*
|
||||
* Helper function to encode RSA public key (N, e) into DER format manually
|
||||
* This creates a PKCS#1 RSAPublicKey structure:
|
||||
*
|
||||
* RSAPublicKey ::= SEQUENCE {
|
||||
* modulus INTEGER, -- n
|
||||
* publicExponent INTEGER -- e
|
||||
* }
|
||||
*/
|
||||
static int encode_rsa_pubkey_der(const uint8_t *modulus, size_t modulus_len,
|
||||
const uint8_t *exponent, size_t exponent_len,
|
||||
uint8_t *der_buf, size_t der_buf_size,
|
||||
uint8_t **der_start, size_t *der_len)
|
||||
{
|
||||
if (!der_buf || !der_start || !der_len || der_buf_size == 0) {
|
||||
return MBEDTLS_ERR_X509_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
int ret;
|
||||
unsigned char *c = der_buf + der_buf_size;
|
||||
size_t len = 0;
|
||||
|
||||
/* Write the exponent (e) as an INTEGER */
|
||||
/* Skip leading zeros in exponent */
|
||||
while (exponent_len > 0 && *exponent == 0) {
|
||||
exponent++;
|
||||
exponent_len--;
|
||||
}
|
||||
|
||||
/* Write exponent */
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(&c, der_buf, exponent, exponent_len));
|
||||
|
||||
/* Add padding byte if MSB is set (to keep it positive) */
|
||||
if (exponent_len > 0 && (exponent[0] & 0x80)) {
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(&c, der_buf, (const unsigned char *)"\x00", 1));
|
||||
}
|
||||
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, der_buf, exponent_len + ((exponent[0] & 0x80) ? 1 : 0)));
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, der_buf, MBEDTLS_ASN1_INTEGER));
|
||||
|
||||
/* Write the modulus (N) as an INTEGER */
|
||||
/* Skip leading zeros in modulus */
|
||||
const uint8_t *mod_ptr = modulus;
|
||||
size_t mod_len = modulus_len;
|
||||
while (mod_len > 0 && *mod_ptr == 0) {
|
||||
mod_ptr++;
|
||||
mod_len--;
|
||||
}
|
||||
|
||||
/* Write modulus */
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(&c, der_buf, mod_ptr, mod_len));
|
||||
|
||||
/* Add padding byte if MSB is set */
|
||||
if (mod_len > 0 && (mod_ptr[0] & 0x80)) {
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_raw_buffer(&c, der_buf, (const unsigned char *)"\x00", 1));
|
||||
}
|
||||
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, der_buf, mod_len + ((mod_ptr[0] & 0x80) ? 1 : 0)));
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, der_buf, MBEDTLS_ASN1_INTEGER));
|
||||
|
||||
/* Write SEQUENCE header */
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&c, der_buf, len));
|
||||
MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&c, der_buf,
|
||||
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE));
|
||||
|
||||
*der_start = c;
|
||||
*der_len = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
esp_err_t verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, const ets_secure_boot_sig_block_t *trusted_block)
|
||||
{
|
||||
if (!sig_block || !image_digest || !trusted_block) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
psa_status_t status;
|
||||
const unsigned rsa_key_size = sizeof(sig_block->block[0].signature);
|
||||
unsigned char *sig_be = NULL;
|
||||
unsigned char *pubkey_der_buf = NULL;
|
||||
|
||||
sig_be = calloc(1, rsa_key_size);
|
||||
if (sig_be == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
/* Create key attributes for RSA public key */
|
||||
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
|
||||
psa_key_id_t key_id = 0;
|
||||
|
||||
/* Allocate buffer for DER-encoded public key */
|
||||
size_t pubkey_der_buf_size = PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(3072);
|
||||
pubkey_der_buf = calloc(1, pubkey_der_buf_size);
|
||||
if (pubkey_der_buf == NULL) {
|
||||
free(sig_be);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
/* Convert raw N and e to DER format manually */
|
||||
uint8_t *der_start = NULL;
|
||||
size_t der_len = 0;
|
||||
|
||||
/* Convert modulus from little-endian to big-endian */
|
||||
uint8_t *n_be = calloc(1, rsa_key_size);
|
||||
if (n_be == NULL) {
|
||||
free(sig_be);
|
||||
free(pubkey_der_buf);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
for (size_t i = 0; i < rsa_key_size; i++) {
|
||||
n_be[i] = trusted_block->key.n[rsa_key_size - 1 - i];
|
||||
}
|
||||
|
||||
/* Convert e from uint32_t to byte array (big-endian) */
|
||||
uint8_t e_bytes[4];
|
||||
e_bytes[0] = (trusted_block->key.e >> 24) & 0xFF;
|
||||
e_bytes[1] = (trusted_block->key.e >> 16) & 0xFF;
|
||||
e_bytes[2] = (trusted_block->key.e >> 8) & 0xFF;
|
||||
e_bytes[3] = trusted_block->key.e & 0xFF;
|
||||
|
||||
ret = encode_rsa_pubkey_der(
|
||||
n_be, rsa_key_size,
|
||||
e_bytes, sizeof(e_bytes),
|
||||
pubkey_der_buf, pubkey_der_buf_size,
|
||||
&der_start, &der_len
|
||||
);
|
||||
|
||||
free(n_be);
|
||||
|
||||
if (ret != 0) {
|
||||
ESP_LOGE(TAG, "Failed to encode RSA public key to DER, err: %d", ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Set key attributes */
|
||||
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_HASH);
|
||||
psa_set_key_algorithm(&key_attributes, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256));
|
||||
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_RSA_PUBLIC_KEY);
|
||||
|
||||
/* Import DER-encoded public key into PSA */
|
||||
status = psa_import_key(&key_attributes, der_start, der_len, &key_id);
|
||||
if (status != PSA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Failed to import key into PSA, err: %d", status);
|
||||
ret = ESP_FAIL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Signature needs to be byte swapped into BE representation */
|
||||
for (int j = 0; j < rsa_key_size; j++) {
|
||||
sig_be[rsa_key_size - j - 1] = trusted_block->signature[j];
|
||||
}
|
||||
|
||||
/* Verify the signature using PSA APIs */
|
||||
status = psa_verify_hash(key_id, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256),
|
||||
image_digest, ESP_SECURE_BOOT_DIGEST_LEN,
|
||||
sig_be, rsa_key_size);
|
||||
|
||||
if (status != PSA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Signature verification failed, err: %d", status);
|
||||
ret = ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Signature verified successfully!");
|
||||
ret = ESP_OK;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
/* Clean up resources */
|
||||
if (key_id != 0) {
|
||||
psa_destroy_key(key_id);
|
||||
}
|
||||
psa_reset_key_attributes(&key_attributes);
|
||||
free(sig_be);
|
||||
free(pubkey_der_buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "esp_secure_boot.h"
|
||||
#include "rom/secure_boot.h"
|
||||
|
||||
esp_err_t verify_ecdsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, const ets_secure_boot_sig_block_t *trusted_block);
|
||||
|
||||
esp_err_t verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, const ets_secure_boot_sig_block_t *trusted_block);
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include "bootloader_flash_priv.h"
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_sha_flash.h"
|
||||
#include "bootloader_signature.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_image_format.h"
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include "esp_secure_boot.h"
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_efuse_chip.h"
|
||||
|
||||
#include "secure_boot_signature_priv.h"
|
||||
#include "esp_macros.h"
|
||||
|
||||
// Secure boot V2 for app
|
||||
|
||||
_Static_assert(SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS == SECURE_BOOT_NUM_BLOCKS,
|
||||
"Parts of this code rely on the max number of signatures appended to an image"
|
||||
"being the same as the max number of trusted keys.");
|
||||
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME || CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "secure_boot_v2");
|
||||
|
||||
/* A signature block is valid when it has correct magic byte, crc. */
|
||||
static esp_err_t validate_signature_block(const ets_secure_boot_sig_block_t *block)
|
||||
{
|
||||
if (block->magic_byte != ETS_SECURE_BOOT_V2_SIGNATURE_MAGIC
|
||||
|| block->block_crc != esp_rom_crc32_le(0, (uint8_t *)block, CRC_SIGN_BLOCK_LEN)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (block->version != ESP_SECURE_BOOT_SCHEME) {
|
||||
ESP_LOGE(TAG, "%s signing scheme selected but signature block generated for %s scheme", esp_secure_boot_get_scheme_name(ESP_SECURE_BOOT_SCHEME), esp_secure_boot_get_scheme_name(block->version));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
if (block->ecdsa.key.curve_id != ESP_SECURE_BOOT_ECDSA_CURVE_ID) {
|
||||
ESP_LOGE(TAG, "ECDSA curve mismatch: actual (curve_id %u), expected (curve_id %u)", (unsigned) block->ecdsa.key.curve_id, (unsigned) ESP_SECURE_BOOT_ECDSA_CURVE_ID);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t calculate_image_public_key_digests(bool verify_image_digest, bool digest_public_keys, esp_image_sig_public_key_digests_t *public_key_digests, esp_partition_pos_t *part_pos)
|
||||
{
|
||||
esp_image_metadata_t img_metadata = {0};
|
||||
esp_err_t ret = esp_image_get_metadata(part_pos, &img_metadata);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error reading metadata from running app (err=0x%x)", ret);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t image_digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0};
|
||||
uint8_t __attribute__((aligned(4))) key_digest[ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN] = {0};
|
||||
size_t sig_block_addr = img_metadata.start_addr + ESP_ALIGN_UP(img_metadata.image_len, FLASH_SECTOR_SIZE);
|
||||
|
||||
ESP_LOGD(TAG, "calculating public key digests for sig blocks of image offset 0x%"PRIu32" (sig block offset 0x%u)", img_metadata.start_addr, sig_block_addr);
|
||||
|
||||
bzero(public_key_digests, sizeof(esp_image_sig_public_key_digests_t));
|
||||
|
||||
if (verify_image_digest) {
|
||||
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
|
||||
ret = bootloader_sha384_flash_contents(img_metadata.start_addr, sig_block_addr - img_metadata.start_addr, image_digest);
|
||||
#else
|
||||
ret = bootloader_sha256_flash_contents(img_metadata.start_addr, sig_block_addr - img_metadata.start_addr, image_digest);
|
||||
#endif
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "error generating image digest, %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "reading signature(s)");
|
||||
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
ets_secure_boot_sig_block_t block;
|
||||
size_t addr = sig_block_addr + sizeof(ets_secure_boot_sig_block_t) * i;
|
||||
esp_err_t err = bootloader_flash_read(addr, &block, sizeof(ets_secure_boot_sig_block_t), true);
|
||||
if (err == ESP_OK) {
|
||||
if (validate_signature_block(&block) == ESP_OK) {
|
||||
if (digest_public_keys) {
|
||||
bootloader_sha256_handle_t sig_block_sha = bootloader_sha256_start();
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
bootloader_sha256_data(sig_block_sha, &block.key, sizeof(block.key));
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
bootloader_sha256_data(sig_block_sha, &block.ecdsa.key, sizeof(block.ecdsa.key));
|
||||
#endif
|
||||
bootloader_sha256_finish(sig_block_sha, key_digest);
|
||||
if (verify_image_digest) {
|
||||
// Check we can verify the image using this signature and this key
|
||||
uint8_t temp_verified_digest[ESP_SECURE_BOOT_DIGEST_LEN];
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
bool verified = ets_rsa_pss_verify(&block.key, block.signature, image_digest, temp_verified_digest);
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
bool verified = ets_ecdsa_verify(&block.ecdsa.key.point[0], block.ecdsa.signature, block.ecdsa.key.curve_id, image_digest, temp_verified_digest);
|
||||
#endif
|
||||
if (!verified) {
|
||||
ESP_LOGE(TAG, "Secure boot key (%d) verification failed.", i);
|
||||
continue;
|
||||
}
|
||||
ESP_LOGD(TAG, "Signature block (%d) is verified", i);
|
||||
}
|
||||
/* Copy the key digest to the buffer provided by the caller */
|
||||
memcpy((void *)public_key_digests->key_digests[public_key_digests->num_digests], key_digest, ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN);
|
||||
}
|
||||
public_key_digests->num_digests++;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Secure boot sign blocks cannot be read from image at %lx (err=0x%x)", part_pos->offset, err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == ESP_OK && public_key_digests->num_digests > 0) {
|
||||
ESP_LOGD(TAG, "Digests successfully calculated, %d valid signatures (image offset 0x%"PRIu32")",
|
||||
public_key_digests->num_digests, img_metadata.start_addr);
|
||||
}
|
||||
|
||||
if (public_key_digests->num_digests == 0) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_get_signature_blocks_for_running_app(bool digest_public_keys, esp_image_sig_public_key_digests_t *public_key_digests)
|
||||
{
|
||||
esp_partition_pos_t part_pos;
|
||||
const esp_partition_t* running_app_part = esp_ota_get_running_partition();
|
||||
if (running_app_part == NULL) {
|
||||
ESP_LOGE(TAG, "Cannot get running partition");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
part_pos.offset = running_app_part->address;
|
||||
part_pos.size = running_app_part->size;
|
||||
|
||||
esp_err_t err = calculate_image_public_key_digests(false, digest_public_keys, public_key_digests, &part_pos);
|
||||
if (public_key_digests->num_digests == 0) {
|
||||
ESP_LOGE(TAG, "No signatures were found for the running app");
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t get_secure_boot_key_digests(esp_image_sig_public_key_digests_t *public_key_digests)
|
||||
{
|
||||
if (!esp_secure_boot_enabled()) { // CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
|
||||
// Gets key digests from running app
|
||||
ESP_LOGI(TAG, "Take trusted digest key(s) from running app");
|
||||
return esp_secure_boot_get_signature_blocks_for_running_app(true, public_key_digests);
|
||||
} else { // CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
ESP_LOGI(TAG, "Take trusted digest key(s) from eFuse block(s)");
|
||||
// Read key digests from efuse
|
||||
esp_secure_boot_key_digests_t efuse_trusted;
|
||||
if (esp_secure_boot_read_key_digests(&efuse_trusted) == ESP_OK) {
|
||||
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
if (efuse_trusted.key_digests[i] != NULL) {
|
||||
memcpy(public_key_digests->key_digests[i], (uint8_t *)efuse_trusted.key_digests[i], ESP_SECURE_BOOT_KEY_DIGEST_LEN);
|
||||
public_key_digests->num_digests++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (public_key_digests->num_digests > 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
uint8_t digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0};
|
||||
|
||||
/* Rounding off length to the upper 4k boundary */
|
||||
uint32_t padded_length = ESP_ALIGN_UP(length, FLASH_SECTOR_SIZE);
|
||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%"PRIx32" length 0x%"PRIx32, src_addr, length);
|
||||
|
||||
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
|
||||
err = bootloader_sha384_flash_contents(src_addr, padded_length, digest);
|
||||
#else
|
||||
err = bootloader_sha256_flash_contents(src_addr, padded_length, digest);
|
||||
#endif
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Digest calculation failed 0x%"PRIx32", 0x%"PRIx32, src_addr, padded_length);
|
||||
return err;
|
||||
}
|
||||
|
||||
const ets_secure_boot_signature_t *sig_block = bootloader_mmap(src_addr + padded_length, sizeof(ets_secure_boot_signature_t));
|
||||
if (sig_block == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to mmap data at offset 0x%"PRIx32, src_addr + padded_length);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
err = esp_secure_boot_verify_sbv2_signature_block(sig_block, digest, NULL);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
||||
}
|
||||
bootloader_munmap(sig_block);
|
||||
return err;
|
||||
}
|
||||
|
||||
// This verify function is called only from app, during ota update.
|
||||
// This function is compiled in case when CONFIG_SECURE_BOOT_V2_ENABLED==y or CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT==y.
|
||||
// if CONFIG_SECURE_BOOT_V2_ENABLED==y and key digests from eFuse are missing, then FAIL (eFuse blocks should be set).
|
||||
esp_err_t esp_secure_boot_verify_sbv2_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
bool any_trusted_key = false;
|
||||
|
||||
if (verified_digest != NULL) {
|
||||
/* Note: in IDF verification we don't add any fault injection resistance, as we don't expect this to be called
|
||||
during boot-time verification. */
|
||||
memset(verified_digest, 0, ESP_SECURE_BOOT_DIGEST_LEN);
|
||||
}
|
||||
|
||||
esp_image_sig_public_key_digests_t trusted = {0};
|
||||
|
||||
if (get_secure_boot_key_digests(&trusted) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Could not read secure boot digests!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
#ifdef CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
|
||||
const unsigned secure_boot_num_blocks = 1;
|
||||
#else
|
||||
const unsigned secure_boot_num_blocks = SECURE_BOOT_NUM_BLOCKS;
|
||||
#endif
|
||||
|
||||
for (unsigned app_blk_idx = 0; app_blk_idx < secure_boot_num_blocks; app_blk_idx++) {
|
||||
uint8_t app_blk_digest[ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN] = { 0 };
|
||||
const ets_secure_boot_sig_block_t *app_blk = &sig_block->block[app_blk_idx];
|
||||
const ets_secure_boot_sig_block_t *trusted_block = NULL;
|
||||
|
||||
if (validate_signature_block(app_blk) != ESP_OK) {
|
||||
continue; // Skip invalid signature blocks
|
||||
}
|
||||
|
||||
/* Generate the SHA of the public key components in the signature block */
|
||||
bootloader_sha256_handle_t sig_block_sha = bootloader_sha256_start();
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
bootloader_sha256_data(sig_block_sha, &app_blk->key, sizeof(app_blk->key));
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
bootloader_sha256_data(sig_block_sha, &app_blk->ecdsa.key, sizeof(app_blk->ecdsa.key));
|
||||
#endif
|
||||
|
||||
bootloader_sha256_finish(sig_block_sha, app_blk_digest);
|
||||
|
||||
/* Check if the key is one we trust */
|
||||
for (unsigned trusted_key_idx = 0; trusted_key_idx < secure_boot_num_blocks; trusted_key_idx++) {
|
||||
if (memcmp(app_blk_digest, trusted.key_digests[trusted_key_idx], ESP_SECURE_BOOT_KEY_DIGEST_LEN) == 0) {
|
||||
ESP_LOGI(TAG, "#%d app key digest == #%d trusted key digest", app_blk_idx, trusted_key_idx);
|
||||
trusted_block = app_blk;
|
||||
any_trusted_key = true;
|
||||
break;
|
||||
}
|
||||
ESP_LOGV(TAG, "not trusting app sig %d trust idx %d", app_blk_idx, trusted_key_idx);
|
||||
}
|
||||
|
||||
if (trusted_block == NULL) {
|
||||
continue; // Skip the signature blocks with no trusted digest
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
ESP_LOGI(TAG, "Verifying with RSA-PSS...");
|
||||
ret = verify_rsa_signature_block(sig_block, image_digest, trusted_block);
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
ESP_LOGI(TAG, "Verifying with ECDSA...");
|
||||
ret = verify_ecdsa_signature_block(sig_block, image_digest, trusted_block);
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (ret != 0 || any_trusted_key == false) ? ESP_ERR_IMAGE_INVALID : ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
// To maintain backward compatibility
|
||||
esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
return esp_secure_boot_verify_sbv2_signature_block(sig_block, image_digest, verified_digest);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME || CONFIG_SECURE_SIGNED_ON_UPDATE_NO_SECURE_BOOT
|
||||
|
||||
#if SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY && CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
|
||||
esp_err_t esp_secure_boot_verify_with_efuse_digest_index(int efuse_digest_index, esp_partition_pos_t *part_pos)
|
||||
{
|
||||
if (!part_pos || (efuse_digest_index < 0 || efuse_digest_index >= SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
esp_image_sig_public_key_digests_t img_key_digests = {0};
|
||||
esp_err_t ret = calculate_image_public_key_digests(true, true, &img_key_digests, part_pos);
|
||||
if (ret != ESP_OK) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (esp_efuse_get_digest_revoke(efuse_digest_index)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Read key digests from efuse
|
||||
esp_secure_boot_key_digests_t efuse_key_digests;
|
||||
memset(&efuse_key_digests, 0, sizeof(esp_secure_boot_key_digests_t));
|
||||
/* A non-revoked slot can still be unprovisioned, leaving its digest pointer NULL even
|
||||
* when the read returns ESP_OK; comparing it would dereference NULL in memcmp(). Check
|
||||
* both the read result and the specific slot (matches get_secure_boot_key_digests()). */
|
||||
if (esp_secure_boot_read_key_digests(&efuse_key_digests) != ESP_OK ||
|
||||
efuse_key_digests.key_digests[efuse_digest_index] == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < img_key_digests.num_digests; i++) {
|
||||
if (!memcmp(img_key_digests.key_digests[i], efuse_key_digests.key_digests[efuse_digest_index], ESP_SECURE_BOOT_KEY_DIGEST_LEN)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif // SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY && CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_fault.h"
|
||||
#include "bootloader_flash_priv.h"
|
||||
#include "bootloader_sha.h"
|
||||
#include "bootloader_sha_flash.h"
|
||||
#include "bootloader_signature.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_secure_boot.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_macros.h"
|
||||
|
||||
// Secure boot V2 for bootloader.
|
||||
|
||||
#if CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
|
||||
ESP_LOG_ATTR_TAG(TAG, "secure_boot_v2");
|
||||
|
||||
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
uint8_t digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0};
|
||||
uint8_t verified_digest[ESP_SECURE_BOOT_DIGEST_LEN] = { 0 }; /* Note: this function doesn't do any anti-FI checks on this buffer */
|
||||
|
||||
/* Rounding off length to the upper 4k boundary */
|
||||
uint32_t padded_length = ESP_ALIGN_UP(length, FLASH_SECTOR_SIZE);
|
||||
ESP_LOGD(TAG, "verifying signature src_addr 0x%" PRIx32 " length 0x%" PRIx32, src_addr, length);
|
||||
|
||||
/* Calculate digest of main image */
|
||||
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
|
||||
err = bootloader_sha384_flash_contents(src_addr, padded_length, digest);
|
||||
#else
|
||||
err = bootloader_sha256_flash_contents(src_addr, padded_length, digest);
|
||||
#endif
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Digest calculation failed 0x%" PRIx32 ", 0x%" PRIx32, src_addr, padded_length);
|
||||
return err;
|
||||
}
|
||||
|
||||
const ets_secure_boot_signature_t *sig_block = bootloader_mmap(src_addr + padded_length, sizeof(ets_secure_boot_signature_t));
|
||||
if (sig_block == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to mmap data at offset 0x%" PRIx32, src_addr + padded_length);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
err = esp_secure_boot_verify_sbv2_signature_block(sig_block, digest, verified_digest);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
||||
}
|
||||
bootloader_munmap(sig_block);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* A signature block is valid when it has correct magic byte, crc. */
|
||||
static esp_err_t validate_signature_block(const ets_secure_boot_sig_block_t *block)
|
||||
{
|
||||
if (block->magic_byte != ETS_SECURE_BOOT_V2_SIGNATURE_MAGIC
|
||||
|| block->block_crc != esp_rom_crc32_le(0, (uint8_t *)block, CRC_SIGN_BLOCK_LEN)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (block->version != ESP_SECURE_BOOT_SCHEME) {
|
||||
ESP_LOGE(TAG, "%s signing scheme selected but signature block generated for %s scheme", esp_secure_boot_get_scheme_name(ESP_SECURE_BOOT_SCHEME), esp_secure_boot_get_scheme_name(block->version));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
if (block->ecdsa.key.curve_id != ESP_SECURE_BOOT_ECDSA_CURVE_ID) {
|
||||
ESP_LOGE(TAG, "ECDSA curve mismatch: actual (curve_id %u), expected (curve_id %u)", (unsigned) block->ecdsa.key.curve_id, (unsigned) ESP_SECURE_BOOT_ECDSA_CURVE_ID);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t get_secure_boot_key_digests(esp_image_sig_public_key_digests_t *public_key_digests)
|
||||
{
|
||||
// Read key digests from efuse
|
||||
esp_secure_boot_key_digests_t trusted_keys;
|
||||
esp_secure_boot_key_digests_t trusted_key_copies[2];
|
||||
|
||||
memset(&trusted_keys, 0, sizeof(esp_secure_boot_key_digests_t));
|
||||
memset(trusted_key_copies, 0, 2 * sizeof(esp_secure_boot_key_digests_t));
|
||||
|
||||
esp_err_t err = esp_secure_boot_read_key_digests(&trusted_keys);
|
||||
|
||||
// Create the copies for FI checks (assuming result is ETS_OK, if it's not then it'll fail the fault check anyhow)
|
||||
esp_secure_boot_read_key_digests(&trusted_key_copies[0]);
|
||||
esp_secure_boot_read_key_digests(&trusted_key_copies[1]);
|
||||
ESP_FAULT_ASSERT(memcmp(&trusted_keys, &trusted_key_copies[0], sizeof(esp_secure_boot_key_digests_t)) == 0);
|
||||
ESP_FAULT_ASSERT(memcmp(&trusted_keys, &trusted_key_copies[1], sizeof(esp_secure_boot_key_digests_t)) == 0);
|
||||
|
||||
if (err == ESP_OK) {
|
||||
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
if (trusted_keys.key_digests[i] != NULL) {
|
||||
memcpy(public_key_digests->key_digests[i], (uint8_t *)trusted_keys.key_digests[i], ESP_SECURE_BOOT_KEY_DIGEST_LEN);
|
||||
public_key_digests->num_digests++;
|
||||
}
|
||||
}
|
||||
#if SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS == 1
|
||||
if (esp_efuse_block_is_empty(EFUSE_BLK_SECURE_BOOT)) {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
#endif // SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS
|
||||
if (public_key_digests->num_digests > 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// if CONFIG_SECURE_BOOT_V2_ENABLED==y and key digests from eFuse are missing, then it is the first boot,
|
||||
// trusted.key_digests are filled from app sig_block.
|
||||
esp_err_t esp_secure_boot_verify_sbv2_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
esp_image_sig_public_key_digests_t trusted = {0};
|
||||
bool efuse_keys_are_not_set = false;
|
||||
if (get_secure_boot_key_digests(&trusted) != ESP_OK) {
|
||||
if (esp_secure_boot_enabled()) {
|
||||
ESP_LOGE(TAG, "Could not read eFuse secure boot digests!");
|
||||
return ESP_FAIL;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Secure boot V2 is not enabled yet and eFuse digest keys are not set");
|
||||
efuse_keys_are_not_set = true;
|
||||
ESP_FAULT_ASSERT(!esp_secure_boot_enabled());
|
||||
}
|
||||
}
|
||||
|
||||
if (!esp_secure_boot_enabled()) {
|
||||
// It is the first boot. eFuse secure boot bit is not set yet. eFuse block(s) can be written or not.
|
||||
// Generating the SHA of the public key components in the signature block
|
||||
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
if (validate_signature_block(&sig_block->block[i]) == ESP_OK) {
|
||||
if (efuse_keys_are_not_set) {
|
||||
// if efuse key digests are not in eFuse yet due to it is the first boot
|
||||
// then use digests from app to skip error in ets_secure_boot_verify_signature().
|
||||
bootloader_sha256_handle_t sig_block_sha = bootloader_sha256_start();
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
bootloader_sha256_data(sig_block_sha, &sig_block->block[i].key, sizeof(sig_block->block[i].key));
|
||||
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
bootloader_sha256_data(sig_block_sha, &sig_block->block[i].ecdsa.key, sizeof(sig_block->block[i].ecdsa.key));
|
||||
#endif
|
||||
bootloader_sha256_finish(sig_block_sha, trusted.key_digests[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
ESP_FAULT_ASSERT(!esp_secure_boot_enabled());
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
ESP_LOGI(TAG, "Verifying with RSA-PSS...");
|
||||
#else
|
||||
ESP_LOGI(TAG, "Verifying with ECDSA...");
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
int sb_result = ets_secure_boot_verify_signature(sig_block, image_digest, trusted.key_digests[0], verified_digest);
|
||||
#else
|
||||
ets_secure_boot_key_digests_t trusted_key_digests = {0};
|
||||
bool valid_sig_blk = false;
|
||||
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
trusted_key_digests.key_digests[i] = &trusted.key_digests[i];
|
||||
if (sig_block->block[i].version != ESP_SECURE_BOOT_SCHEME) {
|
||||
ESP_LOGD(TAG, "%s signing scheme selected but signature block %d generated for %s scheme", esp_secure_boot_get_scheme_name(ESP_SECURE_BOOT_SCHEME), i, esp_secure_boot_get_scheme_name(sig_block->block[i].version));
|
||||
} else {
|
||||
valid_sig_blk = true;
|
||||
}
|
||||
}
|
||||
if (valid_sig_blk != true) {
|
||||
ESP_LOGE(TAG, "No signature block generated for valid scheme");
|
||||
ESP_LOGE(TAG, "%s signing scheme selected but no signature block for the selected scheme", esp_secure_boot_get_scheme_name(ESP_SECURE_BOOT_SCHEME));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Key revocation happens in ROM bootloader.
|
||||
// Do NOT allow key revocation while verifying application
|
||||
trusted_key_digests.allow_key_revoke = false;
|
||||
|
||||
int sb_result = ets_secure_boot_verify_signature(sig_block, image_digest, &trusted_key_digests, verified_digest);
|
||||
#endif // CONFIG_IDF_TARGET_ESP32
|
||||
|
||||
if (sb_result != SB_SUCCESS) {
|
||||
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
|
||||
return ESP_ERR_IMAGE_INVALID;
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Signature verified successfully!");
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME
|
||||
// To maintain backward compatibility
|
||||
esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
|
||||
{
|
||||
return esp_secure_boot_verify_sbv2_signature_block(sig_block, image_digest, verified_digest);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CONFIG_SECURE_BOOT_V2_ENABLED
|
||||
Reference in New Issue
Block a user