feat(esp_image_verify): split image verification out of bootloader_support

This commit is contained in:
Ashish Sharma
2026-07-08 15:54:40 +08:00
committed by Mahavir Jain
parent b912691fcd
commit e7af5c69d9
54 changed files with 650 additions and 520 deletions

View 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) */

View 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

File diff suppressed because it is too large Load Diff

View 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 */

View 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

View File

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

View File

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

View 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

View File

@@ -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;
}

View File

@@ -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;
}

View File

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

View File

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

View File

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