diff --git a/components/hal/Kconfig b/components/hal/Kconfig index 8c3452ba4e..264fbb6a96 100644 --- a/components/hal/Kconfig +++ b/components/hal/Kconfig @@ -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 diff --git a/components/hal/ecdsa_hal.c b/components/hal/ecdsa_hal.c index ce94685db5..d72c5dfd27 100644 --- a/components/hal/ecdsa_hal.c +++ b/components/hal/ecdsa_hal.c @@ -13,6 +13,11 @@ #include "soc/keymng_reg.h" // TODO: IDF-7901 #endif +#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 @@ -39,23 +44,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) { @@ -80,6 +71,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) { diff --git a/components/hal/include/hal/ecdsa_hal.h b/components/hal/include/hal/ecdsa_hal.h index 087bdab05b..316bc0df0c 100644 --- a/components/hal/include/hal/ecdsa_hal.h +++ b/components/hal/include/hal/ecdsa_hal.h @@ -16,11 +16,23 @@ #include #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 */ diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index fbe75b4d30..8dbbe6a51d 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -306,6 +306,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) diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index f22c7eae72..4e73a0d6d2 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -533,6 +533,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 diff --git a/components/mbedtls/port/ecdsa/ecdsa_alt.c b/components/mbedtls/port/ecdsa/ecdsa_alt.c index 14aec8f5a3..f713ace24e 100644 --- a/components/mbedtls/port/ecdsa/ecdsa_alt.c +++ b/components/mbedtls/port/ecdsa/ecdsa_alt.c @@ -22,6 +22,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) @@ -288,8 +310,16 @@ static int esp_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi* r, mbedtls_mpi* s .use_km_key = 0, //TODO: IDF-7992 }; +#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);