feat(mbedtls/ecdsa): Introduce PSA ECDSA driver

This commit is contained in:
harshal.patil
2026-01-19 09:14:37 +05:30
parent ce6e80c129
commit e9ea55bea2
22 changed files with 1736 additions and 2185 deletions
+17 -40
View File
@@ -4,7 +4,7 @@ idf_build_get_property(esp_tee_build ESP_TEE_BUILD)
if(esp_tee_build)
include(${COMPONENT_DIR}/esp_tee/esp_tee_mbedtls.cmake)
return()
return()
elseif(BOOTLOADER_BUILD) # TODO: IDF-11673
if(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL_BOOTLOADER)
@@ -415,47 +415,28 @@ if(CONFIG_MBEDTLS_HARDWARE_GCM OR CONFIG_MBEDTLS_HARDWARE_AES)
endif()
endif()
if(CONFIG_MBEDTLS_HARDWARE_ECC)
target_sources(builtin PRIVATE "${COMPONENT_DIR}/port/ecc/esp_ecc.c"
"${COMPONENT_DIR}/port/ecc/ecc_alt.c")
include_directories("${COMPONENT_DIR}/tf-psa-crypto/drivers/builtin/include/mbedtls")
if(CONFIG_SOC_ECC_SUPPORTED)
target_sources(builtin PRIVATE "${COMPONENT_DIR}/port/ecc/esp_ecc.c")
if(CONFIG_MBEDTLS_HARDWARE_ECC)
target_sources(builtin PRIVATE "${COMPONENT_DIR}/port/ecc/ecc_alt.c")
include_directories("${COMPONENT_DIR}/tf-psa-crypto/drivers/builtin/include/mbedtls")
endif()
endif()
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN OR
CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY OR CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN)
target_sources(builtin PRIVATE "${COMPONENT_DIR}/port/ecdsa/ecdsa_alt.c")
set(WRAP_FUNCTIONS_SIGN
mbedtls_ecdsa_sign
mbedtls_ecdsa_sign_restartable
mbedtls_ecdsa_write_signature
mbedtls_ecdsa_write_signature_restartable)
set(WRAP_FUNCTIONS_VERIFY
mbedtls_ecdsa_verify
mbedtls_ecdsa_verify_restartable
mbedtls_ecdsa_read_signature
mbedtls_ecdsa_read_signature_restartable)
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN OR CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY OR CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN)
# ESP_ECDSA_DRIVER_ENABLED needs to be public because users would use OPAQUE key contexts in their applications.
target_compile_definitions(tfpsacrypto PUBLIC ESP_ECDSA_DRIVER_ENABLED)
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN OR CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN)
foreach(wrap ${WRAP_FUNCTIONS_SIGN})
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=${wrap}")
endforeach()
if(CONFIG_SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=mbedtls_ecdsa_sign_det_ext")
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=mbedtls_ecdsa_sign_det_restartable")
endif()
target_compile_definitions(tfpsacrypto PRIVATE ESP_ECDSA_SIGN_DRIVER_ENABLED)
endif()
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY)
foreach(wrap ${WRAP_FUNCTIONS_VERIFY})
target_link_libraries(${COMPONENT_LIB} INTERFACE "-Wl,--wrap=${wrap}")
endforeach()
target_compile_definitions(tfpsacrypto PRIVATE ESP_ECDSA_VERIFY_DRIVER_ENABLED)
endif()
target_sources(tfpsacrypto PRIVATE "${COMPONENT_DIR}/port/psa_driver/esp_ecdsa/psa_crypto_driver_esp_ecdsa.c")
target_include_directories(tfpsacrypto PUBLIC "${COMPONENT_DIR}/port/psa_driver/include")
target_link_libraries(tfpsacrypto PRIVATE idf::efuse)
if(CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN)
target_link_libraries(builtin PRIVATE idf::tee_sec_storage)
target_link_libraries(tfpsacrypto PRIVATE idf::tee_sec_storage)
endif()
endif()
@@ -508,10 +489,6 @@ if(CONFIG_PM_ENABLE)
target_link_libraries(tfpsacrypto PRIVATE idf::esp_pm)
endif()
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN OR CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY)
target_link_libraries(builtin PRIVATE idf::efuse)
endif()
target_link_libraries(${COMPONENT_LIB} ${linkage_type} ${mbedtls_targets})
# Ensure PSA crypto initialization is included in the build
@@ -529,7 +506,7 @@ function(builtin_optional_deps component_name)
endfunction()
if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM)
builtin_optional_deps(esp_timer idf::esp_timer)
target_link_libraries(tfpsacrypto PRIVATE idf::esp_timer)
endif()
# # Link esp-cryptoauthlib to mbedtls
@@ -21,8 +21,6 @@
#include "esp_aes_internal.h"
#include "hal/aes_hal.h"
#include "mbedtls/gcm.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "soc/soc_caps.h"
File diff suppressed because it is too large Load Diff
@@ -1,161 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "mbedtls/ecp.h"
#include "mbedtls/pk.h"
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#include "hal/ecdsa_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define USE_ECDSA_KEY_FROM_KEY_MANAGER INT_MAX
/**
* @brief ECDSA private key context initialization config structure
* @note Contains configuration information like the efuse key block that should be used as the private key,
* EC group ID of the private key and if the export public key operation is supported
* by the peripheral, a flag load_pubkey that is used specify if the public key has to be populated
*/
typedef struct {
mbedtls_ecp_group_id grp_id; /*!< MbedTLS ECP group identifier */
union {
uint8_t efuse_block; /*!< EFuse block id for ECDSA private key */
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
const char *tee_key_id; /*!< TEE secure storage key id for ECDSA private key */
#endif
bool use_km_key; /*!< Use key deployed in the key manager for ECDSA operation. Note: The key must be already deployed by the application and it must be activated for the lifetime of this context */
}; /*!< Union to hold either EFuse block id or TEE secure storage key id or use key deployed in the key manager for ECDSA operation for ECDSA private key */
#if SOC_ECDSA_SUPPORT_EXPORT_PUBKEY || CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
bool load_pubkey; /*!< Export ECDSA public key from the hardware */
#endif
} esp_ecdsa_pk_conf_t;
#if SOC_ECDSA_SUPPORT_EXPORT_PUBKEY || __DOXYGEN__
/**
* @brief Populate the public key buffer of the mbedtls_ecp_keypair context.
*
* @param keypair The mbedtls ECP key-pair structure
* @param efuse_blk The efuse key block that should be used as the private key.
* The efuse block where ECDSA key is stored. If two blocks are used to store the key, then the macro HAL_ECDSA_COMBINE_KEY_BLOCKS() can be used to combine them. The macro is defined in hal/ecdsa_types.h.
* @return - 0 if successful
* - MBEDTLS_ERR_ECP_BAD_INPUT_DATA if invalid ecp group id specified
* - MBEDTLS_ERR_ECP_INVALID_KEY if efuse block with purpose ECDSA_KEY is not found
* - -1 if invalid efuse block is specified
*/
int esp_ecdsa_load_pubkey(mbedtls_ecp_keypair *keypair, int efuse_blk);
#endif // SOC_ECDSA_SUPPORT_EXPORT_PUBKEY || __DOXYGEN__
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN || __DOXYGEN__
/**
* @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
*
* @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 efuse block where ECDSA key is stored. If two blocks are used to store the key, then the macro HAL_ECDSA_COMBINE_KEY_BLOCKS() can be used to combine them. The macro is defined in hal/ecdsa_types.h.
*
* @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
*
* @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 efuse block where ECDSA key is stored. If two blocks are used to store the key, then the macro HAL_ECDSA_COMBINE_KEY_BLOCKS() can be used to combine them. The macro is defined in hal/ecdsa_types.h.
*
* @return - 0 if successful
* - -1 otherwise
*/
int esp_ecdsa_privkey_load_pk_context(mbedtls_pk_context *key_ctx, int efuse_blk);
/**
* @brief Initialize PK context and completely populate mbedtls_ecp_keypair context.
* 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.
* We also populate the ECP group field present in the mbedtls_ecp_keypair context.
* If the ECDSA peripheral of the chip supports exporting the public key,
* we can also populate the public key buffer of the mbedtls_ecp_keypair context
* if the load_pubkey flag is set in the esp_ecdsa_pk_conf_t config argument.
*
* @param key_ctx The context in which this functions stores the hardware context.
* This must be uninitialized
* @param conf ESP-ECDSA private key context initialization config structure
*
* @return - 0 if successful
* - -1 otherwise
*/
int esp_ecdsa_set_pk_context(mbedtls_pk_context *key_ctx, esp_ecdsa_pk_conf_t *conf);
#endif // CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN || __DOXYGEN__
/**
* @brief Free the PK context initialized with hardware ECDSA key.
* This function properly cleans up the manually allocated mbedtls_ecp_keypair
* structure and then frees the PK context.
*
* Note: In mbedtls v4.0, ECDSA keys are managed through PSA, so the standard
* mbedtls_pk_free() does not deallocate the manually created keypair structure.
* Always use this function instead of mbedtls_pk_free() for contexts initialized
* with esp_ecdsa_set_pk_context().
*
* @param key_ctx The PK context to free (initialized with esp_ecdsa_set_pk_context)
*/
void esp_ecdsa_free_pk_context(mbedtls_pk_context *key_ctx);
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN || __DOXYGEN__
/**
* @brief Populate the public key buffer of the mbedtls_ecp_keypair context from
* the TEE secure storage.
*
* @param keypair The mbedtls ECP key-pair structure
* @param tee_key_id The TEE secure storage key id of the private key
*
* @return - 0 if successful else MBEDTLS_ERR_ECP_BAD_INPUT_DATA
*/
int esp_ecdsa_tee_load_pubkey(mbedtls_ecp_keypair *keypair, const char *tee_key_id);
/**
* @brief Initialize PK context and fully populate the mbedtls_ecp_keypair context.
* This function modifies the MPI struct used to represent the private key `d`
* in the ECP keypair to differentiate between TEE secure storage keys and software keys.
* It also populates the ECP group field in the mbedtls_ecp_keypair context.
* Additionally, if the load_pubkey flag is set in the esp_ecdsa_pk_conf_t config argument,
* the public key buffer of the mbedtls_ecp_keypair context will be populated.
*
* @param key_ctx The context in which this function stores the TEE secure storage context.
* This must be uninitialized.
* @param conf ESP-ECDSA private key context initialization config structure.
*
* @return - 0 if successful
* - -1 otherwise
*/
int esp_ecdsa_tee_set_pk_context(mbedtls_pk_context *key_ctx, esp_ecdsa_pk_conf_t *conf);
#endif // CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN || __DOXYGEN__
#ifdef __cplusplus
}
#endif
@@ -237,6 +237,20 @@
#undef MBEDTLS_MPI_MUL_MPI_ALT
#endif
#ifdef MBEDTLS_HARDWARE_ECDSA_SIGN
#define MBEDTLS_PSA_ACCEL_ECC_SECP_R1_192
#define MBEDTLS_PSA_ACCEL_ECC_SECP_R1_256
#if SOC_ECDSA_SUPPORT_CURVE_P384
#define MBEDTLS_PSA_ACCEL_ECC_SECP_R1_384
#endif
#define MBEDTLS_PSA_ACCEL_ALG_ECDSA
#define MBEDTLS_PSA_ACCEL_ALG_DETERMINISTIC_ECDSA
#define MBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_PUBLIC_KEY
#define MBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_KEY_PAIR_BASIC
#define MBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_KEY_PAIR_IMPORT
#define MBEDTLS_PSA_ACCEL_KEY_TYPE_ECC_KEY_PAIR_EXPORT
#endif
#ifdef CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN
#define MBEDTLS_ECDSA_SIGN_ALT
#endif
@@ -1,20 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(MBEDTLS_GCM_ALT) && defined(MBEDTLS_GCM_NON_AES_CIPHER_SOFT_FALLBACK)
#endif /* MBEDTLS_GCM_ALT && MBEDTLS_GCM_NON_AES_CIPHER_SOFT_FALLBACK*/
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,865 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* ESP ECDSA PSA Crypto Opaque Driver
*
* This driver provides PSA Crypto API integration for ESP ECDSA hardware.
*/
#include <string.h>
#include "soc/soc_caps.h"
#include "esp_log.h"
#include "esp_crypto_lock.h"
#include "esp_crypto_periph_clk.h"
#include "esp_efuse.h"
#include "esp_efuse_chip.h"
#if SOC_ECDSA_SUPPORTED
#include "hal/ecdsa_types.h"
#include "hal/ecdsa_hal.h"
#include "hal/ecdsa_ll.h"
#endif /* SOC_ECDSA_SUPPORTED */
#if SOC_ECC_SUPPORTED
#include "hal/ecc_ll.h"
#include "ecc_impl.h"
#endif /* SOC_ECC_SUPPORTED */
#if SOC_MPI_SUPPORTED && SOC_ECDSA_USES_MPI
#include "hal/mpi_ll.h"
#endif /* SOC_MPI_SUPPORTED && SOC_ECDSA_USES_MPI */
#include "psa_crypto_driver_esp_ecdsa.h"
#include "psa_crypto_driver_wrappers_no_static.h"
#include "sdkconfig.h"
static const char *TAG = "psa_crypto_driver_esp_ecdsa";
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
#include "esp_tee_sec_storage.h"
#endif /* CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN */
/* Key lengths for different ECDSA curves */
#define ECDSA_KEY_LEN_P192 24
#define ECDSA_KEY_LEN_P256 32
#define ECDSA_KEY_LEN_P384 48
#define ECDSA_UNCOMPRESSED_POINT_FORMAT 0x04
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
#include "esp_timer.h"
#include "soc/chip_revision.h"
#include "hal/efuse_hal.h"
#if CONFIG_ESP_CRYPTO_DPA_PROTECTION_LEVEL_HIGH
/*
* This is the maximum time (in us) required for performing 1 ECDSA signature
* in this configuration along some additional margin considerations
*/
#define ECDSA_MAX_SIG_TIME 24000
#else /* CONFIG_ESP_CRYPTO_DPA_PROTECTION_LEVEL_HIGH */
#define ECDSA_MAX_SIG_TIME 17500
#endif /* !CONFIG_ESP_CRYPTO_DPA_PROTECTION_LEVEL_HIGH */
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM
#define DUMMY_OP_COUNT ECDSA_SIGN_MAX_DUMMY_OP_COUNT
#else /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM */
#define DUMMY_OP_COUNT 0
#endif /* !CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_MASKING_CM */
#define ECDSA_CM_FIXED_SIG_TIME ECDSA_MAX_SIG_TIME * (DUMMY_OP_COUNT + 1)
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM */
// Helper function to get curve from mbedtls group ID
static esp_ecdsa_curve_t psa_bits_to_ecdsa_curve(size_t key_len)
{
switch (key_len) {
case ECDSA_KEY_LEN_P192:
return ESP_ECDSA_CURVE_SECP192R1;
case ECDSA_KEY_LEN_P256:
return ESP_ECDSA_CURVE_SECP256R1;
#if SOC_ECDSA_SUPPORT_CURVE_P384
case ECDSA_KEY_LEN_P384:
return ESP_ECDSA_CURVE_SECP384R1;
#endif
default:
return ESP_ECDSA_CURVE_MAX;
}
}
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
static esp_tee_sec_storage_type_t esp_ecdsa_curve_to_tee_sec_storage_type(esp_ecdsa_curve_t curve)
{
switch (curve) {
case ESP_ECDSA_CURVE_SECP256R1:
return ESP_SEC_STG_KEY_ECDSA_SECP256R1;
#if CONFIG_SECURE_TEE_SEC_STG_SUPPORT_SECP384R1_SIGN
case ESP_ECDSA_CURVE_SECP384R1:
return ESP_SEC_STG_KEY_ECDSA_SECP384R1;
#endif
default:
return (esp_tee_sec_storage_type_t)-1;
}
}
#endif /* CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN */
static void change_endianess(const uint8_t *old_point, uint8_t *new_point, uint8_t len)
{
/* When the size is less than MAX_ECDSA_COMPONENT_LEN, it should be padded with 0 bytes*/
memset(new_point, 0x0, len);
for(int i = 0; i < len; i++) {
new_point[i] = old_point[len - i - 1];
}
}
static psa_status_t validate_ecdsa_sha_alg(psa_algorithm_t alg, const esp_ecdsa_curve_t curve)
{
if ((PSA_ALG_SIGN_GET_HASH(alg) != PSA_ALG_SHA_256 && (curve == ESP_ECDSA_CURVE_SECP192R1 || curve == ESP_ECDSA_CURVE_SECP256R1))
#if SOC_ECDSA_SUPPORT_CURVE_P384
|| (PSA_ALG_SIGN_GET_HASH(alg) != PSA_ALG_SHA_384 && curve == ESP_ECDSA_CURVE_SECP384R1)
#endif
) {
return PSA_ERROR_NOT_SUPPORTED;
}
return PSA_SUCCESS;
}
#if SOC_ECDSA_SUPPORTED
static void esp_ecdsa_acquire_hardware(void)
{
esp_crypto_ecdsa_lock_acquire();
esp_crypto_ecdsa_enable_periph_clk(true);
esp_crypto_ecc_enable_periph_clk(true);
#if SOC_ECDSA_USES_MPI
if (ecdsa_ll_is_mpi_required()) {
/* We need to reset the MPI peripheral because ECDSA peripheral
* of some targets use the MPI peripheral as well.
*/
esp_crypto_mpi_enable_periph_clk(true);
}
#endif /* SOC_ECDSA_USES_MPI */
}
static void esp_ecdsa_release_hardware(void)
{
esp_crypto_ecdsa_enable_periph_clk(false);
esp_crypto_ecc_enable_periph_clk(false);
#if SOC_ECDSA_USES_MPI
if (ecdsa_ll_is_mpi_required()) {
esp_crypto_mpi_enable_periph_clk(false);
}
#endif /* SOC_ECDSA_USES_MPI */
esp_crypto_ecdsa_lock_release();
}
psa_status_t esp_ecdsa_transparent_verify_hash_start(
esp_ecdsa_transparent_verify_hash_operation_t *operation,
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
psa_algorithm_t alg,
const uint8_t *hash,
size_t hash_length,
const uint8_t *signature,
size_t signature_length)
{
psa_status_t status = PSA_ERROR_GENERIC_ERROR;
// Check if the ECDSA peripheral is supported on this chip revision
if (!ecdsa_ll_is_supported()) {
ESP_LOGE(TAG, "ECDSA peripheral not supported on this chip revision");
return PSA_ERROR_NOT_SUPPORTED;
}
if (!operation || !attributes || !key_buffer || !hash || !signature) {
return PSA_ERROR_INVALID_ARGUMENT;
}
// Check if ECDSA algorithm
if (!PSA_ALG_IS_ECDSA(alg)) {
return PSA_ERROR_NOT_SUPPORTED;
}
size_t key_len = PSA_BITS_TO_BYTES(psa_get_key_bits(attributes));
esp_ecdsa_curve_t curve = psa_bits_to_ecdsa_curve(key_len);
if (curve == ESP_ECDSA_CURVE_MAX) {
return PSA_ERROR_NOT_SUPPORTED;
}
status = validate_ecdsa_sha_alg(alg, curve);
if (status != PSA_SUCCESS) {
return status;
}
if ((curve == ESP_ECDSA_CURVE_SECP192R1 && hash_length != ECDSA_SHA_LEN) ||
(curve == ESP_ECDSA_CURVE_SECP256R1 && hash_length != ECDSA_SHA_LEN)
#if SOC_ECDSA_SUPPORT_CURVE_P384
|| (curve == ESP_ECDSA_CURVE_SECP384R1 && hash_length != ECDSA_SHA_LEN_P384)
#endif
) {
return PSA_ERROR_NOT_SUPPORTED;
}
if (signature_length != 2 * key_len) {
return PSA_ERROR_INVALID_SIGNATURE;
}
const uint8_t *public_key_buffer = NULL;
size_t public_key_buffer_size = 0;
uint8_t public_key[2 * MAX_ECDSA_COMPONENT_LEN + 1];
/* The contents of key_buffer may either be the private key len bytes
* (private key format), or 0x04 followed by the public key len bytes (public
* key format). To ensure the key is in the latter format, the public key
* is exported. */
if (!PSA_KEY_TYPE_IS_PUBLIC_KEY(psa_get_key_type(attributes))) {
// Private key is provided, convert it to public key
size_t public_key_size = sizeof(public_key);
size_t public_key_length = 0;
status = psa_driver_wrapper_export_public_key(
attributes,
key_buffer,
key_buffer_size,
public_key,
public_key_size,
&public_key_length);
if (status != PSA_SUCCESS) {
return status;
}
public_key_buffer = public_key;
public_key_buffer_size = public_key_length;
} else {
public_key_buffer = key_buffer;
public_key_buffer_size = key_buffer_size;
}
if (public_key_buffer[0] != ECDSA_UNCOMPRESSED_POINT_FORMAT) {
return PSA_ERROR_INVALID_ARGUMENT;
}
// As PSA supports only uncompressed point format, the public key buffer size should be 2 * key_len + 1
if (public_key_buffer_size != 2 * key_len + 1) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Verify the public key */
ecc_point_t point;
memset(&point, 0, sizeof(ecc_point_t));
change_endianess(public_key_buffer + 1, point.x, key_len);
change_endianess(public_key_buffer + 1 + key_len, point.y, key_len);
point.len = key_len;
if (!esp_ecc_point_verify(&point)) {
return PSA_ERROR_INVALID_ARGUMENT;
}
memset(operation, 0, sizeof(esp_ecdsa_transparent_verify_hash_operation_t));
operation->curve = curve;
operation->key_len = key_len;
operation->sha_len = hash_length;
change_endianess(hash, operation->sha, key_len);
change_endianess(signature, operation->r, key_len);
change_endianess(signature + key_len, operation->s, key_len);
/* The public key buffer is in the format 0x04 followed by the 2*key_len bytes public key */
/* The first byte is the format byte, which is ECDSA_UNCOMPRESSED_POINT_FORMAT */
/* The next key_len bytes are the x coordinate */
/* The next key_len bytes are the y coordinate */
change_endianess(public_key_buffer + 1, operation->qx, key_len);
change_endianess(public_key_buffer + 1 + key_len, operation->qy, key_len);
return PSA_SUCCESS;
}
psa_status_t esp_ecdsa_transparent_verify_hash_complete(esp_ecdsa_transparent_verify_hash_operation_t *operation)
{
if (!operation) {
return PSA_ERROR_INVALID_ARGUMENT;
}
esp_ecdsa_acquire_hardware();
ecdsa_hal_config_t conf = {
.mode = ECDSA_MODE_SIGN_VERIFY,
.curve = operation->curve,
.sha_mode = ECDSA_Z_USER_PROVIDED,
};
int ret = ecdsa_hal_verify_signature(&conf, operation->sha, operation->r, operation->s, operation->qx, operation->qy, operation->key_len);
esp_ecdsa_release_hardware();
if (ret != 0) {
return PSA_ERROR_INVALID_SIGNATURE;
}
return PSA_SUCCESS;
}
psa_status_t esp_ecdsa_transparent_verify_hash_abort(esp_ecdsa_transparent_verify_hash_operation_t *operation)
{
if (operation) {
memset(operation, 0, sizeof(esp_ecdsa_transparent_verify_hash_operation_t));
}
return PSA_SUCCESS;
}
psa_status_t esp_ecdsa_transparent_verify_hash(
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
psa_algorithm_t alg,
const uint8_t *hash,
size_t hash_length,
const uint8_t *signature,
size_t signature_length)
{
psa_status_t status = PSA_ERROR_GENERIC_ERROR;
esp_ecdsa_transparent_verify_hash_operation_t operation;
status = esp_ecdsa_transparent_verify_hash_start(&operation, attributes, key_buffer, key_buffer_size, alg, hash, hash_length, signature, signature_length);
if (status != PSA_SUCCESS) {
return status;
}
status = esp_ecdsa_transparent_verify_hash_complete(&operation);
if (status != PSA_SUCCESS) {
return status;
}
return esp_ecdsa_transparent_verify_hash_abort(&operation);
}
#endif /* SOC_ECDSA_SUPPORTED */
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
#if SOC_ECDSA_SUPPORT_CURVE_P384
/**
* @brief Check if the extracted efuse blocks are valid
*
* @param high_blk High efuse block number
* @param low_blk Low efuse block number
* @return true if both blocks are valid, false otherwise
*/
static bool is_efuse_blk_valid(int high_blk, int low_blk)
{
return (high_blk >= EFUSE_BLK0 && high_blk < EFUSE_BLK_MAX &&
low_blk >= EFUSE_BLK0 && low_blk < EFUSE_BLK_MAX);
}
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
/**
* @brief Validate if the efuse block(s) have the appropriate ECDSA key purpose for the given curve
*
* This function validates that the provided efuse block(s) have been programmed with the appropriate
* ECDSA key purpose for the specified curve type. It handles both curve-specific key purposes
* (when SOC_ECDSA_SUPPORT_CURVE_SPECIFIC_KEY_PURPOSES is defined) and generic ECDSA key purpose.
*
* For SECP384R1 curve, it checks for both high and low key blocks when supported.
* For SECP192R1 and SECP256R1 curves, it validates the single block.
*
* @param[in] curve The ECDSA curve to validate the key purpose for
* @param[in] efuse_blk The efuse block(s) to validate (can be combined for 384-bit keys)
*
* @return
* - 0 on success (block(s) have correct purpose)
* - PSA_ERROR_INVALID_ARGUMENT if input parameters are invalid
* - PSA_ERROR_NOT_PERMITTED if block(s) don't have appropriate key purpose
*/
static psa_status_t esp_ecdsa_validate_efuse_block(esp_ecdsa_curve_t curve, int efuse_blk)
{
int low_blk = efuse_blk;
esp_efuse_purpose_t expected_key_purpose_low;
#if SOC_ECDSA_SUPPORT_CURVE_SPECIFIC_KEY_PURPOSES
#if SOC_ECDSA_SUPPORT_CURVE_P384
int high_blk;
HAL_ECDSA_EXTRACT_KEY_BLOCKS(efuse_blk, high_blk, low_blk);
if (!is_efuse_blk_valid(high_blk, low_blk)) {
ESP_LOGE(TAG, "Invalid efuse block selected");
return PSA_ERROR_INVALID_ARGUMENT;
}
esp_efuse_purpose_t expected_key_purpose_high;
#endif
switch (curve) {
case ESP_ECDSA_CURVE_SECP192R1:
expected_key_purpose_low = ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY_P192;
break;
case ESP_ECDSA_CURVE_SECP256R1:
expected_key_purpose_low = ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY_P256;
break;
#if SOC_ECDSA_SUPPORT_CURVE_P384
case ESP_ECDSA_CURVE_SECP384R1:
expected_key_purpose_low = ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY_P384_L;
expected_key_purpose_high = ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY_P384_H;
break;
#endif
default:
ESP_LOGE(TAG, "Unsupported ECDSA curve: %ld", curve);
return PSA_ERROR_INVALID_ARGUMENT;
}
#else /* SOC_ECDSA_SUPPORT_CURVE_SPECIFIC_KEY_PURPOSES */
expected_key_purpose_low = ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY;
#endif /* !SOC_ECDSA_SUPPORT_CURVE_SPECIFIC_KEY_PURPOSES */
if (expected_key_purpose_low != esp_efuse_get_key_purpose((esp_efuse_block_t)low_blk)) {
ESP_LOGE(TAG, "Key burned in efuse has incorrect purpose, expected: %d, got: %d, low_blk: %d", expected_key_purpose_low, esp_efuse_get_key_purpose((esp_efuse_block_t)low_blk), low_blk);
return PSA_ERROR_NOT_PERMITTED;
}
#if SOC_ECDSA_SUPPORT_CURVE_SPECIFIC_KEY_PURPOSES && SOC_ECDSA_SUPPORT_CURVE_P384
// Only check high block purpose for P384 curves that actually use it
if (curve == ESP_ECDSA_CURVE_SECP384R1 &&
expected_key_purpose_high != esp_efuse_get_key_purpose((esp_efuse_block_t)high_blk)) {
ESP_LOGE(TAG, "Key burned in efuse has incorrect purpose for high block");
return PSA_ERROR_NOT_PERMITTED;
}
#endif
return PSA_SUCCESS;
}
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN */
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN || CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
static psa_status_t validate_ecdsa_opaque_key_attributes(const psa_key_attributes_t *attributes, const esp_ecdsa_opaque_key_t *opaque_key)
{
esp_ecdsa_curve_t expected_curve = psa_bits_to_ecdsa_curve(PSA_BITS_TO_BYTES(psa_get_key_bits(attributes)));
if (expected_curve != opaque_key->curve) {
return PSA_ERROR_INVALID_ARGUMENT;
}
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
if (!(opaque_key->use_km_key
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
|| opaque_key->tee_key_id
#endif
)) {
psa_status_t status = esp_ecdsa_validate_efuse_block(opaque_key->curve, opaque_key->efuse_block);
if (status != PSA_SUCCESS) {
return status;
}
}
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN */
return PSA_SUCCESS;
}
psa_status_t esp_ecdsa_opaque_import_key(
const psa_key_attributes_t *attributes,
const uint8_t *data,
size_t data_length,
uint8_t *key_buffer,
size_t key_buffer_size,
size_t *key_buffer_length,
size_t *bits)
{
if (!attributes || !data || data_length < 1 || !key_buffer || !key_buffer_length || !bits) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (key_buffer_size < sizeof(esp_ecdsa_opaque_key_t) || data_length < sizeof(esp_ecdsa_opaque_key_t)) {
return PSA_ERROR_BUFFER_TOO_SMALL;
}
const esp_ecdsa_opaque_key_t *opaque_key = (const esp_ecdsa_opaque_key_t *) data;
psa_status_t status = validate_ecdsa_opaque_key_attributes(attributes, opaque_key);
if (status != PSA_SUCCESS) {
return status;
}
memcpy(key_buffer, opaque_key, sizeof(esp_ecdsa_opaque_key_t));
*key_buffer_length = sizeof(esp_ecdsa_opaque_key_t);
*bits = psa_get_key_bits(attributes);
return PSA_SUCCESS;
}
psa_status_t esp_ecdsa_opaque_sign_hash_start(
esp_ecdsa_opaque_sign_hash_operation_t *operation,
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
psa_algorithm_t alg,
const uint8_t *hash,
size_t hash_length)
{
if (!operation || !attributes || !key_buffer || !hash) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (key_buffer_size < sizeof(esp_ecdsa_opaque_key_t)) {
return PSA_ERROR_INVALID_ARGUMENT;
}
// Check if ECDSA algorithm
if (!PSA_ALG_IS_ECDSA(alg)) {
return PSA_ERROR_INVALID_ARGUMENT;
}
esp_ecdsa_opaque_key_t *opaque_key = (esp_ecdsa_opaque_key_t *) key_buffer;
psa_status_t status = validate_ecdsa_opaque_key_attributes(attributes, opaque_key);
if (status != PSA_SUCCESS) {
return status;
}
status = validate_ecdsa_sha_alg(alg, opaque_key->curve);
if (status != PSA_SUCCESS) {
return status;
}
size_t component_len = PSA_BITS_TO_BYTES(psa_get_key_bits(attributes));
// Validate hash length
if ((opaque_key->curve == ESP_ECDSA_CURVE_SECP192R1 && hash_length != ECDSA_SHA_LEN
#if SOC_ECDSA_SUPPORTED
&& esp_efuse_is_ecdsa_p192_curve_supported()
#endif
) || (opaque_key->curve == ESP_ECDSA_CURVE_SECP256R1 && hash_length != ECDSA_SHA_LEN
#if SOC_ECDSA_SUPPORTED
&& esp_efuse_is_ecdsa_p256_curve_supported()
#endif
)
#if SOC_ECDSA_SUPPORT_CURVE_P384
|| (opaque_key->curve == ESP_ECDSA_CURVE_SECP384R1 && hash_length != ECDSA_SHA_LEN_P384)
#endif
) {
return PSA_ERROR_INVALID_ARGUMENT;
}
memset(operation, 0, sizeof(esp_ecdsa_opaque_sign_hash_operation_t));
operation->key_len = component_len;
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
if (opaque_key->tee_key_id) {
// The ESP-TEE API expects the hash in big endian order
memcpy(operation->sha, hash, component_len);
} else
#endif /* CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN */
{
change_endianess(hash, operation->sha, component_len);
}
operation->opaque_key = opaque_key;
operation->alg = alg;
operation->sha_len = hash_length;
return PSA_SUCCESS;
}
psa_status_t esp_ecdsa_opaque_sign_hash_complete(
esp_ecdsa_opaque_sign_hash_operation_t *operation,
uint8_t *signature, size_t signature_size,
size_t *signature_length)
{
if (!operation || !signature || !signature_length) {
return PSA_ERROR_INVALID_ARGUMENT;
}
size_t component_len = operation->key_len;
if (signature_size < 2 * component_len) {
return PSA_ERROR_BUFFER_TOO_SMALL;
}
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
esp_ecdsa_opaque_key_t *opaque_key = operation->opaque_key;
if (opaque_key->tee_key_id) {
esp_tee_sec_storage_type_t tee_sec_storage_type = esp_ecdsa_curve_to_tee_sec_storage_type(opaque_key->curve);
if (tee_sec_storage_type == (esp_tee_sec_storage_type_t) -1) {
return PSA_ERROR_INVALID_ARGUMENT;
}
esp_tee_sec_storage_key_cfg_t cfg = {
.id = opaque_key->tee_key_id,
.type = tee_sec_storage_type
};
esp_tee_sec_storage_ecdsa_sign_t sign = {};
esp_err_t err = esp_tee_sec_storage_ecdsa_sign(&cfg, operation->sha, operation->sha_len, &sign);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to get signature: 0x%08lx", err);
return PSA_ERROR_INVALID_ARGUMENT;
}
memcpy(signature, sign.signature, 2 * component_len);
*signature_length = 2 * component_len;
} else
#endif /* CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN */
{
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
// Check if the ECDSA peripheral is supported on this chip revision
if (!ecdsa_ll_is_supported()) {
ESP_LOGE(TAG, "ECDSA peripheral not supported on this chip revision");
return PSA_ERROR_NOT_SUPPORTED;
}
ecdsa_sign_type_t k_type = ECDSA_K_TYPE_TRNG;
#if CONFIG_MBEDTLS_ECDSA_DETERMINISTIC && SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE
if (PSA_ALG_ECDSA_IS_DETERMINISTIC(operation->alg)) {
if (ecdsa_ll_is_deterministic_mode_supported()) {
k_type = ECDSA_K_TYPE_DETERMINISITIC;
} else {
return PSA_ERROR_NOT_SUPPORTED;
}
}
#endif /* CONFIG_MBEDTLS_ECDSA_DETERMINISTIC && SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE */
uint8_t zeroes[MAX_ECDSA_COMPONENT_LEN] = {0};
// Acquire hardware
esp_ecdsa_acquire_hardware();
bool process_again = false;
#if CONFIG_MBEDTLS_ECDSA_DETERMINISTIC && !SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP
uint16_t deterministic_loop_number __attribute__((unused)) = 1;
#endif /* CONFIG_MBEDTLS_ECDSA_DETERMINISTIC && !SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP */
do {
ecdsa_hal_config_t conf = {
.mode = ECDSA_MODE_SIGN_GEN,
.curve = operation->opaque_key->curve,
.sha_mode = ECDSA_Z_USER_PROVIDED,
.sign_type = k_type,
};
#if CONFIG_MBEDTLS_ECDSA_DETERMINISTIC && !SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP
if (k_type == ECDSA_K_TYPE_DETERMINISITIC) {
conf.loop_number = deterministic_loop_number++;
}
#endif /* CONFIG_MBEDTLS_ECDSA_DETERMINISTIC && !SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP */
// Set key source (consistent with esp_ecdsa_pk_conf_t)
if (operation->opaque_key->use_km_key) {
conf.use_km_key = 1;
} else {
conf.efuse_key_blk = operation->opaque_key->efuse_block;
}
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
uint64_t sig_time = esp_timer_get_time();
#endif
// Generate signature
ecdsa_hal_gen_signature(&conf, operation->sha, operation->r, operation->s, component_len);
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM
if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 102)) {
sig_time = esp_timer_get_time() - sig_time;
if (sig_time < ECDSA_CM_FIXED_SIG_TIME) {
esp_rom_delay_us(ECDSA_CM_FIXED_SIG_TIME - sig_time);
}
}
#endif
// Check if we need to retry (zero signature or operation failed)
process_again = !ecdsa_hal_get_operation_result()
|| !memcmp(operation->r, zeroes, component_len)
|| !memcmp(operation->s, zeroes, component_len);
#if CONFIG_MBEDTLS_ECDSA_DETERMINISTIC && SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE && !SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP
if (k_type == ECDSA_K_TYPE_DETERMINISITIC) {
process_again |= !ecdsa_hal_det_signature_k_check();
}
#endif /* CONFIG_MBEDTLS_ECDSA_DETERMINISTIC && SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE && !SOC_ECDSA_SUPPORT_HW_DETERMINISTIC_LOOP */
} while (process_again);
esp_ecdsa_release_hardware();
// Convert r from little-endian to big-endian and copy to output
change_endianess(operation->r, signature, component_len);
// Convert s from little-endian to big-endian and copy to output
change_endianess(operation->s, signature + component_len, component_len);
#else
// This is an invalid operation as the hardware ECDSA signing is not supported on this chip
// and still the key is opaque.
return PSA_ERROR_NOT_SUPPORTED;
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN */
}
*signature_length = 2 * component_len;
return PSA_SUCCESS;
}
psa_status_t esp_ecdsa_opaque_sign_hash_abort(esp_ecdsa_opaque_sign_hash_operation_t *operation)
{
if (operation) {
memset(operation, 0, sizeof(esp_ecdsa_opaque_sign_hash_operation_t));
}
return PSA_SUCCESS;
}
psa_status_t esp_ecdsa_opaque_sign_hash(
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
psa_algorithm_t alg,
const uint8_t *hash,
size_t hash_length,
uint8_t *signature,
size_t signature_size,
size_t *signature_length)
{
psa_status_t status = PSA_ERROR_GENERIC_ERROR;
esp_ecdsa_opaque_sign_hash_operation_t operation;
status = esp_ecdsa_opaque_sign_hash_start(&operation, attributes, key_buffer, key_buffer_size, alg, hash, hash_length);
if (status != PSA_SUCCESS) {
esp_ecdsa_opaque_sign_hash_abort(&operation);
return status;
}
status = esp_ecdsa_opaque_sign_hash_complete(&operation, signature, signature_size, signature_length);
if (status != PSA_SUCCESS) {
esp_ecdsa_opaque_sign_hash_abort(&operation);
return status;
}
return esp_ecdsa_opaque_sign_hash_abort(&operation);
}
psa_status_t esp_ecdsa_opaque_export_public_key(
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
uint8_t *data,
size_t data_size,
size_t *data_length)
{
psa_status_t status = PSA_ERROR_GENERIC_ERROR;
if (!attributes || !key_buffer || !data || !data_length) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (key_buffer_size < sizeof(esp_ecdsa_opaque_key_t)) {
return PSA_ERROR_INVALID_ARGUMENT;
}
const esp_ecdsa_opaque_key_t *opaque_key = (const esp_ecdsa_opaque_key_t *) key_buffer;
status = validate_ecdsa_opaque_key_attributes(attributes, opaque_key);
if (status != PSA_SUCCESS) {
return status;
}
// Get curve parameters from attributes
size_t key_len = PSA_BITS_TO_BYTES(psa_get_key_bits(attributes));
// As the ESP ECDSA driver only supports uncompressed point format,
// the public key is exported in uncompressed point format (ECDSA_UNCOMPRESSED_POINT_FORMAT || x || y)
// Need 1 byte for format + key_len bytes for x + key_len bytes for y
size_t required_size = 1 + (2 * key_len);
if (data_size < required_size) {
return PSA_ERROR_BUFFER_TOO_SMALL;
}
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
if (opaque_key->tee_key_id) {
esp_tee_sec_storage_type_t tee_sec_storage_type = esp_ecdsa_curve_to_tee_sec_storage_type(opaque_key->curve);
if (tee_sec_storage_type == (esp_tee_sec_storage_type_t) -1) {
return PSA_ERROR_INVALID_ARGUMENT;
}
esp_tee_sec_storage_key_cfg_t cfg = {
.id = opaque_key->tee_key_id,
.type = tee_sec_storage_type,
};
esp_tee_sec_storage_ecdsa_pubkey_t pubkey;
if (esp_tee_sec_storage_ecdsa_get_pubkey(&cfg, &pubkey) != ESP_OK) {
ESP_LOGE(TAG, "Failed to get public key from secure storage");
return PSA_ERROR_GENERIC_ERROR;
}
data[0] = ECDSA_UNCOMPRESSED_POINT_FORMAT;
memcpy(data + 1, pubkey.pub_x, key_len);
memcpy(data + 1 + key_len, pubkey.pub_y, key_len);
}
else
#endif /* CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN */
{
#if SOC_ECDSA_SUPPORT_EXPORT_PUBKEY
// Check if the ECDSA peripheral is supported on this chip revision
if (!ecdsa_ll_is_supported()) {
ESP_LOGE(TAG, "ECDSA peripheral not supported on this chip revision");
return PSA_ERROR_NOT_SUPPORTED;
}
uint8_t zeroes[MAX_ECDSA_COMPONENT_LEN] = {0};
uint8_t qx[MAX_ECDSA_COMPONENT_LEN];
uint8_t qy[MAX_ECDSA_COMPONENT_LEN];
ecdsa_hal_config_t conf = {
.mode = ECDSA_MODE_EXPORT_PUBKEY,
.curve = opaque_key->curve,
};
if (opaque_key->use_km_key) {
conf.use_km_key = 1;
} else {
conf.efuse_key_blk = opaque_key->efuse_block;
}
esp_ecdsa_acquire_hardware();
bool process_again = false;
do {
ecdsa_hal_export_pubkey(&conf, qx, qy, key_len);
process_again = !ecdsa_hal_get_operation_result()
|| !memcmp(qx, zeroes, key_len)
|| !memcmp(qy, zeroes, key_len);
} while (process_again);
esp_ecdsa_release_hardware();
// Format: uncompressed point (ECDSA_UNCOMPRESSED_POINT_FORMAT byte followed by x and y coordinates)
data[0] = ECDSA_UNCOMPRESSED_POINT_FORMAT;
// Convert qx from little-endian to big-endian and copy to output
change_endianess(qx, data + 1, key_len);
// Convert qy from little-endian to big-endian and copy to output
change_endianess(qy, data + 1 + key_len, key_len);
#else
// This is an invalid operation as the export public key is not supported on this chip
// and still the key is opaque
return PSA_ERROR_NOT_SUPPORTED;
#endif /* SOC_ECDSA_SUPPORT_EXPORT_PUBKEY */
}
*data_length = required_size;
return PSA_SUCCESS;
}
size_t esp_ecdsa_opaque_size_function(
psa_key_type_t key_type,
size_t key_bits)
{
(void)key_type;
(void)key_bits;
// Opaque keys always use the same size structure
return sizeof(esp_ecdsa_opaque_key_t);
}
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN */
@@ -0,0 +1,241 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#if defined(ESP_ECDSA_DRIVER_ENABLED)
#ifndef PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT
#define PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT
#endif
#include "psa/crypto.h"
#include "psa/crypto_types.h"
#include "soc/soc_caps.h"
#include "psa_crypto_driver_esp_ecdsa_contexts.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ESP ECDSA PSA driver location
*
* Vendor-specific location for ESP hardware ECDSA keys.
* Bits 8-31 are location, using vendor flag (0x800000) + ESP vendor ID.
*/
#define PSA_KEY_LOCATION_ESP_ECDSA ((psa_key_location_t) 0x800001)
/**
* @brief Construct a lifetime for ESP ECDSA keys with default persistence
*/
#define PSA_KEY_LIFETIME_ESP_ECDSA \
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION( \
PSA_KEY_PERSISTENCE_DEFAULT, \
PSA_KEY_LOCATION_ESP_ECDSA)
/**
* @brief Construct a volatile lifetime for ESP ECDSA keys
*/
#define PSA_KEY_LIFETIME_ESP_ECDSA_VOLATILE \
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION( \
PSA_KEY_PERSISTENCE_VOLATILE, \
PSA_KEY_LOCATION_ESP_ECDSA)
/**
* @brief Sign a hash using ESP ECDSA opaque driver
*
* @param attributes Key attributes
* @param key_buffer Opaque key buffer (esp_ecdsa_opaque_key_t)
* @param key_buffer_size Size of key buffer
* @param alg Signing algorithm
* @param hash Hash to sign
* @param hash_length Length of hash
* @param signature Output buffer for signature
* @param signature_size Size of signature buffer
* @param signature_length Actual signature length
* @return psa_status_t
*/
psa_status_t esp_ecdsa_opaque_sign_hash(
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
psa_algorithm_t alg,
const uint8_t *hash,
size_t hash_length,
uint8_t *signature,
size_t signature_size,
size_t *signature_length);
/**
* @brief Import an ESP ECDSA key reference (not actual key material)
*
* This function imports an opaque reference to a key stored in eFuse or Key Manager.
* The import data should contain the esp_ecdsa_opaque_key_t structure.
*
* @param attributes Key attributes
* @param data Import data (esp_ecdsa_opaque_key_t)
* @param data_length Length of import data
* @param key_buffer Output buffer for opaque key
* @param key_buffer_size Size of output buffer
* @param key_buffer_length Actual key buffer length
* @param bits Key size in bits
* @return psa_status_t
*/
psa_status_t esp_ecdsa_opaque_import_key(
const psa_key_attributes_t *attributes,
const uint8_t *data,
size_t data_length,
uint8_t *key_buffer,
size_t key_buffer_size,
size_t *key_buffer_length,
size_t *bits);
/**
* @brief Export the public key from an ESP ECDSA opaque key
*
* @param attributes Key attributes
* @param key_buffer Opaque key buffer
* @param key_buffer_size Size of key buffer
* @param data Output buffer for public key
* @param data_size Size of output buffer
* @param data_length Actual public key length
* @return psa_status_t
*/
psa_status_t esp_ecdsa_opaque_export_public_key(
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
uint8_t *data,
size_t data_size,
size_t *data_length);
/**
* @brief Get the key buffer size for an ESP ECDSA opaque key
*
* @param key_type Key type
* @param key_bits Key size in bits
* @return size_t Required buffer size
*/
size_t esp_ecdsa_opaque_size_function(
psa_key_type_t key_type,
size_t key_bits);
/**
* @brief Verify a hash using ESP ECDSA transparent driver
*
* @param attributes Key attributes
* @param key_buffer Key buffer
* @param key_buffer_size Size of key buffer
* @param alg Algorithm
* @param hash Hash to verify
* @param hash_length Length of hash
* @param signature Signature to verify
* @param signature_length Length of signature
* @return psa_status_t
*/
psa_status_t esp_ecdsa_transparent_verify_hash(
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
psa_algorithm_t alg,
const uint8_t *hash,
size_t hash_length,
const uint8_t *signature,
size_t signature_length);
/**
* @brief Start a hash verification operation using ESP ECDSA transparent driver
*
* @param operation Operation context
* @param attributes Key attributes
* @param key_buffer Key buffer
* @param key_buffer_size Size of key buffer
* @param alg Algorithm
* @param hash Hash to verify
* @param hash_length Length of hash
* @param signature Signature to verify
* @param signature_length Length of signature
* @return psa_status_t
*/
psa_status_t esp_ecdsa_transparent_verify_hash_start(
esp_ecdsa_transparent_verify_hash_operation_t *operation,
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
psa_algorithm_t alg,
const uint8_t *hash,
size_t hash_length,
const uint8_t *signature,
size_t signature_length);
/**
* @brief Complete a hash verification operation using ESP ECDSA transparent driver
*
* @param operation Operation context
* @return psa_status_t
*/
psa_status_t esp_ecdsa_transparent_verify_hash_complete(
esp_ecdsa_transparent_verify_hash_operation_t *operation);
/**
* @brief Abort a hash verification operation using ESP ECDSA transparent driver
*
* @param operation Operation context
* @return psa_status_t
*/
psa_status_t esp_ecdsa_transparent_verify_hash_abort(
esp_ecdsa_transparent_verify_hash_operation_t *operation);
/**
* @brief Start a hash signing operation using ESP ECDSA opaque driver
*
* @param operation Operation context
* @param attributes Key attributes
* @param key_buffer Key buffer
* @param key_buffer_size Size of key buffer
* @param alg Algorithm
* @param hash Hash to sign
* @param hash_length Length of hash
* @return psa_status_t
*/
psa_status_t esp_ecdsa_opaque_sign_hash_start(
esp_ecdsa_opaque_sign_hash_operation_t *operation,
const psa_key_attributes_t *attributes,
const uint8_t *key_buffer,
size_t key_buffer_size,
psa_algorithm_t alg,
const uint8_t *hash,
size_t hash_length);
/**
* @brief Complete a hash signing operation using ESP ECDSA opaque driver
*
* @param operation Operation context
* @param signature Signature buffer
* @param signature_size Size of signature buffer
* @param signature_length Actual signature length
* @return psa_status_t
*/
psa_status_t esp_ecdsa_opaque_sign_hash_complete(
esp_ecdsa_opaque_sign_hash_operation_t *operation,
uint8_t *signature, size_t signature_size,
size_t *signature_length);
/**
* @brief Abort a hash signing operation using ESP ECDSA opaque driver
*
* @param operation Operation context
* @return psa_status_t
*/
psa_status_t esp_ecdsa_opaque_sign_hash_abort(
esp_ecdsa_opaque_sign_hash_operation_t *operation);
#ifdef __cplusplus
}
#endif
#endif /* ESP_ECDSA_DRIVER_ENABLED */
@@ -0,0 +1,80 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "stdint.h"
#include "stdbool.h"
#include "soc/soc_caps.h"
#include "psa/crypto.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#if SOC_ECDSA_SUPPORT_CURVE_P384
#define MAX_ECDSA_COMPONENT_LEN 48
#define MAX_ECDSA_SHA_LEN 48
#else
#define MAX_ECDSA_COMPONENT_LEN 32
#define MAX_ECDSA_SHA_LEN 32
#endif
#define ECDSA_SHA_LEN 32
#if SOC_ECDSA_SUPPORT_CURVE_P384
#define ECDSA_SHA_LEN_P384 48
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
/**
* @brief ECDSA curve options
*/
typedef enum {
ESP_ECDSA_CURVE_SECP192R1,
ESP_ECDSA_CURVE_SECP256R1,
ESP_ECDSA_CURVE_SECP384R1,
ESP_ECDSA_CURVE_MAX,
} esp_ecdsa_curve_t;
/**
* @brief Structure to store opaque key metadata.
*/
typedef struct {
esp_ecdsa_curve_t curve; /**< ECDSA curve */
#if CONFIG_MBEDTLS_TEE_SEC_STG_ECDSA_SIGN
const char *tee_key_id; /**< TEE secure storage key id */
#endif
bool use_km_key; /**< Use key deployed in the key manager */
uint8_t efuse_block; /**< eFuse block id for ECDSA private key */
} esp_ecdsa_opaque_key_t;
#if !(__DOXYGEN__) // No need to document these structures, these are internal to the driver
/* The buffers are stored in the little-endian format */
typedef struct {
esp_ecdsa_curve_t curve;
uint8_t r[MAX_ECDSA_COMPONENT_LEN];
uint8_t s[MAX_ECDSA_COMPONENT_LEN];
uint8_t qx[MAX_ECDSA_COMPONENT_LEN];
uint8_t qy[MAX_ECDSA_COMPONENT_LEN];
uint8_t sha[MAX_ECDSA_SHA_LEN];
size_t sha_len;
size_t key_len;
} esp_ecdsa_transparent_verify_hash_operation_t;
/* The buffers are stored in the little-endian format */
typedef struct {
psa_algorithm_t alg;
esp_ecdsa_opaque_key_t *opaque_key;
uint8_t r[MAX_ECDSA_COMPONENT_LEN];
uint8_t s[MAX_ECDSA_COMPONENT_LEN];
uint8_t sha[MAX_ECDSA_SHA_LEN];
size_t sha_len;
size_t key_len;
} esp_ecdsa_opaque_sign_hash_operation_t;
#endif /* !(__DOXYGEN__) */
#ifdef __cplusplus
}
#endif
@@ -1,146 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include "sys/param.h"
#define MBEDTLS_DECLARE_PRIVATE_IDENTIFIERS
#include "esp_heap_caps.h"
#include "mbedtls/gcm.h"
#include "mbedtls/private/gcm.h"
#include "sdkconfig.h"
#include "unity.h"
#if CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER
typedef struct {
uint8_t *plaintext;
size_t plaintext_length;
uint8_t *aad_buf;
size_t aad_length;
uint8_t *iv;
size_t iv_length;
uint8_t *key;
size_t key_bits;
size_t tag_len;
} gcm_test_cfg_t;
typedef struct {
const uint8_t *expected_tag;
const uint8_t *ciphertext_last_block; // Last block of the ciphertext
} gcm_test_expected_res_t;
typedef enum {
GCM_TEST_CRYPT_N_TAG,
GCM_TEST_START_UPDATE_FINISH,
} gcm_test_type_t;
static void gcm_test(gcm_test_cfg_t *cfg, gcm_test_expected_res_t *res, gcm_test_type_t gcm_type)
{
mbedtls_gcm_context ctx;
mbedtls_cipher_id_t cipher = MBEDTLS_CIPHER_ID_ARIA;
uint8_t tag_buf_encrypt[16] = {};
uint8_t tag_buf_decrypt[16] = {};
uint8_t iv_buf[16] = {};
uint8_t *ciphertext = malloc(cfg->plaintext_length);
uint8_t *output = malloc(cfg->plaintext_length);
size_t olen;
if (cfg->plaintext_length != 0) {
TEST_ASSERT_NOT_NULL(ciphertext);
TEST_ASSERT_NOT_NULL(output);
}
memset(ciphertext, 0, cfg->plaintext_length);
memset(output, 0, cfg->plaintext_length);
memcpy(iv_buf, cfg->iv, cfg->iv_length);
mbedtls_gcm_init(&ctx);
TEST_ASSERT(mbedtls_gcm_setkey(&ctx, cipher, cfg->key, cfg->key_bits) == 0);
if (gcm_type == GCM_TEST_CRYPT_N_TAG) {
mbedtls_gcm_crypt_and_tag(&ctx, MBEDTLS_GCM_ENCRYPT, cfg->plaintext_length, iv_buf, cfg->iv_length, cfg->aad_buf, cfg->aad_length, cfg->plaintext, ciphertext, cfg->tag_len, tag_buf_encrypt);
} else if (gcm_type == GCM_TEST_START_UPDATE_FINISH) {
TEST_ASSERT(mbedtls_gcm_starts(&ctx, MBEDTLS_GCM_ENCRYPT, iv_buf, cfg->iv_length) == 0);
TEST_ASSERT(mbedtls_gcm_update_ad(&ctx, cfg->aad_buf, cfg->aad_length) == 0);
TEST_ASSERT(mbedtls_gcm_update(&ctx, cfg->plaintext, cfg->plaintext_length, ciphertext, cfg->plaintext_length, &olen) == 0);
TEST_ASSERT(mbedtls_gcm_finish(&ctx, ciphertext, cfg->plaintext_length, &olen, tag_buf_encrypt, cfg->tag_len) == 0);
}
size_t offset = cfg->plaintext_length > 16 ? cfg->plaintext_length - 16 : 0;
/* Sanity check: make sure the last ciphertext block matches what we expect to see. */
TEST_ASSERT_EQUAL_HEX8_ARRAY(res->ciphertext_last_block, ciphertext + offset, MIN(16, cfg->plaintext_length));
TEST_ASSERT_EQUAL_HEX8_ARRAY(res->expected_tag, tag_buf_encrypt, cfg->tag_len);
if (gcm_type == GCM_TEST_CRYPT_N_TAG) {
TEST_ASSERT(mbedtls_gcm_auth_decrypt(&ctx, cfg->plaintext_length, iv_buf, cfg->iv_length, cfg->aad_buf, cfg->aad_length, res->expected_tag, cfg->tag_len, ciphertext, output) == 0);
} else if (gcm_type == GCM_TEST_START_UPDATE_FINISH) {
TEST_ASSERT(mbedtls_gcm_starts(&ctx, MBEDTLS_GCM_DECRYPT, iv_buf, cfg->iv_length) == 0);
TEST_ASSERT(mbedtls_gcm_update_ad(&ctx, cfg->aad_buf, cfg->aad_length) == 0);
TEST_ASSERT(mbedtls_gcm_update(&ctx, ciphertext, cfg->plaintext_length, output, cfg->plaintext_length, &olen) == 0);
TEST_ASSERT(mbedtls_gcm_finish(&ctx, output, cfg->plaintext_length, &olen, tag_buf_decrypt, cfg->tag_len) == 0);
/* mbedtls_gcm_auth_decrypt already checks tag so only needed for GCM_TEST_START_UPDATE_FINISH */
TEST_ASSERT_EQUAL_HEX8_ARRAY(res->expected_tag, tag_buf_decrypt, cfg->tag_len);
}
TEST_ASSERT_EQUAL_HEX8_ARRAY(cfg->plaintext, output, cfg->plaintext_length);
mbedtls_gcm_free(&ctx);
free(ciphertext);
free(output);
}
TEST_CASE("mbedtls ARIA GCM test", "[gcm]")
{
const unsigned SZ = 1600;
uint8_t aad[16];
uint8_t iv[16];
uint8_t key[16];
const uint8_t expected_last_block[] = {
0xbe, 0x96, 0xf1, 0x57, 0x34, 0x07, 0x3f, 0x9d,
0x87, 0x6b, 0x39, 0x22, 0xe4, 0xef, 0xff, 0xf0,
};
const uint8_t expected_tag[] = {
0xef, 0x4e, 0xa8, 0x24, 0x07, 0x65, 0x36, 0x12,
0xb1, 0xde, 0x7e, 0x23, 0xda, 0xea, 0x7c, 0x6b,
};
uint8_t *plaintext = malloc(SZ);
TEST_ASSERT_NOT_NULL(plaintext);
memset(plaintext, 0xAA, SZ);
memset(iv, 0xEE, 16);
memset(key, 0x44, 16);
memset(aad, 0x76, 16);
gcm_test_cfg_t cfg = {
.plaintext = plaintext,
.plaintext_length = SZ,
.iv = iv,
.iv_length = sizeof(iv),
.key = key,
.key_bits = 8 * sizeof(key),
.aad_buf = aad,
.aad_length = sizeof(aad),
.tag_len = 16
};
gcm_test_expected_res_t res = {
.expected_tag = expected_tag,
.ciphertext_last_block = expected_last_block,
};
gcm_test(&cfg, &res, GCM_TEST_CRYPT_N_TAG);
gcm_test(&cfg, &res, GCM_TEST_START_UPDATE_FINISH);
free(plaintext);
}
#endif /* CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER */
@@ -1,6 +1,6 @@
/* mbedTLS Elliptic Curve Digital Signature performance tests
*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -9,12 +9,10 @@
#include <stdbool.h>
#include <inttypes.h>
#include <esp_log.h>
#define MBEDTLS_DECLARE_PRIVATE_IDENTIFIERS
#include <mbedtls/private/ecdh.h>
#include <mbedtls/private/ecdsa.h>
#include <mbedtls/private/pk_private.h>
#include <mbedtls/error.h>
#include "psa/crypto.h"
#include "psa_crypto_driver_esp_ecdsa_contexts.h"
#include "psa_crypto_driver_esp_ecdsa.h"
#include "hal/efuse_ll.h"
#include "esp_efuse.h"
@@ -26,7 +24,6 @@
#include "esp_heap_caps.h"
#include "crypto_performance.h"
#include "ecdsa/ecdsa_alt.h"
#if SOC_KEY_MANAGER_SUPPORTED
#include "esp_key_mgr.h"
#include "hal/key_mgr_ll.h"
@@ -62,6 +59,8 @@
#define ECDSA_P192_HASH_COMPONENT_LEN 24
#define ECDSA_P256_HASH_COMPONENT_LEN 32
#define ECDSA_UNCOMPRESSED_POINT_FORMAT 0x04
__attribute__((unused)) static const char * TAG = "mbedtls_test";
/*
@@ -172,106 +171,73 @@ const uint8_t ecdsa192_pub_y[] = {
0x23, 0xae, 0x7e, 0x0f, 0x1f, 0x4d, 0x69, 0xd5
};
void test_ecdsa_verify(mbedtls_ecp_group_id id, const uint8_t *hash, const uint8_t *r_comp, const uint8_t *s_comp,
void test_ecdsa_verify(esp_ecdsa_curve_t curve, const uint8_t *hash, const uint8_t *r_comp, const uint8_t *s_comp,
const uint8_t *pub_x, const uint8_t *pub_y)
{
size_t hash_len = HASH_LEN;
size_t hash_len = 0;
int64_t elapsed_time;
mbedtls_mpi r, s;
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
mbedtls_ecdsa_context ecdsa_context;
mbedtls_ecdsa_init(&ecdsa_context);
mbedtls_ecp_group_load(&ecdsa_context.MBEDTLS_PRIVATE(grp), id);
size_t plen = mbedtls_mpi_size(&ecdsa_context.MBEDTLS_PRIVATE(grp).P);
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&r, r_comp, plen));
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&s, s_comp, plen));
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), pub_x, plen));
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_read_binary(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), pub_y, plen));
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_lset(&ecdsa_context.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 1));
size_t plen;
psa_key_id_t key_id;
psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
if (id != MBEDTLS_ECP_DP_SECP192R1) {
psa_key_type_t curve_family = PSA_ECC_FAMILY_SECP_R1;
psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(curve_family));
if (id == MBEDTLS_ECP_DP_SECP256R1) {
psa_set_key_bits(&key_attr, 256);
}
#if SOC_ECDSA_SUPPORT_CURVE_P384
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
psa_set_key_bits(&key_attr, 384);
}
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
uint8_t psa_key[2 * plen + 1];
psa_key[0] = 0x04; // Uncompressed point indicator
memcpy(&psa_key[1], pub_x, plen);
memcpy(&psa_key[1 + plen], pub_y, plen);
psa_status_t status = psa_import_key(&key_attr, psa_key, sizeof(psa_key), &key_id);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
}
if (id == MBEDTLS_ECP_DP_SECP192R1 || id == MBEDTLS_ECP_DP_SECP256R1) {
hash_len = HASH_LEN;
}
switch (curve) {
case ESP_ECDSA_CURVE_SECP192R1:
plen = 192;
hash_len = HASH_LEN;
break;
case ESP_ECDSA_CURVE_SECP256R1:
plen = 256;
hash_len = HASH_LEN;
break;
#if SOC_ECDSA_SUPPORT_CURVE_P384
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
hash_len = HASH_LEN_P384;
}
case ESP_ECDSA_CURVE_SECP384R1:
plen = 384;
hash_len = HASH_LEN_P384;
break;
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
default:
TEST_FAIL_MESSAGE("Invalid curve");
break;
}
psa_set_key_bits(&key_attr, plen);
size_t plen_bytes = plen / 8;
uint8_t psa_key[2 * MAX_ECDSA_COMPONENT_LEN + 1];
size_t psa_key_len = 2 * plen_bytes + 1;
psa_key[0] = ECDSA_UNCOMPRESSED_POINT_FORMAT;
memcpy(psa_key + 1, pub_x, plen_bytes);
memcpy(psa_key + 1 + plen_bytes, pub_y, plen_bytes);
psa_status_t status = psa_import_key(&key_attr, psa_key, psa_key_len, &key_id);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
uint8_t signature[2 * MAX_ECDSA_COMPONENT_LEN];
memcpy(signature, r_comp, plen_bytes);
memcpy(signature + plen_bytes, s_comp, plen_bytes);
ccomp_timer_start();
TEST_ASSERT_MBEDTLS_OK(mbedtls_ecdsa_verify(&ecdsa_context.MBEDTLS_PRIVATE(grp), hash, hash_len, &ecdsa_context.MBEDTLS_PRIVATE(Q), &r, &s));
status = psa_verify_hash(key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256), hash, hash_len, signature, 2 * plen_bytes);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
elapsed_time = ccomp_timer_stop();
if (id == MBEDTLS_ECP_DP_SECP192R1) {
if (curve == ESP_ECDSA_CURVE_SECP192R1) {
TEST_PERFORMANCE_CCOMP_LESS_THAN(ECDSA_P192_VERIFY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time));
} else if (id == MBEDTLS_ECP_DP_SECP256R1) {
} else if (curve == ESP_ECDSA_CURVE_SECP256R1) {
TEST_PERFORMANCE_CCOMP_LESS_THAN(ECDSA_P256_VERIFY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time));
}
#if SOC_ECDSA_SUPPORT_CURVE_P384
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
else if (curve == ESP_ECDSA_CURVE_SECP384R1) {
TEST_PERFORMANCE_CCOMP_LESS_THAN(ECDSA_P384_VERIFY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time));
}
#endif
if (id != MBEDTLS_ECP_DP_SECP192R1) {
uint8_t signature[2 * plen];
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_write_binary(&r, signature, plen));
TEST_ASSERT_MBEDTLS_OK(mbedtls_mpi_write_binary(&s, signature + plen, plen));
ccomp_timer_start();
psa_status_t status = psa_verify_hash(key_id, PSA_ALG_ECDSA(PSA_ALG_SHA_256), hash, hash_len,
signature, sizeof(signature));
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
elapsed_time = ccomp_timer_stop();
if (id == MBEDTLS_ECP_DP_SECP192R1) {
TEST_PERFORMANCE_CCOMP_LESS_THAN(ECDSA_P192_VERIFY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time));
} else if (id == MBEDTLS_ECP_DP_SECP256R1) {
TEST_PERFORMANCE_CCOMP_LESS_THAN(ECDSA_P256_VERIFY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time));
}
#if SOC_ECDSA_SUPPORT_CURVE_P384
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
TEST_PERFORMANCE_CCOMP_LESS_THAN(ECDSA_P384_VERIFY_OP, "%" NEWLIB_NANO_COMPAT_FORMAT" us", NEWLIB_NANO_COMPAT_CAST(elapsed_time));
}
#endif
psa_destroy_key(key_id);
psa_reset_key_attributes(&key_attr);
}
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);
mbedtls_ecdsa_free(&ecdsa_context);
psa_destroy_key(key_id);
psa_reset_key_attributes(&key_attr);
}
TEST_CASE("mbedtls ECDSA signature verification performance on SECP192R1", "[mbedtls]")
@@ -281,8 +247,7 @@ TEST_CASE("mbedtls ECDSA signature verification performance on SECP192R1", "[mbe
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
#endif
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP192R1, sha, ecdsa192_r, ecdsa192_s,
ecdsa192_pub_x, ecdsa192_pub_y);
test_ecdsa_verify(ESP_ECDSA_CURVE_SECP192R1, sha, ecdsa192_r, ecdsa192_s, ecdsa192_pub_x, ecdsa192_pub_y);
}
TEST_CASE("mbedtls ECDSA signature verification performance on SECP256R1", "[mbedtls]")
@@ -292,8 +257,7 @@ TEST_CASE("mbedtls ECDSA signature verification performance on SECP256R1", "[mbe
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
#endif
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP256R1, sha, ecdsa256_r, ecdsa256_s,
ecdsa256_pub_x, ecdsa256_pub_y);
test_ecdsa_verify(ESP_ECDSA_CURVE_SECP256R1, sha, ecdsa256_r, ecdsa256_s, ecdsa256_pub_x, ecdsa256_pub_y);
}
#ifdef SOC_ECDSA_SUPPORT_CURVE_P384
@@ -304,14 +268,14 @@ TEST_CASE("mbedtls ECDSA signature verification performance on SECP384R1", "[mbe
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
#endif
test_ecdsa_verify(MBEDTLS_ECP_DP_SECP384R1, sha, ecdsa384_r, ecdsa384_s,
ecdsa384_pub_x, ecdsa384_pub_y);
test_ecdsa_verify(ESP_ECDSA_CURVE_SECP384R1, sha, ecdsa384_r, ecdsa384_s, ecdsa384_pub_x, ecdsa384_pub_y);
}
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
#endif /* CONFIG_MBEDTLS_HARDWARE_ECC */
#if CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
#define USE_ECDSA_KEY_FROM_KEY_MANAGER INT_MAX
/*
* This test assumes that ECDSA private key has been burnt in efuse.
@@ -343,66 +307,76 @@ const uint8_t k1_ecdsa192_encrypt[] = {
0xde, 0xe9, 0x9c, 0x89, 0xf2, 0x3b, 0x29, 0xb7, 0x9e, 0x33, 0xec, 0x76, 0x75, 0x2f, 0x3e, 0xab, 0x61, 0x06, 0x4d, 0xea, 0x05, 0x2c, 0xc3, 0x29, 0x1c, 0x7f, 0xb7, 0x3d, 0xb8, 0x1c, 0xb2, 0x17,
};
void test_ecdsa_sign(mbedtls_ecp_group_id id, const uint8_t *hash, const uint8_t *pub_x, const uint8_t *pub_y, bool is_deterministic, int efuse_key_block)
void test_ecdsa_sign(esp_ecdsa_curve_t curve, const uint8_t *hash, const uint8_t *pub_x, const uint8_t *pub_y, bool is_deterministic, int efuse_key_block)
{
size_t hash_len = HASH_LEN;
uint8_t r_be[MAX_ECDSA_COMPONENT_LEN] = {0};
uint8_t s_be[MAX_ECDSA_COMPONENT_LEN] = {0};
uint8_t signature[2 * MAX_ECDSA_COMPONENT_LEN];
size_t signature_len = 0;
size_t plen = 0;
psa_algorithm_t sha_alg = 0;
mbedtls_mpi r, s;
mbedtls_mpi key_mpi;
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
mbedtls_ecdsa_context ecdsa_context;
mbedtls_ecdsa_init(&ecdsa_context);
if (id == MBEDTLS_ECP_DP_SECP192R1) {
mbedtls_ecp_group_load(&ecdsa_context.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP192R1);
} else if (id == MBEDTLS_ECP_DP_SECP256R1) {
mbedtls_ecp_group_load(&ecdsa_context.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1);
}
switch (curve) {
case ESP_ECDSA_CURVE_SECP192R1:
hash_len = HASH_LEN;
plen = 192;
sha_alg = PSA_ALG_SHA_256;
break;
case ESP_ECDSA_CURVE_SECP256R1:
hash_len = HASH_LEN;
plen = 256;
sha_alg = PSA_ALG_SHA_256;
break;
#if SOC_ECDSA_SUPPORT_CURVE_P384
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
mbedtls_ecp_group_load(&ecdsa_context.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP384R1);
}
#endif
esp_ecdsa_privkey_load_mpi(&key_mpi, efuse_key_block);
if (id == MBEDTLS_ECP_DP_SECP192R1 || id == MBEDTLS_ECP_DP_SECP256R1) {
hash_len = HASH_LEN;
}
#if SOC_ECDSA_SUPPORT_CURVE_P384
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
hash_len = HASH_LEN_P384;
}
case ESP_ECDSA_CURVE_SECP384R1:
hash_len = HASH_LEN_P384;
plen = 384;
sha_alg = PSA_ALG_SHA_384;
break;
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
default:
TEST_FAIL_MESSAGE("Invalid curve");
break;
}
if (is_deterministic) {
mbedtls_ecdsa_sign_det_ext(&ecdsa_context.MBEDTLS_PRIVATE(grp), &r, &s, &key_mpi, sha, hash_len, 0, NULL, NULL);
size_t plen_bytes = plen / 8;
psa_key_id_t priv_key_id = 0;
psa_key_attributes_t priv_attr = PSA_KEY_ATTRIBUTES_INIT;
esp_ecdsa_opaque_key_t opaque_key = {
.curve = curve,
};
if (efuse_key_block == USE_ECDSA_KEY_FROM_KEY_MANAGER) {
opaque_key.use_km_key = true;
} else {
mbedtls_ecdsa_sign(&ecdsa_context.MBEDTLS_PRIVATE(grp), &r, &s, &key_mpi, sha, hash_len, NULL, NULL);
opaque_key.efuse_block = efuse_key_block;
}
mbedtls_mpi_write_binary(&r, r_be, MAX_ECDSA_COMPONENT_LEN);
mbedtls_mpi_write_binary(&s, s_be, MAX_ECDSA_COMPONENT_LEN);
psa_algorithm_t alg = (is_deterministic ? PSA_ALG_DETERMINISTIC_ECDSA(sha_alg) : PSA_ALG_ECDSA(sha_alg));
if (id == MBEDTLS_ECP_DP_SECP192R1) {
// Skip the initial zeroes
test_ecdsa_verify(id, sha, &r_be[MAX_HASH_LEN - ECDSA_P192_HASH_COMPONENT_LEN], &s_be[MAX_HASH_LEN - ECDSA_P192_HASH_COMPONENT_LEN], pub_x, pub_y);
} else if (id == MBEDTLS_ECP_DP_SECP256R1) {
test_ecdsa_verify(id, sha, &r_be[MAX_HASH_LEN - ECDSA_P256_HASH_COMPONENT_LEN], &s_be[MAX_HASH_LEN - ECDSA_P256_HASH_COMPONENT_LEN], pub_x, pub_y);
}
#if SOC_ECDSA_SUPPORT_CURVE_P384
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
test_ecdsa_verify(id, sha, r_be, s_be, pub_x, pub_y);
}
#endif
// Set attributes for opaque private key
psa_set_key_type(&priv_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&priv_attr, plen);
psa_set_key_usage_flags(&priv_attr, PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&priv_attr, alg);
psa_set_key_lifetime(&priv_attr, PSA_KEY_LIFETIME_ESP_ECDSA_VOLATILE); // Opaque key
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);
mbedtls_mpi_free(&key_mpi);
// Import opaque key reference
psa_status_t status = psa_import_key(&priv_attr, (uint8_t*) &opaque_key, sizeof(opaque_key), &priv_key_id);
TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status);
TEST_ASSERT_NOT_EQUAL(0, priv_key_id);
status = psa_sign_hash(priv_key_id,
alg,
hash, hash_len,
signature, 2 * plen_bytes,
&signature_len);
TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status);
TEST_ASSERT_TRUE(signature_len == 2 * plen_bytes);
test_ecdsa_verify(curve, sha, signature, signature + plen_bytes, pub_x, pub_y);
psa_destroy_key(priv_key_id);
psa_reset_key_attributes(&priv_attr);
}
TEST_CASE("mbedtls ECDSA signature generation on SECP192R1", "[mbedtls][efuse_key]")
@@ -410,7 +384,7 @@ TEST_CASE("mbedtls ECDSA signature generation on SECP192R1", "[mbedtls][efuse_ke
if (!ecdsa_ll_is_supported()) {
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP192R1, sha, ecdsa192_pub_x, ecdsa192_pub_y, false, SECP192R1_EFUSE_BLOCK);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP192R1, sha, ecdsa192_pub_x, ecdsa192_pub_y, false, SECP192R1_EFUSE_BLOCK);
}
TEST_CASE("mbedtls ECDSA signature generation on SECP256R1", "[mbedtls][efuse_key]")
@@ -418,8 +392,9 @@ TEST_CASE("mbedtls ECDSA signature generation on SECP256R1", "[mbedtls][efuse_ke
if (!ecdsa_ll_is_supported()) {
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP256R1, sha, ecdsa256_pub_x, ecdsa256_pub_y, false, SECP256R1_EFUSE_BLOCK);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP256R1, sha, ecdsa256_pub_x, ecdsa256_pub_y, false, SECP256R1_EFUSE_BLOCK);
}
#ifdef SOC_ECDSA_SUPPORT_CURVE_P384
TEST_CASE("mbedtls ECDSA signature generation on SECP384R1", "[mbedtls][efuse_key]")
{
@@ -427,7 +402,7 @@ TEST_CASE("mbedtls ECDSA signature generation on SECP384R1", "[mbedtls][efuse_ke
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
uint8_t efuse_key_block = HAL_ECDSA_COMBINE_KEY_BLOCKS(SECP384R1_EFUSE_BLOCK_HIGH, SECP384R1_EFUSE_BLOCK_LOW);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP384R1, sha, ecdsa384_pub_x, ecdsa384_pub_y, false, efuse_key_block);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP384R1, sha, ecdsa384_pub_x, ecdsa384_pub_y, false, efuse_key_block);
}
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
@@ -469,7 +444,7 @@ TEST_CASE("mbedtls ECDSA signature generation on SECP192R1", "[mbedtls][key_mana
}
deploy_key_in_key_manager(k1_ecdsa192_encrypt, ESP_KEY_MGR_ECDSA_KEY, ESP_KEY_MGR_ECDSA_LEN_192);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP192R1, sha, ecdsa192_pub_x, ecdsa192_pub_y, false, USE_ECDSA_KEY_FROM_KEY_MANAGER);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP192R1, sha, ecdsa192_pub_x, ecdsa192_pub_y, false, USE_ECDSA_KEY_FROM_KEY_MANAGER);
esp_key_mgr_deactivate_key(ESP_KEY_MGR_ECDSA_KEY);
}
@@ -483,7 +458,7 @@ TEST_CASE("mbedtls ECDSA signature generation on SECP256R1", "[mbedtls][key_mana
}
deploy_key_in_key_manager(k1_ecdsa256_encrypt, ESP_KEY_MGR_ECDSA_KEY, ESP_KEY_MGR_ECDSA_LEN_256);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP256R1, sha, ecdsa256_pub_x, ecdsa256_pub_y, false, USE_ECDSA_KEY_FROM_KEY_MANAGER);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP256R1, sha, ecdsa256_pub_x, ecdsa256_pub_y, false, USE_ECDSA_KEY_FROM_KEY_MANAGER);
esp_key_mgr_deactivate_key(ESP_KEY_MGR_ECDSA_KEY);
}
#endif /* SOC_KEY_MANAGER_SUPPORTED */
@@ -497,7 +472,7 @@ TEST_CASE("mbedtls ECDSA deterministic signature generation on SECP192R1", "[mbe
if (!ecdsa_ll_is_deterministic_mode_supported()) {
ESP_LOGI(TAG, "Skipping test because ECDSA deterministic mode is not supported.");
} else {
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP192R1, sha, ecdsa192_pub_x, ecdsa192_pub_y, true, SECP192R1_EFUSE_BLOCK);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP192R1, sha, ecdsa192_pub_x, ecdsa192_pub_y, true, SECP192R1_EFUSE_BLOCK);
}
}
@@ -509,7 +484,7 @@ TEST_CASE("mbedtls ECDSA deterministic signature generation on SECP256R1", "[mbe
if (!ecdsa_ll_is_deterministic_mode_supported()) {
ESP_LOGI(TAG, "Skipping test because ECDSA deterministic mode is not supported.");
} else {
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP256R1, sha, ecdsa256_pub_x, ecdsa256_pub_y, true, SECP256R1_EFUSE_BLOCK);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP256R1, sha, ecdsa256_pub_x, ecdsa256_pub_y, true, SECP256R1_EFUSE_BLOCK);
}
}
@@ -520,7 +495,7 @@ TEST_CASE("mbedtls ECDSA deterministic signature generation on SECP384R1", "[mbe
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
uint8_t efuse_key_block = HAL_ECDSA_COMBINE_KEY_BLOCKS(SECP384R1_EFUSE_BLOCK_HIGH, SECP384R1_EFUSE_BLOCK_LOW);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP384R1, sha, ecdsa384_pub_x, ecdsa384_pub_y, true, efuse_key_block);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP384R1, sha, ecdsa384_pub_x, ecdsa384_pub_y, true, efuse_key_block);
}
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
@@ -538,7 +513,7 @@ TEST_CASE("mbedtls ECDSA deterministic signature generation on SECP192R1", "[mbe
ESP_LOGI(TAG, "Skipping test because ECDSA deterministic mode is not supported.");
} else {
deploy_key_in_key_manager(k1_ecdsa192_encrypt, ESP_KEY_MGR_ECDSA_KEY, ESP_KEY_MGR_ECDSA_LEN_192);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP192R1, sha, ecdsa192_pub_x, ecdsa192_pub_y, true, USE_ECDSA_KEY_FROM_KEY_MANAGER);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP192R1, sha, ecdsa192_pub_x, ecdsa192_pub_y, true, USE_ECDSA_KEY_FROM_KEY_MANAGER);
esp_key_mgr_deactivate_key(ESP_KEY_MGR_ECDSA_KEY);
}
}
@@ -556,7 +531,7 @@ TEST_CASE("mbedtls ECDSA deterministic signature generation on SECP256R1", "[mbe
ESP_LOGI(TAG, "Skipping test because ECDSA deterministic mode is not supported.");
} else {
deploy_key_in_key_manager(k1_ecdsa256_encrypt, ESP_KEY_MGR_ECDSA_KEY, ESP_KEY_MGR_ECDSA_LEN_256);
test_ecdsa_sign(MBEDTLS_ECP_DP_SECP256R1, sha, ecdsa256_pub_x, ecdsa256_pub_y, true, USE_ECDSA_KEY_FROM_KEY_MANAGER);
test_ecdsa_sign(ESP_ECDSA_CURVE_SECP256R1, sha, ecdsa256_pub_x, ecdsa256_pub_y, true, USE_ECDSA_KEY_FROM_KEY_MANAGER);
esp_key_mgr_deactivate_key(ESP_KEY_MGR_ECDSA_KEY);
}
}
@@ -564,48 +539,70 @@ TEST_CASE("mbedtls ECDSA deterministic signature generation on SECP256R1", "[mbe
#endif /* SOC_ECDSA_SUPPORT_DETERMINISTIC_MODE */
#ifdef SOC_ECDSA_SUPPORT_EXPORT_PUBKEY
void test_ecdsa_export_pubkey(mbedtls_ecp_group_id id, const uint8_t *pub_x, const uint8_t *pub_y, int efuse_key_block)
void test_ecdsa_export_pubkey(esp_ecdsa_curve_t curve, const uint8_t *pub_x, const uint8_t *pub_y, int efuse_key_block)
{
uint8_t export_pub_x[48] = {0};
uint8_t export_pub_y[48] = {0};
int len = 0;
uint8_t export_pub_key[1 + 2 * MAX_ECDSA_COMPONENT_LEN] = {0};
size_t len = 0;
esp_ecdsa_pk_conf_t pk_conf = {
.grp_id = id,
.load_pubkey = true,
psa_key_id_t priv_key_id = 0;
psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT;
esp_ecdsa_opaque_key_t opaque_key = {
.curve = curve,
};
if (efuse_key_block == USE_ECDSA_KEY_FROM_KEY_MANAGER) {
pk_conf.use_km_key = true;
} else {
pk_conf.efuse_block = efuse_key_block;
}
size_t plen = 0;
psa_algorithm_t sha_alg = 0;
if (id == MBEDTLS_ECP_DP_SECP192R1) {
len = 24;
} else if (id == MBEDTLS_ECP_DP_SECP256R1) {
len = 32;
}
switch (curve) {
case ESP_ECDSA_CURVE_SECP192R1:
plen = 192;
sha_alg = PSA_ALG_SHA_256;
break;
case ESP_ECDSA_CURVE_SECP256R1:
plen = 256;
sha_alg = PSA_ALG_SHA_256;
break;
#if SOC_ECDSA_SUPPORT_CURVE_P384
else if (id == MBEDTLS_ECP_DP_SECP384R1) {
len = 48;
case ESP_ECDSA_CURVE_SECP384R1:
plen = 384;
sha_alg = PSA_ALG_SHA_384;
break;
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
default:
TEST_FAIL_MESSAGE("Invalid curve");
break;
}
#endif
size_t plen_bytes = plen / 8;
mbedtls_pk_context key_ctx;
if (efuse_key_block == USE_ECDSA_KEY_FROM_KEY_MANAGER) {
opaque_key.use_km_key = true;
} else {
opaque_key.efuse_block = efuse_key_block;
}
// Set attributes for opaque private key
psa_set_key_type(&key_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&key_attr, plen);
psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&key_attr, PSA_ALG_ECDSA(sha_alg));
psa_set_key_lifetime(&key_attr, PSA_KEY_LIFETIME_ESP_ECDSA_VOLATILE); // Opaque key
int ret = esp_ecdsa_set_pk_context(&key_ctx, &pk_conf);
TEST_ASSERT_EQUAL(0, ret);
// Import opaque key reference
psa_status_t status = psa_import_key(&key_attr, (uint8_t*) &opaque_key, sizeof(opaque_key), &priv_key_id);
TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status);
TEST_ASSERT_NOT_EQUAL(0, priv_key_id);
mbedtls_ecp_keypair *keypair = mbedtls_pk_ec(key_ctx);
mbedtls_mpi_write_binary(&(keypair->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X)), export_pub_x, len);
mbedtls_mpi_write_binary(&(keypair->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y)), export_pub_y, len);
status = psa_export_public_key(priv_key_id, export_pub_key, sizeof(export_pub_key), &len);
TEST_ASSERT_EQUAL_HEX8_ARRAY(pub_x, export_pub_x, len);
TEST_ASSERT_EQUAL_HEX8_ARRAY(pub_y, export_pub_y, len);
TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status);
TEST_ASSERT_TRUE(len == 1 + 2 * plen_bytes);
/* Use esp_ecdsa_free_pk_context instead of manual cleanup to avoid memory leak */
esp_ecdsa_free_pk_context(&key_ctx);
TEST_ASSERT_EQUAL_HEX8(ECDSA_UNCOMPRESSED_POINT_FORMAT, export_pub_key[0]);
TEST_ASSERT_EQUAL_HEX8_ARRAY(pub_x, export_pub_key + 1, plen_bytes);
TEST_ASSERT_EQUAL_HEX8_ARRAY(pub_y, export_pub_key + 1 + plen_bytes, plen_bytes);
psa_destroy_key(priv_key_id);
psa_reset_key_attributes(&key_attr);
}
TEST_CASE("mbedtls ECDSA export public key on SECP192R1", "[mbedtls][efuse_key]")
@@ -613,7 +610,7 @@ TEST_CASE("mbedtls ECDSA export public key on SECP192R1", "[mbedtls][efuse_key]"
if (!ecdsa_ll_is_supported()) {
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
test_ecdsa_export_pubkey(MBEDTLS_ECP_DP_SECP192R1, ecdsa192_pub_x, ecdsa192_pub_y, SECP192R1_EFUSE_BLOCK);
test_ecdsa_export_pubkey(ESP_ECDSA_CURVE_SECP192R1, ecdsa192_pub_x, ecdsa192_pub_y, SECP192R1_EFUSE_BLOCK);
}
TEST_CASE("mbedtls ECDSA export public key on SECP256R1", "[mbedtls][efuse_key]")
@@ -621,7 +618,7 @@ TEST_CASE("mbedtls ECDSA export public key on SECP256R1", "[mbedtls][efuse_key]"
if (!ecdsa_ll_is_supported()) {
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
test_ecdsa_export_pubkey(MBEDTLS_ECP_DP_SECP256R1, ecdsa256_pub_x, ecdsa256_pub_y, SECP256R1_EFUSE_BLOCK);
test_ecdsa_export_pubkey(ESP_ECDSA_CURVE_SECP256R1, ecdsa256_pub_x, ecdsa256_pub_y, SECP256R1_EFUSE_BLOCK);
}
#ifdef SOC_ECDSA_SUPPORT_CURVE_P384
@@ -631,7 +628,7 @@ TEST_CASE("mbedtls ECDSA export public key on SECP384R1", "[mbedtls][efuse_key]"
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
uint8_t efuse_key_block = HAL_ECDSA_COMBINE_KEY_BLOCKS(SECP384R1_EFUSE_BLOCK_HIGH, SECP384R1_EFUSE_BLOCK_LOW);
test_ecdsa_export_pubkey(MBEDTLS_ECP_DP_SECP384R1, ecdsa384_pub_x, ecdsa384_pub_y, efuse_key_block);
test_ecdsa_export_pubkey(ESP_ECDSA_CURVE_SECP384R1, ecdsa384_pub_x, ecdsa384_pub_y, efuse_key_block);
}
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
@@ -646,7 +643,7 @@ TEST_CASE("mbedtls ECDSA export public key on SECP192R1", "[mbedtls][key_manager
}
deploy_key_in_key_manager(k1_ecdsa192_encrypt, ESP_KEY_MGR_ECDSA_KEY, ESP_KEY_MGR_ECDSA_LEN_192);
test_ecdsa_export_pubkey(MBEDTLS_ECP_DP_SECP192R1, ecdsa192_pub_x, ecdsa192_pub_y, USE_ECDSA_KEY_FROM_KEY_MANAGER);
test_ecdsa_export_pubkey(ESP_ECDSA_CURVE_SECP192R1, ecdsa192_pub_x, ecdsa192_pub_y, USE_ECDSA_KEY_FROM_KEY_MANAGER);
esp_key_mgr_deactivate_key(ESP_KEY_MGR_ECDSA_KEY);
}
@@ -660,9 +657,161 @@ TEST_CASE("mbedtls ECDSA export public key on SECP256R1", "[mbedtls][key_manager
}
deploy_key_in_key_manager(k1_ecdsa256_encrypt, ESP_KEY_MGR_ECDSA_KEY, ESP_KEY_MGR_ECDSA_LEN_256);
test_ecdsa_export_pubkey(MBEDTLS_ECP_DP_SECP256R1, ecdsa256_pub_x, ecdsa256_pub_y, USE_ECDSA_KEY_FROM_KEY_MANAGER);
test_ecdsa_export_pubkey(ESP_ECDSA_CURVE_SECP256R1, ecdsa256_pub_x, ecdsa256_pub_y, USE_ECDSA_KEY_FROM_KEY_MANAGER);
esp_key_mgr_deactivate_key(ESP_KEY_MGR_ECDSA_KEY);
}
#endif
void test_ecdsa_sign_verify_import_export_error_codes(esp_ecdsa_curve_t curve, const uint8_t *hash, const uint8_t *r_comp, const uint8_t *s_comp, const uint8_t *pub_x, const uint8_t *pub_y, int efuse_key_block)
{
psa_status_t status = PSA_ERROR_GENERIC_ERROR;
size_t hash_len = 0;
size_t plen = 0;
psa_algorithm_t sha_alg = 0;
switch (curve) {
case ESP_ECDSA_CURVE_SECP192R1:
hash_len = HASH_LEN;
plen = 192;
sha_alg = PSA_ALG_SHA_256;
break;
case ESP_ECDSA_CURVE_SECP256R1:
hash_len = HASH_LEN;
plen = 256;
sha_alg = PSA_ALG_SHA_256;
break;
#if SOC_ECDSA_SUPPORT_CURVE_P384
case ESP_ECDSA_CURVE_SECP384R1:
hash_len = HASH_LEN_P384;
plen = 384;
sha_alg = PSA_ALG_SHA_384;
break;
#endif /* SOC_ECDSA_SUPPORT_CURVE_P384 */
default:
TEST_FAIL_MESSAGE("Invalid curve");
break;
}
size_t plen_bytes = plen / 8;
// 1. Signature Verification
psa_key_id_t pub_key_id;
psa_key_attributes_t pub_key_attr = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&pub_key_attr, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_usage_flags(&pub_key_attr, PSA_KEY_USAGE_VERIFY_HASH);
psa_set_key_algorithm(&pub_key_attr, PSA_ALG_ECDSA(sha_alg));
psa_set_key_bits(&pub_key_attr, plen);
uint8_t psa_key[2 * MAX_ECDSA_COMPONENT_LEN + 1];
size_t psa_key_len = 2 * plen_bytes + 1;
psa_key[0] = ECDSA_UNCOMPRESSED_POINT_FORMAT;
memcpy(psa_key + 1, pub_x, plen_bytes);
memcpy(psa_key + 1 + plen_bytes, pub_y, plen_bytes);
status = psa_import_key(&pub_key_attr, psa_key, psa_key_len, &pub_key_id);
TEST_ASSERT_EQUAL(PSA_SUCCESS, status);
uint8_t signature[2 * MAX_ECDSA_COMPONENT_LEN];
memcpy(signature, r_comp, plen_bytes);
memcpy(signature + plen_bytes, s_comp, plen_bytes);
// 1.1 Incorrect signature buffer length
status = psa_verify_hash(pub_key_id, PSA_ALG_ECDSA(sha_alg), hash, hash_len, signature, 2 * plen_bytes - 1);
TEST_ASSERT_EQUAL(PSA_ERROR_INVALID_SIGNATURE, status);
psa_destroy_key(pub_key_id);
psa_reset_key_attributes(&pub_key_attr);
// 2. Import Opaque Private Key
psa_key_id_t priv_key_id = 0;
psa_key_attributes_t priv_attr = PSA_KEY_ATTRIBUTES_INIT;
esp_ecdsa_opaque_key_t opaque_key = { 0 };
opaque_key.curve = curve;
opaque_key.efuse_block = efuse_key_block;
// Set attributes for opaque private key
psa_set_key_type(&priv_attr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1));
psa_set_key_bits(&priv_attr, plen);
psa_set_key_usage_flags(&priv_attr, PSA_KEY_USAGE_SIGN_HASH);
psa_set_key_algorithm(&priv_attr, PSA_ALG_ECDSA(sha_alg));
psa_set_key_lifetime(&priv_attr, PSA_KEY_LIFETIME_ESP_ECDSA_VOLATILE); // Opaque
// incorrect opaque key buffer length
status = psa_import_key(&priv_attr, (uint8_t*) &opaque_key, sizeof(opaque_key) - 1, &priv_key_id);
TEST_ASSERT_EQUAL_HEX32(PSA_ERROR_BUFFER_TOO_SMALL, status);
// incorrect curve
opaque_key.curve = curve + 1;
status = psa_import_key(&priv_attr, (uint8_t*) &opaque_key, sizeof(opaque_key), &priv_key_id);
TEST_ASSERT_EQUAL_HEX32(PSA_ERROR_INVALID_ARGUMENT, status);
// incorrect efuse block
opaque_key.efuse_block = 9; // EFUSE_BLK_KEY5 [EFUSE_BLK_KEY0 to EFUSE_BLK_KEY4 are used for the ECDSA tests]
status = psa_import_key(&priv_attr, (uint8_t*) &opaque_key, sizeof(opaque_key), &priv_key_id);
TEST_ASSERT_EQUAL_HEX32(PSA_ERROR_INVALID_ARGUMENT, status);
opaque_key.efuse_block = efuse_key_block;
opaque_key.curve = curve;
status = psa_import_key(&priv_attr, (uint8_t*) &opaque_key, sizeof(opaque_key), &priv_key_id);
TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status);
TEST_ASSERT_NOT_EQUAL(0, priv_key_id);
// Signature Generation
psa_algorithm_t alg = PSA_ALG_ECDSA(sha_alg);
memset(signature, 0, sizeof(signature));
size_t signature_len = 0;
// Invalid hash length
status = psa_sign_hash(priv_key_id,
alg,
hash, hash_len - 1,
signature, 2 * plen_bytes,
&signature_len);
TEST_ASSERT_EQUAL_HEX32(PSA_ERROR_INVALID_ARGUMENT, status);
// Invalid signature buffer length
status = psa_sign_hash(priv_key_id,
alg,
hash, hash_len,
signature, 2 * plen_bytes - 1,
&signature_len);
TEST_ASSERT_EQUAL_HEX32(PSA_ERROR_BUFFER_TOO_SMALL, status);
status = psa_sign_hash(priv_key_id,
alg,
hash, hash_len,
signature, 2 * plen_bytes,
&signature_len);
TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status);
TEST_ASSERT_TRUE(signature_len == 2 * plen_bytes);
// Export Public Key
uint8_t export_pub_key[1 + 2 * MAX_ECDSA_COMPONENT_LEN] = {0};
size_t export_pub_key_len = 0;
// incorrect export public key buffer length
status = psa_export_public_key(priv_key_id, export_pub_key, (2 * plen_bytes), &export_pub_key_len);
TEST_ASSERT_EQUAL_HEX32(PSA_ERROR_BUFFER_TOO_SMALL, status);
// correct export public key buffer length
status = psa_export_public_key(priv_key_id, export_pub_key, sizeof(export_pub_key), &export_pub_key_len);
TEST_ASSERT_EQUAL_HEX32(PSA_SUCCESS, status);
psa_destroy_key(priv_key_id);
psa_reset_key_attributes(&priv_attr);
}
TEST_CASE("mbedtls ECDSA signature generation verification, import and export error codes", "[mbedtls][efuse_key]")
{
if (!ecdsa_ll_is_supported()) {
TEST_IGNORE_MESSAGE("ECDSA is not supported");
}
test_ecdsa_sign_verify_import_export_error_codes(ESP_ECDSA_CURVE_SECP256R1, sha, ecdsa256_r, ecdsa256_s, ecdsa256_pub_x, ecdsa256_pub_y, SECP256R1_EFUSE_BLOCK);
}
#endif /* SOC_ECDSA_SUPPORT_EXPORT_PUBKEY */
#endif /* CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN */