Skip to content

Commit

Permalink
Merge branch 'bugfix/esp32h2_ecdsa_hardware_k_v5.1' into 'release/v5.1'
Browse files Browse the repository at this point in the history
fix(esp32h2): program use_hardware_k efuse bit for ECDSA key purpose (v5.1)

See merge request espressif/esp-idf!27271
  • Loading branch information
AdityaHPatwardhan committed Nov 21, 2023
2 parents 4d88e7b + 78453c8 commit 514cd78
Show file tree
Hide file tree
Showing 20 changed files with 148 additions and 51 deletions.
16 changes: 14 additions & 2 deletions components/efuse/include/esp_efuse.h
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -235,7 +235,7 @@ esp_err_t esp_efuse_write_reg(esp_efuse_block_t blk, unsigned int num_reg, uint3
/**
* @brief Return efuse coding scheme for blocks.
*
* Note: The coding scheme is applicable only to 1, 2 and 3 blocks. For 0 block, the coding scheme is always ``NONE``.
* @note The coding scheme is applicable only to 1, 2 and 3 blocks. For 0 block, the coding scheme is always ``NONE``.
*
* @param[in] blk Block number of eFuse.
* @return Return efuse coding scheme for blocks
Expand Down Expand Up @@ -708,6 +708,12 @@ esp_err_t esp_efuse_set_write_protect_of_digest_revoke(unsigned num_digest);
*
* The burn of a key, protection bits, and a purpose happens in batch mode.
*
* @note This API also enables the read protection efuse bit for certain key blocks like XTS-AES, HMAC, ECDSA etc.
* This ensures that the key is only accessible to hardware peripheral.
*
* @note For SoC's with capability `SOC_EFUSE_ECDSA_USE_HARDWARE_K` (e.g., ESP32-H2), this API writes an additional
* efuse bit for ECDSA key purpose to enforce hardware TRNG generated k mode in the peripheral.
*
* @param[in] block Block to read purpose for. Must be in range EFUSE_BLK_KEY0 to EFUSE_BLK_KEY_MAX. Key block must be unused (esp_efuse_key_block_unused).
* @param[in] purpose Purpose to set for this key. Purpose must be already unset.
* @param[in] key Pointer to data to write.
Expand All @@ -727,6 +733,12 @@ esp_err_t esp_efuse_write_key(esp_efuse_block_t block, esp_efuse_purpose_t purpo
*
* The burn of keys, protection bits, and purposes happens in batch mode.
*
* @note This API also enables the read protection efuse bit for certain key blocks like XTS-AES, HMAC, ECDSA etc.
* This ensures that the key is only accessible to hardware peripheral.
*
* @note For SoC's with capability `SOC_EFUSE_ECDSA_USE_HARDWARE_K` (e.g., ESP32-H2), this API writes an additional
* efuse bit for ECDSA key purpose to enforce hardware TRNG generated k mode in the peripheral.
*
* @param[in] purposes Array of purposes (purpose[number_of_keys]).
* @param[in] keys Array of keys (uint8_t keys[number_of_keys][32]). Each key is 32 bytes long.
* @param[in] number_of_keys The number of keys to write (up to 6 keys).
Expand Down
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -312,6 +312,12 @@ esp_err_t esp_efuse_write_key(esp_efuse_block_t block, esp_efuse_purpose_t purpo
purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_UP) {
ESP_EFUSE_CHK(esp_efuse_set_key_dis_read(block));
}
#if SOC_EFUSE_ECDSA_USE_HARDWARE_K
if (purpose == ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY) {
// Permanently enable the hardware TRNG supplied k mode (most secure mode)
ESP_EFUSE_CHK(esp_efuse_write_field_bit(ESP_EFUSE_ECDSA_FORCE_USE_HARDWARE_K));
}
#endif
ESP_EFUSE_CHK(esp_efuse_set_key_purpose(block, purpose));
ESP_EFUSE_CHK(esp_efuse_set_keypurpose_dis_write(block));
return esp_efuse_batch_write_commit();
Expand Down
10 changes: 10 additions & 0 deletions components/esp_system/startup.c
Expand Up @@ -27,6 +27,7 @@
#include "esp_newlib.h"
#include "esp_timer.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#include "esp_flash_encrypt.h"
#include "esp_secure_boot.h"
#include "esp_xt_wdt.h"
Expand Down Expand Up @@ -364,6 +365,15 @@ static void do_core_init(void)
esp_secure_boot_init_checks();
#endif

#if SOC_EFUSE_ECDSA_USE_HARDWARE_K
if (esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY, NULL)) {
// ECDSA key purpose block is present and hence permanently enable
// the hardware TRNG supplied k mode (most secure mode)
err = esp_efuse_write_field_bit(ESP_EFUSE_ECDSA_FORCE_USE_HARDWARE_K);
assert(err == ESP_OK && "Failed to enable ECDSA hardware k mode");
}
#endif

#if CONFIG_SECURE_DISABLE_ROM_DL_MODE
err = esp_efuse_disable_rom_download_mode();
assert(err == ESP_OK && "Failed to disable ROM download mode");
Expand Down
7 changes: 1 addition & 6 deletions components/hal/ecdsa_hal.c
Expand Up @@ -18,21 +18,16 @@ static void configure_ecdsa_periph(ecdsa_hal_config_t *conf)

ecdsa_ll_set_mode(conf->mode);
ecdsa_ll_set_curve(conf->curve);
ecdsa_ll_set_k_mode(conf->k_mode);
ecdsa_ll_set_z_mode(conf->sha_mode);
}

void ecdsa_hal_gen_signature(ecdsa_hal_config_t *conf, const uint8_t *k, const uint8_t *hash,
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->k_mode == ECDSA_K_USER_PROVIDED && k == NULL) {
HAL_ASSERT(false && "Mismatch in K configuration");
}

if (conf->sha_mode == ECDSA_Z_USER_PROVIDED && hash == NULL) {
HAL_ASSERT(false && "Mismatch in SHA configuration");
}
Expand Down
23 changes: 0 additions & 23 deletions components/hal/esp32h2/include/hal/ecdsa_ll.h
Expand Up @@ -22,7 +22,6 @@ typedef enum {
ECDSA_PARAM_R,
ECDSA_PARAM_S,
ECDSA_PARAM_Z,
ECDSA_PARAM_K,
ECDSA_PARAM_QAX,
ECDSA_PARAM_QAY
} ecdsa_ll_param_t;
Expand Down Expand Up @@ -170,26 +169,6 @@ static inline void ecdsa_ll_set_curve(ecdsa_curve_t curve)
}
}

/**
* @brief Set the source of `K`
*
* @param mode Mode of K generation
*/
static inline void ecdsa_ll_set_k_mode(ecdsa_k_mode_t mode)
{
switch (mode) {
case ECDSA_K_USE_TRNG:
REG_CLR_BIT(ECDSA_CONF_REG, ECDSA_SOFTWARE_SET_K);
break;
case ECDSA_K_USER_PROVIDED:
REG_SET_BIT(ECDSA_CONF_REG, ECDSA_SOFTWARE_SET_K);
break;
default:
HAL_ASSERT(false && "Unsupported curve");
break;
}
}

/**
* @brief Set the source of `Z` (SHA message)
*
Expand Down Expand Up @@ -315,7 +294,6 @@ static inline void ecdsa_ll_write_param(ecdsa_ll_param_t param, const uint8_t *b
case ECDSA_PARAM_Z:
reg = ECDSA_Z_MEM;
break;
case ECDSA_PARAM_K:
case ECDSA_PARAM_QAX:
reg = ECDSA_QAX_MEM;
break;
Expand Down Expand Up @@ -353,7 +331,6 @@ static inline void ecdsa_ll_read_param(ecdsa_ll_param_t param, uint8_t *buf, uin
case ECDSA_PARAM_Z:
reg = ECDSA_Z_MEM;
break;
case ECDSA_PARAM_K:
case ECDSA_PARAM_QAX:
reg = ECDSA_QAX_MEM;
break;
Expand Down
4 changes: 1 addition & 3 deletions components/hal/include/hal/ecdsa_hal.h
Expand Up @@ -25,7 +25,6 @@ extern "C" {
typedef struct {
ecdsa_mode_t mode; /* Mode of operation */
ecdsa_curve_t curve; /* Curve to use for operation */
ecdsa_k_mode_t k_mode; /* Source of K */
ecdsa_sha_mode_t sha_mode; /* Source of SHA that needs to be signed */
int efuse_key_blk; /* Efuse block to use as ECDSA key (The purpose of the efuse block must be ECDSA_KEY) */
} ecdsa_hal_config_t;
Expand All @@ -34,13 +33,12 @@ typedef struct {
* @brief Generate ECDSA signature
*
* @param conf Configuration for ECDSA operation, see ``ecdsa_hal_config_t``
* @param k Value of K used internally. Set this to NULL if K is generated by hardware
* @param hash Hash that is to be signed
* @param r_out Buffer that will contain `R` component of ECDSA signature
* @param s_out Buffer that will contain `S` component of ECDSA signature
* @param len Length of the r_out and s_out buffer (32 bytes for SECP256R1, 24 for SECP192R1)
*/
void ecdsa_hal_gen_signature(ecdsa_hal_config_t *conf, const uint8_t *k, const uint8_t *hash,
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);

/**
Expand Down
8 changes: 0 additions & 8 deletions components/hal/include/hal/ecdsa_types.h
Expand Up @@ -25,14 +25,6 @@ typedef enum {
ECDSA_CURVE_SECP256R1,
} ecdsa_curve_t;

/**
* @brief Source of 'K' used internally for generating signature
*/
typedef enum {
ECDSA_K_USE_TRNG,
ECDSA_K_USER_PROVIDED,
} ecdsa_k_mode_t;

/**
* @brief Source of SHA message that is to be signed/verified
*/
Expand Down
4 changes: 1 addition & 3 deletions components/mbedtls/port/ecdsa/ecdsa_alt.c
Expand Up @@ -139,12 +139,11 @@ static int esp_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi* r, mbedtls_mpi* s
ecdsa_hal_config_t conf = {
.mode = ECDSA_MODE_SIGN_GEN,
.curve = curve,
.k_mode = ECDSA_K_USE_TRNG,
.sha_mode = ECDSA_Z_USER_PROVIDED,
.efuse_key_blk = d->MBEDTLS_PRIVATE(n),
};

ecdsa_hal_gen_signature(&conf, NULL, sha_le, r_le, s_le, len);
ecdsa_hal_gen_signature(&conf, sha_le, r_le, s_le, len);
} while (!memcmp(r_le, zeroes, len) || !memcmp(s_le, zeroes, len));

esp_ecdsa_release_hardware();
Expand Down Expand Up @@ -364,7 +363,6 @@ static int esp_ecdsa_verify(mbedtls_ecp_group *grp,
ecdsa_hal_config_t conf = {
.mode = ECDSA_MODE_SIGN_VERIFY,
.curve = curve,
.k_mode = ECDSA_K_USE_TRNG,
.sha_mode = ECDSA_Z_USER_PROVIDED,
};

Expand Down
5 changes: 3 additions & 2 deletions components/mbedtls/port/include/ecdsa/ecdsa_alt.h
Expand Up @@ -14,7 +14,7 @@
extern "C" {
#endif

#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN || __DOXYGEN__

/**
* @brief Initialize MPI to notify mbedtls_ecdsa_sign to use the private key in efuse
Expand Down Expand Up @@ -46,7 +46,8 @@ int esp_ecdsa_privkey_load_mpi(mbedtls_mpi *key, int efuse_blk);
* - -1 otherwise
*/
int esp_ecdsa_privkey_load_pk_context(mbedtls_pk_context *key_ctx, int efuse_blk);
#endif

#endif // CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN || __DOXYGEN__

#ifdef __cplusplus
}
Expand Down
4 changes: 4 additions & 0 deletions components/soc/esp32h2/include/soc/Kconfig.soc_caps.in
Expand Up @@ -1011,6 +1011,10 @@ config SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK
bool
default y

config SOC_EFUSE_ECDSA_USE_HARDWARE_K
bool
default y

config SOC_SECURE_BOOT_V2_RSA
bool
default y
Expand Down
1 change: 1 addition & 0 deletions components/soc/esp32h2/include/soc/soc_caps.h
Expand Up @@ -418,6 +418,7 @@
#define SOC_EFUSE_SOFT_DIS_JTAG 1
#define SOC_EFUSE_DIS_ICACHE 1
#define SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK 1 // AES-XTS and ECDSA key purposes not supported for this block
#define SOC_EFUSE_ECDSA_USE_HARDWARE_K 1 // Force use hardware TRNG supplied K for ECDSA

/*-------------------------- Secure Boot CAPS----------------------------*/
#define SOC_SECURE_BOOT_V2_RSA 1
Expand Down
1 change: 1 addition & 0 deletions docs/conf_common.py
Expand Up @@ -196,6 +196,7 @@
'SOC_ULP_FSM_SUPPORTED':ULP_FSM_DOCS,
'SOC_RISCV_COPROC_SUPPORTED':RISCV_COPROC_DOCS,
'SOC_DIG_SIGN_SUPPORTED':['api-reference/peripherals/ds.rst'],
'SOC_ECDSA_SUPPORTED':['api-reference/peripherals/ecdsa.rst'],
'SOC_HMAC_SUPPORTED':['api-reference/peripherals/hmac.rst'],
'SOC_ASYNC_MEMCPY_SUPPORTED':['api-reference/system/async_memcpy.rst'],
'CONFIG_IDF_TARGET_ARCH_XTENSA':XTENSA_DOCS,
Expand Down
1 change: 1 addition & 0 deletions docs/doxygen/Doxyfile
Expand Up @@ -238,6 +238,7 @@ INPUT = \
$(PROJECT_PATH)/components/lwip/include/apps/esp_sntp.h \
$(PROJECT_PATH)/components/lwip/include/apps/ping/ping_sock.h \
$(PROJECT_PATH)/components/mbedtls/esp_crt_bundle/include/esp_crt_bundle.h \
$(PROJECT_PATH)/components/mbedtls/port/include/ecdsa/ecdsa_alt.h \
$(PROJECT_PATH)/components/mqtt/esp-mqtt/include/mqtt_client.h \
$(PROJECT_PATH)/components/nvs_flash/include/nvs_flash.h \
$(PROJECT_PATH)/components/nvs_flash/include/nvs.h \
Expand Down
82 changes: 82 additions & 0 deletions docs/en/api-reference/peripherals/ecdsa.rst
@@ -0,0 +1,82 @@
Elliptic Curve Digital Signature Algorithm (ECDSA)
==================================================

The Elliptic Curve Digital Signature Algorithm (ECDSA) offers a variant of the Digital Signature Algorithm (DSA) which uses elliptic-curve cryptography.

{IDF_TARGET_NAME}'s ECDSA peripheral provides a secure and efficient environment for computing ECDSA signatures. It offers fast computations while ensuring the confidentiality of the signing process to prevent information leakage. ECDSA private key used in the signing process is accessible only to the hardware peripheral, and it is not readable by software.

ECDSA peripheral can help to establish **Secure Device Identity** for TLS mutual authentication and similar use-cases.

Supported Features
------------------

- ECDSA digital signature generation and verification
- Two different elliptic curves, namely P-192 and P-256 (FIPS 186-3 specification)
- Two hash algorithms for message hash in the ECDSA operation, namely SHA-224 and SHA-256 (FIPS PUB 180-4 specification)


ECDSA on {IDF_TARGET_NAME}
--------------------------

On {IDF_TARGET_NAME}, the ECDSA module works with a secret key burnt into an eFuse block. This eFuse key is made completely inaccessible (default mode) for any resources outside the cryptographic modules, thus avoiding key leakage.

ECDSA key can be programmed externally through ``espefuse.py`` script using:

.. code:: bash
espefuse.py burn_key <BLOCK_NUM> </path/to/ecdsa_private_key.pem> ECDSA_KEY
.. only:: SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK

.. note::

Five physical eFuse blocks can be used as keys for the ECDSA module: block 4 ~ block 8. E.g., for block 4 (which is the first key block) , the argument should be ``BLOCK_KEY0``.

.. only:: not SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK

.. note::

Six physical eFuse blocks can be used as keys for the ECDSA module: block 4 ~ block 9. E.g., for block 4 (which is the first key block) , the argument should be ``BLOCK_KEY0``.


Alternatively the ECDSA key can also be programmed through the application running on the target.

Following code snippet uses :cpp:func:`esp_efuse_write_key` to set physical key block 0 in the eFuse with key purpose as :cpp:enumerator:`esp_efuse_purpose_t::ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY`:

.. code-block:: c
#include "esp_efuse.h"
const uint8_t key_data[32] = { ... };
esp_err_t status = esp_efuse_write_key(EFUSE_BLK_KEY0,
ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY,
key_data, sizeof(key_data));
if (status == ESP_OK) {
// written key
} else {
// writing key failed, maybe written already
}
Dependency on TRNG
------------------

ECDSA peripheral relies on the hardware True Random Number Generator (TRNG) for its internal entropy requirement. During ECDSA signature creation, the algorithm requires a random integer to be generated as specified in the `RFC 6090 <https://tools.ietf.org/html/rfc6090>`_ section 5.3.2.

Please ensure that hardware :doc:`RNG <../system/random>` is enabled before starting ECDSA computations (primarily signing) in the application.

Application Outline
-------------------

Please refer to the :ref:`ecdsa-peri-with-esp-tls` guide for details on how-to use ECDSA peripheral for establishing a mutually authenticated TLS connection.

The ECDSA peripheral in mbedTLS stack is integrated by overriding the ECDSA sign and verify APIs. Please note that, the ECDSA peripheral does not support all curves or hash algorithms and hence for cases where the requirements do not meet the hardware, implementation falls back to the software.

For a particular TLS context, additional APIs have been supplied to populate certain fields (e.g., private key ctx) to differentiate routing to hardware. ESP-TLS layer integrates these APIs internally and hence no additional work is required at the application layer. However, for custom use-cases please refer to API details below.

API Reference
-------------

.. include-build-file:: inc/ecdsa_alt.inc
1 change: 1 addition & 0 deletions docs/en/api-reference/peripherals/index.rst
Expand Up @@ -12,6 +12,7 @@ Peripherals API
:SOC_ANA_CMPR_SUPPORTED: ana_cmpr
clk_tree
:SOC_DAC_SUPPORTED: dac
:SOC_ECDSA_SUPPORTED: ecdsa
:SOC_ETM_SUPPORTED: etm
gpio
gptimer
Expand Down
2 changes: 2 additions & 0 deletions docs/en/api-reference/protocols/esp_tls.rst
Expand Up @@ -194,6 +194,8 @@ SSL/TLS libraries and with all respective configurations set to default.

.. only:: SOC_ECDSA_SUPPORTED

.. _ecdsa-peri-with-esp-tls:

ECDSA Peripheral with ESP-TLS
-----------------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/en/api-reference/system/random.rst
@@ -1,7 +1,7 @@
Random Number Generation
========================

{IDF_TARGET_RF_NAME: default="Wi-Fi or Bluetooth", esp32s2="Wi-Fi"}
{IDF_TARGET_RF_NAME: default="Wi-Fi or Bluetooth", esp32s2="Wi-Fi", esp32h2="Bluetooth or 802.15.4 Thread/Zigbee", esp32c6="Wi-Fi or Bluetooth or 802.15.4 Thread/Zigbee"}
{IDF_TARGET_RF_IS: default="are", esp32s2="is"}
{IDF_TARGET_BOOTLOADER_RANDOM_INCOMPATIBLE: default="", esp32="I2S, "}

Expand Down

0 comments on commit 514cd78

Please sign in to comment.