Skip to content

Commit 11128b7

Browse files
AdityaHPatwardhanmahavirj
authored andcommitted
feat(hal): Add countermeasure for ECDSA generate signature
The ECDSA peripheral before ECO5 of esp32h2 does not perform the ECDSA sign operation in constant time. This allows an attacker to read the power signature of the ECDSA sign operation and then calculate the ECDSA key stored inside the eFuse. The commit adds a countermeasure for this attack. In this case the real ECDSA sign operation is masked under dummy ECDSA sign operations to hide its real power signature
1 parent 0690e53 commit 11128b7

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)