mbedtls: Add port layer for ECDSA peripheral

This commit is contained in:
Sachin Parekh 2023-03-06 17:31:38 +05:30
parent d345c684ba
commit d2940c5ff3
9 changed files with 388 additions and 4 deletions

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -62,7 +62,8 @@ typedef enum {
*/
typedef enum {
ESP_EFUSE_KEY_PURPOSE_USER = 0, /**< User purposes (software-only use) */
ESP_EFUSE_KEY_PURPOSE_RESERVED = 1, /**< Reserved */
ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY = 1, /**< ECDSA private key (Expected in little endian order)*/
ESP_EFUSE_KEY_PURPOSE_RESERVED = 2, /**< Reserved (Used as a place holder)*/
ESP_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY = 4, /**< XTS_AES_128_KEY (flash/PSRAM encryption) */
ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_ALL = 5, /**< HMAC Downstream mode */
ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_JTAG = 6, /**< JTAG soft enable key (uses HMAC Downstream mode) */

View File

@ -287,6 +287,9 @@ esp_err_t esp_efuse_write_key(esp_efuse_block_t block, esp_efuse_purpose_t purpo
purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_1 ||
purpose == ESP_EFUSE_KEY_PURPOSE_XTS_AES_256_KEY_2 ||
#endif //#ifdef SOC_EFUSE_SUPPORT_XTS_AES_256_KEYS
#if SOC_ECDSA_SUPPORTED
purpose == ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY ||
#endif
purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_ALL ||
purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_JTAG ||
purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_DIGITAL_SIGNATURE ||

View File

@ -27,7 +27,8 @@ extern "C" {
typedef enum {
ETS_EFUSE_KEY_PURPOSE_USER = 0,
ETS_EFUSE_KEY_PURPOSE_RESERVED = 1,
ETS_EFUSE_KEY_PURPOSE_ECDSA_KEY = 1,
ETS_EFUSE_KEY_PURPOSE_RESERVED = 2,
ETS_EFUSE_KEY_PURPOSE_XTS_AES_128_KEY = 4,
ETS_EFUSE_KEY_PURPOSE_HMAC_DOWN_ALL = 5,
ETS_EFUSE_KEY_PURPOSE_HMAC_DOWN_JTAG = 6,

View File

@ -235,6 +235,18 @@ if(CONFIG_MBEDTLS_HARDWARE_ECC)
"${COMPONENT_DIR}/port/ecc/ecc_alt.c")
endif()
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN OR CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY)
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/ecdsa/ecdsa_alt.c")
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=mbedtls_ecdsa_sign")
endif()
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=mbedtls_ecdsa_verify")
endif()
endif()
if(CONFIG_MBEDTLS_ROM_MD5)
target_sources(mbedcrypto PRIVATE "${COMPONENT_DIR}/port/md/esp_md.c")
endif()
@ -267,6 +279,10 @@ if(CONFIG_PM_ENABLE)
target_link_libraries(mbedcrypto PRIVATE idf::esp_pm)
endif()
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN OR CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY)
target_link_libraries(mbedcrypto PRIVATE idf::efuse)
endif()
target_link_libraries(${COMPONENT_LIB} ${linkage_type} ${mbedtls_targets})
if(CONFIG_ESP_TLS_USE_DS_PERIPHERAL)

View File

@ -455,6 +455,29 @@ menu "mbedTLS"
help
Use ROM MD5 in mbedTLS.
config MBEDTLS_HARDWARE_ECDSA_SIGN
bool "Enable ECDSA signing using on-chip ECDSA peripheral"
default n
depends on SOC_ECDSA_SUPPORTED
help
Enable hardware accelerated ECDSA peripheral to sign data
on curve SECP192R1 and SECP256R1 in mbedTLS.
Note that for signing, the private key has to be burnt in an efuse key block
with key purpose set to ECDSA_KEY.
If no key is burnt, it will report an error
The key should be burnt in little endian format. espefuse.py utility handles it internally
but care needs to be taken while burning using esp_efuse APIs
config MBEDTLS_HARDWARE_ECDSA_VERIFY
bool "Enable ECDSA signature verification using on-chip ECDSA peripheral"
default y
depends on SOC_ECDSA_SUPPORTED
help
Enable hardware accelerated ECDSA peripheral to verify signature
on curve SECP192R1 and SECP256R1 in mbedTLS.
config MBEDTLS_ATCA_HW_ECDSA_SIGN
bool "Enable hardware ECDSA sign acceleration when using ATECC608A"
default n

View File

@ -0,0 +1,276 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "hal/ecdsa_hal.h"
#include "esp_efuse.h"
#include "mbedtls/ecp.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/platform_util.h"
#include "esp_private/periph_ctrl.h"
#include "ecdsa/ecdsa_alt.h"
#define ECDSA_KEY_MAGIC 0xECD5A
#define ECDSA_SHA_LEN 32
#define MAX_ECDSA_COMPONENT_LEN 32
__attribute__((unused)) static const char *TAG = "ecdsa_alt";
static _lock_t s_crypto_ecdsa_lock;
static void esp_ecdsa_acquire_hardware(void)
{
_lock_acquire(&s_crypto_ecdsa_lock);
periph_module_enable(PERIPH_ECDSA_MODULE);
}
static void esp_ecdsa_release_hardware(void)
{
periph_module_disable(PERIPH_ECDSA_MODULE);
_lock_release(&s_crypto_ecdsa_lock);
}
static void ecdsa_be_to_le(const uint8_t* be_point, uint8_t *le_point, uint8_t len)
{
/* When the size is 24 bytes, it should be padded with 0 bytes*/
memset(le_point, 0x0, 32);
for(int i = 0; i < len; i++) {
le_point[i] = be_point[len - i - 1];
}
}
#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
int esp_ecdsa_privkey_load_mpi(mbedtls_mpi *key, int efuse_blk)
{
if (!key) {
ESP_LOGE(TAG, "Invalid memory");
return -1;
}
if (efuse_blk < EFUSE_BLK_KEY0 || efuse_blk >= EFUSE_BLK_KEY_MAX) {
ESP_LOGE(TAG, "Invalid efuse block");
return -1;
}
mbedtls_mpi_init(key);
/* We use the mbedtls_mpi struct to pass our own context to hardware ECDSA peripheral
* MPI struct expects `s` to be either 1 or -1, by setting it to 0xECD5A, we ensure that it does
* not collide with a valid MPI. This is done to differentiate between using the private key stored in efuse
* or using the private key provided by software
*
* `n` is used to store the efuse block which should be used as key
*/
key->MBEDTLS_PRIVATE(s) = ECDSA_KEY_MAGIC;
key->MBEDTLS_PRIVATE(n) = efuse_blk;
key->MBEDTLS_PRIVATE(p) = NULL;
return 0;
}
int esp_ecdsa_privkey_load_pk_context(mbedtls_pk_context *key_ctx, int efuse_blk)
{
const mbedtls_pk_info_t *pk_info;
mbedtls_ecp_keypair *keypair;
if (!key_ctx) {
ESP_LOGE(TAG, "Invalid memory");
return -1;
}
if (efuse_blk < EFUSE_BLK_KEY0 || efuse_blk >= EFUSE_BLK_KEY_MAX) {
ESP_LOGE(TAG, "Invalid efuse block");
return -1;
}
mbedtls_pk_init(key_ctx);
pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_ECDSA);
mbedtls_pk_setup(key_ctx, pk_info);
keypair = mbedtls_pk_ec(*key_ctx);
return esp_ecdsa_privkey_load_mpi(&(keypair->MBEDTLS_PRIVATE(d)), efuse_blk);
}
static int esp_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi* r, mbedtls_mpi* s,
const mbedtls_mpi *d, const unsigned char* msg, size_t msg_len)
{
ecdsa_curve_t curve;
esp_efuse_block_t blk;
uint16_t len;
uint8_t zeroes[MAX_ECDSA_COMPONENT_LEN] = {0};
uint8_t sha_le[ECDSA_SHA_LEN];
uint8_t r_le[MAX_ECDSA_COMPONENT_LEN];
uint8_t s_le[MAX_ECDSA_COMPONENT_LEN];
if (!grp || !r || !s || !d || !msg) {
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
if (msg_len != ECDSA_SHA_LEN) {
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
if (grp->id == MBEDTLS_ECP_DP_SECP192R1) {
curve = ECDSA_CURVE_SECP192R1;
len = 24;
} else if (grp->id == MBEDTLS_ECP_DP_SECP256R1) {
curve = ECDSA_CURVE_SECP256R1;
len = 32;
} else {
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
if (!esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY, &blk)) {
ESP_LOGE(TAG, "No efuse block with purpose ECDSA_KEY found");
return MBEDTLS_ERR_ECP_INVALID_KEY;
}
ecdsa_be_to_le(msg, sha_le, len);
esp_ecdsa_acquire_hardware();
do {
ecdsa_hal_config_t conf = {
.mode = ECDSA_MODE_SIGN_GEN,
.curve = curve,
.k_mode = ECDSA_K_USE_TRNG,
.sha_mode = ECDSA_Z_USER_PROVIDED,
};
ecdsa_hal_gen_signature(&conf, NULL, sha_le, r_le, s_le, len);
} while (!memcmp(r_le, zeroes, len) || !memcmp(s_le, zeroes, len));
esp_ecdsa_release_hardware();
mbedtls_mpi_read_binary_le(r, r_le, len);
mbedtls_mpi_read_binary_le(s, s_le, len);
return 0;
}
/*
* Compute ECDSA signature of a hashed message;
*/
extern int __real_mbedtls_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng);
int __wrap_mbedtls_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng);
int __wrap_mbedtls_ecdsa_sign(mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng)
{
/*
* Check `d` whether it contains the hardware key
*/
if (d->MBEDTLS_PRIVATE(s) == ECDSA_KEY_MAGIC) {
// Use hardware ECDSA peripheral
return esp_ecdsa_sign(grp, r, s, d, buf, blen);
} else {
return __real_mbedtls_ecdsa_sign(grp, r, s, d, buf, blen, f_rng, p_rng);
}
}
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN */
#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY
static int esp_ecdsa_verify(mbedtls_ecp_group *grp,
const unsigned char *buf, size_t blen,
const mbedtls_ecp_point *Q,
const mbedtls_mpi *r,
const mbedtls_mpi *s)
{
ecdsa_curve_t curve;
uint16_t len;
uint8_t r_le[MAX_ECDSA_COMPONENT_LEN];
uint8_t s_le[MAX_ECDSA_COMPONENT_LEN];
uint8_t qx_le[MAX_ECDSA_COMPONENT_LEN];
uint8_t qy_le[MAX_ECDSA_COMPONENT_LEN];
uint8_t sha_le[ECDSA_SHA_LEN];
if (!grp || !buf || !Q || !r || !s) {
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
if (blen != ECDSA_SHA_LEN) {
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
if (grp->id == MBEDTLS_ECP_DP_SECP192R1) {
curve = ECDSA_CURVE_SECP192R1;
len = 24;
} else if (grp->id == MBEDTLS_ECP_DP_SECP256R1) {
curve = ECDSA_CURVE_SECP256R1;
len = 32;
} else {
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
if (mbedtls_mpi_cmp_int(r, 1) < 0 || mbedtls_mpi_cmp_mpi(r, &grp->N) >= 0 ||
mbedtls_mpi_cmp_int(s, 1) < 0 || mbedtls_mpi_cmp_mpi(s, &grp->N) >= 0 )
{
return MBEDTLS_ERR_ECP_VERIFY_FAILED;
}
ecdsa_be_to_le(buf, sha_le, len);
mbedtls_mpi_write_binary_le(&Q->MBEDTLS_PRIVATE(X), qx_le, len);
mbedtls_mpi_write_binary_le(&Q->MBEDTLS_PRIVATE(Y), qy_le, len);
mbedtls_mpi_write_binary_le(r, r_le, len);
mbedtls_mpi_write_binary_le(s, s_le, len);
esp_ecdsa_acquire_hardware();
ecdsa_hal_config_t conf = {
.mode = ECDSA_MODE_SIGN_VERIFY,
.curve = curve,
.k_mode = ECDSA_K_USE_TRNG,
.sha_mode = ECDSA_Z_USER_PROVIDED,
};
int ret = ecdsa_hal_verify_signature(&conf, sha_le, r_le, s_le, qx_le, qy_le, len);
esp_ecdsa_release_hardware();
if (ret != 0) {
return MBEDTLS_ERR_ECP_VERIFY_FAILED;
}
return ret;
}
/*
* Verify ECDSA signature of hashed message
*/
extern int __real_mbedtls_ecdsa_verify(mbedtls_ecp_group *grp,
const unsigned char *buf, size_t blen,
const mbedtls_ecp_point *Q,
const mbedtls_mpi *r,
const mbedtls_mpi *s);
int __wrap_mbedtls_ecdsa_verify(mbedtls_ecp_group *grp,
const unsigned char *buf, size_t blen,
const mbedtls_ecp_point *Q,
const mbedtls_mpi *r,
const mbedtls_mpi *s);
int __wrap_mbedtls_ecdsa_verify(mbedtls_ecp_group *grp,
const unsigned char *buf, size_t blen,
const mbedtls_ecp_point *Q,
const mbedtls_mpi *r,
const mbedtls_mpi *s)
{
if (grp->id == MBEDTLS_ECP_DP_SECP192R1 || grp->id == MBEDTLS_ECP_DP_SECP256R1) {
return esp_ecdsa_verify(grp, buf, blen, Q, r, s);
} else {
return __real_mbedtls_ecdsa_verify(grp, buf, blen, Q, r, s);
}
}
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY */

View File

@ -0,0 +1,63 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "mbedtls/pk.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
/**
* @brief Initialize MPI to notify mbedtls_ecdsa_sign to use the private key in efuse
* We break the MPI struct of the private key in order to
* differentiate between hardware key and software key
*
* @note Currently, `efuse_blk` is not used internally.
* Hardware will choose the efuse block that has purpose set to ECDSA_KEY.
* In case of multiple ECDSA_KEY burnt in efuse, hardware will choose the
* greater efuse block number as the private key.
*
* @param key The MPI in which this functions stores the hardware context.
* This must be uninitialized
* @param efuse_blk The efuse key block that should be used as the private key.
* The key purpose of this block must be ECDSA_KEY
*
* @return - 0 if successful
* - -1 otherwise
*
*/
int esp_ecdsa_privkey_load_mpi(mbedtls_mpi *key, int efuse_blk);
/**
* @brief Initialize PK context to notify mbedtls_ecdsa_sign to use the private key in efuse
* We break the MPI struct used to represent the private key `d` in ECP keypair
* in order to differentiate between hardware key and software key
*
* @note Currently, `efuse_blk` is not used internally.
* Hardware will choose the efuse block that has purpose set to ECDSA_KEY.
* In case of multiple ECDSA_KEY burnt in efuse, hardware will choose the
* greater efuse block number as the private key.
*
* @param key_ctx The context in which this functions stores the hardware context.
* This must be uninitialized
* @param efuse_blk The efuse key block that should be used as the private key.
* The key purpose of this block must be ECDSA_KEY
*
* @return - 0 if successful
* - -1 otherwise
*/
int esp_ecdsa_privkey_load_pk_context(mbedtls_pk_context *key_ctx, int efuse_blk);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -224,6 +224,7 @@
#undef MBEDTLS_ECP_VERIFY_ALT
#undef MBEDTLS_ECP_VERIFY_ALT_SOFT_FALLBACK
#endif
/**
* \def MBEDTLS_ENTROPY_HARDWARE_ALT
*

View File

@ -30,7 +30,7 @@
#define NEWLIB_NANO_COMPAT_CAST(int64_t_var) int64_t_var
#endif
#if CONFIG_MBEDTLS_HARDWARE_ECC
#if CONFIG_MBEDTLS_HARDWARE_ECC || CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY
/*
* All the following values are in big endian format, as required by the mbedTLS APIs