Kaynağa Gözat

mbedtls: Add port layer for ECDSA peripheral

Sachin Parekh 2 yıl önce
ebeveyn
işleme
d2940c5ff3

+ 3 - 2
components/efuse/esp32h2/include/esp_efuse_chip.h

@@ -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) */

+ 3 - 0
components/efuse/src/efuse_controller/keys/with_key_purposes/esp_efuse_api_key.c

@@ -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 ||

+ 2 - 1
components/esp_rom/include/esp32h2/rom/efuse.h

@@ -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,

+ 16 - 0
components/mbedtls/CMakeLists.txt

@@ -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)

+ 23 - 0
components/mbedtls/Kconfig

@@ -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

+ 276 - 0
components/mbedtls/port/ecdsa/ecdsa_alt.c

@@ -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 */

+ 63 - 0
components/mbedtls/port/include/ecdsa/ecdsa_alt.h

@@ -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

+ 1 - 0
components/mbedtls/port/include/mbedtls/esp_config.h

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

+ 1 - 1
components/mbedtls/test_apps/main/test_mbedtls_ecdsa.c

@@ -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