Merge branch 'feat/secure_boot_ecdsa_p384' into 'master'

Support Secure Boot using ECDSA-P384 curve

Closes IDF-10016, IDF-10221, and IDF-12990

See merge request espressif/esp-idf!38517
This commit is contained in:
Mahavir Jain
2025-07-24 11:59:59 +05:30
42 changed files with 1049 additions and 289 deletions

View File

@@ -559,12 +559,13 @@ menu "Security features"
depends on SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
default SECURE_BOOT_ECDSA_KEY_LEN_256_BITS
help
Select the ECDSA key size. Two key sizes are supported
Select the ECDSA key size. Three key sizes are supported depending upon on the target:
- 192 bit key using NISTP192 curve
- 256 bit key using NISTP256 curve (Recommended)
- 384 bit key using NISTP384 curve (Recommended)
The advantage of using 256 bit key is the extra randomness which makes it difficult to be
The advantage of using 384 and 256 bit keys is the extra randomness which makes it difficult to be
bruteforced compared to 192 bit key.
At present, both key sizes are practically implausible to bruteforce.
@@ -576,6 +577,10 @@ menu "Security features"
bool "Using ECC curve NISTP256 (Recommended)"
depends on SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
config SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
bool "Using ECC curve NISTP384 (Recommended)"
depends on SECURE_SIGNED_APPS_ECDSA_V2_SCHEME && SOC_ECDSA_SUPPORT_CURVE_P384
endchoice
config SECURE_SIGNED_ON_BOOT_NO_SECURE_BOOT

View File

@@ -70,6 +70,8 @@ if(CONFIG_SECURE_SIGNED_APPS)
set(scheme "ecdsa192")
elseif(CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_256_BITS)
set(scheme "ecdsa256")
elseif(CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS)
set(scheme "ecdsa384")
endif()
fail_at_build_time(gen_secure_boot_signing_key
"Secure Boot Signing Key ${CONFIG_SECURE_BOOT_SIGNING_KEY} does not exist. Generate using:"

View File

@@ -17,7 +17,6 @@
#if !CONFIG_IDF_TARGET_LINUX
#include "rom/secure_boot.h"
#endif
#ifdef CONFIG_SECURE_BOOT_V1_ENABLED
#if !defined(CONFIG_SECURE_SIGNED_ON_BOOT) || !defined(CONFIG_SECURE_SIGNED_ON_UPDATE) || !defined(CONFIG_SECURE_SIGNED_APPS)
#error "internal sdkconfig error, secure boot should always enable all signature options"
@@ -33,12 +32,20 @@ extern "C" {
Can be compiled as part of app or bootloader code.
*/
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
#define ESP_SECURE_BOOT_DIGEST_LEN 48
#else /* !CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS */
#define ESP_SECURE_BOOT_DIGEST_LEN 32
#endif /* CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS */
/* SHA-256 length of the public key digest */
#define ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN 32
/* Length of the public key digest that is stored in efuses */
#if CONFIG_IDF_TARGET_ESP32C2
#define ESP_SECURE_BOOT_KEY_DIGEST_LEN 16
#define ESP_SECURE_BOOT_KEY_DIGEST_LEN ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN / 2
#else
#define ESP_SECURE_BOOT_KEY_DIGEST_LEN 32
#define ESP_SECURE_BOOT_KEY_DIGEST_LEN ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN
#endif
#ifdef CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH
@@ -193,7 +200,8 @@ esp_err_t esp_secure_boot_v2_permanently_enable(const esp_image_metadata_t *imag
/** @brief Verify the secure boot signature appended to some binary data in flash.
*
* For ECDSA Scheme (Secure Boot V1) - deterministic ECDSA w/ SHA256 image
* For RSA Scheme (Secure Boot V2) - RSA-PSS Verification of the SHA-256 image
* For RSA Scheme (Secure Boot V2) - RSA-PSS Verification of the SHA-256 image digest
* For ECDSA Scheme (Secure Boot V2) - ECDSA Verification of the SHA-256 / SHA-384 (in case of ECDSA-P384 secure boot key) image digest
*
* Public key is compiled into the calling program in the ECDSA Scheme.
* See the apt docs/security/secure-boot-v1.rst or docs/security/secure-boot-v2.rst for details.
@@ -236,13 +244,13 @@ esp_err_t esp_secure_boot_verify_ecdsa_signature_block(const esp_secure_boot_sig
/** @brief Verify the secure boot signature block for Secure Boot V2.
*
* Performs RSA-PSS or ECDSA verification of the SHA-256 image based on the public key
* Performs RSA-PSS or ECDSA verification of the SHA-256 / SHA-384 image based on the public key
* in the signature block, compared against the public key digest stored in efuse.
*
* Similar to esp_secure_boot_verify_signature(), but can be used when the digest is precalculated.
* @param[in] sig_block Pointer to signature block data
* @param[in] image_digest Pointer to 32 byte buffer holding SHA-256 hash.
* @param[out] verified_digest Pointer to 32 byte buffer that will receive verified digest if verification completes. (Used during bootloader implementation only, result is invalid otherwise.)
* @param[in] image_digest Pointer to 32/48 byte buffer holding SHA-256/SHA-384 hash.
* @param[out] verified_digest Pointer to 32/48 byte buffer that will receive verified digest if verification completes. (Used during bootloader implementation only, result is invalid otherwise.)
*
*/
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);
@@ -255,7 +263,7 @@ esp_err_t esp_secure_boot_verify_sbv2_signature_block(const ets_secure_boot_sign
* Each image can have one or more signature blocks (up to SECURE_BOOT_NUM_BLOCKS). Each signature block includes a public key.
*/
typedef struct {
uint8_t key_digests[SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS][ESP_SECURE_BOOT_DIGEST_LEN]; /* SHA of the public key components in the signature block */
uint8_t key_digests[SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS][ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN]; /* SHA of the public key components in the signature block */
unsigned num_digests; /* Number of valid digests, starting at index 0 */
} esp_image_sig_public_key_digests_t;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2017-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -12,15 +12,18 @@
Use mbedTLS APIs or include esp32/sha.h to calculate SHA256 in IDF apps.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "esp_err.h"
#include "soc/soc_caps.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void *bootloader_sha256_handle_t;
typedef bootloader_sha256_handle_t bootloader_sha_handle_t;
bootloader_sha256_handle_t bootloader_sha256_start(void);
@@ -28,6 +31,14 @@ void bootloader_sha256_data(bootloader_sha256_handle_t handle, const void *data,
void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest);
#if SOC_SHA_SUPPORT_SHA512
bootloader_sha_handle_t bootloader_sha512_start(bool is384);
void bootloader_sha512_data(bootloader_sha_handle_t handle, const void *data, size_t data_len);
void bootloader_sha512_finish(bootloader_sha_handle_t handle, uint8_t *digest);
#endif /* SOC_SHA_SUPPORT_SHA512 */
#ifdef __cplusplus
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -138,6 +138,20 @@ void bootloader_debug_buffer(const void *buffer, size_t length, const char *labe
*/
esp_err_t bootloader_sha256_flash_contents(uint32_t flash_offset, uint32_t len, uint8_t *digest);
/** @brief Generates the digest of the data between offset & offset+length.
*
* This function should be used when the size of the data is larger than 3.2MB.
* The MMU capacity is 3.2MB (50 pages - 64KB each). This function generates the SHA-384
* of the data in chunks of 3.2MB, considering the MMU capacity.
*
* @param[in] flash_offset Offset of the data in flash.
* @param[in] len Length of data in bytes.
* @param[out] digest Pointer to buffer where the digest is written, if ESP_OK is returned.
*
* @return ESP_OK if secure boot digest is generated successfully.
*/
esp_err_t bootloader_sha384_flash_contents(uint32_t flash_offset, uint32_t len, uint8_t *digest);
#ifdef __cplusplus
}
#endif

View File

@@ -29,15 +29,6 @@ bootloader_sha256_handle_t bootloader_sha256_start()
void bootloader_sha256_data(bootloader_sha256_handle_t handle, const void *data, size_t data_len)
{
assert(handle != NULL);
#if !SOC_SECURE_BOOT_V2_ECC
/* For secure boot, the key field consists of 1 byte of curve identifier and 64 bytes of ECDSA public key.
* While verifying the signature block, we need to calculate the SHA of this key field which is of 65 bytes.
* ets_sha_update handles it cleanly so we can safely remove the check:
*/
assert(data_len % 4 == 0);
#endif /* SOC_SECURE_BOOT_V2_ECC */
ets_sha_update(&ctx, data, data_len, false);
}
@@ -51,6 +42,33 @@ void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest
}
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)
{
assert(handle != NULL);
ets_sha_update(&ctx, data, data_len, false);
}
void bootloader_sha512_finish(bootloader_sha_handle_t handle, uint8_t *digest)
{
assert(handle != NULL);
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"
@@ -162,6 +180,7 @@ void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest
#include "bootloader_flash_priv.h"
#include <mbedtls/sha256.h>
#include <mbedtls/sha512.h>
bootloader_sha256_handle_t bootloader_sha256_start(void)
{
@@ -199,4 +218,43 @@ void bootloader_sha256_finish(bootloader_sha256_handle_t handle, uint8_t *digest
free(handle);
handle = NULL;
}
#if SOC_SHA_SUPPORT_SHA512
bootloader_sha_handle_t bootloader_sha512_start(bool is384)
{
mbedtls_sha512_context *ctx = (mbedtls_sha512_context *)malloc(sizeof(mbedtls_sha512_context));
if (!ctx) {
return NULL;
}
mbedtls_sha512_init(ctx);
int ret = mbedtls_sha512_starts(ctx, is384);
if (ret != 0) {
return NULL;
}
return ctx;
}
void bootloader_sha512_data(bootloader_sha_handle_t handle, const void *data, size_t data_len)
{
assert(handle != NULL);
mbedtls_sha512_context *ctx = (mbedtls_sha512_context *)handle;
int ret = mbedtls_sha512_update(ctx, data, data_len);
assert(ret == 0);
(void)ret;
}
void bootloader_sha512_finish(bootloader_sha_handle_t handle, uint8_t *digest)
{
assert(handle != NULL);
mbedtls_sha512_context *ctx = (mbedtls_sha512_context *)handle;
if (digest != NULL) {
int ret = mbedtls_sha512_finish(ctx, digest);
assert(ret == 0);
(void)ret;
}
mbedtls_sha512_free(ctx);
free(handle);
handle = NULL;
}
#endif /* SOC_SHA_SUPPORT_SHA512 */
#endif /* !(NON_OS_BUILD || CONFIG_APP_BUILD_TYPE_RAM) */

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2018-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -30,6 +30,7 @@
#include "hal/cache_types.h"
#include "hal/cache_ll.h"
#include "hal/cache_hal.h"
#include "hal/sha_types.h"
#include "esp_cpu.h"
#include "esp_image_format.h"
@@ -1213,18 +1214,29 @@ void bootloader_debug_buffer(const void *buffer, size_t length, const char *labe
#endif
}
esp_err_t bootloader_sha256_flash_contents(uint32_t flash_offset, uint32_t len, uint8_t *digest)
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_sha256_handle_t sha_handle = NULL;
bootloader_sha_handle_t sha_handle = NULL;
if (type == SHA2_256) {
sha_handle = bootloader_sha256_start();
} else
// Using SOC_ECDSA_SUPPORT_CURVE_P384 here so that there is no flash size impact in the case of existing targets like ESP32.
#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;
}
sha_handle = bootloader_sha256_start();
if (sha_handle == NULL) {
return ESP_ERR_NO_MEM;
}
@@ -1234,7 +1246,14 @@ esp_err_t bootloader_sha256_flash_contents(uint32_t flash_offset, uint32_t len,
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");
bootloader_sha256_finish(sha_handle, 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_ERR_NO_MEM;
}
uint32_t max_image_len;
@@ -1245,15 +1264,51 @@ esp_err_t bootloader_sha256_flash_contents(uint32_t flash_offset, uint32_t len,
const void * image = bootloader_mmap(flash_offset, partial_image_len);
if (image == NULL) {
bootloader_sha256_finish(sha_handle, 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;
}
bootloader_sha256_data(sha_handle, image, partial_image_len);
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;
}
bootloader_sha256_finish(sha_handle, digest);
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 /* SOC_SHA_SUPPORT_SHA384 && SOC_ECDSA_SUPPORT_CURVE_P384 */

View File

@@ -49,6 +49,12 @@ esp_err_t esp_secure_boot_enable_secure_features(void)
esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_AGGRESSIVE_REVOKE);
#endif
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_SHA384_EN);
#endif
esp_efuse_write_field_bit(ESP_EFUSE_WR_DIS_SECURE_BOOT_SHA384_EN);
esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_EN);
#ifndef CONFIG_SECURE_BOOT_V2_ALLOW_EFUSE_RD_DIS

View File

@@ -105,7 +105,6 @@ static esp_err_t verify_segment_header(int index, const esp_image_segment_header
static esp_err_t process_image_header(esp_image_metadata_t *data, uint32_t part_offset, bootloader_sha256_handle_t *sha_handle, bool do_verify, bool silent);
static esp_err_t process_appended_hash_and_sig(esp_image_metadata_t *data, uint32_t part_offset, uint32_t part_len, bool do_verify, bool silent);
static esp_err_t process_checksum(bootloader_sha256_handle_t sha_handle, uint32_t checksum_word, esp_image_metadata_t *data, bool silent, bool skip_check_checksum);
static esp_err_t __attribute__((unused)) verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest);
static esp_err_t __attribute__((unused)) verify_simple_hash(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data);
@@ -160,8 +159,8 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
bool verify_sha;
#if (SECURE_BOOT_CHECK_SIGNATURE == 1)
/* used for anti-FI checks */
uint8_t image_digest[HASH_LEN] = { [ 0 ... 31] = 0xEE };
uint8_t verified_digest[HASH_LEN] = { [ 0 ... 31 ] = 0x01 };
uint8_t image_digest[ESP_SECURE_BOOT_DIGEST_LEN] = { [ 0 ... ESP_SECURE_BOOT_DIGEST_LEN - 1 ] = 0xEE };
uint8_t verified_digest[ESP_SECURE_BOOT_DIGEST_LEN] = { [ 0 ... ESP_SECURE_BOOT_DIGEST_LEN - 1 ] = 0x01 };
#endif
if (data == NULL || part == NULL) {
@@ -237,7 +236,7 @@ static esp_err_t image_load(esp_image_load_mode_t mode, const esp_partition_pos_
"only verify signature in bootloader" into the macro so it's tested multiple times.
*/
#if CONFIG_SECURE_BOOT_V2_ENABLED
ESP_FAULT_ASSERT(!esp_secure_boot_enabled() || memcmp(image_digest, verified_digest, HASH_LEN) == 0);
ESP_FAULT_ASSERT(!esp_secure_boot_enabled() || memcmp(image_digest, verified_digest, ESP_SECURE_BOOT_DIGEST_LEN) == 0);
#else // Secure Boot V1 on ESP32, only verify signatures for apps not bootloaders
ESP_FAULT_ASSERT(is_bootloader(data->start_addr) || memcmp(image_digest, verified_digest, HASH_LEN) == 0);
#endif
@@ -1028,43 +1027,14 @@ err:
return err;
}
static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest)
{
#if (SECURE_BOOT_CHECK_SIGNATURE == 1)
uint32_t end = data->start_addr + data->image_len;
ESP_LOGI(TAG, "Verifying image signature...");
// For secure boot, we calculate the signature hash over the whole file, which includes any "simple" hash
// appended to the image for corruption detection
if (data->image.hash_appended) {
const void *simple_hash = bootloader_mmap(end - HASH_LEN, HASH_LEN);
bootloader_sha256_data(sha_handle, simple_hash, HASH_LEN);
bootloader_munmap(simple_hash);
}
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
// End of the image needs to be padded all the way to a 4KB boundary, after the simple hash
// (for apps they are usually already padded due to --secure-pad-v2, only a problem if this option was not used.)
uint32_t padded_end = ALIGN_UP(end, FLASH_SECTOR_SIZE);
if (padded_end > end) {
const void *padding = bootloader_mmap(end, padded_end - end);
bootloader_sha256_data(sha_handle, padding, padded_end - end);
bootloader_munmap(padding);
end = padded_end;
}
#endif // CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
bootloader_sha256_finish(sha_handle, image_digest);
// Log the hash for debugging
bootloader_debug_buffer(image_digest, HASH_LEN, "Calculated secure boot hash");
static esp_err_t verify_signature_and_adjust_image_len(esp_image_metadata_t *data, uint32_t end, uint8_t *image_digest, uint8_t *verified_digest)
{
// Use hash to verify signature block
esp_err_t err = ESP_ERR_IMAGE_INVALID;
#if CONFIG_SECURE_BOOT || CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT
const void *sig_block;
ESP_FAULT_ASSERT(memcmp(image_digest, verified_digest, HASH_LEN) != 0); /* sanity check that these values start differently */
ESP_FAULT_ASSERT(memcmp(image_digest, verified_digest, ESP_SECURE_BOOT_DIGEST_LEN) != 0); /* sanity check that these values start differently */
#if defined(CONFIG_SECURE_SIGNED_APPS_ECDSA_SCHEME)
sig_block = bootloader_mmap(data->start_addr + data->image_len, sizeof(esp_secure_boot_sig_block_t));
err = esp_secure_boot_verify_ecdsa_signature_block(sig_block, image_digest, verified_digest);
@@ -1081,7 +1051,7 @@ static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_han
ESP_LOGI(TAG, "Calculating simple hash to check for corruption...");
const void *whole_image = bootloader_mmap(data->start_addr, data->image_len - HASH_LEN);
if (whole_image != NULL) {
sha_handle = bootloader_sha256_start();
bootloader_sha256_handle_t sha_handle = bootloader_sha256_start();
bootloader_sha256_data(sha_handle, whole_image, data->image_len - HASH_LEN);
bootloader_munmap(whole_image);
if (verify_simple_hash(sha_handle, data) != ESP_OK) {
@@ -1102,6 +1072,64 @@ static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_han
}
#endif
return ESP_OK;
}
#endif /* SECURE_BOOT_CHECK_SIGNATURE */
static esp_err_t verify_secure_boot_signature(bootloader_sha256_handle_t sha_handle, esp_image_metadata_t *data, uint8_t *image_digest, uint8_t *verified_digest)
{
#if (SECURE_BOOT_CHECK_SIGNATURE == 1)
uint32_t end = data->start_addr + data->image_len;
ESP_LOGI(TAG, "Verifying image signature...");
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
(void) sha_handle;
/* Re-calculating image digest using SHA384 */
const void *image_data = bootloader_mmap(data->start_addr, data->image_len - HASH_LEN);
bootloader_sha_handle_t sha384_handle = bootloader_sha512_start(true);
bootloader_sha512_data(sha384_handle, image_data, data->image_len - HASH_LEN);
bootloader_munmap(image_data);
#endif
// For secure boot, we calculate the signature hash over the whole file, which includes any "simple" hash
// appended to the image for corruption detection
if (data->image.hash_appended) {
const void *simple_hash = bootloader_mmap(end - HASH_LEN, HASH_LEN);
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
bootloader_sha512_data(sha384_handle, simple_hash, HASH_LEN);
#else
bootloader_sha256_data(sha_handle, simple_hash, HASH_LEN);
#endif
bootloader_munmap(simple_hash);
}
#if CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
// End of the image needs to be padded all the way to a 4KB boundary, after the simple hash
// (for apps they are usually already padded due to --secure-pad-v2, only a problem if this option was not used.)
uint32_t padded_end = ALIGN_UP(end, FLASH_SECTOR_SIZE);
if (padded_end > end) {
const void *padding = bootloader_mmap(end, padded_end - end);
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
bootloader_sha512_data(sha384_handle, padding, padded_end - end);
#else
bootloader_sha256_data(sha_handle, padding, padded_end - end);
#endif
bootloader_munmap(padding);
end = padded_end;
}
#endif // CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME || CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
bootloader_sha512_finish(sha384_handle, image_digest);
#else
bootloader_sha256_finish(sha_handle, image_digest);
#endif
// Log the hash for debugging
bootloader_debug_buffer(image_digest, ESP_SECURE_BOOT_DIGEST_LEN, "Calculated secure boot hash");
return verify_signature_and_adjust_image_len(data, end, image_digest, verified_digest);
#endif // SECURE_BOOT_CHECK_SIGNATURE
return ESP_OK;
}

View File

@@ -427,7 +427,19 @@ bool esp_secure_boot_cfg_verify_release_mode(void)
#endif
}
}
#if SOC_ECDSA_SUPPORT_CURVE_P384
/* When using Secure Boot with SHA-384, the efuse bit representing Secure Boot with SHA-384 would already be programmed.
* But in the case of the existing Secure Boot V2 schemes using SHA-256, the efuse bit representing
* Secure Boot with SHA-384 needs to be write-protected, so that an attacker cannot perform a denial-of-service
* attack by changing the existing secure boot mode using SHA-256 to SHA-384.
*/
secure = esp_efuse_read_field_bit(ESP_EFUSE_WR_DIS_SECURE_BOOT_SHA384_EN);
result &= secure;
if (!secure) {
ESP_LOGW(TAG, "Not write-protected secure boot using SHA-384 mode (set WR_DIS_SECURE_BOOT_SHA384_EN->1)");
}
#endif
secure = (num_keys != 0);
result &= secure;

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -69,16 +69,21 @@ static esp_err_t validate_signature_block(const ets_secure_boot_sig_block_t *blo
*/
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_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_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 + 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;
@@ -129,7 +134,7 @@ static esp_err_t s_calculate_image_public_key_digests(uint32_t flash_offset, uin
}
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_DIGEST_LEN);
memcpy((void *)public_key_digests->key_digests[i], key_digest, ESP_SECURE_BOOT_KEY_DIGEST_SHA_256_LEN);
public_key_digests->num_digests++;
}
@@ -317,7 +322,7 @@ static esp_err_t check_and_generate_secure_boot_keys(const esp_image_metadata_t
}
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_DIGEST_LEN)) {
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;
}

View File

@@ -73,7 +73,7 @@ static esp_err_t calculate_image_public_key_digests(bool verify_image_digest, bo
}
uint8_t image_digest[ESP_SECURE_BOOT_DIGEST_LEN] = {0};
uint8_t __attribute__((aligned(4))) key_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 + 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);
@@ -81,7 +81,11 @@ static esp_err_t calculate_image_public_key_digests(bool verify_image_digest, bo
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;
@@ -118,7 +122,7 @@ static esp_err_t calculate_image_public_key_digests(bool verify_image_digest, bo
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_DIGEST_LEN);
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++;
}
@@ -184,14 +188,19 @@ static esp_err_t get_secure_boot_key_digests(esp_image_sig_public_key_digests_t
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
{
uint8_t digest[ESP_SECURE_BOOT_KEY_DIGEST_LEN] = {0};
uint8_t verified_digest[ESP_SECURE_BOOT_KEY_DIGEST_LEN] = {0};
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 = ALIGN_UP(length, FLASH_SECTOR_SIZE);
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, padded_length, digest);
#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;
@@ -203,7 +212,7 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
return ESP_FAIL;
}
err = esp_secure_boot_verify_sbv2_signature_block(sig_block, digest, verified_digest);
err = esp_secure_boot_verify_sbv2_signature_block(sig_block, digest, NULL);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Secure Boot V2 verification failed.");
}
@@ -218,9 +227,11 @@ esp_err_t esp_secure_boot_verify_sbv2_signature_block(const ets_secure_boot_sign
{
bool any_trusted_key = false;
/* 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_KEY_DIGEST_LEN);
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};
@@ -237,7 +248,7 @@ esp_err_t esp_secure_boot_verify_sbv2_signature_block(const ets_secure_boot_sign
#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_DIGEST_LEN] = { 0 };
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;

View File

@@ -26,6 +26,7 @@ static const char* 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 */
@@ -34,7 +35,12 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
ESP_LOGD(TAG, "verifying signature src_addr 0x%" PRIx32 " length 0x%" PRIx32, src_addr, length);
/* Calculate digest of main image */
esp_err_t err = bootloader_sha256_flash_contents(src_addr, padded_length, digest);
#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;

View File

@@ -16,7 +16,8 @@ extern "C" {
typedef enum {
ECDSA_CURVE_P192 = 1,
ECDSA_CURVE_P256 = 2
ECDSA_CURVE_P256 = 2,
ECDSA_CURVE_P384 = 3
} ECDSA_CURVE;
int ets_ecdsa_verify(const uint8_t *key, const uint8_t *sig, ECDSA_CURVE curve_id, const uint8_t *digest, uint8_t *verified_digest);

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -88,6 +88,25 @@ struct ets_secure_boot_sig_block {
#elif CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
#if CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS
struct __attribute((packed)) ets_secure_boot_sig_block {
uint8_t magic_byte;
uint8_t version;
uint8_t sha_version;
uint8_t _reserved2;
uint8_t image_digest[48];
struct {
struct {
uint8_t curve_id; /* ETS_ECDSA_CURVE_P192 / ETS_ECDSA_CURVE_P256 */
uint8_t point[96]; /* X followed by Y (both little-endian), plus zero bytes if P192 */
} key;
uint8_t signature[96]; /* r followed by s (both little-endian) */
uint8_t padding[951];
} ecdsa;
uint32_t block_crc; /* note: crc covers all bytes in the structure before it, regardless of version field */
uint8_t _padding[16];
};
#else
struct __attribute((packed)) ets_secure_boot_sig_block {
uint8_t magic_byte;
uint8_t version;
@@ -105,6 +124,7 @@ struct __attribute((packed)) ets_secure_boot_sig_block {
uint32_t block_crc; /* note: crc covers all bytes in the structure before it, regardless of version field */
uint8_t _padding[16];
};
#endif /* CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS */
#endif
ESP_STATIC_ASSERT(sizeof(ets_secure_boot_sig_block_t) == 1216, "invalid sig block size");

View File

@@ -157,9 +157,6 @@ __attribute__((weak)) void esp_clk_init(void)
// Re calculate the ccount to make time calculation correct.
esp_cpu_set_cycle_count((uint64_t)esp_cpu_get_cycle_count() * new_freq_mhz / old_freq_mhz);
// Set crypto clock (`clk_sec`) to use 480M SPLL clock
REG_SET_FIELD(PCR_SEC_CONF_REG, PCR_SEC_CLK_SEL, 0x2);
}
static void select_rtc_slow_clk(soc_rtc_slow_clk_src_t rtc_slow_clk_src)

View File

@@ -1119,6 +1119,10 @@ config SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP
bool
default y
config SOC_ECDSA_SUPPORT_CURVE_P384
bool
default y
config SOC_SDM_GROUPS
int
default 1

View File

@@ -442,6 +442,7 @@
#define SOC_ECDSA_SUPPORT_EXPORT_PUBKEY (1)
#define SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE (1)
#define SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP (1)
#define SOC_ECDSA_SUPPORT_CURVE_P384 (1)
/*-------------------------- Sigma Delta Modulator CAPS -----------------*/
#define SOC_SDM_GROUPS 1U

View File

@@ -2430,13 +2430,13 @@ extern "C" {
#define EFUSE_XTS_DPA_CLK_ENABLE_ERR_M (EFUSE_XTS_DPA_CLK_ENABLE_ERR_V << EFUSE_XTS_DPA_CLK_ENABLE_ERR_S)
#define EFUSE_XTS_DPA_CLK_ENABLE_ERR_V 0x00000001U
#define EFUSE_XTS_DPA_CLK_ENABLE_ERR_S 29
/** EFUSE_ECDSA_P384_ENABLE_ERR : RO; bitpos: [31]; default: 0;
* Represents the programming error of EFUSE_ECDSA_P384_ENABLE
/** EFUSE_SECURE_BOOT_SHA384_EN_ERR : RO; bitpos: [31]; default: 0;
* Represents the programming error of EFUSE_SECURE_BOOT_SHA384_EN
*/
#define EFUSE_ECDSA_P384_ENABLE_ERR (BIT(31))
#define EFUSE_ECDSA_P384_ENABLE_ERR_M (EFUSE_ECDSA_P384_ENABLE_ERR_V << EFUSE_ECDSA_P384_ENABLE_ERR_S)
#define EFUSE_ECDSA_P384_ENABLE_ERR_V 0x00000001U
#define EFUSE_ECDSA_P384_ENABLE_ERR_S 31
#define EFUSE_SECURE_BOOT_SHA384_EN_ERR (BIT(31))
#define EFUSE_SECURE_BOOT_SHA384_EN_ERR_M (EFUSE_SECURE_BOOT_SHA384_EN_ERR_V << EFUSE_SECURE_BOOT_SHA384_EN_ERR_S)
#define EFUSE_SECURE_BOOT_SHA384_EN_ERR_V 0x00000001U
#define EFUSE_SECURE_BOOT_SHA384_EN_ERR_S 31
/** EFUSE_RD_REPEAT_DATA_ERR4_REG register
* Represents rd_repeat_data_err

View File

@@ -1345,10 +1345,10 @@ typedef union {
*/
uint32_t xts_dpa_clk_enable_err:1;
uint32_t reserved_30:1;
/** ecdsa_p384_enable_err : RO; bitpos: [31]; default: 0;
* Represents the programming error of EFUSE_ECDSA_P384_ENABLE
/** secure_boot_sha384_en_err : RO; bitpos: [31]; default: 0;
* Represents the programming error of EFUSE_SECURE_BOOT_SHA384_EN
*/
uint32_t ecdsa_p384_enable_err:1;
uint32_t secure_boot_sha384_en_err:1;
};
uint32_t val;
} efuse_rd_repeat_data_err3_reg_t;

View File

@@ -5,21 +5,27 @@ Secure Boot v2
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_SBV2_SCHEME:default="RSA-PSS", esp32c2="ECDSA", esp32c6="RSA-PSS or ECDSA", esp32h2="RSA-PSS or ECDSA", esp32p4="RSA-PSS or ECDSA", esp32c5="RSA-PSS or ECDSA", esp32c61="ECDSA", esp32h21="RSA-PSS or ECDSA"}
{IDF_TARGET_SBV2_SCHEME:default="RSA-PSS", esp32c2, esp32c61="ECDSA", esp32c6, esp32h2, esp32p4, esp32c5, esp32h21="RSA-PSS or ECDSA"}
{IDF_TARGET_SBV2_KEY:default="RSA-3072", esp32c2="ECDSA-256 or ECDSA-192", esp32c6="RSA-3072, ECDSA-256, or ECDSA-192", esp32h2="RSA-3072, ECDSA-256, or ECDSA-192", esp32p4="RSA-3072, ECDSA-256, or ECDSA-192", esp32c5="RSA-3072, ECDSA-256, or ECDSA-192", esp32c61="ECDSA-256 or ECDSA-192", esp32h21="RSA-3072, ECDSA-256, or ECDSA-192"}
{IDF_TARGET_SBV2_KEY:default="RSA-3072", esp32c2, esp32c61="ECDSA-256 or ECDSA-192", esp32c6, esp32h2, esp32p4, esp32h21="RSA-3072, ECDSA-256, or ECDSA-192", esp32c5="RSA-3072, ECDSA-384, ECDSA-256, or ECDSA-192"}
{IDF_TARGET_SECURE_BOOT_OPTION_TEXT:default="", esp32c6="RSA is recommended for faster verification. You can choose either the RSA or ECDSA scheme from the menu.", esp32h2="RSA is recommended for faster verification. You can choose either the RSA or ECDSA scheme from the menu.", esp32p4="RSA is recommended for faster verification. You can choose either the RSA or ECDSA scheme from the menu.", esp32c5="RSA is recommended for faster verification. You can choose either the RSA or ECDSA scheme from the menu.", esp32h21="RSA is recommended for faster verification. You can choose either the RSA or ECDSA scheme from the menu."}
{IDF_TARGET_SECURE_BOOT_OPTION_TEXT:default="", esp32c6, esp32h2, esp32p4, esp32h21="RSA is recommended for faster verification. You can choose either the RSA or ECDSA scheme from the menu.", esp32c5="ECDSA is recommended for faster verification. You can choose either the RSA or ECDSA scheme from the menu."}
{IDF_TARGET_SBV2_SCHEME_RECOMMENDATION:default="RSA is recommended for use cases where fast boot-up time is required whereas ECDSA is recommended for use cases where shorter key length is required.", esp32c5="ECDSA is recommended for use cases where fast boot-up time and shorter key length is required."}
{IDF_TARGET_ECO_VERSION:default="", esp32="(v3.0 onwards)", esp32c3="(v0.3 onwards)"}
{IDF_TARGET_RSA_TIME:default="", esp32c6="about 2.7 ms", esp32h2="about 4.5 ms", esp32p4="about 2.4 ms"}
{IDF_TARGET_RSA_TIME:default="", esp32c5="about 12.1 ms", esp32c6="about 10.2 ms", esp32h2="about 18.3 ms", esp32p4="about 14.8 ms"}
{IDF_TARGET_ECDSA_TIME:default="", esp32c6="about 21.5 ms", esp32h2="about 36 ms", esp32p4="about 10.3 ms"}
{IDF_TARGET_ECDSA_P256_TIME:default="", esp32c5="about 5.6 ms", esp32c6="about 83.9 ms", esp32h2="about 76.2 ms", esp32p4="about 61.1 ms"}
{IDF_TARGET_CPU_FREQ:default="", esp32c6="160 MHz", esp32h2="96 MHz", esp32p4="360 MHz"}
{IDF_TARGET_ECDSA_P384_TIME:default="", esp32c5="about 20.6 ms"}
{IDF_TARGET_SBV2_DEFAULT_SCHEME:default="RSA", esp32c2="ECDSA (v2)", esp32c5="ECDSA (v2)", esp32c61="ECDSA (v2)"}
{IDF_TARGET_ROM_CPU_FREQ:default="", esp32c5="48 MHz", esp32c6="40 MHz", esp32h2="32 MHz", esp32p4="40 MHz"}
{IDF_TARGET_CPU_FREQ:default="", esp32c5="240 MHz", esp32c6="160 MHz", esp32h2="96 MHz", esp32p4="360 MHz"}
{IDF_TARGET_SBV2_DEFAULT_SCHEME:default="RSA", esp32c2, esp32c61, esp32c5="ECDSA (v2)"}
{IDF_TARGET_EFUSE_WR_DIS_RD_DIS:default="ESP_EFUSE_WR_DIS_RD_DIS", esp32="ESP_EFUSE_WR_DIS_EFUSE_RD_DISABLE"}
@@ -136,6 +142,59 @@ The Secure Boot v2 process follows these steps:
7. The bootloader executes the verified application image.
.. only:: SOC_SECURE_BOOT_V2_RSA and SOC_SECURE_BOOT_V2_ECC
.. _secure-boot-v2-scheme-selection:
Secure Boot v2 Scheme Selection
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{IDF_TARGET_NAME} has a provision to choose between the RSA scheme and the ECDSA scheme. Only one scheme can be used per device.
ECDSA provides similar security strength, compared to RSA, with shorter key lengths. Current estimates are that ECDSA with curve P-256 has an approximate equivalent strength to RSA with 3072-bit keys. However, ECDSA signature verification takes considerably more amount of time as compared to RSA signature verification.
{IDF_TARGET_SBV2_SCHEME_RECOMMENDATION}
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
{IDF_TARGET_NAME} also supports Secure Boot v2 with the ECDSA signature scheme using the P-384 curve. This provides stronger security than both ECDSA-P256 and RSA-3072, but at the cost of increased signature verification time. Therefore, for use cases that require higher security strength, Secure Boot v2 should be enabled with the ECDSA P-384 signature scheme.
.. list-table:: Comparison Between Signature Verification Time
:widths: 10 10 20
:header-rows: 1
* - **Verification Scheme**
- **Time**
- **CPU Frequency**
* - RSA-3072
- {IDF_TARGET_RSA_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
* - ECDSA-P256
- {IDF_TARGET_ECDSA_P256_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
* - ECDSA-P384
- {IDF_TARGET_ECDSA_P384_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
.. only:: not SOC_ECDSA_SUPPORT_CURVE_P384
.. list-table:: Comparison Between Signature Verification Time
:widths: 10 10 20
:header-rows: 1
* - **Verification Scheme**
- **Time**
- **CPU Frequency**
* - RSA-3072
- {IDF_TARGET_RSA_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
* - ECDSA-P256
- {IDF_TARGET_ECDSA_P256_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
The above table compares the time taken for the first stage (ROM) bootloader to just verify the signature of the bootloader image in a particular scheme. It does not indicate the boot-up time. Also, note that the CPU frequency is lower because it is the frequency of the CPU when the first stage (ROM) bootloader is running.
.. _signature-block-format:
Signature Block Format
@@ -143,34 +202,6 @@ Signature Block Format
The signature block starts on a 4 KB aligned boundary and has a flash sector of its own. The signature is calculated over all bytes in the image including the padding bytes, see :ref:`secure_padding`.
.. only:: SOC_SECURE_BOOT_V2_RSA and SOC_SECURE_BOOT_V2_ECC
.. note::
{IDF_TARGET_NAME} has a provision to choose between the RSA scheme and the ECDSA scheme. Only one scheme can be used per device.
ECDSA provides similar security strength, compared to RSA, with shorter key lengths. Current estimates are that ECDSA with curve P-256 has an approximate equivalent strength to RSA with 3072-bit keys. However, ECDSA signature verification takes considerably more amount of time as compared to RSA signature verification.
RSA is recommended for use cases where fast boot-up time is required whereas ECDSA is recommended for use cases where shorter key length is required.
.. only:: not esp32p4 or not esp32c5
.. list-table:: Comparison between signature verification time
:widths: 10 10 20
:header-rows: 1
* - **Verification scheme**
- **Time**
- **CPU Frequency**
* - RSA-3072
- {IDF_TARGET_RSA_TIME}
- {IDF_TARGET_CPU_FREQ}
* - ECDSA-P256
- {IDF_TARGET_ECDSA_TIME}
- {IDF_TARGET_CPU_FREQ}
The above table compares the time taken to verify a signature in a particular scheme. It does not indicate the boot-up time.
The content of each signature block is shown in the following table:
.. only:: esp32 or SOC_SECURE_BOOT_V2_RSA
@@ -223,7 +254,7 @@ The content of each signature block is shown in the following table:
.. only:: SOC_SECURE_BOOT_V2_ECC
.. list-table:: Content of an ECDSA Signature Block
.. list-table:: Content of an ECDSA-256 / ECDSA-192 Signature Block
:widths: 10 10 40
:header-rows: 1
@@ -261,6 +292,50 @@ The content of each signature block is shown in the following table:
- 16
- Zero padding to length 1216 bytes.
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
.. list-table:: Content of an ECDSA-384 Signature Block
:widths: 10 10 40
:header-rows: 1
* - **Offset**
- **Size (bytes)**
- **Description**
* - 0
- 1
- Magic byte.
* - 1
- 1
- Version number byte, currently 0x03.
* - 2
- 1
- SHA Version used for digest calculation when generating the signature (1 for SHA-384)
* - 3
- 1
- Padding bytes. Reserved, should be zero.
* - 4
- 48
- SHA-384 hash of only the image content, not including the signature block.
* - 52
- 1
- Curve ID. 3 for NIST384p curve
* - 53
- 96
- ECDSA Public key: 48-byte X coordinate followed by 48-byte Y coordinate.
* - 149
- 96
- ECDSA Signature result (section 5.3.2 of RFC6090) of the image content: 48-byte R component followed by-48 byte S component.
* - 245
- 951
- Reserved.
* - 1196
- 4
- CRC32 of the preceding 1196 bytes.
* - 1200
- 16
- Zero padding to length 1216 bytes.
The remainder of the signature sector is erased flash (0xFF) which allows writing other signature blocks after the previous signature block.
@@ -358,6 +433,10 @@ eFuse Usage
- SECURE_BOOT_EN - Enables Secure Boot protection on boot.
.. only:: SOC_SECURE_BOOT_V2_ECC and SOC_ECDSA_SUPPORT_CURVE_P384
- SECURE_BOOT_SHA384_EN - Enables SHA-384 digest calculation for Secure Boot signature verification.
.. only:: SOC_EFUSE_KEY_PURPOSE_FIELD
- KEY_PURPOSE_X - Set the purpose of the key block on {IDF_TARGET_NAME} by programming SECURE_BOOT_DIGESTX (X = 0, 1, 2) into KEY_PURPOSE_X (X = 0, 1, 2, 3, 4, 5). Example: If KEY_PURPOSE_2 is set to SECURE_BOOT_DIGEST1, then BLOCK_KEY2 will have the Secure Boot v2 public key digest. The write-protection bit must be set, and this field does not have a read-protection bit.
@@ -489,6 +568,12 @@ The build system will prompt you with a command to generate a new signing key vi
.. only:: SOC_SECURE_BOOT_V2_ECC
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
Select the ECDSA scheme by passing ``--version 2 --scheme ecdsa384``, ``--version 2 --scheme ecdsa256`` or ``--version 2 --scheme ecdsa192`` to generate corresponding ECDSA private key.
.. only:: not SOC_ECDSA_SUPPORT_CURVE_P384
Select the ECDSA scheme by passing ``--version 2 --scheme ecdsa256`` or ``--version 2 --scheme ecdsa192`` to generate corresponding ECDSA private key.
The strength of the signing key is proportional to (a) the random number source of the system, and (b) the correctness of the algorithm used. For production devices, we recommend generating signing keys from a system with a quality entropy source and using the best available {IDF_TARGET_SBV2_SCHEME} key generation utilities.
@@ -517,6 +602,14 @@ For example, to generate a signing key using the OpenSSL command line:
openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key.pem
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
For the ECC NIST384p curve
.. code-block::
openssl ecparam -name secp384r1 -genkey -noout -out my_secure_boot_signing_key.pem
Remember that the strength of the Secure Boot system depends on keeping the signing key private.
@@ -620,6 +713,12 @@ Secure Boot Best Practices
espsecure.py signature_info_v2 datafile.bin
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
.. note::
If Secure Boot v2 is configured using the ECDSA P-384 signature scheme, all signing keys used must be ECDSA-P384 keys. Using keys with different elliptic curves (e.g., P-192 or P-256) alongside P-384 is not supported and will cause signature verification to fail during boot.
.. _secure-boot-v2-key-revocation:
Key Revocation
@@ -711,6 +810,14 @@ It is preferred to use the ``idf.py`` tool to generate and verify signatures, bu
openssl dgst -sha256 -binary BINARY_FILE > DIGEST_BINARY_FILE
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
In case of ECDSA-P384 signature scheme, SHA-384 must be used to calculate the digest of the image.
.. code-block:: bash
openssl dgst -sha384 -binary BINARY_FILE > DIGEST_BINARY_FILE
2. Generate signature of the image using the above calculated digest.
.. only:: SOC_SECURE_BOOT_V2_RSA

View File

@@ -406,7 +406,13 @@ In this workflow we shall use ``espsecure`` tool to generate signing keys and us
espsecure.py generate_signing_key --version 2 --scheme ecdsa256 secure_boot_signing_key.pem
The scheme in the above command can be changed to ``ecdsa192`` to generate ecdsa192 private key.
.. only:: not SOC_ECDSA_SUPPORT_CURVE_P384
The scheme in the above command can be changed to ``ecdsa192`` to generate ecdsa192 private key.
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
The scheme in the above command can be changed to ``ecdsa384`` or ``ecdsa192`` to generate ecdsa384 or ecdsa192 private key.
.. only:: SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS
@@ -466,6 +472,15 @@ In this workflow we shall use ``espsecure`` tool to generate signing keys and us
espefuse.py --port PORT --chip {IDF_TARGET_PATH_NAME} burn_efuse SECURE_BOOT_EN
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
In case Secure Boot v2 is enabled with the ECDSA-P384 signature scheme, SHA-384 must be used to calculate the digest of the image. Thus, the following eFuse needs to be burned:
.. code:: bash
espefuse.py --port PORT --chip {IDF_TARGET_PATH_NAME} burn_efuse SECURE_BOOT_SHA384_EN
5. Burn relevant eFuses
A) Burn security eFuses
@@ -485,7 +500,8 @@ In this workflow we shall use ``espsecure`` tool to generate signing keys and us
:SOC_EFUSE_DIS_USB_JTAG: - ``DIS_USB_JTAG``: Disable USB switch to JTAG.
:SOC_EFUSE_DIS_PAD_JTAG: - ``DIS_PAD_JTAG``: Disable JTAG permanently.
:SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS: - ``SECURE_BOOT_AGGRESSIVE_REVOKE``: Aggressive revocation of key digests, see :ref:`secure-boot-v2-aggressive-key-revocation` for more details.
:SOC_ECDSA_P192_CURVE_DEFAULT_DISABLED: - ``WR_DIS_ECDSA_CURVE_MODE``: Disable ECDSA curve mode.
:SOC_ECDSA_P192_CURVE_DEFAULT_DISABLED: - ``WR_DIS_ECDSA_CURVE_MODE``: Disable writing to the ECDSA curve mode eFuse bit.
:SOC_ECDSA_SUPPORT_CURVE_P384: - ``WR_DIS_SECURE_BOOT_SHA384_EN``: Disable writing to the SHA-384 secure boot eFuse bit.
The respective eFuses can be burned by running:

View File

@@ -1,25 +1,31 @@
:orphan:
安全启动 (secure boot) v2
安全启动 (Secure Boot) v2
=========================
:link_to_translation:`en:[English]`
{IDF_TARGET_SBV2_SCHEME:default="RSA-PSS", esp32c2="ECDSA", esp32c6="RSA-PSS 或 ECDSA", esp32h2="RSA-PSS 或 ECDSA", esp32p4="RSA-PSS 或 ECDSA", esp32c5="RSA-PSS 或 ECDSA", esp32c61="ECDSA", esp32h21="RSA-PSS 或 ECDSA"}
{IDF_TARGET_SBV2_SCHEME:default="RSA-PSS", esp32c2, esp32c61="ECDSA", esp32c6, esp32h2, esp32p4, esp32c5, esp32h21="RSA-PSS 或 ECDSA"}
{IDF_TARGET_SBV2_KEY:default="RSA-3072", esp32c2="ECDSA-256 或 ECDSA-192", esp32c6="RSA-3072、ECDSA-256 或 ECDSA-192", esp32h2="RSA-3072、ECDSA-256 或 ECDSA-192", esp32p4="RSA-3072、ECDSA-256 或 ECDSA-192", esp32c5="RSA-3072、ECDSA-256 或 ECDSA-192", esp32c61="ECDSA-256 或 ECDSA-192", esp32h21="RSA-3072、ECDSA-256 或 ECDSA-192"}
{IDF_TARGET_SBV2_KEY:default="RSA-3072", esp32c2, esp32c61="ECDSA-256 或 ECDSA-192", esp32c6, esp32h2, esp32p4, esp32h21="RSA-3072、ECDSA-256 或 ECDSA-192", esp32c5="RSA-3072、ECDSA-384、ECDSA-256 或 ECDSA-192"}
{IDF_TARGET_SECURE_BOOT_OPTION_TEXT:default="", esp32c6="推荐使用 RSA其验证时间更短。可以在菜单中选择 RSA 或 ECDSA 方案。", esp32h2="推荐使用 RSA其验证时间更短。可以在菜单中选择 RSA 或 ECDSA 方案。", esp32p4="推荐使用 RSA其验证时间更短。可以在菜单中选择 RSA 或 ECDSA 方案。", esp32c5="推荐使用 RSA其验证时间更短。可以在菜单中选择 RSA 或 ECDSA 方案。", esp32h21="推荐使用 RSA其验证时间更短。可以在菜单中选择 RSA 或 ECDSA 方案。"}
{IDF_TARGET_SECURE_BOOT_OPTION_TEXT:default="", esp32c6, esp32h2, esp32p4, esp32h21="推荐使用 RSA其验证时间更短。可以在菜单中选择 RSA 或 ECDSA 方案。", esp32c5="推荐使用 ECDSA其验证时间更短。可以在菜单中选择 RSA 或 ECDSA 方案。"}
{IDF_TARGET_SBV2_SCHEME_RECOMMENDATION:default="如果需要快速启动,推荐使用 RSA如果需要较短的密钥长度建议使用 ECDSA。", esp32c5="如果需要快速启动且需要较短的密钥长度,建议使用 ECDSA。"}
{IDF_TARGET_ECO_VERSION:default="", esp32="v3.0 及以上版本)", esp32c3="v0.3 及以上版本)"}
{IDF_TARGET_RSA_TIME:default="", esp32c6="约 2.7 ms", esp32h2="约 4.5 ms", esp32p4="约 2.4 ms"}
{IDF_TARGET_RSA_TIME:default="", esp32c5="约 12.1 ms", esp32c6="约 10.2 ms", esp32h2="约 18.3 ms", esp32p4="约 14.8 ms"}
{IDF_TARGET_ECDSA_TIME:default="", esp32c6="约 21.5 ms", esp32h2="约 36 ms", esp32p4="约 10.3 ms"}
{IDF_TARGET_ECDSA_P256_TIME:default="", esp32c5="约 5.6 ms", esp32c6="约 83.9 ms", esp32h2="约 76.2 ms", esp32p4="约 61.1 ms"}
{IDF_TARGET_CPU_FREQ:default="", esp32c6="160 MHz", esp32h2="96 MHz", esp32p4="360 MHz"}
{IDF_TARGET_ECDSA_P384_TIME:default="", esp32c5="20.6 ms"}
{IDF_TARGET_SBV2_DEFAULT_SCHEME:default="RSA", esp32c2="ECDSA (v2)", esp32c5="ECDSA (v2)", esp32c61="ECDSA (v2)"}
{IDF_TARGET_ROM_CPU_FREQ:default="", esp32c5="48 MHz", esp32c6="40 MHz", esp32h2="32 MHz", esp32p4="40 MHz"}
{IDF_TARGET_CPU_FREQ:default="", esp32c5="240 MHz", esp32c6="160 MHz", esp32h2="96 MHz", esp32p4="360 MHz"}
{IDF_TARGET_SBV2_DEFAULT_SCHEME:default="RSA", esp32c2, esp32c61, esp32c5="ECDSA (v2)"}
{IDF_TARGET_EFUSE_WR_DIS_RD_DIS:default="ESP_EFUSE_WR_DIS_RD_DIS", esp32="ESP_EFUSE_WR_DIS_EFUSE_RD_DISABLE"}
@@ -136,40 +142,65 @@
7. 引导加载程序执行经验证的应用程序镜像。
.. only:: SOC_SECURE_BOOT_V2_RSA and SOC_SECURE_BOOT_V2_ECC
.. _secure-boot-v2-scheme-selection:
安全启动 v2 签名方案选择
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{IDF_TARGET_NAME} 支持在 RSA 签名方案与 ECDSA 签名方案之间进行选择。每个设备只能使用一种签名方案。
与 RSA 相比ECDSA 在提供类似安全强度的同时,密钥长度更短。目前估算表明,使用 P-256 曲线的 ECDSA 在安全强度上大致等同于使用 3072 位密钥的 RSA。然而ECDSA 的签名验证所需时间明显多于 RSA。
{IDF_TARGET_SBV2_SCHEME_RECOMMENDATION}
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
{IDF_TARGET_NAME} 还支持使用 P-384 曲线的 ECDSA 签名方案进行 Secure Boot v2。这种方案比 ECDSA-P256 和 RSA-3072 具备更强的安全性,但签名验证时间也相应更长。因此,对于对安全强度有更高要求的场景,建议使用 ECDSA P-384 签名方案启用 Secure Boot v2。
.. list-table:: 签名验证时间对比
:widths: 10 10 20
:header-rows: 1
* - **验证方案**
- **耗时**
- **CPU 频率**
* - RSA-3072
- {IDF_TARGET_RSA_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
* - ECDSA-P256
- {IDF_TARGET_ECDSA_P256_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
* - ECDSA-P384
- {IDF_TARGET_ECDSA_P384_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
.. only:: not SOC_ECDSA_SUPPORT_CURVE_P384
.. list-table:: 签名验证时间对比
:widths: 10 10 20
:header-rows: 1
* - **验证方案**
- **耗时**
- **CPU 频率**
* - RSA-3072
- {IDF_TARGET_RSA_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
* - ECDSA-P256
- {IDF_TARGET_ECDSA_P256_TIME}
- {IDF_TARGET_ROM_CPU_FREQ}
以上表格比较的是第一阶段 (ROM) 引导加载程序在特定签名方案下仅用于验证引导加载程序镜像签名所耗费的时间。该数据不代表整体启动时间。另外请注意,表中的 CPU 频率较低,因为这是第一阶段 (ROM) 引导加载程序运行时的 CPU 频率。
.. _signature-block-format:
签名块格式
----------
签名块以 4 KB 的整数倍为起始位置,拥有独立 flash 扇区。签名计算覆盖了镜像中的所有字节,包括填充字节,请参阅 :ref:`secure_padding`
.. only:: SOC_SECURE_BOOT_V2_RSA and SOC_SECURE_BOOT_V2_ECC
.. note::
{IDF_TARGET_NAME} 可以选择 RSA 或 ECDSA 方案,每个设备只能选择一种方案。
与 RSA 相比ECDSA 拥有类似的安全性,但密钥长度更短。据估计,使用 P-256 曲线的 ECDSA 签名安全性大致相当于具有 3072 位密钥的 RSA。然而ECDSA 签名验证耗时明显长于 RSA 签名验证。
如果需要快速启动,建议使用 RSA如果需要较短的密钥建议使用 ECDSA。
.. only:: not esp32p4 or not esp32c5
.. list-table:: 签名验证耗时比较
:widths: 10 10 20
:header-rows: 1
* - **验证方案**
- **耗时**
- **CPU 频率**
* - RSA-3072
- {IDF_TARGET_RSA_TIME}
- {IDF_TARGET_CPU_FREQ}
* - ECDSA-P256
- {IDF_TARGET_ECDSA_TIME}
- {IDF_TARGET_CPU_FREQ}
上表比较了特定方案中验证签名所需的时间,不代表启动时间。
签名块以一个 4 KB 的对齐边界为起始位置,占用一个独立 flash 扇区。签名计算覆盖了镜像中的所有字节,包括填充字节,详情参见 :ref:`secure_padding`
各签名块内容如下表所示:
@@ -184,7 +215,7 @@
- **描述**
* - 0
- 1
- 魔字节。
- 魔字节。
* - 1
- 1
- 版本号字节,当前为 0x02安全启动 v1 的版本号字节为 0x01。
@@ -214,7 +245,7 @@
- CRC32 的前 1196 字节。
* - 1200
- 16
- 长度填充为 1216 字节的零填充
- 补零填充,保证总长度为 1216 字节。
.. note::
@@ -223,7 +254,7 @@
.. only:: SOC_SECURE_BOOT_V2_ECC
.. list-table:: ECDSA 签名块的内容
.. list-table:: ECDSA-256 / ECDSA-192 签名块的内容
:widths: 10 10 40
:header-rows: 1
@@ -232,7 +263,7 @@
- **描述**
* - 0
- 1
- 魔字节。
- 魔字节。
* - 1
- 1
- 版本号字节,当前为 0x03。
@@ -250,16 +281,60 @@
- ECDSA 公钥32 字节的 X 坐标,后跟 32 字节的 Y 坐标。
* - 101
- 64
- 对镜像内容的 ECDSA 签名结果RFC6090 中的 5.3.2 节32 字节的 R 组件,后跟 32 字节的 S 组件
- 对镜像内容的 ECDSA 签名结果RFC6090 5.3.2 节32 字节的 R 分量,其后连接 32 字节的 S 分量
* - 165
- 1031
- 保留。
- 保留字段
* - 1196
- 4
- 前面 1196 字节的 CRC32。
* - 1200
- 16
- 长度填充为 1216 字节的零填充
- 补零填充,保证总长度为 1216 字节。
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
.. list-table:: ECDSA-384 签名块的内容
:widths: 10 10 40
:header-rows: 1
* - **偏移量**
- **大小(字节)**
- **描述**
* - 0
- 1
- 魔术字节。
* - 1
- 1
- 版本号字节,当前为 0x03。
* - 2
- 1
- 生成签名时用于摘要计算的 SHA 版本1 表示使用 SHA-384
* - 3
- 1
- 填充字节。保留,应设置为 0。
* - 4
- 48
- 仅针对镜像内容的 SHA-384 哈希值,不包括签名块。
* - 52
- 1
- 曲线 ID。3 代表 NIST384p 曲线。
* - 53
- 96
- ECDSA 公钥48 字节的 X 坐标,后跟 48 字节的 Y 坐标。
* - 149
- 96
- 对镜像内容的 ECDSA 签名结果RFC6090 第 5.3.2 节48 字节的 R 分量,其后连接 48 字节的 S 分量。
* - 245
- 951
- 保留字段。
* - 1196
- 4
- 前面 1196 字节的 CRC32。
* - 1200
- 16
- 补零填充,保证总长度为 1216 字节。
签名扇区的其余部分是已擦除的 flash (0xFF),支持在前一个签名块之后写入其他签名块。
@@ -358,6 +433,10 @@
- SECURE_BOOT_EN - 在启动时启用安全启动保护。
.. only:: SOC_SECURE_BOOT_V2_ECC and SOC_ECDSA_SUPPORT_CURVE_P384
- SECURE_BOOT_SHA384_EN - 启用 SHA-384 摘要计算,用于 Secure Boot 签名验证。
.. only:: SOC_EFUSE_KEY_PURPOSE_FIELD
- KEY_PURPOSE_X - 将 SECURE_BOOT_DIGESTX (X = 0, 1, 2) 烧录到 KEY_PURPOSE_X (X = 0, 1, 2, 3, 4, 5),设置密钥块功能。例如:若设置 KEY_PURPOSE_2 为 SECURE_BOOT_DIGEST1则 BLOCK_KEY2 将具有安全启动 v2 公钥摘要。注意,必须设置写保护位,该字段无读保护位。
@@ -489,6 +568,12 @@
.. only:: SOC_SECURE_BOOT_V2_ECC
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
传递 ``--version 2 --scheme ecdsa384````--version 2 --scheme ecdsa256````--version 2 --scheme ecdsa192`` 选择 ECDSA 方案,生成相应的 ECDSA 私钥。
.. only:: not SOC_ECDSA_SUPPORT_CURVE_P384
传递 ``--version 2 --scheme ecdsa256````--version 2 --scheme ecdsa192`` 选择 ECDSA 方案,生成相应的 ECDSA 私钥。
签名密钥的强度取决于 (a) 系统的随机数源和 (b) 所用算法的正确性。对于生产设备,建议从具有高质量熵源的系统生成签名密钥,并使用最佳的可用 {IDF_TARGET_SBV2_SCHEME} 密钥生成工具。
@@ -517,6 +602,14 @@
openssl ecparam -name prime256v1 -genkey -noout -out my_secure_boot_signing_key.pem
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
生成 ECC NIST384p 曲线密钥
.. code-block::
openssl ecparam -name secp384r1 -genkey -noout -out my_secure_boot_signing_key.pem
注意,安全启动系统的强度取决于能否保持签名密钥的私密性。
@@ -620,6 +713,12 @@
espsecure.py signature_info_v2 datafile.bin
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
.. note::
如果 Secure Boot v2 配置为使用 ECDSA P-384 签名方案,则所有用于签名的密钥必须为 ECDSA-P384 密钥。不支持与 P-384 同时使用其他椭圆曲线(例如 P-192 或 P-256密钥否则在启动过程中会导致签名验证失败。
.. _secure-boot-v2-key-revocation:
撤销密钥管理
@@ -711,6 +810,14 @@ Keyfile 是包含 {IDF_TARGET_SBV2_KEY} 签名公钥/私钥的 PEM 文件。
openssl dgst -sha256 -binary BINARY_FILE > DIGEST_BINARY_FILE
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
在使用 ECDSA-P384 签名方案的情况下,必须使用 SHA-384 来计算镜像的摘要。
.. code-block:: bash
openssl dgst -sha384 -binary BINARY_FILE > DIGEST_BINARY_FILE
2. 使用上述摘要,生成镜像签名。
.. only:: SOC_SECURE_BOOT_V2_RSA

View File

@@ -406,7 +406,13 @@ flash 加密指南
bashespsecure.py generate_signing_key --version 2 --scheme ecdsa256 secure_boot_signing_key.pem
将上述命令中的方案更改为 ``ecdsa192``,可生成 ecdsa192 私钥。
.. only:: not SOC_ECDSA_SUPPORT_CURVE_P384
将上述命令中的方案更改为 ``ecdsa192``,可生成 ecdsa192 私钥。
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
将上述命令中的方案更改为 ``ecdsa384````ecdsa192``,可生成 ecdsa384 或 ecdsa192 私钥。
.. only:: SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS
@@ -466,6 +472,15 @@ flash 加密指南
espefuse.py --port PORT --chip {IDF_TARGET_PATH_NAME} burn_efuse SECURE_BOOT_EN
.. only:: SOC_ECDSA_SUPPORT_CURVE_P384
如果启用了带有 ECDSA-P384 签名方案的 Secure Boot v2则必须使用 SHA-384 来计算镜像的摘要。因此,需要烧录以下 eFuse
.. code:: bash
espefuse.py --port PORT --chip {IDF_TARGET_PATH_NAME} burn_efuse SECURE_BOOT_SHA384_EN
5. 烧录相关 eFuse
A) 烧录安全 eFuse
@@ -485,7 +500,8 @@ flash 加密指南
:SOC_EFUSE_DIS_USB_JTAG: - ``DIS_USB_JTAG``:禁止从 USB 切换到 JTAG。
:SOC_EFUSE_DIS_PAD_JTAG: - ``DIS_PAD_JTAG``:永久禁用 JTAG。
:SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS: - ``SECURE_BOOT_AGGRESSIVE_REVOKE``:主动吊销密钥摘要。详请请参阅 :ref:`secure-boot-v2-aggressive-key-revocation`。
:SOC_ECDSA_P192_CURVE_DEFAULT_DISABLED: - ``WR_DIS_ECDSA_CURVE_MODE``:禁 ECDSA 曲线模式。
:SOC_ECDSA_P192_CURVE_DEFAULT_DISABLED: - ``WR_DIS_ECDSA_CURVE_MODE``:禁止写入 ECDSA 曲线模式。
:SOC_ECDSA_SUPPORT_CURVE_P384: - ``WR_DIS_SECURE_BOOT_SHA384_EN``:禁止写入 SHA-384 Secure Boot 的 SHA-384 eFuse 位。
运行以下命令烧录相应的 eFuse

View File

@@ -47,7 +47,11 @@ examples/system/efuse:
- if: IDF_TARGET == "linux" and CONFIG_NAME == "virt_flash_enc_release"
- if: IDF_TARGET == "linux" and CONFIG_NAME == "virt_sb_v2_and_fe"
- if: IDF_TARGET == "linux" and CONFIG_NAME == "virt_secure_boot_v2"
- if: IDF_TARGET == "linux" and CONFIG_NAME == "virt_secure_boot_v2_ecdsa_p384"
- if: IDF_TARGET == "linux" and CONFIG_NAME == "virt_sb_v2_ecdsa_p384_and_fe"
reason: Security features are not yet supported for Linux.
- if: SOC_ECDSA_SUPPORT_CURVE_P384 != 1 and (CONFIG_NAME == "virt_secure_boot_v2_ecdsa_p384" or CONFIG_NAME == "virt_sb_v2_ecdsa_p384_and_fe")
reason: Secure Boot V2 with ECDSA P384 is not supported.
depends_components:
- efuse
- bootloader_support

View File

@@ -6,6 +6,7 @@ import os
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
from pytest_embedded_idf.utils import soc_filtered_targets
from pytest_embedded_qemu.dut import QemuDut
@@ -588,15 +589,7 @@ def test_examples_efuse_with_virt_secure_boot_v2_pre_loaded(dut: Dut) -> None:
dut.expect('example: Done')
@pytest.mark.generic
@pytest.mark.parametrize('config', ['virt_secure_boot_v2'], indirect=True)
@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True)
@idf_parametrize(
'target',
['esp32c3', 'esp32c2', 'esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32s2', 'esp32s3'],
indirect=['target'],
)
def test_examples_efuse_with_virt_secure_boot_v2_esp32xx(dut: Dut) -> None:
def example_efuse_with_virt_secure_boot_v2_esp32xx(dut: Dut) -> None:
# check and log bin size
binary_file = os.path.join(dut.app.binary_path, 'bootloader', 'bootloader.bin')
bin_size = os.path.getsize(binary_file)
@@ -659,6 +652,26 @@ def test_examples_efuse_with_virt_secure_boot_v2_esp32xx(dut: Dut) -> None:
dut.expect('example: Done')
@pytest.mark.generic
@pytest.mark.parametrize('config', ['virt_secure_boot_v2'], indirect=True)
@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True)
@idf_parametrize(
'target',
['esp32c3', 'esp32c2', 'esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32p4', 'esp32s2', 'esp32s3'],
indirect=['target'],
)
def test_examples_efuse_with_virt_secure_boot_v2_esp32xx(dut: Dut) -> None:
example_efuse_with_virt_secure_boot_v2_esp32xx(dut)
@pytest.mark.generic
@pytest.mark.parametrize('config', ['virt_secure_boot_v2_ecdsa_p384'], indirect=True)
@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True)
@idf_parametrize('target', soc_filtered_targets('SOC_ECDSA_SUPPORT_CURVE_P384 == 1'), indirect=['target'])
def test_examples_efuse_with_virt_secure_boot_v2_ecdsa_p384_esp32xx(dut: Dut) -> None:
example_efuse_with_virt_secure_boot_v2_esp32xx(dut)
@pytest.mark.generic
@pytest.mark.parametrize('config', ['virt_secure_boot_v2'], indirect=True)
@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True)
@@ -685,12 +698,14 @@ def test_example_efuse_with_virt_secure_boot_v2_esp32xx_pre_loaded(dut: Dut) ->
# Resets eFuse, which enables Secure boot feature
# Resets eFuses, which control digest slots
if dut.app.sdkconfig.get('SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS'):
dut.serial.erase_field_on_emul_efuse_by_name([
'SECURE_BOOT_EN',
'SECURE_BOOT_KEY_REVOKE0',
'SECURE_BOOT_KEY_REVOKE1',
'SECURE_BOOT_KEY_REVOKE2',
])
dut.serial.erase_field_on_emul_efuse_by_name(
[
'SECURE_BOOT_EN',
'SECURE_BOOT_KEY_REVOKE0',
'SECURE_BOOT_KEY_REVOKE1',
'SECURE_BOOT_KEY_REVOKE2',
]
)
else:
dut.serial.erase_field_on_emul_efuse_by_name(['SECURE_BOOT_EN'])
@@ -906,7 +921,8 @@ def test_examples_efuse_with_virt_sb_v2_and_fe(dut: Dut) -> None:
@pytest.mark.parametrize(
'qemu_extra_args',
[
f'-drive file={os.path.join(os.path.dirname(__file__), "test", "esp32eco3_efuses.bin")},if=none,format=raw,id=efuse '
f'-drive file={os.path.join(os.path.dirname(__file__), "test", "esp32eco3_efuses.bin")},'
'if=none,format=raw,id=efuse '
'-global driver=nvram.esp32.efuse,property=drive,value=efuse '
'-global driver=timer.esp32.timg,property=wdt_disable,value=true',
],
@@ -983,15 +999,7 @@ def test_examples_efuse_with_virt_sb_v2_and_fe_qemu(dut: QemuDut) -> None:
efuse_file.write(bytearray.fromhex(esp32eco3_efuses))
@pytest.mark.generic
@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True)
@pytest.mark.parametrize('config', ['virt_sb_v2_and_fe'], indirect=True)
@idf_parametrize(
'target',
['esp32c3', 'esp32c2', 'esp32c5', 'esp32c61', 'esp32c6', 'esp32h2', 'esp32s2', 'esp32s3'],
indirect=['target'],
)
def test_examples_efuse_with_virt_sb_v2_and_fe_esp32xx(dut: Dut) -> None:
def example_efuse_with_virt_sb_v2_and_fe(dut: Dut) -> None:
# check and log bin size
binary_file = os.path.join(dut.app.binary_path, 'bootloader', 'bootloader.bin')
bin_size = os.path.getsize(binary_file)
@@ -1082,3 +1090,23 @@ def test_examples_efuse_with_virt_sb_v2_and_fe_esp32xx(dut: Dut) -> None:
dut.expect('example: Flash Encryption is NOT in RELEASE mode')
dut.expect('example: Secure Boot is in RELEASE mode')
dut.expect('example: Done')
@pytest.mark.generic
@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True)
@pytest.mark.parametrize('config', ['virt_sb_v2_and_fe'], indirect=True)
@idf_parametrize(
'target',
['esp32c3', 'esp32c2', 'esp32c5', 'esp32c61', 'esp32c6', 'esp32h2', 'esp32s2', 'esp32s3'],
indirect=['target'],
)
def test_examples_efuse_with_virt_sb_v2_and_fe_esp32xx(dut: Dut) -> None:
example_efuse_with_virt_sb_v2_and_fe(dut)
@pytest.mark.generic
@pytest.mark.parametrize('skip_autoflash', ['y'], indirect=True)
@pytest.mark.parametrize('config', ['virt_sb_v2_ecdsa_p384_and_fe'], indirect=True)
@idf_parametrize('target', soc_filtered_targets('SOC_ECDSA_SUPPORT_CURVE_P384 == 1'), indirect=['target'])
def test_examples_efuse_with_virt_sb_v2_ecdsa_p384_and_fe_esp32xx(dut: Dut) -> None:
example_efuse_with_virt_sb_v2_and_fe(dut)

View File

@@ -0,0 +1,20 @@
# FLASH_ENCRYPTION & SECURE_BOOT_V2 with EFUSE_VIRTUAL_KEEP_IN_FLASH
CONFIG_IDF_TARGET="esp32c5"
CONFIG_PARTITION_TABLE_OFFSET=0xE000
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="test/partitions_efuse_emul.csv"
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_V2_ENABLED=y
CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME=y
CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS=y
CONFIG_SECURE_BOOT_SIGNING_KEY="test/secure_boot_signing_key_ecdsa_nistp384.pem"
CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE=y
CONFIG_SECURE_FLASH_ENC_ENABLED=y
# IMPORTANT: ONLY VIRTUAL eFuse MODE!
CONFIG_EFUSE_VIRTUAL=y
CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=y

View File

@@ -0,0 +1,18 @@
# SECURE_BOOT_V2 with EFUSE_VIRTUAL_KEEP_IN_FLASH
CONFIG_IDF_TARGET="esp32c5"
CONFIG_PARTITION_TABLE_OFFSET=0xD000
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="test/partitions_efuse_emul.csv"
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_V2_ENABLED=y
CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME=y
CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS=y
CONFIG_SECURE_BOOT_SIGNING_KEY="test/secure_boot_signing_key_ecdsa_nistp384.pem"
CONFIG_SECURE_INSECURE_ALLOW_DL_MODE=y
# IMPORTANT: ONLY VIRTUAL eFuse MODE!
CONFIG_EFUSE_VIRTUAL=y
CONFIG_EFUSE_VIRTUAL_KEEP_IN_FLASH=y

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDC/5UVo7tqYLt17BnN85NRhMRakLUYNrxCEb0nA5bN5WzpILMqCFkjzWWyC6FkeK02g
BwYFK4EEACKhZANiAAQPbOGJCBJtR6oB29nt6BP+JeMc4+KUkJbusFDT26arFZwcsDEZ/m+GatAl
GmlOuNGa4F4fJDjSsuz0ejKK4LQ1DliDopGmieIUWPCEScVZNu9DX2PuGC8NIyMU9Ry4Poc=
-----END EC PRIVATE KEY-----

View File

@@ -813,9 +813,9 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
'secure-generate-signing-key': {
'callback': secure_generate_signing_key,
'help': (
'Generate a private key for signing secure boot images as per the secure boot version. Key file is '
'generated in PEM format, Secure Boot V1 - ECDSA NIST256p private key. Secure Boot V2 - RSA 3072, '
'ECDSA NIST256p, ECDSA NIST192p private key.'
'Generate a private key for signing secure boot images as per the secure boot version.'
' Key file is generated in PEM format, Secure Boot V1 - ECDSA NIST256p private key.'
' Secure Boot V2 - RSA 3072, ECDSA NIST384p, ECDSA NIST256p, ECDSA NIST192p private key.'
),
'options': [
{
@@ -827,7 +827,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
{
'names': ['--scheme', '-s'],
'help': ('Scheme of secure boot signing.'),
'type': click.Choice(['rsa3072', 'ecdsa192', 'ecdsa256']),
'type': click.Choice(['rsa3072', 'ecdsa192', 'ecdsa256', 'ecdsa384']),
},
],
'arguments': [
@@ -854,8 +854,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
'secure-sign-data': {
'callback': secure_sign_data,
'help': (
'Sign a data file for use with secure boot. Signing algorithm is deterministic ECDSA w/ SHA-512 '
'(V1) or either RSA-PSS or ECDSA w/ SHA-256 (V2).'
'Sign a data file for use with secure boot. Signing algorithm is deterministic'
' ECDSA w/ SHA-512 (V1) or either RSA-PSS or ECDSA w/ SHA-256 (V2) or ECDSA w/ SHA-384 (V2).'
),
'options': [
{
@@ -871,7 +871,9 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
{
'names': ['--append-signatures', '-a'],
'is_flag': True,
'help': ('Append signature block(s) to already signed image. Valid only for ESP32-S2.'),
'help': (
'Append signature block(s) to already signed image. Not valid for ESP32 and ESP32-C2.'
),
},
{
'names': ['--pub-key'],

View File

@@ -1,13 +1,12 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
tools/test_apps/security/secure_boot:
disable:
- if: IDF_ENV_FPGA != 1 and CONFIG_NAME != "qemu"
enable:
- if: CONFIG_NAME == "qemu"
reason: the test can only run on an FPGA as efuses need to be reset during the test.
disable_test:
- if: IDF_TARGET in ["esp32", "esp32c2", "esp32c6", "esp32h2", "esp32s2", "esp32c61", "esp32p4", "esp32s3"]
temporary: true
reason: Can't use Kconfig option IDF_ENV_FPGA in `disable`. IDFCI-2992
disable:
- if: CONFIG_NAME != "qemu" or IDF_TARGET == "linux"
reason: Skipping redundant CI builds for all the targets.
tools/test_apps/security/signed_app_no_secure_boot:
enable:

View File

@@ -9,17 +9,6 @@ The example checks if the secure boot feature is enabled/disabled and if enabled
### Hardware Required
Any of the following ESP module:
* ESP32 (supports Secure Boot V1)
* ESP32-ECO3 (supports Secure Boot V2 & Secure Boot V1)
* ESP32S2 (supports Secure Boot V2)
* ESP32C3-ECO3 (supports Secure Boot V2)
* ESP32S3 (supports Secure Boot V2)
* ESP32P4 (supports Secure Boot V2)
* ESP32C5 (supports Secure Boot V2)
* ESP32C61 (supports Secure Boot V2)
* ESP32H21 (supports Secure Boot V2)
It is recommended to use Secure Boot V2 from ESP32-ECO3 onwards.
### Configure the project
@@ -73,7 +62,7 @@ Purpose of the test case (`pytest_secure_boot.py`) is to test the secure boot im
### Hardware required
* FPGA setup with ESP32C3/ESP32S3/ESP32P4/ESP32C5/ESP32C61/ESP32H21 image
* FPGA setup with the target image
* COM port for programming and export it as ESPPORT
e.g `export ESPPORT=/dev/ttyUSB0`
@@ -86,7 +75,7 @@ Purpose of the test case (`pytest_secure_boot.py`) is to test the secure boot im
```
export IDF_ENV_FPGA=1
idf.py set-target esp32c3 #(or esp32s3 / esp32p4 / esp32c5 / esp32c61 / esp32h21)
idf.py set-target {target}
idf.py menuconfig
```
@@ -95,7 +84,7 @@ Under `Security features`
- Enable the `Enable hardware Secure Boot`
- Set the secure boot signing key ("test_rsa_3072_key.pem")
- Set the secure boot signing key
- Set UART ROM download mode to ENABLED (Required for the script to read the EFUSE)
@@ -116,5 +105,5 @@ Under `Security features`
- Run the example test
```
pytest --target esp32c3
pytest --target {target}
```

View File

@@ -1,5 +1,6 @@
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import itertools
import os
import struct
import zlib
@@ -19,13 +20,63 @@ from pytest_embedded_idf.utils import idf_parametrize
CORRUPT_ALL_BLOCKS = 0xFF
SIGNATURE_TYPE_RSA = 0
SIGNATURE_TYPE_RSA_3072 = 1
def corrupt_signature(signed_bootloader, seed=0, corrupt_sig=True, corrupt_crc=False, corrupt_block=CORRUPT_ALL_BLOCKS):
# type: (bytes, int, bool, bool, int) -> bytes
SIGNATURE_TYPE_ECDSA = 10
SIGNATURE_TYPE_ECDSA_P192 = 11
SIGNATURE_TYPE_ECDSA_P256 = 12
SIGNATURE_TYPE_ECDSA_P384 = 13
SIGNATURE_TYPE_RSA_3072_SIZE = 384
SIGNATURE_TYPE_ECDSA_P192_SIZE = 64
SIGNATURE_TYPE_ECDSA_P256_SIZE = 64
SIGNATURE_TYPE_ECDSA_P384_SIZE = 96
EFUSE_KEY_BLOCKS = 6
SECURE_BOOT_RSA_TARGETS = [
'esp32',
'esp32c3',
'esp32c5',
'esp32c6',
'esp32c61',
'esp32h2',
'esp32h21',
'esp32s2',
'esp32s3',
'esp32p4',
]
SECURE_BOOT_ECDSA_TARGETS = ['esp32c2', 'esp32c5', 'esp32c6', 'esp32c61', 'esp32h2', 'esp32h21', 'esp32p4']
SECURE_BOOT_ECDSA_P384_TARGETS = ['esp32c5']
CONFIGS_SECURE_BOOT_ECDSA = list(
itertools.chain(
itertools.product(['ecdsa_p192', 'ecdsa_p256'], SECURE_BOOT_ECDSA_TARGETS),
itertools.product(['ecdsa_p384'], SECURE_BOOT_ECDSA_P384_TARGETS),
)
)
CONFIGS_SECURE_BOOT_RSA = list(
itertools.chain(
itertools.product(['rsa_3072'], SECURE_BOOT_RSA_TARGETS),
)
)
def corrupt_signature(
signed_bootloader,
seed=0,
corrupt_sig=True,
corrupt_crc=False,
corrupt_block=CORRUPT_ALL_BLOCKS,
signature_type=SIGNATURE_TYPE_RSA_3072,
):
# type: (bytes, int, bool, bool, int, int) -> bytes
image = signed_bootloader[:-4096]
signature = signed_bootloader[-4096:]
sig_blocks = (signature[0:1216], signature[1216:2432], signature[2432:3648])
new_blocks = tuple(corrupt_sig_block(s, seed, corrupt_sig, corrupt_crc) for s in sig_blocks)
new_blocks = tuple(corrupt_sig_block(s, seed, corrupt_sig, corrupt_crc, signature_type) for s in sig_blocks)
# if corrupt_block is CORRUPT_ALL_BLOCKS, corrupt all blocks
# otherwise, only corrupt the one with that index set
@@ -36,15 +87,26 @@ def corrupt_signature(signed_bootloader, seed=0, corrupt_sig=True, corrupt_crc=F
return image + b''.join(corr_sig_blocks) + signature[3648:]
def corrupt_sig_block(sig_block, seed=0, corrupt_sig=True, corrupt_crc=False):
# type: (bytes, int, bool, bool) -> bytes
def corrupt_sig_block(sig_block, seed=0, corrupt_sig=True, corrupt_crc=False, signature_type=SIGNATURE_TYPE_RSA_3072):
# type: (bytes, int, bool, bool, int) -> bytes
assert len(sig_block) == 1216
magic = sig_block[0]
assert magic in [0xE7, 0xFF]
if magic != 0xE7:
return sig_block # not valid
data = sig_block[:812]
new_sig = sig = sig_block[812:1196]
if signature_type == SIGNATURE_TYPE_RSA_3072:
data = sig_block[:812]
new_sig = sig = sig_block[812:1196]
elif signature_type in [SIGNATURE_TYPE_ECDSA_P192, SIGNATURE_TYPE_ECDSA_P256]:
data = sig_block[:101]
new_sig = sig = sig_block[101:165]
elif signature_type == SIGNATURE_TYPE_ECDSA_P384:
data = sig_block[:149]
new_sig = sig = sig_block[149:245]
else:
raise ValueError('Invalid signature type: {}'.format(signature_type))
crc = sig_block[1196:1200]
padding = sig_block[1200:1216]
@@ -80,16 +142,27 @@ def dut_start_secure_app(dut: Dut) -> None:
dut.serial.app_flash(os.path.join(dut.app.binary_path, 'secure_boot.bin'))
# Test secure boot flow.
# Correctly signed bootloader + correctly signed app should work
@idf_parametrize('target', ['esp32c3', 'esp32c5', 'esp32c61', 'esp32s3', 'esp32p4'], indirect=['target'])
def test_examples_security_secure_boot(dut: Dut) -> None:
def _examples_security_secure_boot(dut: Dut) -> None:
dut_start_secure_app(dut)
dut.expect('Secure Boot is enabled', timeout=10)
dut.serial.reset_efuses()
dut.burn_wafer_version()
# Test secure boot flow.
# Correctly signed bootloader + correctly signed app should work
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_RSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_rsa(dut: Dut) -> None:
_examples_security_secure_boot(dut)
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_ECDSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_ecdsa(dut: Dut) -> None:
_examples_security_secure_boot(dut)
# Test secure boot flow.
# Correctly signed bootloader + correctly signed app should work
@pytest.mark.host_test
@@ -104,8 +177,8 @@ def test_examples_security_secure_boot(dut: Dut) -> None:
],
indirect=True,
)
@pytest.mark.parametrize('target', ['esp32c3'], indirect=True)
@pytest.mark.parametrize('config', ['qemu'], indirect=True)
@idf_parametrize('target', ['esp32c3'], indirect=['target'])
def test_examples_security_secure_boot_qemu(dut: Dut) -> None:
try:
dut.expect('Secure Boot is enabled', timeout=10)
@@ -121,50 +194,96 @@ def test_examples_security_secure_boot_qemu(dut: Dut) -> None:
efuse_file.write(bytearray.fromhex(esp32c3_efuses))
def _examples_security_secure_boot_key_combo(dut: Dut) -> None:
dut_start_secure_app(dut)
dut.expect('Secure Boot is enabled', timeout=10)
efuse_secure_boot_key_digests = dut.app.sdkconfig.get('SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS')
secure_boot_key = dut.app.sdkconfig.get('SECURE_BOOT_SIGNING_KEY')
for index in range(efuse_secure_boot_key_digests):
for block in range(EFUSE_KEY_BLOCKS):
dut.serial.reset_efuses()
dut.burn_wafer_version()
dut.secure_boot_burn_en_bit()
dut.secure_boot_burn_digest(secure_boot_key, index, block)
dut.expect('Secure Boot is enabled', timeout=10)
dut.serial.reset_efuses()
dut.burn_wafer_version()
# Test efuse key index and key block combination.
# Any key index can be written to any key block and should work
# Increasing the test timeout to 1200s as the test runs for 18 iterations
# and thus the default 600s timeout is not sufficient
@pytest.mark.timeout(1200)
@idf_parametrize('target', ['esp32c3', 'esp32c5', 'esp32c61', 'esp32s3', 'esp32p4'], indirect=['target'])
def test_examples_security_secure_boot_key_combo(dut: Dut) -> None:
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_RSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_key_combo_rsa(dut: Dut) -> None:
_examples_security_secure_boot_key_combo(dut)
# Test efuse key index and key block combination.
# Any key index can be written to any key block and should work
# Increasing the test timeout to 1200s as the test runs for 18 iterations
# and thus the default 600s timeout is not sufficient
@pytest.mark.timeout(1200)
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_ECDSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_key_combo_ecdsa(dut: Dut) -> None:
_examples_security_secure_boot_key_combo(dut)
def _examples_security_secure_boot_key_revoke(dut: Dut) -> None:
dut_start_secure_app(dut)
dut.expect('Secure Boot is enabled', timeout=10)
for index in range(3):
for block in range(6):
dut.serial.reset_efuses()
dut.burn_wafer_version()
dut.secure_boot_burn_en_bit()
dut.secure_boot_burn_digest('test_rsa_3072_key.pem', index, block)
dut.expect('Secure Boot is enabled', timeout=10)
efuse_secure_boot_key_digests = dut.app.sdkconfig.get('SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS')
secure_boot_key = dut.app.sdkconfig.get('SECURE_BOOT_SIGNING_KEY')
for index in range(efuse_secure_boot_key_digests):
dut.serial.reset_efuses()
dut.burn_wafer_version()
dut.secure_boot_burn_en_bit()
dut.serial.burn_efuse('SECURE_BOOT_KEY_REVOKE%d' % index, 1)
dut.secure_boot_burn_digest(secure_boot_key, index, 0)
dut.expect('secure boot verification failed', timeout=5)
dut.serial.reset_efuses()
dut.burn_wafer_version()
# Test secure boot key revoke.
# If a key is revoked, bootloader signed with that key should fail verification
@idf_parametrize('target', ['esp32c3', 'esp32c5', 'esp32c61', 'esp32s3', 'esp32p4'], indirect=['target'])
def test_examples_security_secure_boot_key_revoke(dut: Dut) -> None:
dut_start_secure_app(dut)
dut.expect('Secure Boot is enabled', timeout=10)
for index in range(3):
dut.serial.reset_efuses()
dut.burn_wafer_version()
dut.secure_boot_burn_en_bit()
dut.serial.burn_efuse('SECURE_BOOT_KEY_REVOKE%d' % index, 1)
dut.secure_boot_burn_digest('test_rsa_3072_key.pem', index, 0)
dut.expect('secure boot verification failed', timeout=5)
dut.serial.reset_efuses()
dut.burn_wafer_version()
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_RSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_key_revoke_rsa(dut: Dut) -> None:
_examples_security_secure_boot_key_revoke(dut)
# Test bootloader signature corruption.
# Corrupt one byte at a time of bootloader signature and test that the verification fails
@pytest.mark.timeout(18000)
# Increasing the test timeout to 18000s as the test runs for 384 iterations
# and thus the default 600s timeout is not sufficient
@idf_parametrize('target', ['esp32c3', 'esp32c5', 'esp32c61', 'esp32s3', 'esp32p4'], indirect=['target'])
def test_examples_security_secure_boot_corrupt_bl_sig(dut: Dut) -> None:
# Test secure boot key revoke.
# If a key is revoked, bootloader signed with that key should fail verification
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_ECDSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_key_revoke_ecdsa(dut: Dut) -> None:
_examples_security_secure_boot_key_revoke(dut)
def get_signature_type_size(dut: Dut, signature_type: int) -> int:
signature_type_size = 0
if signature_type == SIGNATURE_TYPE_RSA:
signature_type_size = SIGNATURE_TYPE_RSA_3072_SIZE
elif signature_type == SIGNATURE_TYPE_ECDSA:
if dut.app.sdkconfig.get('CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_192_BITS'):
signature_type_size = SIGNATURE_TYPE_ECDSA_P192_SIZE
elif dut.app.sdkconfig.get('CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_256_BITS'):
signature_type_size = SIGNATURE_TYPE_ECDSA_P256_SIZE
elif dut.app.sdkconfig.get('CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS'):
signature_type_size = SIGNATURE_TYPE_ECDSA_P384_SIZE
else:
raise ValueError('Invalid signature type: {}'.format(signature_type))
else:
raise ValueError('Invalid signature type: {}'.format(signature_type))
return signature_type_size
def _examples_security_secure_boot_corrupt_bl_sig(dut: Dut, signature_type: int) -> None:
dut_start_secure_app(dut)
dut.expect('Secure Boot is enabled', timeout=10)
@@ -172,8 +291,10 @@ def test_examples_security_secure_boot_corrupt_bl_sig(dut: Dut) -> None:
with open(bootloader_bin, 'rb') as f:
signed_bl = f.read()
seeds = range(0, 384)
signature_type_size = get_signature_type_size(dut, signature_type)
seeds = range(0, signature_type_size)
max_seed = max(seeds)
secure_boot_key = dut.app.sdkconfig.get('SECURE_BOOT_SIGNING_KEY')
for seed in seeds:
print('Case %d / %d' % (seed, max_seed))
@@ -184,7 +305,7 @@ def test_examples_security_secure_boot_corrupt_bl_sig(dut: Dut) -> None:
dut.burn_wafer_version()
dut.serial.bootloader_flash('corrupt_bl.bin')
dut.secure_boot_burn_en_bit()
dut.secure_boot_burn_digest('test_rsa_3072_key.pem', 0, 0)
dut.secure_boot_burn_digest(secure_boot_key, 0, 0)
# Though the order of flashing and burning efuse would not effect the test,
# if we flash bootlader before burning en bit, even with no_stub = True
# it still calls run_stub() and throws an error as it fails to start stub.
@@ -193,13 +314,29 @@ def test_examples_security_secure_boot_corrupt_bl_sig(dut: Dut) -> None:
dut.burn_wafer_version()
# Test app signature corruption.
# Corrupt app signature, one byte at a time, and test that the verification fails
# Test bootloader signature corruption.
# Corrupt one byte at a time of bootloader signature and test that the verification fails
@pytest.mark.timeout(18000)
# Increasing the test timeout to 18000s as the test runs for 385 iterations
# Increasing the test timeout to 18000s as the test runs for 384 iterations
# and thus the default 600s timeout is not sufficient
@idf_parametrize('target', ['esp32c3', 'esp32c5', 'esp32c61', 'esp32s3', 'esp32p4'], indirect=['target'])
def test_examples_security_secure_boot_corrupt_app_sig(dut: Dut) -> None:
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_RSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_corrupt_bl_sig_rsa(dut: Dut) -> None:
_examples_security_secure_boot_corrupt_bl_sig(dut, signature_type=SIGNATURE_TYPE_RSA)
# Test bootloader signature corruption.
# Corrupt one byte at a time of bootloader signature and test that the verification fails
@pytest.mark.timeout(18000)
# Increasing the test timeout to 18000s as the test runs for 384 iterations
# and thus the default 600s timeout is not sufficient
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_ECDSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_corrupt_bl_sig_ecdsa(dut: Dut) -> None:
_examples_security_secure_boot_corrupt_bl_sig(dut, signature_type=SIGNATURE_TYPE_ECDSA)
def _examples_security_secure_boot_corrupt_app_sig(dut: Dut, signature_type: int) -> None:
dut_start_secure_app(dut)
dut.expect('Secure Boot is enabled', timeout=10)
@@ -207,7 +344,8 @@ def test_examples_security_secure_boot_corrupt_app_sig(dut: Dut) -> None:
with open(app_bin, 'rb') as f:
signed_app = f.read()
seeds = range(0, 384)
signature_size = get_signature_type_size(dut, signature_type)
seeds = range(0, signature_size)
max_seed = max(seeds)
for seed in seeds:
@@ -245,3 +383,25 @@ def test_examples_security_secure_boot_corrupt_app_sig(dut: Dut) -> None:
)
dut.expect('Secure boot signature verification failed', timeout=2)
dut.expect('No bootable app partitions in the partition table', timeout=2)
# Test app signature corruption.
# Corrupt app signature, one byte at a time, and test that the verification fails
@pytest.mark.timeout(18000)
# Increasing the test timeout to 18000s as the test runs for 385 iterations
# and thus the default 600s timeout is not sufficient
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_RSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_corrupt_app_sig_rsa(dut: Dut) -> None:
_examples_security_secure_boot_corrupt_app_sig(dut, signature_type=SIGNATURE_TYPE_RSA)
# Test app signature corruption.
# Corrupt app signature, one byte at a time, and test that the verification fails
@pytest.mark.timeout(18000)
# Increasing the test timeout to 18000s as the test runs for 385 iterations
# and thus the default 600s timeout is not sufficient
@pytest.mark.generic
@idf_parametrize('config, target', CONFIGS_SECURE_BOOT_ECDSA, indirect=['config', 'target'])
def test_examples_security_secure_boot_corrupt_app_sig_ecdsa(dut: Dut) -> None:
_examples_security_secure_boot_corrupt_app_sig(dut, signature_type=SIGNATURE_TYPE_ECDSA)

View File

@@ -1,4 +0,0 @@
# ESP32-S2 Secure Boot
CONFIG_IDF_TARGET="esp32s2"
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_SIGNING_KEY="test_rsa_3072_key.pem"

View File

@@ -1,5 +1,4 @@
# ESP32-S2 Secure Boot & Flash Encryption (Release mode)
CONFIG_IDF_TARGET="esp32s2"
# Secure Boot & Flash Encryption (Release mode)
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_SIGNING_KEY="test_rsa_3072_key.pem"
CONFIG_SECURE_FLASH_ENC_ENABLED=y

View File

@@ -0,0 +1,9 @@
CONFIG_PARTITION_TABLE_OFFSET=0xD000
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_V2_ENABLED=y
CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME=y
CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_256_BITS=y
CONFIG_SECURE_BOOT_SIGNING_KEY="test_ecdsa_p192_key.pem"
CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y

View File

@@ -0,0 +1,9 @@
CONFIG_PARTITION_TABLE_OFFSET=0xD000
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_V2_ENABLED=y
CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME=y
CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_256_BITS=y
CONFIG_SECURE_BOOT_SIGNING_KEY="test_ecdsa_p256_key.pem"
CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y

View File

@@ -0,0 +1,9 @@
CONFIG_PARTITION_TABLE_OFFSET=0xD000
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_V2_ENABLED=y
CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME=y
CONFIG_SECURE_BOOT_ECDSA_KEY_LEN_384_BITS=y
CONFIG_SECURE_BOOT_SIGNING_KEY="test_ecdsa_p384_key.pem"
CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y

View File

@@ -0,0 +1,8 @@
CONFIG_PARTITION_TABLE_OFFSET=0xD000
CONFIG_SECURE_BOOT=y
CONFIG_SECURE_BOOT_V2_ENABLED=y
CONFIG_SECURE_SIGNED_APPS_RSA_SCHEME=y
CONFIG_SECURE_BOOT_SIGNING_KEY="test_rsa_3072_key.pem"
CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES=y

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MF8CAQEEGCqtXL4T69v9OhhrHcI0kQNC0NFkmOQ6DqAKBggqhkjOPQMBAaE0AzIA
BDvdwlHoSE5QQ6JBU0Ovy2LjEEuoXVwpPebH3Z87B1ByYLWPZp8XhXWl7Vj7wFK7
dw==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJLB2fFAa7istjO0AWfEbvJM8Kn0T+R38GXalwX3oP6GoAoGCCqGSM49
AwEHoUQDQgAEy2N3ohJ1hIjU2AHNyVKGafSrmGhizG1/xOTOtASbJpiVI3ccUVXI
zrDSnrTwg331qOAT7WWkY1p4ixZvP6HWzA==
-----END EC PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDC/5UVo7tqYLt17BnN85NRhMRakLUYNrxCEb0nA5bN5WzpILMqCFkjzWWyC6FkeK02g
BwYFK4EEACKhZANiAAQPbOGJCBJtR6oB29nt6BP+JeMc4+KUkJbusFDT26arFZwcsDEZ/m+GatAl
GmlOuNGa4F4fJDjSsuz0ejKK4LQ1DliDopGmieIUWPCEScVZNu9DX2PuGC8NIyMU9Ry4Poc=
-----END EC PRIVATE KEY-----