Skip to content

Commit

Permalink
Merge branch 'fix/add_countermeasure_for_ecdsa_peripheral_issue_v5.3'…
Browse files Browse the repository at this point in the history
… into 'release/v5.3'

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

See merge request espressif/esp-idf!33745
  • Loading branch information
AdityaHPatwardhan committed Oct 1, 2024
2 parents 98440e1 + 44ce512 commit 4f29e3f
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 17 deletions.
9 changes: 9 additions & 0 deletions components/hal/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
80 changes: 64 additions & 16 deletions components/hal/ecdsa_hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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

#if CONFIG_IDF_TARGET_ESP32C5
#include "soc/keymng_reg.h"
#endif
Expand Down Expand Up @@ -62,23 +67,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) {
Expand All @@ -103,6 +94,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)
{
Expand Down
12 changes: 12 additions & 0 deletions components/hal/include/hal/ecdsa_hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,23 @@
#include <stdint.h>
#include "hal/ecdsa_types.h"
#include "soc/soc_caps.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
*/
Expand Down
13 changes: 13 additions & 0 deletions components/mbedtls/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,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)
Expand Down
31 changes: 31 additions & 0 deletions components/mbedtls/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,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
Expand Down
32 changes: 31 additions & 1 deletion components/mbedtls/port/ecdsa/ecdsa_alt.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,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)
Expand Down Expand Up @@ -333,8 +355,16 @@ static int esp_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi* r, mbedtls_mpi* s
conf.use_km_key = 0;
conf.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);
Expand Down

0 comments on commit 4f29e3f

Please sign in to comment.