From 84d2a8818cddd0775d5c0611c1e75db8688db6f7 Mon Sep 17 00:00:00 2001 From: Mahavir Jain Date: Tue, 22 Oct 2024 15:17:16 +0530 Subject: [PATCH] feat(ecc): enable ECC constant time mode for ESP32-H2 ECO5 --- components/esp_hw_support/Kconfig | 14 ++++ components/esp_system/port/cpu_start.c | 12 ++++ components/hal/Kconfig | 2 +- components/hal/ecc_hal.c | 5 ++ components/hal/esp32c2/include/hal/ecc_ll.h | 8 ++- components/hal/esp32c6/include/hal/ecc_ll.h | 6 ++ components/hal/esp32h2/include/hal/ecc_ll.h | 14 ++++ components/hal/esp32p4/include/hal/ecc_ll.h | 8 ++- components/hal/include/hal/ecc_hal.h | 7 ++ .../hal/test_apps/crypto/main/ecc/test_ecc.c | 72 +++++++++++++++++++ components/mbedtls/Kconfig | 3 +- components/mbedtls/port/ecc/esp_ecc.c | 8 +++ .../esp32h2/include/soc/Kconfig.soc_caps.in | 4 ++ components/soc/esp32h2/include/soc/soc_caps.h | 3 + 14 files changed, 161 insertions(+), 5 deletions(-) diff --git a/components/esp_hw_support/Kconfig b/components/esp_hw_support/Kconfig index 5d2fd16c43..54f45551a9 100644 --- a/components/esp_hw_support/Kconfig +++ b/components/esp_hw_support/Kconfig @@ -336,6 +336,20 @@ menu "Hardware Settings" endmenu + config ESP_CRYPTO_FORCE_ECC_CONSTANT_TIME_POINT_MUL + bool "Forcefully enable ECC constant time point multiplication operations" + depends on SOC_ECC_CONSTANT_TIME_POINT_MUL + default N + help + If enabled, the app startup code will burn the ECC_FORCE_CONST_TIME efuse bit to force the + ECC peripheral to always perform constant time point multiplication operations, + irrespective of the ECC_MULT_SECURITY_MODE status bit that is present in the ECC_MULT_CONF_REG + register. By default, ESP-IDF configures the ECC peripheral to perform constant time point + multiplication operations, so enabling this config would provide security enhancement only in + the cases when trusted boot is not enabled and the attacker tries carrying out non-constant + time point multiplication operations by changing the default ESP-IDF configurations. + Performing constant time operations protect the ECC multiplication operations from timing attacks. + # Invisible bringup bypass options for esp_hw_support component config ESP_BRINGUP_BYPASS_CPU_CLK_SETTING bool diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index d6a9dbdf71..81de518cc9 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -301,6 +301,18 @@ static void start_other_core(void) REG_CLR_BIT(HP_SYS_CLKRST_HP_RST_EN0_REG, HP_SYS_CLKRST_REG_RST_EN_CORE1_GLOBAL); } #endif + +#if CONFIG_ESP_CRYPTO_FORCE_ECC_CONSTANT_TIME_POINT_MUL + if (!esp_efuse_read_field_bit(ESP_EFUSE_ECC_FORCE_CONST_TIME)) { + ESP_EARLY_LOGD(TAG, "Forcefully enabling ECC constant time operations"); + esp_err_t err = esp_efuse_write_field_bit(ESP_EFUSE_ECC_FORCE_CONST_TIME); + if (err != ESP_OK) { + ESP_EARLY_LOGE(TAG, "Enabling ECC constant time operations forcefully failed."); + return err; + } + } +#endif + ets_set_appcpu_boot_addr((uint32_t)call_start_cpu1); bool cpus_up = false; diff --git a/components/hal/Kconfig b/components/hal/Kconfig index 264fbb6a96..aac591bd36 100644 --- a/components/hal/Kconfig +++ b/components/hal/Kconfig @@ -105,8 +105,8 @@ menu "Hardware Abstraction Layer (HAL) and Low Level (LL)" config HAL_ECDSA_GEN_SIG_CM bool "Enable countermeasure for ECDSA signature generation" + depends on IDF_TARGET_ESP32H2 && ESP32H2_REV_MIN_FULL < 102 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 diff --git a/components/hal/ecc_hal.c b/components/hal/ecc_hal.c index 18ca952f8e..a3cb192ab2 100644 --- a/components/hal/ecc_hal.c +++ b/components/hal/ecc_hal.c @@ -177,3 +177,8 @@ int ecc_hal_read_mod_op_result(uint8_t *r, uint16_t len) } #endif /* SOC_ECC_EXTENDED_MODES_SUPPORTED */ + +void ecc_hal_enable_constant_time_point_mul(bool enable) +{ + ecc_ll_enable_constant_time_point_mul(enable); +} diff --git a/components/hal/esp32c2/include/hal/ecc_ll.h b/components/hal/esp32c2/include/hal/ecc_ll.h index 58ddd82dd0..ca84307425 100644 --- a/components/hal/esp32c2/include/hal/ecc_ll.h +++ b/components/hal/esp32c2/include/hal/ecc_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -171,6 +171,12 @@ static inline void ecc_ll_read_param(ecc_ll_param_t param, uint8_t *buf, uint16_ memcpy(buf, (void *)reg, len); } +static inline void ecc_ll_enable_constant_time_point_mul(bool enable) +{ + // Not supported for ESP32-C2 + (void) enable; //unused +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c6/include/hal/ecc_ll.h b/components/hal/esp32c6/include/hal/ecc_ll.h index cb8b4ca2cd..d144dbff29 100644 --- a/components/hal/esp32c6/include/hal/ecc_ll.h +++ b/components/hal/esp32c6/include/hal/ecc_ll.h @@ -173,6 +173,12 @@ static inline void ecc_ll_read_param(ecc_ll_param_t param, uint8_t *buf, uint16_ memcpy(buf, (void *)reg, len); } +static inline void ecc_ll_enable_constant_time_point_mul(bool enable) +{ + // Not supported for ESP32-C6 + (void) enable; //unused +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32h2/include/hal/ecc_ll.h b/components/hal/esp32h2/include/hal/ecc_ll.h index 46667692a7..7b0b98985d 100644 --- a/components/hal/esp32h2/include/hal/ecc_ll.h +++ b/components/hal/esp32h2/include/hal/ecc_ll.h @@ -12,6 +12,8 @@ #include "soc/ecc_mult_reg.h" #include "soc/pcr_struct.h" #include "soc/pcr_reg.h" +#include "soc/chip_revision.h" +#include "hal/efuse_ll.h" #ifdef __cplusplus extern "C" { @@ -211,6 +213,18 @@ static inline ecc_mod_base_t ecc_ll_get_mod_base(void) return (ecc_mod_base_t)(REG_GET_FIELD(ECC_MULT_CONF_REG, ECC_MULT_MOD_BASE)); } +static inline void ecc_ll_enable_constant_time_point_mul(bool enable) +{ + // ECC constant time point multiplication is supported only on rev 1.2 and above + if ((efuse_ll_get_chip_wafer_version_major() >= 1) && (efuse_ll_get_chip_wafer_version_minor() >= 2)) { + if (enable) { + REG_SET_BIT(ECC_MULT_CONF_REG, ECC_MULT_SECURITY_MODE); + } else { + REG_CLR_BIT(ECC_MULT_CONF_REG, ECC_MULT_SECURITY_MODE); + } + } +} + static inline void ecc_ll_read_param(ecc_ll_param_t param, uint8_t *buf, uint16_t len) { uint32_t reg; diff --git a/components/hal/esp32p4/include/hal/ecc_ll.h b/components/hal/esp32p4/include/hal/ecc_ll.h index bffedd2e85..6336a9d167 100644 --- a/components/hal/esp32p4/include/hal/ecc_ll.h +++ b/components/hal/esp32p4/include/hal/ecc_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -241,6 +241,12 @@ static inline void ecc_ll_read_param(ecc_ll_param_t param, uint8_t *buf, uint16_ memcpy(buf, (void *)reg, len); } +static inline void ecc_ll_enable_constant_time_point_mul(bool enable) +{ + // Not supported for ESP32-P4 + (void) enable; //unused +} + #ifdef __cplusplus } #endif diff --git a/components/hal/include/hal/ecc_hal.h b/components/hal/include/hal/ecc_hal.h index 23cfbbbda1..3d85dd6104 100644 --- a/components/hal/include/hal/ecc_hal.h +++ b/components/hal/include/hal/ecc_hal.h @@ -195,6 +195,13 @@ int ecc_hal_read_mod_op_result(uint8_t *r, uint16_t len); #endif /* SOC_ECC_EXTENDED_MODES_SUPPORTED */ +/** + * @brief Enable constant time multiplication operations + * + * @param true: enable; false: disable + */ +void ecc_hal_enable_constant_time_point_mul(bool enable); + #ifdef __cplusplus } #endif diff --git a/components/hal/test_apps/crypto/main/ecc/test_ecc.c b/components/hal/test_apps/crypto/main/ecc/test_ecc.c index fcf72814f2..506fd5458a 100644 --- a/components/hal/test_apps/crypto/main/ecc/test_ecc.c +++ b/components/hal/test_apps/crypto/main/ecc/test_ecc.c @@ -86,6 +86,7 @@ static void ecc_point_mul(const uint8_t *k_le, const uint8_t *x_le, const uint8_ } else { ecc_hal_set_mode(ECC_MODE_POINT_MUL); } + ecc_hal_enable_constant_time_point_mul(true); ecc_hal_start_calc(); while (!ecc_hal_is_calc_finished()) { @@ -160,6 +161,74 @@ TEST(ecc, ecc_point_multiplication_on_SECP192R1_and_SECP256R1) { test_ecc_point_mul_inner(false); } + +#if SOC_ECC_CONSTANT_TIME_POINT_MUL || (CONFIG_IDF_TARGET_ESP32H2 && CONFIG_ESP32H2_REV_MIN_FULL >= 102) + +#define CONST_TIME_DEVIATION_PERCENT 0.002 + +static void test_ecc_point_mul_inner_constant_time(void) +{ + uint8_t scalar_le[32]; + uint8_t x_le[32]; + uint8_t y_le[32]; + + /* P256 */ + ecc_be_to_le(ecc_p256_scalar, scalar_le, 32); + ecc_be_to_le(ecc_p256_point_x, x_le, 32); + ecc_be_to_le(ecc_p256_point_y, y_le, 32); + + uint8_t x_res_le[32]; + uint8_t y_res_le[32]; + + double deviation = 0; + uint32_t elapsed_time, mean_elapsed_time, total_elapsed_time = 0; + uint32_t max_time = 0, min_time = UINT32_MAX; + int loop_count = 10; + + for (int i = 0; i < loop_count; i++) { + ccomp_timer_start(); + ecc_point_mul(scalar_le, x_le, y_le, 32, 0, x_res_le, y_res_le); + elapsed_time = ccomp_timer_stop(); + + max_time = MAX(elapsed_time, max_time); + min_time = MIN(elapsed_time, min_time); + total_elapsed_time += elapsed_time; + } + mean_elapsed_time = total_elapsed_time / loop_count; + deviation = ((double)(max_time - mean_elapsed_time) / mean_elapsed_time); + + TEST_ASSERT_LESS_THAN_DOUBLE(CONST_TIME_DEVIATION_PERCENT, deviation); + + /* P192 */ + ecc_be_to_le(ecc_p192_scalar, scalar_le, 24); + ecc_be_to_le(ecc_p192_point_x, x_le, 24); + ecc_be_to_le(ecc_p192_point_y, y_le, 24); + + max_time = 0; + min_time = UINT32_MAX; + total_elapsed_time = 0; + + for (int i = 0; i < loop_count; i++) { + ccomp_timer_start(); + ecc_point_mul(scalar_le, x_le, y_le, 24, 0, x_res_le, y_res_le); + elapsed_time = ccomp_timer_stop(); + + max_time = MAX(elapsed_time, max_time); + min_time = MIN(elapsed_time, min_time); + total_elapsed_time += elapsed_time; + } + mean_elapsed_time = total_elapsed_time / loop_count; + deviation = ((double)(max_time - mean_elapsed_time) / mean_elapsed_time); + + TEST_ASSERT_LESS_THAN_DOUBLE(CONST_TIME_DEVIATION_PERCENT, deviation); +} + +TEST(ecc, ecc_point_multiplication_const_time_check_on_SECP192R1_and_SECP256R1) +{ + test_ecc_point_mul_inner_constant_time(); +} +#endif + #endif #if SOC_ECC_SUPPORT_POINT_VERIFY && !defined(SOC_ECC_SUPPORT_POINT_VERIFY_QUIRK) @@ -493,6 +562,9 @@ TEST_GROUP_RUNNER(ecc) { #if SOC_ECC_SUPPORT_POINT_MULT RUN_TEST_CASE(ecc, ecc_point_multiplication_on_SECP192R1_and_SECP256R1); +#if SOC_ECC_CONSTANT_TIME_POINT_MUL || (CONFIG_IDF_TARGET_ESP32H2 && CONFIG_ESP32H2_REV_MIN_FULL >= 102) + RUN_TEST_CASE(ecc, ecc_point_multiplication_const_time_check_on_SECP192R1_and_SECP256R1); +#endif #endif #if SOC_ECC_SUPPORT_POINT_VERIFY && !defined(SOC_ECC_SUPPORT_POINT_VERIFY_QUIRK) diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index 08dfe046a5..1d00759ab2 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -535,11 +535,10 @@ menu "mbedTLS" menu "Enable Software Countermeasure for ECDSA signing using on-chip ECDSA peripheral" depends on MBEDTLS_HARDWARE_ECDSA_SIGN - depends on IDF_TARGET_ESP32H2 + depends on IDF_TARGET_ESP32H2 && ESP32H2_REV_MIN_FULL < 102 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. diff --git a/components/mbedtls/port/ecc/esp_ecc.c b/components/mbedtls/port/ecc/esp_ecc.c index 0cb4e72e9c..e1dbb34cb1 100644 --- a/components/mbedtls/port/ecc/esp_ecc.c +++ b/components/mbedtls/port/ecc/esp_ecc.c @@ -44,6 +44,14 @@ int esp_ecc_point_multiply(const ecc_point_t *point, const uint8_t *scalar, ecc_ ecc_hal_write_mul_param(scalar, point->x, point->y, len); ecc_hal_set_mode(work_mode); + + /* + * Enable constant-time point multiplication operations for the ECC hardware accelerator, + * if supported for the given target. This protects the ECC multiplication operation from + * timing attacks. This increases the time taken (by almost 50%) for some point + * multiplication operations performed by the ECC hardware accelerator. + */ + ecc_hal_enable_constant_time_point_mul(true); ecc_hal_start_calc(); memset(result, 0, sizeof(ecc_point_t)); diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index 7a6b25673e..883a9426dc 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -1131,6 +1131,10 @@ config SOC_CRYPTO_DPA_PROTECTION_SUPPORTED bool default y +config SOC_ECC_CONSTANT_TIME_POINT_MUL + bool + default y + config SOC_ECDSA_USES_MPI bool default y diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index 558f4fb4ab..28b8c4d5b2 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -477,6 +477,9 @@ /*------------------------ Anti DPA (Security) CAPS --------------------------*/ #define SOC_CRYPTO_DPA_PROTECTION_SUPPORTED 1 +/*--------------------------- ECC CAPS ---------------------------------------*/ +#define SOC_ECC_CONSTANT_TIME_POINT_MUL 1 + /*------------------------- ECDSA CAPS -------------------------*/ #define SOC_ECDSA_USES_MPI (1)