mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 18:50:34 +03:00
Merge branch 'fix/ecdsa_ecc_hw_input_validation_v5.4' into 'release/v5.4'
Validate ECDSA signature range and harden ECC memory power-down (v5.4) See merge request espressif/esp-idf!49444
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Assert a condition is true, in a way that should be resistant to fault injection for
|
||||
* single fault attacks.
|
||||
*
|
||||
* - Expands CONDITION multiple times (condition must have no side effects)
|
||||
* - Compiler is told all registers are invalid before evaluating CONDITION each time, to avoid a fault
|
||||
* causing a misread of a register used in all three evaluations of CONDITION.
|
||||
* - The result of each evaluation is stored into a volatile variable and re-read before the branch.
|
||||
* This prevents the compiler from constant-folding CONDITION and deleting the whole check when it
|
||||
* has already proven the value - e.g. when this macro follows a normal "if (!cond) { ... }" check
|
||||
* of the same value, which would otherwise silently remove the fault-injection protection.
|
||||
* - If CONDITION is ever false, a system reset is triggered.
|
||||
*
|
||||
* @note Place this macro after a "normal" check of CONDITION that will fail with a normal error
|
||||
* message. This is the fallback in case a fault injection attack skips or corrupts the result of
|
||||
* that check. (Although ensure that an attacker can't use fault injection to skip past the "normal"
|
||||
* error message, to avoid this check entirely.)
|
||||
*
|
||||
* @note This macro increases binary size and is slow and should be used sparingly.
|
||||
*
|
||||
* @note This macro does not guarantee fault injection resistance. In particular CONDITION must be
|
||||
* chosen carefully - a fault injection attack which sets CONDITION to true will not be detected by
|
||||
* this macro. Care must also be taken that an attacker can't use a fault to completely bypass calling
|
||||
* whatever function tests ESP_FAULT_ASSERT.
|
||||
*
|
||||
* @note This is difficult to debug as a failure triggers an instant software reset, and UART output
|
||||
* is often truncated (as FIFO is not flushed). Define the ESP_FAULT_ASSERT_DEBUG macro to debug any
|
||||
* failures of this macro due to software bugs.
|
||||
*
|
||||
* @param CONDITION A condition which will evaluate true unless an attacker used fault injection to skip or corrupt some other critical system calculation.
|
||||
*
|
||||
*/
|
||||
#define ESP_FAULT_ASSERT(CONDITION) do { \
|
||||
bool esp_fault_assert_chk; \
|
||||
asm volatile ("" ::: "memory"); \
|
||||
esp_fault_assert_chk = (CONDITION); \
|
||||
asm volatile ("" : "+r"(esp_fault_assert_chk)); \
|
||||
if(!esp_fault_assert_chk) _ESP_FAULT_RESET(); \
|
||||
asm volatile ("" ::: "memory"); \
|
||||
esp_fault_assert_chk = (CONDITION); \
|
||||
asm volatile ("" : "+r"(esp_fault_assert_chk)); \
|
||||
if(!esp_fault_assert_chk) _ESP_FAULT_RESET(); \
|
||||
asm volatile ("" ::: "memory"); \
|
||||
esp_fault_assert_chk = (CONDITION); \
|
||||
asm volatile ("" : "+r"(esp_fault_assert_chk)); \
|
||||
if(!esp_fault_assert_chk) _ESP_FAULT_RESET(); \
|
||||
} while(0)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
#define _ESP_FAULT_ILLEGAL_INSTRUCTION asm volatile("ill.n; ill.n; ill.n; ill.n; ill.n; ill.n; ill.n;")
|
||||
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
#define _ESP_FAULT_ILLEGAL_INSTRUCTION asm volatile("unimp; unimp; unimp; unimp; unimp;")
|
||||
#elif CONFIG_IDF_TARGET_LINUX
|
||||
#define _ESP_FAULT_ILLEGAL_INSTRUCTION
|
||||
#else
|
||||
#error "_ESP_FAULT_ILLEGAL_INSTRUCTION is not defined for this TARGET"
|
||||
#endif
|
||||
|
||||
// Uncomment this macro to get debug output if ESP_FAULT_ASSERT() fails
|
||||
//
|
||||
// Note that uncommenting this macro reduces the anti-FI effectiveness
|
||||
//
|
||||
//#define ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
/* Internal macro, purpose is to trigger a system reset if an inconsistency due to fault injection
|
||||
is detected.
|
||||
|
||||
Illegal instruction opcodes are there as a fallback to crash the CPU in case it doesn't
|
||||
reset as expected.
|
||||
*/
|
||||
#ifndef ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
#define _ESP_FAULT_RESET() do { \
|
||||
esp_rom_software_reset_system(); \
|
||||
_ESP_FAULT_ILLEGAL_INSTRUCTION; \
|
||||
} while(0)
|
||||
|
||||
#else // ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
#warning "Enabling ESP_FAULT_ASSERT_DEBUG makes ESP_FAULT_ASSERT() less effective"
|
||||
|
||||
#define _ESP_FAULT_RESET() do { \
|
||||
esp_rom_printf("ESP_FAULT_ASSERT %s:%d\n", __FILE__, __LINE__); \
|
||||
_ESP_FAULT_ILLEGAL_INSTRUCTION; \
|
||||
} while(0)
|
||||
|
||||
#endif // ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,94 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Assert a condition is true, in a way that should be resistant to fault injection for
|
||||
* single fault attacks.
|
||||
*
|
||||
* - Expands CONDITION multiple times (condition must have no side effects)
|
||||
* - Compiler is told all registers are invalid before evaluating CONDITION each time, to avoid a fault
|
||||
* causing a misread of a register used in all three evaluations of CONDITION.
|
||||
* - If CONDITION is ever false, a system reset is triggered.
|
||||
*
|
||||
* @note Place this macro after a "normal" check of CONDITION that will fail with a normal error
|
||||
* message. This is the fallback in case a fault injection attack skips or corrupts the result of
|
||||
* that check. (Although ensure that an attacker can't use fault injection to skip past the "normal"
|
||||
* error message, to avoid this check entirely.)
|
||||
*
|
||||
* @note This macro increases binary size and is slow and should be used sparingly.
|
||||
*
|
||||
* @note This macro does not guarantee fault injection resistance. In particular CONDITION must be
|
||||
* chosen carefully - a fault injection attack which sets CONDITION to true will not be detected by
|
||||
* this macro. Care must also be taken that an attacker can't use a fault to completely bypass calling
|
||||
* whatever function tests ESP_FAULT_ASSERT.
|
||||
*
|
||||
* @note This is difficult to debug as a failure triggers an instant software reset, and UART output
|
||||
* is often truncated (as FIFO is not flushed). Define the ESP_FAULT_ASSERT_DEBUG macro to debug any
|
||||
* failures of this macro due to software bugs.
|
||||
*
|
||||
* @param CONDITION A condition which will evaluate true unless an attacker used fault injection to skip or corrupt some other critical system calculation.
|
||||
*
|
||||
*/
|
||||
#define ESP_FAULT_ASSERT(CONDITION) do { \
|
||||
asm volatile ("" ::: "memory"); \
|
||||
if(!(CONDITION)) _ESP_FAULT_RESET(); \
|
||||
asm volatile ("" ::: "memory"); \
|
||||
if(!(CONDITION)) _ESP_FAULT_RESET(); \
|
||||
asm volatile ("" ::: "memory"); \
|
||||
if(!(CONDITION)) _ESP_FAULT_RESET(); \
|
||||
} while(0)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
#define _ESP_FAULT_ILLEGAL_INSTRUCTION asm volatile("ill.n; ill.n; ill.n; ill.n; ill.n; ill.n; ill.n;")
|
||||
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
#define _ESP_FAULT_ILLEGAL_INSTRUCTION asm volatile("unimp; unimp; unimp; unimp; unimp;")
|
||||
#elif CONFIG_IDF_TARGET_LINUX
|
||||
#define _ESP_FAULT_ILLEGAL_INSTRUCTION
|
||||
#else
|
||||
#error "_ESP_FAULT_ILLEGAL_INSTRUCTION is not defined for this TARGET"
|
||||
#endif
|
||||
|
||||
// Uncomment this macro to get debug output if ESP_FAULT_ASSERT() fails
|
||||
//
|
||||
// Note that uncommenting this macro reduces the anti-FI effectiveness
|
||||
//
|
||||
//#define ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
/* Internal macro, purpose is to trigger a system reset if an inconsistency due to fault injection
|
||||
is detected.
|
||||
|
||||
Illegal instruction opcodes are there as a fallback to crash the CPU in case it doesn't
|
||||
reset as expected.
|
||||
*/
|
||||
#ifndef ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
#define _ESP_FAULT_RESET() do { \
|
||||
esp_rom_software_reset_system(); \
|
||||
_ESP_FAULT_ILLEGAL_INSTRUCTION; \
|
||||
} while(0)
|
||||
|
||||
#else // ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
#warning "Enabling ESP_FAULT_ASSERT_DEBUG makes ESP_FAULT_ASSERT() less effective"
|
||||
|
||||
#define _ESP_FAULT_RESET() do { \
|
||||
esp_rom_printf("ESP_FAULT_ASSERT %s:%d\n", __FILE__, __LINE__); \
|
||||
_ESP_FAULT_ILLEGAL_INSTRUCTION; \
|
||||
} while(0)
|
||||
|
||||
#endif // ESP_FAULT_ASSERT_DEBUG
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#include "esp_fault_internal.h"
|
||||
|
||||
@@ -83,6 +83,10 @@ if(CONFIG_ESP_ROM_CACHE_WRITEBACK_NEEDS_SYNC_TWICE_MAP)
|
||||
list(APPEND sources "patches/esp_rom_cache_writeback_esp32p4.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP_ROM_ECDSA_VERIFY_PATCH)
|
||||
list(APPEND sources "patches/esp_rom_ecdsa.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${sources}
|
||||
INCLUDE_DIRS ${include_dirs}
|
||||
PRIV_REQUIRES ${private_required_comp}
|
||||
|
||||
@@ -110,3 +110,7 @@ config ESP_ROM_NO_USB_SERIAL_OUTPUT_API
|
||||
config ESP_ROM_SUPPORT_SECURE_BOOT_FAST_WAKEUP
|
||||
bool
|
||||
default y
|
||||
|
||||
config ESP_ROM_ECDSA_VERIFY_PATCH
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -33,3 +33,4 @@
|
||||
#define ESP_ROM_HAS_OUTPUT_PUTC_FUNC (1) // ROM has esp_rom_output_putc (or ets_write_char_uart)
|
||||
#define ESP_ROM_NO_USB_SERIAL_OUTPUT_API (1) // ROM does not export the usb-serial-jtag write char function
|
||||
#define ESP_ROM_SUPPORT_SECURE_BOOT_FAST_WAKEUP (1) // ROM supports the secure boot fast wakeup feature
|
||||
#define ESP_ROM_ECDSA_VERIFY_PATCH (1) // ROM ets_ecdsa_verify API requires a software patch
|
||||
|
||||
@@ -361,9 +361,8 @@ ets_efuse_secure_boot_fast_wake_enabled = 0x40000830;
|
||||
/* Functions */
|
||||
ets_emsa_pss_verify = 0x40000834;
|
||||
ets_rsa_pss_verify = 0x40000838;
|
||||
ets_ecdsa_verify = 0x4000083c;
|
||||
ets_secure_boot_verify_bootloader_with_keys = 0x40000840;
|
||||
ets_secure_boot_verify_signature = 0x40000844;
|
||||
_rom_ets_ecdsa_verify = 0x4000083c;
|
||||
_rom_ets_secure_boot_verify_signature = 0x40000844;
|
||||
ets_secure_boot_read_key_digests = 0x40000848;
|
||||
ets_secure_boot_revoke_public_key_digest = 0x4000084c;
|
||||
|
||||
|
||||
@@ -83,6 +83,10 @@ config ESP_ROM_HAS_OUTPUT_PUTC_FUNC
|
||||
bool
|
||||
default y
|
||||
|
||||
config ESP_ROM_ECDSA_VERIFY_PATCH
|
||||
bool
|
||||
default y
|
||||
|
||||
config ESP_ROM_CACHE_WRITEBACK_NEEDS_SYNC_TWICE_MAP
|
||||
bool
|
||||
default y
|
||||
|
||||
@@ -26,4 +26,5 @@
|
||||
#define ESP_ROM_HAS_VERSION (1) // ROM has version/eco information
|
||||
#define ESP_ROM_CLIC_INT_TYPE_PATCH (1) // ROM api esprv_intc_int_set_type configuring edge type interrupt is invalid TODO: IDF-13409
|
||||
#define ESP_ROM_HAS_OUTPUT_PUTC_FUNC (1) // ROM has esp_rom_output_putc (or ets_write_char_uart)
|
||||
#define ESP_ROM_ECDSA_VERIFY_PATCH (1) // ROM ets_ecdsa_verify API requires a software patch
|
||||
#define ESP_ROM_CACHE_WRITEBACK_NEEDS_SYNC_TWICE_MAP (1) // ROM cache writeback related needs patch to avoid sync loss, need map parameter
|
||||
|
||||
@@ -470,9 +470,8 @@ esp_rom_km_huk_risk = 0x4fc00710;
|
||||
/* Functions */
|
||||
ets_emsa_pss_verify = 0x4fc00714;
|
||||
ets_rsa_pss_verify = 0x4fc00718;
|
||||
ets_ecdsa_verify = 0x4fc0071c;
|
||||
ets_secure_boot_verify_bootloader_with_keys = 0x4fc00720;
|
||||
ets_secure_boot_verify_signature = 0x4fc00724;
|
||||
_rom_ets_ecdsa_verify = 0x4fc0071c;
|
||||
_rom_ets_secure_boot_verify_signature = 0x4fc00724;
|
||||
ets_secure_boot_read_key_digests = 0x4fc00728;
|
||||
ets_secure_boot_revoke_public_key_digest = 0x4fc0072c;
|
||||
|
||||
|
||||
@@ -450,9 +450,8 @@ esp_rom_km_huk_risk = 0x4fc0071c;
|
||||
/* Functions */
|
||||
ets_emsa_pss_verify = 0x4fc00720;
|
||||
ets_rsa_pss_verify = 0x4fc00724;
|
||||
ets_ecdsa_verify = 0x4fc00728;
|
||||
ets_secure_boot_verify_bootloader_with_keys = 0x4fc0072c;
|
||||
ets_secure_boot_verify_signature = 0x4fc00730;
|
||||
_rom_ets_ecdsa_verify = 0x4fc00728;
|
||||
_rom_ets_secure_boot_verify_signature = 0x4fc00730;
|
||||
ets_secure_boot_read_key_digests = 0x4fc00734;
|
||||
ets_secure_boot_revoke_public_key_digest = 0x4fc00738;
|
||||
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_rom_caps.h"
|
||||
#if ESP_ROM_ECDSA_VERIFY_PATCH
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_fault_internal.h"
|
||||
#include "hal/ecc_ll.h"
|
||||
#include "rom/ecdsa.h"
|
||||
|
||||
#define VALID_MAGIC_OK 0x6A6A6A6AU
|
||||
#define VALID_MAGIC_FAIL 0x95959595U
|
||||
|
||||
static const uint32_t ecdsa_n_p192[6] = {
|
||||
0xb4d22831U, 0x146bc9b1U, 0x99def836U, 0xffffffffU, 0xffffffffU, 0xffffffffU,
|
||||
};
|
||||
|
||||
static const uint32_t ecdsa_n_p256[8] = {
|
||||
0xfc632551U, 0xf3b9cac2U, 0xa7179e84U, 0xbce6faadU, 0xffffffffU, 0xffffffffU, 0x00000000U, 0xffffffffU,
|
||||
};
|
||||
|
||||
#if SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
static const uint32_t ecdsa_n_p384[12] = {
|
||||
0xccc52973U, 0xecec196aU, 0x48b0a77aU, 0x581a0db2U, 0xf4372ddfU, 0xc7634d81U,
|
||||
0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU,
|
||||
};
|
||||
#endif
|
||||
|
||||
static uint32_t ecdsa_mpi_isZero(const uint32_t *mpi, int num_words)
|
||||
{
|
||||
uint32_t bits = 0;
|
||||
for (int i = 0; i < num_words; ++i) {
|
||||
bits |= mpi[i];
|
||||
}
|
||||
return (bits == 0);
|
||||
}
|
||||
|
||||
static int ecdsa_mpi_cmp_unsafe(const uint32_t *left, const uint32_t *right, int num_words)
|
||||
{
|
||||
for (int i = num_words - 1; i >= 0; --i) {
|
||||
if (left[i] > right[i]) {
|
||||
return 1;
|
||||
} else if (left[i] < right[i]) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ecdsa_scalars_in_range(const uint32_t *r, const uint32_t *s, const uint32_t *n, int num_words, uint32_t *result)
|
||||
{
|
||||
volatile uint32_t verdict = VALID_MAGIC_FAIL;
|
||||
if (ecdsa_mpi_isZero(r, num_words) == 0 && ecdsa_mpi_cmp_unsafe(n, r, num_words) == 1 && ecdsa_mpi_isZero(s, num_words) == 0
|
||||
&& ecdsa_mpi_cmp_unsafe(n, s, num_words) == 1) {
|
||||
verdict = VALID_MAGIC_OK;
|
||||
}
|
||||
if (verdict != VALID_MAGIC_OK) {
|
||||
return false;
|
||||
}
|
||||
ESP_FAULT_ASSERT(verdict == VALID_MAGIC_OK);
|
||||
|
||||
*result = VALID_MAGIC_OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: IDF-15721
|
||||
/*
|
||||
* Runtime gate that decides whether the ROM ECDSA verification routines
|
||||
* (ets_ecdsa_verify / ets_secure_boot_verify_signature) need the software patch
|
||||
* in this file, or whether the ROM implementation is safe to call directly.
|
||||
*
|
||||
* When a future revision of one of these chips ships a ROM with these ECDSA
|
||||
* verification issues fixed, add a ROM-version check here (e.g. compare the
|
||||
* _rom_eco_version symbol against the first fixed ROM ECO version for that
|
||||
* target) and return false for the fixed ROMs, so they skip the patch and jump
|
||||
* straight to the _rom_ routine.
|
||||
*/
|
||||
|
||||
extern int _rom_ets_ecdsa_verify(const uint8_t *key, const uint8_t *sig,
|
||||
ECDSA_CURVE curve_id, const uint8_t *image_digest,
|
||||
uint8_t *verified_digest);
|
||||
|
||||
int ets_ecdsa_verify(const uint8_t *key, const uint8_t *sig,
|
||||
ECDSA_CURVE curve_id, const uint8_t *image_digest,
|
||||
uint8_t *verified_digest)
|
||||
{
|
||||
int words;
|
||||
int bytes;
|
||||
const uint32_t *n;
|
||||
|
||||
if (curve_id == ECDSA_CURVE_P256) {
|
||||
words = 8;
|
||||
bytes = 32;
|
||||
n = ecdsa_n_p256;
|
||||
}
|
||||
#if SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
else if (curve_id == ECDSA_CURVE_P384) {
|
||||
words = 12;
|
||||
bytes = 48;
|
||||
n = ecdsa_n_p384;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// curve_id == ECDSA_CURVE_P192
|
||||
words = 6;
|
||||
bytes = 24;
|
||||
n = ecdsa_n_p192;
|
||||
}
|
||||
|
||||
uint32_t r[12] = { 0 };
|
||||
uint32_t s[12] = { 0 };
|
||||
memcpy(r, &sig[0], bytes);
|
||||
memcpy(s, &sig[bytes], bytes);
|
||||
|
||||
uint32_t ret_status = VALID_MAGIC_FAIL;
|
||||
bool ok = ecdsa_scalars_in_range(r, s, n, words, &ret_status);
|
||||
if (!ok || ret_status != VALID_MAGIC_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ESP_FAULT_ASSERT(ok && ret_status == VALID_MAGIC_OK);
|
||||
|
||||
ecc_ll_power_up();
|
||||
ESP_FAULT_ASSERT(ecc_ll_mem_force_pd_is_clear());
|
||||
|
||||
int ret = _rom_ets_ecdsa_verify(key, sig, curve_id, image_digest, verified_digest);
|
||||
|
||||
if (ret == 1) {
|
||||
ESP_FAULT_ASSERT(ret_status == VALID_MAGIC_OK);
|
||||
int sig_diff = (memcmp(r, &sig[0], bytes) | memcmp(s, &sig[bytes], bytes));
|
||||
ESP_FAULT_ASSERT(sig_diff == 0);
|
||||
ESP_FAULT_ASSERT(ret == 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if CONFIG_SECURE_BOOT_V2_ENABLED || CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT
|
||||
#include "rom/secure_boot.h"
|
||||
|
||||
#if CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
static bool esp_rom_ecdsa_scalars_in_range(const uint8_t *r_le, const uint8_t *s_le, size_t component_len)
|
||||
{
|
||||
const uint32_t *n;
|
||||
int words;
|
||||
switch (component_len) {
|
||||
case 24: n = ecdsa_n_p192; words = 6; break;
|
||||
case 32: n = ecdsa_n_p256; words = 8; break;
|
||||
#if SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
case 48: n = ecdsa_n_p384; words = 12; break;
|
||||
#endif
|
||||
default: return false;
|
||||
}
|
||||
|
||||
uint32_t r[12] = { 0 };
|
||||
uint32_t s[12] = { 0 };
|
||||
memcpy(r, r_le, component_len);
|
||||
memcpy(s, s_le, component_len);
|
||||
|
||||
uint32_t result = VALID_MAGIC_FAIL;
|
||||
bool ok = ecdsa_scalars_in_range(r, s, n, words, &result);
|
||||
if (!ok || result != VALID_MAGIC_OK) {
|
||||
return false;
|
||||
}
|
||||
ESP_FAULT_ASSERT(ok && result == VALID_MAGIC_OK);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool esp_rom_ecdsa_sig_block_in_range(const ets_secure_boot_sig_block_t *block)
|
||||
{
|
||||
if (block->magic_byte != ETS_SECURE_BOOT_V2_SIGNATURE_MAGIC) {
|
||||
return true;
|
||||
}
|
||||
size_t component_len;
|
||||
switch (block->ecdsa.key.curve_id) {
|
||||
case ECDSA_CURVE_P256: component_len = 32; break;
|
||||
#if SOC_ECDSA_SUPPORT_CURVE_P384
|
||||
case ECDSA_CURVE_P384: component_len = 48; break;
|
||||
#endif
|
||||
default: return false;
|
||||
}
|
||||
return esp_rom_ecdsa_scalars_in_range(&block->ecdsa.signature[0],
|
||||
&block->ecdsa.signature[component_len],
|
||||
component_len);
|
||||
}
|
||||
#endif /* CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME */
|
||||
|
||||
extern ets_secure_boot_status_t _rom_ets_secure_boot_verify_signature(const ets_secure_boot_signature_t *sig,
|
||||
const uint8_t *image_digest,
|
||||
const ets_secure_boot_key_digests_t *trusted_keys,
|
||||
uint8_t *verified_digest);
|
||||
|
||||
ets_secure_boot_status_t ets_secure_boot_verify_signature(const ets_secure_boot_signature_t *sig,
|
||||
const uint8_t *image_digest,
|
||||
const ets_secure_boot_key_digests_t *trusted_keys,
|
||||
uint8_t *verified_digest)
|
||||
{
|
||||
#if CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME
|
||||
volatile ets_secure_boot_status_t range_status = SB_FAILED;
|
||||
unsigned blocks_in_range = 0;
|
||||
for (unsigned i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
|
||||
if (esp_rom_ecdsa_sig_block_in_range(&sig->block[i])) {
|
||||
blocks_in_range++;
|
||||
}
|
||||
}
|
||||
if (blocks_in_range == SECURE_BOOT_NUM_BLOCKS) {
|
||||
range_status = SB_SUCCESS;
|
||||
}
|
||||
if (range_status != SB_SUCCESS) {
|
||||
return SB_FAILED;
|
||||
}
|
||||
ESP_FAULT_ASSERT(range_status == SB_SUCCESS);
|
||||
ESP_FAULT_ASSERT(blocks_in_range == SECURE_BOOT_NUM_BLOCKS);
|
||||
|
||||
ecc_ll_power_up();
|
||||
ESP_FAULT_ASSERT(ecc_ll_mem_force_pd_is_clear());
|
||||
#endif /* CONFIG_SECURE_SIGNED_APPS_ECDSA_V2_SCHEME */
|
||||
return _rom_ets_secure_boot_verify_signature(sig, image_digest, trusted_keys, verified_digest);
|
||||
}
|
||||
#endif /* CONFIG_SECURE_BOOT_V2_ENABLED || CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT */
|
||||
#endif /* ESP_ROM_ECDSA_VERIFY_PATCH */
|
||||
@@ -5,3 +5,10 @@ components/esp_security/test_apps/crypto_drivers:
|
||||
- if: ((SOC_HMAC_SUPPORTED == 1) or (SOC_DIG_SIGN_SUPPORTED == 1)) or (SOC_KEY_MANAGER_SUPPORTED == 1)
|
||||
depends_components:
|
||||
- esp_security
|
||||
- esp_hal_security
|
||||
|
||||
components/esp_security/test_apps/fault_assert_opt_check:
|
||||
enable:
|
||||
- if: IDF_TARGET in ["esp32", "esp32c3"] # one Xtensa + one RISC-V
|
||||
depends_components:
|
||||
- esp_common
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
project(fault_assert_opt_check)
|
||||
|
||||
# Regression guard: fail the build if ESP_FAULT_ASSERT() gets optimized away.
|
||||
idf_build_get_property(python PYTHON)
|
||||
add_custom_command(
|
||||
TARGET ${CMAKE_PROJECT_NAME}.elf POST_BUILD
|
||||
COMMAND ${python} "${CMAKE_CURRENT_SOURCE_DIR}/check_fault_asserts.py"
|
||||
"$<TARGET_FILE:${CMAKE_PROJECT_NAME}.elf>" "${CMAKE_OBJDUMP}"
|
||||
COMMENT "Verifying ESP_FAULT_ASSERT() survived optimization"
|
||||
VERBATIM)
|
||||
@@ -0,0 +1,2 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 |
|
||||
| ----------------- | ----- | -------- |
|
||||
@@ -0,0 +1,75 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Post-build regression guard for ESP_FAULT_ASSERT().
|
||||
|
||||
Disassembles the test app and verifies that the ESP_FAULT_ASSERT() calls in the
|
||||
known test functions still emit their reset blocks. One intact assert produces
|
||||
three independent reset-on-failure paths, i.e. three references to
|
||||
``esp_rom_software_reset_system`` in the function. If the macro ever regresses
|
||||
and the optimizer folds the checks away, the count drops and this exits non-zero,
|
||||
failing the build.
|
||||
|
||||
Usage: check_fault_asserts.py <app.elf> <objdump>
|
||||
"""
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
RESET_SYM = 'esp_rom_software_reset_system'
|
||||
|
||||
# function name -> number of ESP_FAULT_ASSERT calls it contains (x3 reset blocks each)
|
||||
EXPECTED = {
|
||||
'test_fa_guarded_flag': 1,
|
||||
'test_fa_guarded_status': 1,
|
||||
}
|
||||
|
||||
FUNC_RE = re.compile(r'^[0-9a-fA-F]+ <(.+)>:$')
|
||||
|
||||
|
||||
def reset_counts(elf: str, objdump: str) -> dict:
|
||||
dis = subprocess.run([objdump, '-d', elf], capture_output=True, text=True, check=True).stdout
|
||||
counts: dict = {}
|
||||
cur = None
|
||||
for line in dis.splitlines():
|
||||
m = FUNC_RE.match(line)
|
||||
if m:
|
||||
cur = m.group(1)
|
||||
counts.setdefault(cur, 0)
|
||||
elif cur and RESET_SYM in line:
|
||||
counts[cur] += 1
|
||||
return counts
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) != 3:
|
||||
print(__doc__)
|
||||
return 2
|
||||
elf, objdump = sys.argv[1], sys.argv[2]
|
||||
counts = reset_counts(elf, objdump)
|
||||
|
||||
failed = False
|
||||
for fn, n_asserts in EXPECTED.items():
|
||||
expected = 3 * n_asserts
|
||||
found = counts.get(fn)
|
||||
if found is None:
|
||||
print(f'ERROR: {fn} not found in {elf} (renamed/removed?)')
|
||||
failed = True
|
||||
elif found < expected:
|
||||
print(
|
||||
f'ERROR: ESP_FAULT_ASSERT optimized away in {fn}: '
|
||||
f'{found} reset checks, expected {expected}. '
|
||||
f'See components/esp_common/include/esp_fault.h'
|
||||
)
|
||||
failed = True
|
||||
else:
|
||||
print(f'OK: {fn} -> {found} reset checks ({found // 3} assert(s) x3)')
|
||||
|
||||
if failed:
|
||||
print('FAILED: ESP_FAULT_ASSERT regression check')
|
||||
return 1
|
||||
print('PASSED: ESP_FAULT_ASSERT checks survived optimization')
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "test_fault_assert.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* Regression guard for ESP_FAULT_ASSERT() being silently optimised away.
|
||||
*
|
||||
* ESP_FAULT_ASSERT(C) must emit three independent "evaluate C -> reset if false"
|
||||
* checks. When C is a value the optimiser can already prove (e.g. a flag pinned
|
||||
* by a preceding "if (!C) return"), a naive implementation lets GCC constant-fold
|
||||
* C and delete all three checks, removing the fault-injection protection with no
|
||||
* warning.
|
||||
*
|
||||
* The functions below place ESP_FAULT_ASSERT in exactly that
|
||||
* "proven-true cached value" shape. check_fault_asserts.py disassembles the built
|
||||
* app and fails the build if any of them lost its reset blocks. Keep them
|
||||
* noinline+used so each is an independent symbol the checker can find.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_fault.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
/* volatile so the initial value is opaque: only the early-return "proves" it,
|
||||
* which is the precise condition that triggers the optimiser elimination. */
|
||||
volatile bool fa_test_flag = true;
|
||||
volatile int fa_test_status = 0;
|
||||
volatile int fa_test_sink;
|
||||
|
||||
/* Cached bool guarded by an early return -> the original elimination case. */
|
||||
bool NOINLINE_ATTR test_fa_guarded_flag(void)
|
||||
{
|
||||
bool valid = fa_test_flag;
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
ESP_FAULT_ASSERT(valid);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Cached status compared to a constant, guarded by an early return. */
|
||||
int NOINLINE_ATTR test_fa_guarded_status(void)
|
||||
{
|
||||
int status = fa_test_status;
|
||||
if (status != 0) {
|
||||
return status;
|
||||
}
|
||||
ESP_FAULT_ASSERT(status == 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/* Reference the test functions so they are linked (not GC'd). */
|
||||
fa_test_sink = (int)test_fa_guarded_flag() + test_fa_guarded_status();
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# -O2
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
@@ -0,0 +1,2 @@
|
||||
# -Os
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
@@ -0,0 +1,4 @@
|
||||
# The clang-built esp32 (Xtensa) bootloader is slightly larger than the GCC one and
|
||||
# overflows the default 0x7000 limit; move the partition table offset to give it room.
|
||||
# Harmless for GCC builds.
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x9000
|
||||
@@ -80,6 +80,8 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void)
|
||||
CLEAR_PERI_REG_MASK(PCR_HMAC_CONF_REG, PCR_HMAC_RST_EN);
|
||||
CLEAR_PERI_REG_MASK(PCR_RSA_CONF_REG, PCR_RSA_RST_EN);
|
||||
CLEAR_PERI_REG_MASK(PCR_SHA_CONF_REG, PCR_SHA_RST_EN);
|
||||
CLEAR_PERI_REG_MASK(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD);
|
||||
|
||||
CLEAR_PERI_REG_MASK(PCR_REGDMA_CONF_REG, PCR_REGDMA_RST_EN);
|
||||
|
||||
// UART's sclk is controlled in the PCR register and does not reset with the UART module. The ROM missed enabling
|
||||
|
||||
@@ -79,6 +79,7 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void)
|
||||
CLEAR_PERI_REG_MASK(PCR_HMAC_CONF_REG, PCR_HMAC_RST_EN);
|
||||
CLEAR_PERI_REG_MASK(PCR_RSA_CONF_REG, PCR_RSA_RST_EN);
|
||||
CLEAR_PERI_REG_MASK(PCR_SHA_CONF_REG, PCR_SHA_RST_EN);
|
||||
CLEAR_PERI_REG_MASK(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD);
|
||||
|
||||
// UART's sclk is controlled in the PCR register and does not reset with the UART module. The ROM missed enabling
|
||||
// it when initializing the ROM UART. If it is not turned on, it will trigger LP_WDT in the ROM.
|
||||
|
||||
@@ -122,6 +122,7 @@ void IRAM_ATTR esp_system_reset_modules_on_exit(void)
|
||||
CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN2_REG, HP_SYS_CLKRST_REG_RST_EN_KM);
|
||||
CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN2_REG, HP_SYS_CLKRST_REG_RST_EN_RSA);
|
||||
CLEAR_PERI_REG_MASK(HP_SYS_CLKRST_HP_RST_EN2_REG, HP_SYS_CLKRST_REG_RST_EN_SHA);
|
||||
CLEAR_PERI_REG_MASK(HP_SYSTEM_ECC_PD_CTRL_REG, HP_SYSTEM_ECC_MEM_FORCE_PD);
|
||||
|
||||
#if CONFIG_ESP32P4_REV_MIN_FULL <= 100
|
||||
// enable soc clk and reset parent crypto
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "hal/efuse_hal.h"
|
||||
|
||||
#if CONFIG_HAL_ECDSA_GEN_SIG_CM
|
||||
#include "esp_fault.h"
|
||||
#include "esp_fault_internal.h"
|
||||
#include "esp_random.h"
|
||||
#include "soc/chip_revision.h"
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "soc/ecc_mult_reg.h"
|
||||
#include "soc/pcr_struct.h"
|
||||
#include "soc/pcr_reg.h"
|
||||
#include "esp_fault_internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -46,6 +47,7 @@ static inline void ecc_ll_power_up(void)
|
||||
{
|
||||
REG_CLR_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_PD);
|
||||
REG_CLR_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD);
|
||||
ESP_FAULT_ASSERT(REG_GET_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD) == 0);
|
||||
}
|
||||
|
||||
static inline void ecc_ll_power_down(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "soc/ecc_mult_reg.h"
|
||||
#include "soc/pcr_struct.h"
|
||||
#include "soc/pcr_reg.h"
|
||||
#include "esp_fault_internal.h"
|
||||
#include "soc/chip_revision.h"
|
||||
#include "hal/efuse_hal.h"
|
||||
|
||||
@@ -54,6 +55,12 @@ static inline void ecc_ll_power_up(void)
|
||||
{
|
||||
REG_CLR_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_PD);
|
||||
REG_CLR_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD);
|
||||
ESP_FAULT_ASSERT(REG_GET_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD) == 0);
|
||||
}
|
||||
|
||||
static inline bool ecc_ll_mem_force_pd_is_clear(void)
|
||||
{
|
||||
return REG_GET_BIT(PCR_ECC_PD_CTRL_REG, PCR_ECC_MEM_FORCE_PD) == 0;
|
||||
}
|
||||
|
||||
static inline void ecc_ll_power_down(void)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "hal/ecc_types.h"
|
||||
#include "soc/ecc_mult_reg.h"
|
||||
#include "soc/hp_sys_clkrst_struct.h"
|
||||
#include "soc/hp_system_reg.h"
|
||||
#include "esp_fault_internal.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -56,8 +58,25 @@ static inline void ecc_ll_reset_register(void)
|
||||
/// the critical section needs to declare the __DECLARE_RCC_ATOMIC_ENV variable in advance
|
||||
#define ecc_ll_reset_register(...) (void)__DECLARE_RCC_ATOMIC_ENV; ecc_ll_reset_register(__VA_ARGS__)
|
||||
|
||||
static inline void ecc_ll_power_up(void) {}
|
||||
static inline void ecc_ll_power_down(void) {}
|
||||
static inline void ecc_ll_power_up(void)
|
||||
{
|
||||
/* Power up the ECC peripheral (default state is power-up) */
|
||||
REG_CLR_BIT(HP_SYSTEM_ECC_PD_CTRL_REG, HP_SYSTEM_ECC_MEM_PD);
|
||||
REG_CLR_BIT(HP_SYSTEM_ECC_PD_CTRL_REG, HP_SYSTEM_ECC_MEM_FORCE_PD);
|
||||
ESP_FAULT_ASSERT(REG_GET_BIT(HP_SYSTEM_ECC_PD_CTRL_REG, HP_SYSTEM_ECC_MEM_FORCE_PD) == 0);
|
||||
}
|
||||
|
||||
static inline bool ecc_ll_mem_force_pd_is_clear(void)
|
||||
{
|
||||
return REG_GET_BIT(HP_SYSTEM_ECC_PD_CTRL_REG, HP_SYSTEM_ECC_MEM_FORCE_PD) == 0;
|
||||
}
|
||||
|
||||
static inline void ecc_ll_power_down(void)
|
||||
{
|
||||
/* Power down the ECC peripheral */
|
||||
REG_CLR_BIT(HP_SYSTEM_ECC_PD_CTRL_REG, HP_SYSTEM_ECC_MEM_FORCE_PU);
|
||||
REG_SET_BIT(HP_SYSTEM_ECC_PD_CTRL_REG, HP_SYSTEM_ECC_MEM_PD);
|
||||
}
|
||||
|
||||
static inline void ecc_ll_enable_interrupt(void)
|
||||
{
|
||||
|
||||
@@ -140,7 +140,7 @@ void test_ecdsa_key_aes_mode(test_data_aes_mode_t *ecdsa_test_data, ecdsa_sign_t
|
||||
|
||||
extern void test_ecdsa_sign(bool is_p256, uint8_t* sha, uint8_t* r_le, uint8_t* s_le, bool use_km_key, ecdsa_sign_type_t k_type);
|
||||
|
||||
extern int test_ecdsa_verify(bool is_p256, uint8_t* sha, uint8_t* r_le, uint8_t* s_le, uint8_t *pub_x, uint8_t *pub_y);
|
||||
extern int test_ecdsa_verify(bool is_p256, uint8_t* sha, uint8_t* r_le, uint8_t* s_le, uint8_t *pub_x, uint8_t *pub_y, int expected_ret);
|
||||
|
||||
void key_mgr_test_ecdsa_key(bool is_p256, ecdsa_sign_type_t k_type)
|
||||
{
|
||||
@@ -161,7 +161,7 @@ void key_mgr_test_ecdsa_key(bool is_p256, ecdsa_sign_type_t k_type)
|
||||
|
||||
print_data_in_hex(pub_x, pubkey_len, "ECDSA key pubx");
|
||||
print_data_in_hex(pub_y, pubkey_len, "ECDSA key puby");
|
||||
TEST_ASSERT_EQUAL(0, test_ecdsa_verify(is_p256, sha256_digest, r_le, s_le, pub_x, pub_y));
|
||||
TEST_ASSERT_EQUAL(0, test_ecdsa_verify(is_p256, sha256_digest, r_le, s_le, pub_x, pub_y, 0));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -10,6 +10,15 @@
|
||||
#ifdef SOC_MPI_SUPPORTED
|
||||
#include "hal/mpi_ll.h"
|
||||
#endif
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_fault.h"
|
||||
|
||||
#include "hal/ecdsa_types.h"
|
||||
#include "ecdsa/ecdsa_alt.h"
|
||||
#include "soc/soc_caps.h"
|
||||
|
||||
#include "esp_crypto_lock.h"
|
||||
#include "esp_efuse.h"
|
||||
#include "esp_private/esp_crypto_lock_internal.h"
|
||||
@@ -681,11 +690,22 @@ static int esp_ecdsa_verify(mbedtls_ecp_group *grp,
|
||||
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
|
||||
}
|
||||
|
||||
if (mbedtls_mpi_cmp_int(r, 1) < 0 || mbedtls_mpi_cmp_mpi(r, &grp->N) >= 0 ||
|
||||
mbedtls_mpi_cmp_int(s, 1) < 0 || mbedtls_mpi_cmp_mpi(s, &grp->N) >= 0 )
|
||||
{
|
||||
/* 1 <= scalar <= n-1: that is, scalar > 0 and scalar < n. */
|
||||
#define RANGE_OK 0x6A6A6A6AU
|
||||
#define RANGE_FAIL 0x95959595U
|
||||
volatile uint32_t verdict = RANGE_FAIL;
|
||||
if (mbedtls_mpi_cmp_int(r, 0) > 0 &&
|
||||
mbedtls_mpi_cmp_mpi(r, &grp->N) < 0 &&
|
||||
mbedtls_mpi_cmp_int(s, 0) > 0 &&
|
||||
mbedtls_mpi_cmp_mpi(s, &grp->N) < 0) {
|
||||
verdict = RANGE_OK;
|
||||
}
|
||||
if (verdict != RANGE_OK) {
|
||||
return MBEDTLS_ERR_ECP_VERIFY_FAILED;
|
||||
}
|
||||
ESP_FAULT_ASSERT(verdict == RANGE_OK);
|
||||
#undef RANGE_OK
|
||||
#undef RANGE_FAIL
|
||||
|
||||
ecdsa_be_to_le(buf, sha_le, len);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* mbedTLS Elliptic Curve Digital Signature performance tests
|
||||
*
|
||||
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -15,6 +15,8 @@
|
||||
#include <mbedtls/ecdh.h>
|
||||
#include <mbedtls/ecdsa.h>
|
||||
#include <mbedtls/error.h>
|
||||
#include <mbedtls/ecp.h>
|
||||
#include <mbedtls/bignum.h>
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "test_utils.h"
|
||||
@@ -108,8 +110,18 @@ const uint8_t ecdsa192_pub_y[] = {
|
||||
0x7e, 0x4b, 0x23, 0xad, 0x46, 0x5c, 0x87, 0xc2
|
||||
};
|
||||
|
||||
/* Curve order N in big-endian, taken from mbedtls instead of a hard-coded table. */
|
||||
static void ecdsa_get_curve_order_be(mbedtls_ecp_group_id id, uint8_t *n_be, size_t len)
|
||||
{
|
||||
mbedtls_ecp_group grp;
|
||||
mbedtls_ecp_group_init(&grp);
|
||||
TEST_ASSERT_EQUAL(0, mbedtls_ecp_group_load(&grp, id));
|
||||
TEST_ASSERT_EQUAL(0, mbedtls_mpi_write_binary(&grp.N, n_be, len));
|
||||
mbedtls_ecp_group_free(&grp);
|
||||
}
|
||||
|
||||
void test_ecdsa_verify(mbedtls_ecp_group_id id, const uint8_t *hash, const uint8_t *r_comp, const uint8_t *s_comp,
|
||||
const uint8_t *pub_x, const uint8_t *pub_y)
|
||||
const uint8_t *pub_x, const uint8_t *pub_y, int expected_ret)
|
||||
{
|
||||
int64_t elapsed_time;
|
||||
mbedtls_mpi r, s;
|
||||
@@ -132,7 +144,8 @@ void test_ecdsa_verify(mbedtls_ecp_group_id id, const uint8_t *hash, const uint8
|
||||
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_lset(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 1));
|
||||
|
||||
ccomp_timer_start();
|
||||
TEST_ASSERT_MBEDTLS_OK(mbedtls_ecdsa_verify(&ecdsa_context.MBEDTLS_PRIVATE(grp), hash, 32, &ecdsa_context.MBEDTLS_PRIVATE(Q), &r, &s));
|
||||
int actual_ret = mbedtls_ecdsa_verify(&ecdsa_context.MBEDTLS_PRIVATE(grp), hash, 32, &ecdsa_context.MBEDTLS_PRIVATE(Q), &r, &s);
|
||||
TEST_ASSERT_EQUAL(expected_ret, actual_ret);
|
||||
elapsed_time = ccomp_timer_stop();
|
||||
|
||||
if (id == MBEDTLS_ECP_DP_SECP192R1) {
|
||||
@@ -149,15 +162,29 @@ void test_ecdsa_verify(mbedtls_ecp_group_id id, const uint8_t *hash, const uint8
|
||||
TEST_CASE("mbedtls ECDSA signature verification performance on SECP192R1", "[mbedtls]")
|
||||
{
|
||||
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP192R1, sha, ecdsa192_r, ecdsa192_s,
|
||||
ecdsa192_pub_x, ecdsa192_pub_y);
|
||||
ecdsa192_pub_x, ecdsa192_pub_y, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("mbedtls ECDSA signature verification performance on SECP256R1", "[mbedtls]")
|
||||
{
|
||||
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP256R1, sha, ecdsa256_r, ecdsa256_s,
|
||||
ecdsa256_pub_x, ecdsa256_pub_y);
|
||||
ecdsa256_pub_x, ecdsa256_pub_y, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("mbedtls ECDSA signature verification rejects out-of-range r, s on SECP256R1", "[mbedtls]")
|
||||
{
|
||||
static const uint8_t zero32[32] = { 0 };
|
||||
uint8_t p256_n_be[32];
|
||||
ecdsa_get_curve_order_be(MBEDTLS_ECP_DP_SECP256R1, p256_n_be, sizeof(p256_n_be));
|
||||
|
||||
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP256R1, sha, zero32, zero32, ecdsa256_pub_x, ecdsa256_pub_y, MBEDTLS_ERR_ECP_VERIFY_FAILED); /* r=0, s=0 */
|
||||
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP256R1, sha, zero32, p256_n_be, ecdsa256_pub_x, ecdsa256_pub_y, MBEDTLS_ERR_ECP_VERIFY_FAILED); /* r=0, s=N */
|
||||
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP256R1, sha, p256_n_be, zero32, ecdsa256_pub_x, ecdsa256_pub_y, MBEDTLS_ERR_ECP_VERIFY_FAILED); /* r=N, s=0 */
|
||||
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP256R1, sha, p256_n_be, p256_n_be, ecdsa256_pub_x, ecdsa256_pub_y, MBEDTLS_ERR_ECP_VERIFY_FAILED); /* r=N, s=N */
|
||||
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP256R1, sha, ecdsa256_r, zero32, ecdsa256_pub_x, ecdsa256_pub_y, MBEDTLS_ERR_ECP_VERIFY_FAILED); /* r=valid, s=0 */
|
||||
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP256R1, sha, ecdsa256_r, p256_n_be, ecdsa256_pub_x, ecdsa256_pub_y, MBEDTLS_ERR_ECP_VERIFY_FAILED); /* r=valid, s=N */
|
||||
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP256R1, sha, p256_n_be, ecdsa256_s, ecdsa256_pub_x, ecdsa256_pub_y, MBEDTLS_ERR_ECP_VERIFY_FAILED); /* r=N, s=valid */
|
||||
}
|
||||
#endif /* CONFIG_MBEDTLS_HARDWARE_ECC */
|
||||
|
||||
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
|
||||
@@ -250,9 +277,9 @@ void test_ecdsa_sign(mbedtls_ecp_group_id id, const uint8_t *hash, const uint8_t
|
||||
|
||||
if (id == MBEDTLS_ECP_DP_SECP192R1) {
|
||||
// Skip the initial zeroes
|
||||
test_ecdsa_verify(id, sha, &r_be[8], &s_be[8], pub_x, pub_y);
|
||||
test_ecdsa_verify(id, sha, &r_be[8], &s_be[8], pub_x, pub_y, 0);
|
||||
} else if (id == MBEDTLS_ECP_DP_SECP256R1) {
|
||||
test_ecdsa_verify(id, sha, r_be, s_be, pub_x, pub_y);
|
||||
test_ecdsa_verify(id, sha, r_be, s_be, pub_x, pub_y, 0);
|
||||
}
|
||||
|
||||
mbedtls_mpi_free(&r);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
factory, 0, 0, 0x10000, 1M
|
||||
nvs, data, nvs, , 0x6000,
|
||||
factory, 0, 0, , 1M
|
||||
flash_test, data, fat, , 528K
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0X9000
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_SECURE_FLASH_ENC_ENABLED=y
|
||||
|
||||
Reference in New Issue
Block a user