mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
5b6d8812d1
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
202 lines
5.7 KiB
C
202 lines
5.7 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "hal/assert.h"
|
|
#include "hal/ecdsa_ll.h"
|
|
#include "hal/ecdsa_hal.h"
|
|
#include "hal/efuse_hal.h"
|
|
|
|
#ifdef SOC_KEY_MANAGER_SUPPORTED
|
|
#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
|
|
|
|
static void configure_ecdsa_periph(ecdsa_hal_config_t *conf)
|
|
{
|
|
#ifdef SOC_KEY_MANAGER_SUPPORTED
|
|
REG_SET_FIELD(KEYMNG_STATIC_REG, KEYMNG_USE_EFUSE_KEY, 1); // TODO: IDF-7901
|
|
#endif
|
|
|
|
if (conf->use_km_key == 0) {
|
|
efuse_hal_set_ecdsa_key(conf->efuse_key_blk);
|
|
}
|
|
|
|
ecdsa_ll_set_mode(conf->mode);
|
|
ecdsa_ll_set_curve(conf->curve);
|
|
|
|
if (conf->mode != ECDSA_MODE_EXPORT_PUBKEY) {
|
|
ecdsa_ll_set_z_mode(conf->sha_mode);
|
|
}
|
|
}
|
|
|
|
bool ecdsa_hal_get_operation_result(void)
|
|
{
|
|
return ecdsa_ll_get_operation_result();
|
|
}
|
|
|
|
static void ecdsa_hal_gen_signature_inner(const uint8_t *hash, uint8_t *r_out,
|
|
uint8_t *s_out, uint16_t len)
|
|
{
|
|
ecdsa_ll_set_stage(ECDSA_STAGE_START_CALC);
|
|
|
|
while(ecdsa_ll_get_state() != ECDSA_STATE_LOAD) {
|
|
;
|
|
}
|
|
|
|
ecdsa_ll_write_param(ECDSA_PARAM_Z, hash, len);
|
|
|
|
ecdsa_ll_set_stage(ECDSA_STAGE_LOAD_DONE);
|
|
|
|
while (ecdsa_ll_get_state() != ECDSA_STATE_GET) {
|
|
;
|
|
}
|
|
|
|
ecdsa_ll_read_param(ECDSA_PARAM_R, r_out, len);
|
|
ecdsa_ll_read_param(ECDSA_PARAM_S, s_out, len);
|
|
|
|
ecdsa_ll_set_stage(ECDSA_STAGE_GET_DONE);
|
|
|
|
while (ecdsa_ll_get_state() != ECDSA_STATE_IDLE) {
|
|
;
|
|
}
|
|
}
|
|
|
|
#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)
|
|
{
|
|
if (len != ECDSA_HAL_P192_COMPONENT_LEN && len != ECDSA_HAL_P256_COMPONENT_LEN) {
|
|
HAL_ASSERT(false && "Incorrect length");
|
|
}
|
|
|
|
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) {
|
|
;
|
|
}
|
|
|
|
ecdsa_ll_write_param(ECDSA_PARAM_Z, hash, len);
|
|
ecdsa_ll_write_param(ECDSA_PARAM_R, r, len);
|
|
ecdsa_ll_write_param(ECDSA_PARAM_S, s, len);
|
|
ecdsa_ll_write_param(ECDSA_PARAM_QAX, pub_x, len);
|
|
ecdsa_ll_write_param(ECDSA_PARAM_QAY, pub_y, len);
|
|
|
|
ecdsa_ll_set_stage(ECDSA_STAGE_LOAD_DONE);
|
|
|
|
while (ecdsa_ll_get_state() != ECDSA_STATE_IDLE) {
|
|
;
|
|
}
|
|
|
|
bool res = ecdsa_hal_get_operation_result();
|
|
|
|
return (res ? 0 : -1);
|
|
}
|
|
|
|
#ifdef SOC_ECDSA_SUPPORT_EXPORT_PUBKEY
|
|
void ecdsa_hal_export_pubkey(ecdsa_hal_config_t *conf, uint8_t *pub_x, uint8_t *pub_y, uint16_t len)
|
|
{
|
|
if (len != ECDSA_HAL_P192_COMPONENT_LEN && len != ECDSA_HAL_P256_COMPONENT_LEN) {
|
|
HAL_ASSERT(false && "Incorrect length");
|
|
}
|
|
|
|
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) {
|
|
;
|
|
}
|
|
|
|
ecdsa_ll_set_stage(ECDSA_STAGE_LOAD_DONE);
|
|
|
|
while (ecdsa_ll_get_state() != ECDSA_STATE_GET) {
|
|
;
|
|
}
|
|
|
|
ecdsa_ll_read_param(ECDSA_PARAM_QAX, pub_x, len);
|
|
ecdsa_ll_read_param(ECDSA_PARAM_QAY, pub_y, len);
|
|
|
|
ecdsa_ll_set_stage(ECDSA_STAGE_GET_DONE);
|
|
|
|
while (ecdsa_ll_get_state() != ECDSA_STATE_IDLE) {
|
|
;
|
|
}
|
|
}
|
|
#endif /* SOC_ECDSA_SUPPORT_EXPORT_PUBKEY */
|