Merge branch 'fix/add_countermeasure_for_ecdsa_peripheral_issue_v5.1' into 'release/v5.1'

feat(hal): Add countermeasure for ECDSA generate signature (v5.1)

See merge request espressif/esp-idf!33748
This commit is contained in:
Mahavir Jain 2024-09-24 12:13:29 +08:00
commit 8b2abcc839
6 changed files with 160 additions and 17 deletions

View File

@ -103,4 +103,13 @@ menu "Hardware Abstraction Layer (HAL) and Low Level (LL)"
help
Enable this option to place SPI slave hal layer functions into IRAM.
config HAL_ECDSA_GEN_SIG_CM
bool "Enable countermeasure for ECDSA signature generation"
default n
# ToDo - IDF-11051
help
Enable this option to apply the countermeasure for ECDSA signature operation
This countermeasure masks the real ECDSA sign operation
under dummy sign operations to add randomness in the generated power signature.
endmenu

View File

@ -9,6 +9,11 @@
#include "hal/ecdsa_hal.h"
#include "hal/efuse_hal.h"
#if CONFIG_HAL_ECDSA_GEN_SIG_CM
#include "esp_fault.h"
#include "esp_random.h"
#endif
#define ECDSA_HAL_P192_COMPONENT_LEN 24
#define ECDSA_HAL_P256_COMPONENT_LEN 32
@ -26,23 +31,9 @@ bool ecdsa_hal_get_operation_result(void)
return ecdsa_ll_get_operation_result();
}
void ecdsa_hal_gen_signature(ecdsa_hal_config_t *conf, const uint8_t *hash,
uint8_t *r_out, uint8_t *s_out, uint16_t len)
static void ecdsa_hal_gen_signature_inner(const uint8_t *hash, uint8_t *r_out,
uint8_t *s_out, uint16_t len)
{
if (len != ECDSA_HAL_P192_COMPONENT_LEN && len != ECDSA_HAL_P256_COMPONENT_LEN) {
HAL_ASSERT(false && "Incorrect length");
}
if (conf->sha_mode == ECDSA_Z_USER_PROVIDED && hash == NULL) {
HAL_ASSERT(false && "Mismatch in SHA configuration");
}
if (ecdsa_ll_get_state() != ECDSA_STATE_IDLE) {
HAL_ASSERT(false && "Incorrect ECDSA state");
}
configure_ecdsa_periph(conf);
ecdsa_ll_set_stage(ECDSA_STAGE_START_CALC);
while(ecdsa_ll_get_state() != ECDSA_STATE_LOAD) {
@ -67,6 +58,63 @@ void ecdsa_hal_gen_signature(ecdsa_hal_config_t *conf, const uint8_t *hash,
}
}
#if CONFIG_HAL_ECDSA_GEN_SIG_CM
__attribute__((optimize("O0"))) static void ecdsa_hal_gen_signature_with_countermeasure(const uint8_t *hash, uint8_t *r_out,
uint8_t *s_out, uint16_t len)
{
uint8_t tmp_r_out[32] = {};
uint8_t tmp_s_out[32] = {};
uint8_t tmp_hash[64] = {};
uint8_t dummy_op_count_prior = esp_random() % ECDSA_SIGN_MAX_DUMMY_OP_COUNT;
uint8_t dummy_op_count_later = ECDSA_SIGN_MAX_DUMMY_OP_COUNT - dummy_op_count_prior;
ESP_FAULT_ASSERT((dummy_op_count_prior != 0) || (dummy_op_count_later != 0));
ESP_FAULT_ASSERT(dummy_op_count_prior + dummy_op_count_later == ECDSA_SIGN_MAX_DUMMY_OP_COUNT);
esp_fill_random(tmp_hash, 64);
/* Dummy ecdsa signature operations prior to the actual one */
for (int i = 0; i < dummy_op_count_prior; i++) {
ecdsa_hal_gen_signature_inner(tmp_hash + ((6 * i) % 32), (uint8_t *) tmp_r_out, (uint8_t *) tmp_s_out, len);
}
/* Actual ecdsa signature operation */
ecdsa_hal_gen_signature_inner(hash, r_out, s_out, len);
/* Dummy ecdsa signature operations after the actual one */
for (int i = 0; i < dummy_op_count_later; i++) {
ecdsa_hal_gen_signature_inner(tmp_hash + ((6 * i) % 32), (uint8_t *)tmp_r_out, (uint8_t *)tmp_s_out, len);
}
}
#endif /* CONFIG_HAL_ECDSA_GEN_SIG_CM */
void ecdsa_hal_gen_signature(ecdsa_hal_config_t *conf, const uint8_t *hash,
uint8_t *r_out, uint8_t *s_out, uint16_t len)
{
if (len != ECDSA_HAL_P192_COMPONENT_LEN && len != ECDSA_HAL_P256_COMPONENT_LEN) {
HAL_ASSERT(false && "Incorrect length");
}
if (conf->sha_mode == ECDSA_Z_USER_PROVIDED && hash == NULL) {
HAL_ASSERT(false && "Mismatch in SHA configuration");
}
if (ecdsa_ll_get_state() != ECDSA_STATE_IDLE) {
HAL_ASSERT(false && "Incorrect ECDSA state");
}
configure_ecdsa_periph(conf);
#if CONFIG_HAL_ECDSA_GEN_SIG_CM
ecdsa_hal_gen_signature_with_countermeasure(hash, r_out, s_out, len);
#else /* CONFIG_HAL_ECDSA_GEN_SIG_CM */
ecdsa_hal_gen_signature_inner(hash, r_out, s_out, len);
#endif /* !CONFIG_HAL_ECDSA_GEN_SIG_CM */
}
int ecdsa_hal_verify_signature(ecdsa_hal_config_t *conf, const uint8_t *hash, const uint8_t *r, const uint8_t *s,
const uint8_t *pub_x, const uint8_t *pub_y, uint16_t len)
{

View File

@ -15,11 +15,23 @@
#include <stdbool.h>
#include <stdint.h>
#include "hal/ecdsa_types.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#if CONFIG_HAL_ECDSA_GEN_SIG_CM
#define ECDSA_SIGN_MAX_DUMMY_OP_COUNT 0x7
/* This value defines the maximum dummy operation count for the ECDSA signature countermeasure.
Higher the number, better the countermeasure's effectiveness against attacks.
At the same time higher number leads to slower performance.
After the countermeasure is enabled, hardware ECDSA signature operation
shall take time approximately equal to original time multiplied by this number.
If you observe that the reduced performance is affecting your use-case then you may try reducing this time to the minimum. */
#endif /* CONFIG_HAL_ECDSA_GEN_SIG_CM */
/*
* ECDSA peripheral config structure
*/

View File

@ -317,6 +317,19 @@ if(CONFIG_ESP_TLS_USE_DS_PERIPHERAL)
set_property(TARGET mbedcrypto APPEND PROPERTY LINK_INTERFACE_MULTIPLICITY 6)
endif()
# Additional optional dependencies for the mbedcrypto library
function(mbedcrypto_optional_deps component_name)
idf_build_get_property(components BUILD_COMPONENTS)
if(${component_name} IN_LIST components)
idf_component_get_property(lib_name ${component_name} COMPONENT_LIB)
target_link_libraries(mbedcrypto PRIVATE ${lib_name})
endif()
endfunction()
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM)
mbedcrypto_optional_deps(esp_timer idf::esp_timer)
endif()
# Link esp-cryptoauthlib to mbedtls
if(CONFIG_ATCA_MBEDTLS_ECDSA)
idf_component_optional_requires(PRIVATE espressif__esp-cryptoauthlib esp-cryptoauthlib)

View File

@ -511,6 +511,37 @@ menu "mbedTLS"
The key should be burnt in little endian format. espefuse.py utility handles it internally
but care needs to be taken while burning using esp_efuse APIs
menu "Enable Software Countermeasure for ECDSA signing using on-chip ECDSA peripheral"
depends on MBEDTLS_HARDWARE_ECDSA_SIGN
depends on IDF_TARGET_ESP32H2
config MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM
bool "Mask original ECDSA sign operation under dummy sign operations"
select HAL_ECDSA_GEN_SIG_CM
# ToDo: IDF-11051
default y
help
The ECDSA peripheral before ECO5 does not offer constant time ECDSA sign operation.
This time can be observed through power profiling of the device,
making the ECDSA private key vulnerable to side-channel timing attacks.
This countermeasure masks the real ECDSA sign operation
under dummy sign operations to add randomness in the generated power signature.
It is highly recommended to also enable Secure Boot for the device in addition to this countermeasure
so that only trusted software can execute on the device.
config MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
bool "Make ECDSA signature operation pseudo constant time for software"
default y
help
This option adds a delay after the actual ECDSA signature operation
so that the entire operation appears to be constant time for the software.
This fix helps in protecting the device only in case of remote timing attack on the ECDSA private key.
For e.g., When an interface is exposed by the device to perform ECDSA signature
of an arbitrary message.
The signature time would appear to be constant to the external entity after enabling
this option.
endmenu
config MBEDTLS_HARDWARE_ECDSA_VERIFY
bool "Enable ECDSA signature verification using on-chip ECDSA peripheral"
default y

View File

@ -20,6 +20,28 @@
#define ECDSA_SHA_LEN 32
#define MAX_ECDSA_COMPONENT_LEN 32
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
#include "esp_timer.h"
#if CONFIG_ESP_CRYPTO_DPA_PROTECTION_LEVEL_HIGH
/*
* This is the maximum time (in us) required for performing 1 ECDSA signature
* in this configuration along some additional margin considerations
*/
#define ECDSA_MAX_SIG_TIME 24000
#else /* CONFIG_ESP_CRYPTO_DPA_PROTECTION_LEVEL_HIGH */
#define ECDSA_MAX_SIG_TIME 17500
#endif /* !CONFIG_ESP_CRYPTO_DPA_PROTECTION_LEVEL_HIGH */
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM
#define DUMMY_OP_COUNT ECDSA_SIGN_MAX_DUMMY_OP_COUNT
#else /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM */
#define DUMMY_OP_COUNT 0
#endif /* !CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM */
#define ECDSA_CM_FIXED_SIG_TIME ECDSA_MAX_SIG_TIME * (DUMMY_OP_COUNT + 1)
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM */
__attribute__((unused)) static const char *TAG = "ecdsa_alt";
static void esp_ecdsa_acquire_hardware(void)
@ -148,8 +170,16 @@ static int esp_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi* r, mbedtls_mpi* s
.efuse_key_blk = d->MBEDTLS_PRIVATE(n),
};
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
uint64_t sig_time = esp_timer_get_time();
#endif
ecdsa_hal_gen_signature(&conf, sha_le, r_le, s_le, len);
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
sig_time = esp_timer_get_time() - sig_time;
if (sig_time < ECDSA_CM_FIXED_SIG_TIME) {
esp_rom_delay_us(ECDSA_CM_FIXED_SIG_TIME - sig_time);
}
#endif
process_again = !ecdsa_hal_get_operation_result()
|| !memcmp(r_le, zeroes, len)
|| !memcmp(s_le, zeroes, len);