Skip to content

Commit 5bfa1fb

Browse files
Merge branch 'fix/add_countermeasure_for_ecdsa_peripheral_issue' into 'master'
feat(hal): Add countermeasure for ECDSA generate signature Closes IDF-10001 See merge request espressif/esp-idf!31894
2 parents 4530b0e + 11128b7 commit 5bfa1fb

File tree

6 files changed

+152
-18
lines changed

6 files changed

+152
-18
lines changed

components/hal/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,13 @@ menu "Hardware Abstraction Layer (HAL) and Low Level (LL)"
103103
help
104104
Enable this option to place SPI slave hal layer functions into IRAM.
105105

106+
config HAL_ECDSA_GEN_SIG_CM
107+
bool "Enable countermeasure for ECDSA signature generation"
108+
default n
109+
# ToDo - IDF-11051
110+
help
111+
Enable this option to apply the countermeasure for ECDSA signature operation
112+
This countermeasure masks the real ECDSA sign operation
113+
under dummy sign operations to add randomness in the generated power signature.
114+
106115
endmenu

components/hal/ecdsa_hal.c

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
#include "hal/ecdsa_hal.h"
1010
#include "hal/efuse_hal.h"
1111

12+
#if CONFIG_HAL_ECDSA_GEN_SIG_CM
13+
#include "esp_fault.h"
14+
#include "esp_random.h"
15+
#endif
16+
1217
// Need to remove in IDF-8621
1318
#if CONFIG_IDF_TARGET_ESP32C5
1419
#include "soc/keymng_reg.h"
@@ -64,23 +69,9 @@ bool ecdsa_hal_get_operation_result(void)
6469
return ecdsa_ll_get_operation_result();
6570
}
6671

67-
void ecdsa_hal_gen_signature(ecdsa_hal_config_t *conf, const uint8_t *hash,
68-
uint8_t *r_out, uint8_t *s_out, uint16_t len)
72+
static void ecdsa_hal_gen_signature_inner(const uint8_t *hash, uint8_t *r_out,
73+
uint8_t *s_out, uint16_t len)
6974
{
70-
if (len != ECDSA_HAL_P192_COMPONENT_LEN && len != ECDSA_HAL_P256_COMPONENT_LEN) {
71-
HAL_ASSERT(false && "Incorrect length");
72-
}
73-
74-
if (conf->sha_mode == ECDSA_Z_USER_PROVIDED && hash == NULL) {
75-
HAL_ASSERT(false && "Mismatch in SHA configuration");
76-
}
77-
78-
if (ecdsa_ll_get_state() != ECDSA_STATE_IDLE) {
79-
HAL_ASSERT(false && "Incorrect ECDSA state");
80-
}
81-
82-
configure_ecdsa_periph(conf);
83-
8475
ecdsa_ll_set_stage(ECDSA_STAGE_START_CALC);
8576

8677
while(ecdsa_ll_get_state() != ECDSA_STATE_LOAD) {
@@ -105,6 +96,63 @@ void ecdsa_hal_gen_signature(ecdsa_hal_config_t *conf, const uint8_t *hash,
10596
}
10697
}
10798

99+
#if CONFIG_HAL_ECDSA_GEN_SIG_CM
100+
__attribute__((optimize("O0"))) static void ecdsa_hal_gen_signature_with_countermeasure(const uint8_t *hash, uint8_t *r_out,
101+
uint8_t *s_out, uint16_t len)
102+
{
103+
uint8_t tmp_r_out[32] = {};
104+
uint8_t tmp_s_out[32] = {};
105+
uint8_t tmp_hash[64] = {};
106+
107+
uint8_t dummy_op_count_prior = esp_random() % ECDSA_SIGN_MAX_DUMMY_OP_COUNT;
108+
uint8_t dummy_op_count_later = ECDSA_SIGN_MAX_DUMMY_OP_COUNT - dummy_op_count_prior;
109+
ESP_FAULT_ASSERT((dummy_op_count_prior != 0) || (dummy_op_count_later != 0));
110+
ESP_FAULT_ASSERT(dummy_op_count_prior + dummy_op_count_later == ECDSA_SIGN_MAX_DUMMY_OP_COUNT);
111+
112+
esp_fill_random(tmp_hash, 64);
113+
/* Dummy ecdsa signature operations prior to the actual one */
114+
for (int i = 0; i < dummy_op_count_prior; i++) {
115+
ecdsa_hal_gen_signature_inner(tmp_hash + ((6 * i) % 32), (uint8_t *) tmp_r_out, (uint8_t *) tmp_s_out, len);
116+
}
117+
118+
/* Actual ecdsa signature operation */
119+
ecdsa_hal_gen_signature_inner(hash, r_out, s_out, len);
120+
121+
/* Dummy ecdsa signature operations after the actual one */
122+
for (int i = 0; i < dummy_op_count_later; i++) {
123+
ecdsa_hal_gen_signature_inner(tmp_hash + ((6 * i) % 32), (uint8_t *)tmp_r_out, (uint8_t *)tmp_s_out, len);
124+
}
125+
126+
}
127+
#endif /* CONFIG_HAL_ECDSA_GEN_SIG_CM */
128+
129+
130+
131+
void ecdsa_hal_gen_signature(ecdsa_hal_config_t *conf, const uint8_t *hash,
132+
uint8_t *r_out, uint8_t *s_out, uint16_t len)
133+
{
134+
if (len != ECDSA_HAL_P192_COMPONENT_LEN && len != ECDSA_HAL_P256_COMPONENT_LEN) {
135+
HAL_ASSERT(false && "Incorrect length");
136+
}
137+
138+
if (conf->sha_mode == ECDSA_Z_USER_PROVIDED && hash == NULL) {
139+
HAL_ASSERT(false && "Mismatch in SHA configuration");
140+
}
141+
142+
if (ecdsa_ll_get_state() != ECDSA_STATE_IDLE) {
143+
HAL_ASSERT(false && "Incorrect ECDSA state");
144+
}
145+
146+
configure_ecdsa_periph(conf);
147+
148+
#if CONFIG_HAL_ECDSA_GEN_SIG_CM
149+
ecdsa_hal_gen_signature_with_countermeasure(hash, r_out, s_out, len);
150+
#else /* CONFIG_HAL_ECDSA_GEN_SIG_CM */
151+
ecdsa_hal_gen_signature_inner(hash, r_out, s_out, len);
152+
#endif /* !CONFIG_HAL_ECDSA_GEN_SIG_CM */
153+
154+
}
155+
108156
int ecdsa_hal_verify_signature(ecdsa_hal_config_t *conf, const uint8_t *hash, const uint8_t *r, const uint8_t *s,
109157
const uint8_t *pub_x, const uint8_t *pub_y, uint16_t len)
110158
{

components/hal/include/hal/ecdsa_hal.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,23 @@
1616
#include <stdint.h>
1717
#include "hal/ecdsa_types.h"
1818
#include "soc/soc_caps.h"
19+
#include "sdkconfig.h"
1920

2021
#ifdef __cplusplus
2122
extern "C" {
2223
#endif
2324

25+
#if CONFIG_HAL_ECDSA_GEN_SIG_CM
26+
27+
#define ECDSA_SIGN_MAX_DUMMY_OP_COUNT 0x7
28+
29+
/* This value defines the maximum dummy operation count for the ECDSA signature countermeasure.
30+
Higher the number, better the countermeasure's effectiveness against attacks.
31+
At the same time higher number leads to slower performance.
32+
After the countermeasure is enabled, hardware ECDSA signature operation
33+
shall take time approximately equal to original time multiplied by this number.
34+
If you observe that the reduced performance is affecting your use-case then you may try reducing this time to the minimum. */
35+
#endif /* CONFIG_HAL_ECDSA_GEN_SIG_CM */
2436
/*
2537
* ECDSA peripheral config structure
2638
*/

components/mbedtls/CMakeLists.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,11 @@ function(mbedcrypto_optional_deps component_name)
358358
endif()
359359
endfunction()
360360

361-
# Link esp-cryptoauthlib to mbedcrypto
361+
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM)
362+
mbedcrypto_optional_deps(esp_timer idf::esp_timer)
363+
endif()
364+
365+
# Link esp-cryptoauthlib to mbedtls
362366
if(CONFIG_ATCA_MBEDTLS_ECDSA)
363367
mbedcrypto_optional_deps(espressif__esp-cryptoauthlib esp-cryptoauthlib)
364368
endif()

components/mbedtls/Kconfig

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,37 @@ menu "mbedTLS"
548548
The key should be burnt in little endian format. espefuse.py utility handles it internally
549549
but care needs to be taken while burning using esp_efuse APIs
550550

551+
menu "Enable Software Countermeasure for ECDSA signing using on-chip ECDSA peripheral"
552+
depends on MBEDTLS_HARDWARE_ECDSA_SIGN
553+
depends on IDF_TARGET_ESP32H2
554+
config MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM
555+
bool "Mask original ECDSA sign operation under dummy sign operations"
556+
select HAL_ECDSA_GEN_SIG_CM
557+
# ToDo: IDF-11051
558+
default y
559+
help
560+
The ECDSA peripheral before ECO5 does not offer constant time ECDSA sign operation.
561+
This time can be observed through power profiling of the device,
562+
making the ECDSA private key vulnerable to side-channel timing attacks.
563+
This countermeasure masks the real ECDSA sign operation
564+
under dummy sign operations to add randomness in the generated power signature.
565+
It is highly recommended to also enable Secure Boot for the device in addition to this countermeasure
566+
so that only trusted software can execute on the device.
567+
568+
config MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
569+
bool "Make ECDSA signature operation pseudo constant time for software"
570+
default y
571+
help
572+
This option adds a delay after the actual ECDSA signature operation
573+
so that the entire operation appears to be constant time for the software.
574+
This fix helps in protecting the device only in case of remote timing attack on the ECDSA private key.
575+
For e.g., When an interface is exposed by the device to perform ECDSA signature
576+
of an arbitrary message.
577+
The signature time would appear to be constant to the external entity after enabling
578+
this option.
579+
580+
endmenu
581+
551582
config MBEDTLS_HARDWARE_ECDSA_VERIFY
552583
bool "Enable ECDSA signature verification using on-chip ECDSA peripheral"
553584
default y

components/mbedtls/port/ecdsa/ecdsa_alt.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,28 @@
2525
#define ECDSA_SHA_LEN 32
2626
#define MAX_ECDSA_COMPONENT_LEN 32
2727

28+
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
29+
#include "esp_timer.h"
30+
31+
#if CONFIG_ESP_CRYPTO_DPA_PROTECTION_LEVEL_HIGH
32+
/*
33+
* This is the maximum time (in us) required for performing 1 ECDSA signature
34+
* in this configuration along some additional margin considerations
35+
*/
36+
#define ECDSA_MAX_SIG_TIME 24000
37+
#else /* CONFIG_ESP_CRYPTO_DPA_PROTECTION_LEVEL_HIGH */
38+
#define ECDSA_MAX_SIG_TIME 17500
39+
#endif /* !CONFIG_ESP_CRYPTO_DPA_PROTECTION_LEVEL_HIGH */
40+
41+
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM
42+
#define DUMMY_OP_COUNT ECDSA_SIGN_MAX_DUMMY_OP_COUNT
43+
#else /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM */
44+
#define DUMMY_OP_COUNT 0
45+
#endif /* !CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM */
46+
#define ECDSA_CM_FIXED_SIG_TIME ECDSA_MAX_SIG_TIME * (DUMMY_OP_COUNT + 1)
47+
48+
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM */
49+
2850
__attribute__((unused)) static const char *TAG = "ecdsa_alt";
2951

3052
static void esp_ecdsa_acquire_hardware(void)
@@ -335,8 +357,16 @@ static int esp_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi* r, mbedtls_mpi* s
335357
conf.use_km_key = 0;
336358
conf.efuse_key_blk = d->MBEDTLS_PRIVATE(n);
337359
}
360+
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
361+
uint64_t sig_time = esp_timer_get_time();
362+
#endif
338363
ecdsa_hal_gen_signature(&conf, sha_le, r_le, s_le, len);
339-
364+
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
365+
sig_time = esp_timer_get_time() - sig_time;
366+
if (sig_time < ECDSA_CM_FIXED_SIG_TIME) {
367+
esp_rom_delay_us(ECDSA_CM_FIXED_SIG_TIME - sig_time);
368+
}
369+
#endif
340370
process_again = !ecdsa_hal_get_operation_result()
341371
|| !memcmp(r_le, zeroes, len)
342372
|| !memcmp(s_le, zeroes, len);

0 commit comments

Comments
 (0)