Merge branch 'feature/unified_priv_key_interface_across_idf' into 'master'

feat(esp-tls): Added a PSA driver for Secure Element

See merge request espressif/esp-idf!44987
This commit is contained in:
Aditya Patwardhan
2026-07-02 11:00:42 +05:30
34 changed files with 1275 additions and 300 deletions
+4 -6
View File
@@ -22,7 +22,10 @@ endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} esp-tls-crypto
PRIV_INCLUDE_DIRS "private_include"
REQUIRES mbedtls
# mbedtls is public requirements because esp_tls.h
# includes mbedtls header files.
# esp_security is public because esp_tls.h includes esp_key_config.h
REQUIRES mbedtls esp_security
PRIV_REQUIRES ${priv_req})
idf_define_esp_err_codes(HEADERS esp_tls_errors.h)
@@ -35,8 +38,3 @@ else()
target_compile_definitions(${COMPONENT_LIB} PRIVATE ESP_TLS_WITH_LWIP=1)
endif()
endif()
if(CONFIG_ESP_TLS_USE_SECURE_ELEMENT)
idf_component_optional_requires(PRIVATE espressif__esp-cryptoauthlib esp-cryptoauthlib)
endif()
-10
View File
@@ -22,16 +22,6 @@ menu "ESP-TLS"
esp_tls_stack_ops_t interface.
endchoice
config ESP_TLS_USE_SECURE_ELEMENT
bool "Use Secure Element (ATECC608A) with ESP-TLS"
depends on ESP_TLS_USING_MBEDTLS
select ATCA_MBEDTLS_ECDSA
select ATCA_MBEDTLS_ECDSA_SIGN
select ATCA_MBEDTLS_ECDSA_VERIFY
help
Enable use of Secure Element for ESP-TLS, this enables internal support for
ATECC608A peripheral, which can be used for TLS connection.
config ESP_TLS_USE_DS_PERIPHERAL
bool "Use Digital Signature (DS) Peripheral with ESP-TLS"
depends on ESP_TLS_USING_MBEDTLS && SOC_DIG_SIGN_SUPPORTED
+9 -6
View File
@@ -9,6 +9,7 @@
#include <stdbool.h>
#include "esp_err.h"
#include "esp_tls_errors.h"
#include "esp_key_config.h"
#include "sdkconfig.h"
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
#include "mbedtls/ssl.h"
@@ -160,6 +161,10 @@ typedef struct esp_tls_cfg {
const unsigned char *clientkey_pem_buf; /*!< Client key legacy name */
};
const esp_key_config_t *client_key; /*!< Unified key config. Must remain valid for session lifetime.
Any PSA key referenced here remains owned by the caller; ESP-TLS does not
destroy it on cleanup, so the application must release it with psa_destroy_key(). */
union {
unsigned int clientkey_bytes; /*!< Size of client key pointed to by
clientkey_pem_buf
@@ -184,9 +189,6 @@ typedef struct esp_tls_cfg {
underneath socket will be configured in non
blocking mode after tls session is established */
bool use_secure_element; /*!< Enable this option to use secure element or
atecc608a chip */
int timeout_ms; /*!< Network timeout in milliseconds.
Note: If this value is not set, by default the timeout is
set to 10 seconds. If you wish that the session should wait
@@ -315,6 +317,10 @@ typedef struct esp_tls_cfg_server {
const unsigned char *serverkey_pem_buf; /*!< Server key legacy name */
};
const esp_key_config_t *server_key; /*!< Unified key config. Must remain valid for session lifetime.
Any PSA key referenced here remains owned by the caller; ESP-TLS does not
destroy it on cleanup, so the application must release it with psa_destroy_key(). */
union {
unsigned int serverkey_bytes; /*!< Size of server key pointed to by
serverkey_pem_buf */
@@ -334,9 +340,6 @@ typedef struct esp_tls_cfg_server {
esp_tls_ecdsa_curve_t ecdsa_curve; /*!< ECDSA curve to use (SECP256R1 or SECP384R1) */
bool use_secure_element; /*!< Enable this option to use secure element or
atecc608a chip */
uint32_t tls_handshake_timeout_ms; /*!< TLS handshake timeout in milliseconds.
Note: If this value is not set, by default the timeout is
set to 10 seconds. If you wish that the session should wait
+97 -137
View File
@@ -32,16 +32,6 @@
#include "esp_crt_bundle.h"
#endif
#ifdef CONFIG_ESP_TLS_USE_SECURE_ELEMENT
/* cryptoauthlib includes */
#include "mbedtls/atca_mbedtls_wrap.h"
#include "tng_atca.h"
#include "cryptoauthlib.h"
static const atcacert_def_t *cert_def = NULL;
/* Prototypes for functions */
static esp_err_t esp_set_atecc608a_pki_context(esp_tls_t *tls, const void *pki);
#endif /* CONFIG_ESP_TLS_USE_SECURE_ELEMENT */
#if defined(CONFIG_ESP_TLS_USE_DS_PERIPHERAL)
#include <pk_wrap.h>
#include "psa/crypto.h"
@@ -493,27 +483,32 @@ void esp_mbedtls_cleanup(esp_tls_t *tls)
mbedtls_x509_crt_free(&tls->cacert);
mbedtls_x509_crt_free(&tls->clientcert);
/* For opaque keys (DS peripheral, hardware ECDSA), mbedtls_pk_free() does
* not destroy the PSA key — ownership is external. Destroy it manually
* before calling mbedtls_pk_free(). mbedtls_pk_wrap_psa() sets the pk_info
* to mbedtls_{rsa,ecdsa}_opaque_info, both of which have type
* MBEDTLS_PK_OPAQUE — so a single check covers both DS and ECDSA paths.
* clientkey and serverkey share storage via union, so one branch suffices. */
#if defined(CONFIG_ESP_TLS_USE_DS_PERIPHERAL) || defined(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN)
/* For opaque keys, mbedtls_pk_free() does not release the underlying PSA
* key — ownership is tracked separately. Dispatch on ownership, not on the
* key lifetime: a caller-supplied key (ESP_KEY_SOURCE_PSA, e.g. a
* pre-provisioned secure element) is owned externally and must never be
* destroyed on connection close — at most purge it to drop the cached slot
* (a no-op for a volatile key). A key esp-tls created itself is always
* volatile and is destroyed to release its slot. mbedtls_pk_wrap_psa() sets
* the pk_info to mbedtls_{rsa,ecdsa}_opaque_info, both of type
* MBEDTLS_PK_OPAQUE, so one runtime check covers every opaque path (DS
* peripheral, hardware ECDSA, and caller-supplied PSA keys). clientkey and
* serverkey share storage via union, so one branch suffices. */
if (mbedtls_pk_get_type(&tls->clientkey) == MBEDTLS_PK_OPAQUE) {
if (tls->clientkey.MBEDTLS_PRIVATE(priv_id) != PSA_KEY_ID_NULL) {
psa_destroy_key(tls->clientkey.MBEDTLS_PRIVATE(priv_id));
psa_key_id_t kid = tls->clientkey.MBEDTLS_PRIVATE(priv_id);
if (kid != PSA_KEY_ID_NULL) {
if (tls->opaque_key_is_external) {
psa_purge_key(kid);
} else {
psa_destroy_key(kid);
}
tls->clientkey.MBEDTLS_PRIVATE(priv_id) = PSA_KEY_ID_NULL;
}
}
#endif
mbedtls_pk_free(&tls->clientkey);
mbedtls_ssl_config_free(&tls->conf);
mbedtls_ssl_free(&tls->ssl);
#ifdef CONFIG_ESP_TLS_USE_SECURE_ELEMENT
atcab_release();
#endif
}
static esp_err_t set_ca_cert(esp_tls_t *tls, const unsigned char *cacert, size_t cacert_len)
@@ -757,28 +752,53 @@ static esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls)
#endif // CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL
}
if (cfg->use_secure_element) {
#ifdef CONFIG_ESP_TLS_USE_SECURE_ELEMENT
if (cfg->server_key != NULL && cfg->server_key->source == ESP_KEY_SOURCE_BUFFER) {
/* Unified key config with buffer source */
esp_tls_pki_t pki = {
.public_cert = &tls->servercert,
.pk_key = &tls->serverkey,
.publiccert_pem_buf = cfg->servercert_buf,
.publiccert_pem_bytes = cfg->servercert_bytes,
.privkey_pem_buf = NULL,
.privkey_pem_bytes = 0,
.privkey_password = NULL,
.privkey_password_len = 0,
.privkey_pem_buf = cfg->server_key->buffer.data,
.privkey_pem_bytes = cfg->server_key->buffer.len,
.privkey_password = (const unsigned char *)cfg->server_key->buffer.password,
.privkey_password_len = cfg->server_key->buffer.password_len,
};
ret = esp_set_atecc608a_pki_context(tls, (void*) &pki);
if (ret != ESP_OK) {
return ret;
esp_ret = set_pki_context(tls, &pki);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set server pki context");
return esp_ret;
}
#else
ESP_LOGE(TAG, "Please enable secure element support for ESP-TLS in menuconfig");
return ESP_FAIL;
#endif /* CONFIG_ESP_TLS_USE_SECURE_ELEMENT */
} else if (cfg->use_ecdsa_peripheral) {
} else if (cfg->server_key != NULL && cfg->server_key->source == ESP_KEY_SOURCE_PSA) {
if (cfg->servercert_buf == NULL) {
ESP_LOGE(TAG, "Server certificate is required when using a PSA-backed server key");
return ESP_ERR_INVALID_ARG;
}
mbedtls_svc_key_id_t key_id = cfg->server_key->psa.key_id;
mbedtls_pk_init(&tls->serverkey);
tls->opaque_key_is_external = true;
ret = mbedtls_pk_wrap_psa(&tls->serverkey, key_id);
if (ret != 0) {
ESP_LOGE(TAG, "mbedtls_pk_wrap_psa returned -0x%04X", -ret);
mbedtls_print_error_msg(ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_PK_PARSE_KEY_FAILED;
}
ret = mbedtls_x509_crt_parse(&tls->servercert, cfg->servercert_buf, cfg->servercert_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%04X", -ret);
mbedtls_print_error_msg(ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED;
}
ret = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->servercert, &tls->serverkey);
if (ret != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_conf_own_cert returned -0x%04X", -ret);
mbedtls_print_error_msg(ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONF_OWN_CERT_FAILED;
}
} else if (cfg->use_ecdsa_peripheral) {
#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
tls->use_ecdsa_peripheral = cfg->use_ecdsa_peripheral;
#if SOC_ECDSA_SUPPORT_CURVE_P384
@@ -997,26 +1017,52 @@ esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t
#endif
}
if (cfg->use_secure_element) {
#ifdef CONFIG_ESP_TLS_USE_SECURE_ELEMENT
if (cfg->client_key != NULL && cfg->client_key->source == ESP_KEY_SOURCE_BUFFER) {
/* Unified key config with buffer source */
esp_tls_pki_t pki = {
.public_cert = &tls->clientcert,
.pk_key = &tls->clientkey,
.publiccert_pem_buf = cfg->clientcert_buf,
.publiccert_pem_bytes = cfg->clientcert_bytes,
.privkey_pem_buf = NULL,
.privkey_pem_bytes = 0,
.privkey_password = NULL,
.privkey_password_len = 0,
.privkey_pem_buf = cfg->client_key->buffer.data,
.privkey_pem_bytes = cfg->client_key->buffer.len,
.privkey_password = (const unsigned char *)cfg->client_key->buffer.password,
.privkey_password_len = cfg->client_key->buffer.password_len,
};
ret = esp_set_atecc608a_pki_context(tls, (void*) &pki);
if (ret != ESP_OK) {
return ret;
esp_err_t esp_ret = set_pki_context(tls, &pki);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set client pki context");
return esp_ret;
}
} else if (cfg->client_key != NULL && cfg->client_key->source == ESP_KEY_SOURCE_PSA) {
if (cfg->clientcert_buf == NULL) {
ESP_LOGE(TAG, "Client certificate is required when using a PSA-backed client key");
return ESP_ERR_INVALID_ARG;
}
mbedtls_svc_key_id_t key_id = cfg->client_key->psa.key_id;
mbedtls_pk_init(&tls->clientkey);
tls->opaque_key_is_external = true;
ret = mbedtls_pk_wrap_psa(&tls->clientkey, key_id);
if (ret != 0) {
ESP_LOGE(TAG, "mbedtls_pk_wrap_psa returned -0x%04X", -ret);
mbedtls_print_error_msg(ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_PK_PARSE_KEY_FAILED;
}
ret = mbedtls_x509_crt_parse(&tls->clientcert, cfg->clientcert_buf, cfg->clientcert_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%04X", -ret);
mbedtls_print_error_msg(ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED;
}
ret = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->clientcert, &tls->clientkey);
if (ret != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_conf_own_cert returned -0x%04X", -ret);
mbedtls_print_error_msg(ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONF_OWN_CERT_FAILED;
}
#else
ESP_LOGE(TAG, "Please enable secure element support for ESP-TLS in menuconfig");
return ESP_FAIL;
#endif /* CONFIG_ESP_TLS_USE_SECURE_ELEMENT */
} else if (cfg->ds_data != NULL) {
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
if (cfg->clientcert_pem_buf == NULL) {
@@ -1278,92 +1324,6 @@ const int *esp_mbedtls_get_ciphersuites_list(void)
return mbedtls_ssl_list_ciphersuites();
}
#ifdef CONFIG_ESP_TLS_USE_SECURE_ELEMENT
static esp_err_t esp_init_atecc608a(uint8_t i2c_addr)
{
cfg_ateccx08a_i2c_default.atcai2c.address = i2c_addr;
int ret = atcab_init(&cfg_ateccx08a_i2c_default);
if(ret != 0) {
ESP_LOGE(TAG, "Failed to initialize atca device, returned -0x%04X", -ret);
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t esp_set_atecc608a_pki_context(esp_tls_t *tls, const void *pki)
{
int ret = 0;
esp_err_t esp_ret = ESP_FAIL;
ESP_LOGI(TAG, "Initialize the ATECC interface...");
(void)esp_ret;
(void)cert_def;
#if defined(CONFIG_ATECC608A_TNG) || defined(CONFIG_ATECC608A_TFLEX)
#ifdef CONFIG_ATECC608A_TNG
esp_ret = esp_init_atecc608a(CONFIG_ATCA_I2C_ADDRESS);
if (ret != ESP_OK) {
return ESP_ERR_ESP_TLS_SE_FAILED;
}
#elif CONFIG_ATECC608A_TFLEX /* CONFIG_ATECC608A_TNG */
esp_ret = esp_init_atecc608a(CONFIG_ATCA_I2C_ADDRESS);
if (ret != ESP_OK) {
return ESP_ERR_ESP_TLS_SE_FAILED;
}
#endif /* CONFIG_ATECC608A_TFLEX */
mbedtls_x509_crt_init(&tls->clientcert);
ret = tng_get_device_cert_def(&cert_def);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to get device cert def");
return ESP_ERR_ESP_TLS_SE_FAILED;
}
/* Extract the device certificate and convert to mbedtls cert */
ret = atca_mbedtls_cert_add(&tls->clientcert, cert_def);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to parse cert from device, return 0x%04X", ret);
mbedtls_print_error_msg(ret);
return ESP_ERR_ESP_TLS_SE_FAILED;
}
#elif CONFIG_ATECC608A_TCUSTOM
esp_ret = esp_init_atecc608a(CONFIG_ATCA_I2C_ADDRESS);
if (ret != ESP_OK) {
return ESP_ERR_ESP_TLS_SE_FAILED;
}
mbedtls_x509_crt_init(&tls->clientcert);
esp_tls_pki_t *pki_l = (esp_tls_pki_t *) pki;
if (pki_l->publiccert_pem_buf != NULL) {
ret = mbedtls_x509_crt_parse(&tls->clientcert, pki_l->publiccert_pem_buf, pki_l->publiccert_pem_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse of client cert returned -0x%04X", -ret);
mbedtls_print_error_msg(ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED;
}
} else {
ESP_LOGE(TAG, "Device certificate must be provided for TrustCustom Certs");
return ESP_FAIL;
}
#endif /* CONFIG_ATECC608A_TCUSTOM */
ret = atca_mbedtls_pk_init(&tls->clientkey, 0);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to parse key from device");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_MBEDTLS, -ret);
mbedtls_print_error_msg(ret);
return ESP_ERR_ESP_TLS_SE_FAILED;
}
ret = mbedtls_ssl_conf_own_cert(&tls->conf, &tls->clientcert, &tls->clientkey);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to configure client cert, returned -0x%04X", ret);
mbedtls_print_error_msg(ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ESP_TLS_ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_ESP_TLS_SE_FAILED;
}
return ESP_OK;
}
#endif /* CONFIG_ESP_TLS_USE_SECURE_ELEMENT */
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
/*
* tf-psa-crypto 1.1 made mbedtls_pk_wrap_psa() call psa_export_public_key() on
+1 -1
View File
@@ -3,4 +3,4 @@
hint: "The struct 'esp_tls_t' has now been made private - its elements can be only be accessed/modified through respective getter/setter functions. Please refer to the migration guide for more information."
-
re: "fatal error: .*atca_mbedtls_wrap\\.h: No such file or directory"
hint: "To use CONFIG_ESP_TLS_USE_SECURE_ELEMENT option, please install `esp-cryptoauthlib` using 'idf.py add-dependency espressif/esp-cryptoauthlib'"
hint: "The cryptoauthlib mbedTLS wrapper (atca_mbedtls_wrap.h) is no longer used to integrate a secure element. Enable CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED, install `esp-cryptoauthlib` using 'idf.py add-dependency espressif/esp-cryptoauthlib', and provide the key via esp_key_config_t (ESP_KEY_SOURCE_PSA). Please refer to the migration guide for more information."
@@ -52,6 +52,10 @@ struct esp_tls {
mbedtls_pk_context serverkey; /*!< Container for the private key of the server
certificate */
};
bool opaque_key_is_external; /*!< True when the opaque PSA client/server key was supplied
by the caller (ESP_KEY_SOURCE_PSA). Such keys are owned
externally and must never be destroyed on cleanup. */
#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
bool use_ecdsa_peripheral; /*!< Use the ECDSA peripheral for the private key operations. */
uint8_t ecdsa_efuse_blk; /*!< The efuse block number where the ECDSA key is stored. */
+2 -2
View File
@@ -1,7 +1,7 @@
if(NOT ${IDF_TARGET} STREQUAL "linux")
set(req lwip esp_event)
set(req lwip esp_event esp_security)
else()
set(req linux esp_event)
set(req linux esp_event esp_security)
endif()
idf_component_register(SRCS "esp_http_client.c"
+20 -20
View File
@@ -938,12 +938,6 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co
}
#endif
#if CONFIG_ESP_TLS_USE_SECURE_ELEMENT
if (config->use_secure_element) {
esp_transport_ssl_use_secure_element(ssl);
}
#endif
#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL
if (config->ds_data != NULL) {
esp_transport_ssl_set_ds_data(ssl, config->ds_data);
@@ -962,26 +956,32 @@ esp_http_client_handle_t esp_http_client_init(const esp_http_client_config_t *co
}
#endif
if (config->client_key_pem) {
if (!config->client_key_len) {
esp_transport_ssl_set_client_key_data(ssl, config->client_key_pem, strlen(config->client_key_pem));
} else {
esp_transport_ssl_set_client_key_data_der(ssl, config->client_key_pem, config->client_key_len);
/* Check for unified key config */
if (config->client_key != NULL) {
esp_transport_ssl_set_client_key_config(ssl, config->client_key);
} else {
/* Legacy key configuration */
if (config->client_key_pem) {
if (!config->client_key_len) {
esp_transport_ssl_set_client_key_data(ssl, config->client_key_pem, strlen(config->client_key_pem));
} else {
esp_transport_ssl_set_client_key_data_der(ssl, config->client_key_pem, config->client_key_len);
}
}
}
#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
if (config->use_ecdsa_peripheral) {
if (config->use_ecdsa_peripheral) {
#if SOC_ECDSA_SUPPORT_CURVE_P384
esp_transport_ssl_set_client_key_ecdsa_peripheral_extended(ssl, config->ecdsa_key_efuse_blk, config->ecdsa_key_efuse_blk_high);
esp_transport_ssl_set_client_key_ecdsa_peripheral_extended(ssl, config->ecdsa_key_efuse_blk, config->ecdsa_key_efuse_blk_high);
#else
esp_transport_ssl_set_client_key_ecdsa_peripheral(ssl, config->ecdsa_key_efuse_blk);
esp_transport_ssl_set_client_key_ecdsa_peripheral(ssl, config->ecdsa_key_efuse_blk);
#endif
// Set the ECDSA curve
esp_transport_ssl_set_ecdsa_curve(ssl, config->ecdsa_curve);
}
// Set the ECDSA curve
esp_transport_ssl_set_ecdsa_curve(ssl, config->ecdsa_curve);
}
#endif
if (config->client_key_password && config->client_key_password_len > 0) {
esp_transport_ssl_set_client_key_password(ssl, config->client_key_password, config->client_key_password_len);
if (config->client_key_password && config->client_key_password_len > 0) {
esp_transport_ssl_set_client_key_password(ssl, config->client_key_password, config->client_key_password_len);
}
}
if (config->skip_cert_common_name_check) {
@@ -10,6 +10,7 @@
#include "freertos/FreeRTOS.h"
#include "sdkconfig.h"
#include "esp_err.h"
#include "esp_key_config.h"
#include <sys/socket.h>
#ifdef __cplusplus
@@ -200,6 +201,7 @@ typedef struct {
DER Certificate - Length of the buffer pointed to by client_cert_der. Should be the length of the certificate. */
const char *client_key_pem; /*!< SSL client key, PEM format as string, if the server requires to verify client */
size_t client_key_len; /*!< Length of the buffer pointed to by client_key_pem. May be 0 for null-terminated pem */
const esp_key_config_t *client_key; /*!< Unified client key configuration. Takes precedence over client_key_pem when set */
const char *client_key_password; /*!< Client key decryption password string */
size_t client_key_password_len; /*!< String length of the password pointed to by client_key_password */
esp_http_client_proto_ver_t tls_version; /*!< TLS protocol version of the connection, e.g., TLS 1.2, TLS 1.3 (default - no preference) */
@@ -237,9 +239,6 @@ typedef struct {
const char **alpn_protos; /*!< Application protocols required for HTTP2. If HTTP2/ALPN support is required, a list of protocols that should be negotiated. The format is length followed by protocol
name. For the most common cases the following is ok: const char **alpn_protos = { "h2", NULL }; - where 'h2' is the protocol name */
#endif
#if CONFIG_ESP_TLS_USE_SECURE_ELEMENT
bool use_secure_element; /*!< Enable this option to use secure element */
#endif
#if CONFIG_ESP_TLS_USE_DS_PERIPHERAL
void *ds_data; /*!< Pointer for digital signature peripheral context, see ESP-TLS Documentation for more details */
#endif
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2018-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2018-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -105,6 +105,9 @@ struct httpd_ssl_config {
/** Private key byte length */
size_t prvtkey_len;
/** Unified key config. Takes precedence over prvtkey_pem when set */
const esp_key_config_t *server_key;
/** Use ECDSA peripheral to use private key */
bool use_ecdsa_peripheral;
@@ -129,9 +132,6 @@ struct httpd_ssl_config {
/** Enable tls session tickets */
bool session_tickets;
/** Enable secure element for server session */
bool use_secure_element;
/** User callback for esp_https_server */
esp_https_server_user_cb *user_cb;
@@ -219,6 +219,7 @@ typedef struct httpd_ssl_config httpd_ssl_config_t;
HTTPD_SSL_CONFIG_CLIENT_AUTH_OPTIONAL_INIT \
.prvtkey_pem = NULL, \
.prvtkey_len = 0, \
.server_key = NULL, \
.use_ecdsa_peripheral = false, \
.ecdsa_key_efuse_blk = 0, \
.ecdsa_key_efuse_blk_high = 0, \
@@ -227,7 +228,6 @@ typedef struct httpd_ssl_config httpd_ssl_config_t;
.port_secure = 443, \
.port_insecure = 80, \
.session_tickets = false, \
.use_secure_element = false, \
.user_cb = NULL, \
.ssl_userdata = NULL, \
.cert_select_cb = NULL, \
+30 -31
View File
@@ -348,48 +348,47 @@ static esp_err_t create_secure_context(const struct httpd_ssl_config *config, ht
#endif
}
/* Pass on secure element boolean */
cfg->use_secure_element = config->use_secure_element;
if (!cfg->use_secure_element) {
if (config->use_ecdsa_peripheral) {
if (config->use_ecdsa_peripheral) {
#ifdef CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN
(*ssl_ctx)->tls_cfg->use_ecdsa_peripheral = config->use_ecdsa_peripheral;
(*ssl_ctx)->tls_cfg->ecdsa_key_efuse_blk = config->ecdsa_key_efuse_blk;
(*ssl_ctx)->tls_cfg->use_ecdsa_peripheral = config->use_ecdsa_peripheral;
(*ssl_ctx)->tls_cfg->ecdsa_key_efuse_blk = config->ecdsa_key_efuse_blk;
#if SOC_ECDSA_SUPPORT_CURVE_P384
(*ssl_ctx)->tls_cfg->ecdsa_key_efuse_blk_high = config->ecdsa_key_efuse_blk_high;
(*ssl_ctx)->tls_cfg->ecdsa_key_efuse_blk_high = config->ecdsa_key_efuse_blk_high;
#endif
(*ssl_ctx)->tls_cfg->ecdsa_curve = config->ecdsa_curve;
(*ssl_ctx)->tls_cfg->ecdsa_curve = config->ecdsa_curve;
#else
ESP_LOGE(TAG, "Please enable the support for signing using ECDSA peripheral in menuconfig.");
ret = ESP_ERR_NOT_SUPPORTED;
goto exit;
ESP_LOGE(TAG, "Please enable the support for signing using ECDSA peripheral in menuconfig.");
ret = ESP_ERR_NOT_SUPPORTED;
goto exit;
#endif
} else if (config->prvtkey_pem != NULL && config->prvtkey_len > 0) {
cfg->serverkey_buf = malloc(config->prvtkey_len);
} else if (config->server_key != NULL) {
/* Unified key config - pass directly to esp_tls */
cfg->server_key = config->server_key;
} else if (config->prvtkey_pem != NULL && config->prvtkey_len > 0) {
cfg->serverkey_buf = malloc(config->prvtkey_len);
if (cfg->serverkey_buf) {
memcpy((char *) cfg->serverkey_buf, config->prvtkey_pem, config->prvtkey_len);
cfg->serverkey_bytes = config->prvtkey_len;
} else {
ESP_LOGE(TAG, "Could not allocate memory for server key");
ret = ESP_ERR_NO_MEM;
goto exit;
}
if (cfg->serverkey_buf) {
memcpy((char *) cfg->serverkey_buf, config->prvtkey_pem, config->prvtkey_len);
cfg->serverkey_bytes = config->prvtkey_len;
} else {
ESP_LOGE(TAG, "Could not allocate memory for server key");
ret = ESP_ERR_NO_MEM;
goto exit;
}
} else {
#if defined(CONFIG_ESP_HTTPS_SERVER_CERT_SELECT_HOOK)
if (config->cert_select_cb == NULL) {
ESP_LOGE(TAG, "No Server key supplied and no certificate selection hook is present");
ret = ESP_ERR_INVALID_ARG;
goto exit;
} else {
ESP_LOGW(TAG, "Server key not supplied, make sure to supply it in the certificate selection hook");
}
#else
ESP_LOGE(TAG, "No Server key supplied");
if (config->cert_select_cb == NULL) {
ESP_LOGE(TAG, "No Server key supplied and no certificate selection hook is present");
ret = ESP_ERR_INVALID_ARG;
goto exit;
#endif
} else {
ESP_LOGW(TAG, "Server key not supplied, make sure to supply it in the certificate selection hook");
}
#else
ESP_LOGE(TAG, "No Server key supplied");
ret = ESP_ERR_INVALID_ARG;
goto exit;
#endif
}
return ret;
+3 -1
View File
@@ -2,7 +2,9 @@ idf_build_get_property(target IDF_TARGET)
idf_build_get_property(non_os_build NON_OS_BUILD)
if(${target} STREQUAL "linux")
return() # This component is not supported by the POSIX/Linux simulator
# On linux, only provide headers (esp_key_config.h) without any source files
idf_component_register(INCLUDE_DIRS "include")
return()
endif()
set(srcs "")
@@ -0,0 +1,69 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Key format */
typedef enum {
ESP_KEY_FORMAT_AUTO = 0, /*!< Auto-detect */
ESP_KEY_FORMAT_PEM, /*!< PEM (base64) */
ESP_KEY_FORMAT_DER, /*!< DER (binary) */
ESP_KEY_FORMAT_RAW, /*!< Raw key bytes */
} esp_key_format_t;
/**
* Key source types
*
* Hardware-backed key sources (DS peripheral, ECDSA peripheral,
* secure element, key manager) as well as software PSA imported keys
* are accessed via PSA Crypto drivers. Use ESP_KEY_SOURCE_PSA with
* the PSA key ID obtained from psa_import_key() or the corresponding
* hardware setup helper API.
*/
typedef enum {
ESP_KEY_SOURCE_NONE = 0, /*!< No private key configured */
ESP_KEY_SOURCE_BUFFER, /*!< Key in memory buffer (PEM/DER/RAW) */
ESP_KEY_SOURCE_PSA, /*!< PSA Crypto key (opaque or transparent) */
} esp_key_source_t;
/**
* Unified private key configuration
*
* For hardware-backed keys (DS peripheral, ECDSA peripheral, ATECC608,
* key manager), use ESP_KEY_SOURCE_PSA with the key ID returned by
* the respective setup helper (e.g., esp_secure_element_psa_setup()).
*
* @note Must remain valid for the entire TLS session lifetime
*/
typedef struct esp_key_config {
esp_key_source_t source; /*!< Key source type */
union {
struct {
const void *data; /*!< Key data (PEM/DER/RAW) */
size_t len; /*!< Length (0 for null-terminated PEM) */
const char *password; /*!< Decryption password */
size_t password_len; /*!< Password length */
esp_key_format_t format; /*!< Key format */
} buffer;
struct {
uint32_t key_id; /*!< PSA key identifier */
} psa;
};
} esp_key_config_t;
#ifdef __cplusplus
}
#endif
+6 -5
View File
@@ -481,6 +481,12 @@ if(CONFIG_MBEDTLS_USE_CRYPTO_ROM_IMPL)
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u mbedtls_rom_osi_functions_init")
endif()
if(CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED)
target_sources(tfpsacrypto PRIVATE
"${COMPONENT_DIR}/port/psa_driver/secure_element/psa_crypto_driver_secure_element.c")
target_include_directories(tfpsacrypto PUBLIC "${COMPONENT_DIR}/port/psa_driver/include")
endif()
if(CONFIG_COMPILER_STATIC_ANALYZER AND CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(${COMPONENT_LIB} PRIVATE "-fno-analyzer")
target_compile_options(tfpsacrypto PRIVATE "-fno-analyzer")
@@ -548,11 +554,6 @@ if(CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN_CONSTANT_TIME_CM)
target_link_libraries(tfpsacrypto PRIVATE idf::esp_timer)
endif()
# # Link esp-cryptoauthlib to mbedtls
# if(CONFIG_ATCA_MBEDTLS_ECDSA)
# mbedcrypto_optional_deps(espressif__esp-cryptoauthlib esp-cryptoauthlib)
# endif()
# Apply -fno-analyzer to ALL mbedTLS targets at the very end when all targets are created
if(CONFIG_COMPILER_STATIC_ANALYZER AND CMAKE_C_COMPILER_ID STREQUAL "GNU")
message(STATUS "Applying -fno-analyzer to all mbedTLS targets...")
+13 -18
View File
@@ -1032,49 +1032,49 @@ menu "mbedTLS"
config MBEDTLS_ECP_DP_SECP384R1_ENABLED
bool "Enable SECP384R1 curve"
depends on MBEDTLS_ECP_C
default y if !(MBEDTLS_ATCA_HW_ECDSA_SIGN || MBEDTLS_ATCA_HW_ECDSA_VERIFY)
default y
help
Enable support for SECP384R1 Elliptic Curve.
config MBEDTLS_ECP_DP_SECP521R1_ENABLED
bool "Enable SECP521R1 curve"
depends on MBEDTLS_ECP_C
default y if !(MBEDTLS_ATCA_HW_ECDSA_SIGN || MBEDTLS_ATCA_HW_ECDSA_VERIFY)
default y
help
Enable support for SECP521R1 Elliptic Curve.
config MBEDTLS_ECP_DP_SECP256K1_ENABLED
bool "Enable SECP256K1 curve"
depends on MBEDTLS_ECP_C
default y if !(MBEDTLS_ATCA_HW_ECDSA_SIGN || MBEDTLS_ATCA_HW_ECDSA_VERIFY)
default y
help
Enable support for SECP256K1 Elliptic Curve.
config MBEDTLS_ECP_DP_BP256R1_ENABLED
bool "Enable BP256R1 curve"
depends on MBEDTLS_ECP_C
default y if !(MBEDTLS_ATCA_HW_ECDSA_SIGN || MBEDTLS_ATCA_HW_ECDSA_VERIFY)
default y
help
support for DP Elliptic Curve.
config MBEDTLS_ECP_DP_BP384R1_ENABLED
bool "Enable BP384R1 curve"
depends on MBEDTLS_ECP_C
default y if !(MBEDTLS_ATCA_HW_ECDSA_SIGN || MBEDTLS_ATCA_HW_ECDSA_VERIFY)
default y
help
support for DP Elliptic Curve.
config MBEDTLS_ECP_DP_BP512R1_ENABLED
bool "Enable BP512R1 curve"
depends on MBEDTLS_ECP_C
default y if !(MBEDTLS_ATCA_HW_ECDSA_SIGN || MBEDTLS_ATCA_HW_ECDSA_VERIFY)
default y
help
support for DP Elliptic Curve.
config MBEDTLS_ECP_DP_CURVE25519_ENABLED
bool "Enable CURVE25519 curve"
depends on MBEDTLS_ECP_C
default y if !(MBEDTLS_ATCA_HW_ECDSA_SIGN || MBEDTLS_ATCA_HW_ECDSA_VERIFY)
default y
help
Enable support for CURVE25519 Elliptic Curve.
endmenu
@@ -1542,19 +1542,14 @@ menu "mbedTLS"
it also increases the binary size by ~1.2 KB as it pulls in the peripheral's block
mode code as well.
config MBEDTLS_ATCA_HW_ECDSA_SIGN
bool "Enable hardware ECDSA sign acceleration when using ATECC608A"
config MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED
bool "Enable secure element hardware support (e.g., ATECC608A)"
default n
help
This option enables hardware acceleration for ECDSA sign function, only
when using ATECC608A cryptoauth chip.
config MBEDTLS_ATCA_HW_ECDSA_VERIFY
bool "Enable hardware ECDSA verify acceleration when using ATECC608A"
default n
help
This option enables hardware acceleration for ECDSA sign function, only
when using ATECC608A cryptoauth chip.
Enable PSA Crypto driver support for external secure elements.
This enables sign, verify, and key export operations via runtime-registered
callbacks from the secure element component (e.g., esp-cryptoauthlib).
The private key never leaves the secure element.
config MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL
bool "Enable hardware RSA digital signature peripheral acceleration"
@@ -153,8 +153,7 @@ CONFIG_MBEDTLS_HARDWARE_ECC=y
CONFIG_MBEDTLS_ECC_OTHER_CURVES_SOFT_FALLBACK=y
CONFIG_MBEDTLS_HARDWARE_ECDSA_SIGN=n
CONFIG_MBEDTLS_HARDWARE_ECDSA_VERIFY=y
CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN=n
CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY=n
CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED=n
CONFIG_MBEDTLS_PKCS7_C=y
CONFIG_MBEDTLS_PKCS1_V15=y
@@ -267,12 +267,8 @@
#endif
#endif
#ifdef CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN
#define MBEDTLS_ECDSA_SIGN_ALT
#endif
#ifdef CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY
#define MBEDTLS_ECDSA_VERIFY_ALT
#ifdef CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED
#define SECURE_ELEMENT_DRIVER_ENABLED
#endif
#ifdef CONFIG_MBEDTLS_HARDWARE_ECC
@@ -0,0 +1,241 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "psa/crypto.h"
#include "psa/crypto_types.h"
#include "psa_crypto_driver_secure_element_contexts.h"
#if defined(SECURE_ELEMENT_DRIVER_ENABLED) || defined(CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED)
#ifndef PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT
#define PSA_CRYPTO_ACCELERATOR_DRIVER_PRESENT
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Secure element PSA driver location
*
* Vendor-specific location for secure element keys.
* Bits 8-31 are location, using vendor flag (0x800000) + SE vendor ID.
*/
#define PSA_KEY_LOCATION_SECURE_ELEMENT ((psa_key_location_t) 0x800004)
/**
* @brief Construct a lifetime for secure element keys with default persistence
*/
#define PSA_KEY_LIFETIME_SECURE_ELEMENT \
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION( \
PSA_KEY_PERSISTENCE_DEFAULT, \
PSA_KEY_LOCATION_SECURE_ELEMENT)
/**
* @brief Construct a volatile lifetime for secure element keys
*/
#define PSA_KEY_LIFETIME_SECURE_ELEMENT_VOLATILE \
PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION( \
PSA_KEY_PERSISTENCE_VOLATILE, \
PSA_KEY_LOCATION_SECURE_ELEMENT)
/*
* Callback typedefs for secure element operations.
*
* These allow the SE-specific backend (e.g. esp-cryptoauthlib for ATECC608A)
* to register its implementation at runtime without any build-time dependency.
*/
/**
* @brief Callback to sign a hash using the secure element
*
* @param slot_id Slot containing the private key
* @param hash Hash to sign
* @param hash_len Length of hash (e.g. 32 for SHA-256)
* @param sig Output buffer for raw signature (R || S)
* @param sig_size Size of sig buffer
* @param sig_len Actual signature length written
* @return psa_status_t
*/
typedef psa_status_t (*secure_element_sign_cb_t)(uint8_t slot_id, const uint8_t *hash, size_t hash_len,
uint8_t *sig, size_t sig_size, size_t *sig_len);
/**
* @brief Callback to export the public key from a secure element slot
*
* @param slot_id Slot containing the key pair
* @param pubkey Output buffer for raw public key (X || Y)
* @param pubkey_size Size of pubkey buffer
* @param pubkey_len Actual public key length written
* @return psa_status_t
*/
typedef psa_status_t (*secure_element_export_pubkey_cb_t)(uint8_t slot_id, uint8_t *pubkey,
size_t pubkey_size, size_t *pubkey_len);
/**
* @brief Callback to verify a hash signature using the secure element
*
* @param hash Hash that was signed
* @param hash_len Length of hash
* @param sig Raw signature (R || S)
* @param sig_len Length of signature
* @param pubkey Raw public key (X || Y)
* @param pubkey_len Length of public key
* @param is_verified Output: true if signature is valid
* @return psa_status_t
*/
typedef psa_status_t (*secure_element_verify_cb_t)(const uint8_t *hash, size_t hash_len,
const uint8_t *sig, size_t sig_len, const uint8_t *pubkey, size_t pubkey_len, bool *is_verified);
/**
* @brief Secure element callback table
*
* The algorithm, key_type, and key_bits fields declare what the SE supports.
* The driver validates incoming requests against these and returns
* PSA_ERROR_NOT_SUPPORTED for anything that doesn't match, allowing the
* PSA framework to fall back to software.
*/
typedef struct {
secure_element_sign_cb_t sign; /**< Sign hash callback (NULL if not supported) */
secure_element_export_pubkey_cb_t export_pubkey; /**< Export public key callback (NULL if not supported) */
secure_element_verify_cb_t verify; /**< Verify hash callback (NULL if not supported) */
psa_algorithm_t algorithm; /**< Supported algorithm, e.g. PSA_ALG_ECDSA(PSA_ALG_SHA_256) */
psa_key_type_t key_type; /**< Supported key type, e.g. PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1) */
size_t key_bits; /**< Supported key size in bits, e.g. 256 */
} secure_element_callbacks_t;
/**
* @brief Register secure element callbacks
*
* Must be called once during application initialization, before any PSA
* operations targeting PSA_KEY_LOCATION_SECURE_ELEMENT. Only the first
* call succeeds; subsequent calls return PSA_ERROR_BAD_STATE.
*
* @param callbacks Pointer to callback table. The contents are copied
* internally, so the struct need not remain valid after
* this call returns.
* @return PSA_SUCCESS on success
* @return PSA_ERROR_BAD_STATE if callbacks were already registered
* @return PSA_ERROR_INVALID_ARGUMENT if callbacks is NULL
*/
psa_status_t secure_element_register_callbacks(const secure_element_callbacks_t *callbacks);
/**
* @brief Sign a hash using secure element opaque driver
*/
psa_status_t secure_element_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 a secure element key reference (not actual key material)
*/
psa_status_t secure_element_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 a secure element opaque key
*/
psa_status_t secure_element_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 a secure element opaque key
*/
size_t secure_element_opaque_size_function(
psa_key_type_t key_type,
size_t key_bits);
/**
* @brief Verify a hash using secure element transparent driver
*/
psa_status_t secure_element_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
*/
psa_status_t secure_element_transparent_verify_hash_start(
secure_element_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
*/
psa_status_t secure_element_transparent_verify_hash_complete(
secure_element_transparent_verify_hash_operation_t *operation);
/**
* @brief Abort a hash verification operation
*/
psa_status_t secure_element_transparent_verify_hash_abort(
secure_element_transparent_verify_hash_operation_t *operation);
/**
* @brief Start a hash signing operation using opaque driver
*/
psa_status_t secure_element_opaque_sign_hash_start(
secure_element_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 opaque driver
*/
psa_status_t secure_element_opaque_sign_hash_complete(
secure_element_opaque_sign_hash_operation_t *operation,
uint8_t *signature, size_t signature_size,
size_t *signature_length);
/**
* @brief Abort a hash signing operation using opaque driver
*/
psa_status_t secure_element_opaque_sign_hash_abort(
secure_element_opaque_sign_hash_operation_t *operation);
#ifdef __cplusplus
}
#endif
#endif /* SECURE_ELEMENT_DRIVER_ENABLED || CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED */
@@ -0,0 +1,65 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* Maximum key component length in bytes.
* Supports up to P-384 (48 bytes). Increase to 66 for P-521 if needed.
*/
#define SECURE_ELEMENT_MAX_KEY_BYTES 48
/**
* @brief Opaque key structure for secure element import
*
* This struct is passed as the "key material" to psa_import_key().
* It identifies which SE slot the key refers to.
*/
typedef struct {
uint8_t slot_id; /**< Slot index on the secure element */
} secure_element_opaque_key_t;
/**
* @brief Operation context for transparent verify_hash
*
* Used by the interruptible verify API to store intermediate state
* between start / complete / abort calls.
*/
typedef struct {
uint8_t sha[SECURE_ELEMENT_MAX_KEY_BYTES];
uint8_t r[SECURE_ELEMENT_MAX_KEY_BYTES];
uint8_t s[SECURE_ELEMENT_MAX_KEY_BYTES];
uint8_t qx[SECURE_ELEMENT_MAX_KEY_BYTES];
uint8_t qy[SECURE_ELEMENT_MAX_KEY_BYTES];
size_t key_len;
size_t sha_len;
} secure_element_transparent_verify_hash_operation_t;
/**
* @brief Operation context for opaque sign_hash
*
* Used by the interruptible sign API to store intermediate state
* between start / complete / abort calls.
*/
typedef struct {
uint8_t sha[SECURE_ELEMENT_MAX_KEY_BYTES];
size_t key_len;
size_t sha_len;
secure_element_opaque_key_t opaque_key;
unsigned int alg;
} secure_element_opaque_sign_hash_operation_t;
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,510 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Generic Secure Element PSA Crypto Driver
*
* This driver provides PSA Crypto API integration for external secure elements
* (e.g., ATECC608A). The SE-specific operations are dispatched through
* runtime-registered callbacks, removing any build-time dependency on a
* particular SE library.
*
* The callbacks struct declares which algorithm/key_type/key_bits the SE
* supports. The driver validates each request against these and returns
* PSA_ERROR_NOT_SUPPORTED for mismatches, letting PSA fall back to software.
*/
#include <string.h>
#include "sdkconfig.h"
#ifdef CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED
#include "esp_log.h"
#include "psa_crypto_driver_secure_element.h"
static const char *TAG = "psa_crypto_driver_secure_element";
#define UNCOMPRESSED_POINT_FORMAT 0x04
/* Runtime-registered SE callbacks (set once via secure_element_register_callbacks).
* We keep a value copy so the caller's struct lifetime does not matter. */
static secure_element_callbacks_t s_se_callbacks;
static const secure_element_callbacks_t *s_se_callbacks_ptr = NULL;
psa_status_t secure_element_register_callbacks(const secure_element_callbacks_t *callbacks)
{
if (callbacks == NULL) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (callbacks->key_bits == 0) {
ESP_LOGE(TAG, "key_bits must be set in callbacks");
return PSA_ERROR_INVALID_ARGUMENT;
}
if (s_se_callbacks_ptr != NULL) {
ESP_LOGE(TAG, "Secure element callbacks already registered");
return PSA_ERROR_BAD_STATE;
}
s_se_callbacks = *callbacks;
s_se_callbacks_ptr = &s_se_callbacks;
return PSA_SUCCESS;
}
/**
* @brief Get the registered callbacks, or NULL if not yet registered
*/
static inline const secure_element_callbacks_t *se_get_callbacks(void)
{
return s_se_callbacks_ptr;
}
/**
* @brief Check if a request matches the registered SE capabilities
*
* Compares the algorithm and key type from the request against what the SE
* declared at registration time. Returns PSA_ERROR_NOT_SUPPORTED on mismatch.
*/
static psa_status_t validate_request(psa_algorithm_t alg, const psa_key_attributes_t *attributes)
{
const secure_element_callbacks_t *cbs = se_get_callbacks();
if (!cbs) {
return PSA_ERROR_NOT_SUPPORTED;
}
/* Check algorithm family matches (e.g., both are ECDSA) */
psa_algorithm_t registered_alg = cbs->algorithm;
/* Use PSA_ALG_SIGN_GET_HASH to compare base algorithm ignoring hash */
if (PSA_ALG_IS_ECDSA(registered_alg)) {
if (!PSA_ALG_IS_ECDSA(alg)) {
return PSA_ERROR_NOT_SUPPORTED;
}
/* If registered with a specific hash, check it matches */
psa_algorithm_t reg_hash = PSA_ALG_SIGN_GET_HASH(registered_alg);
if (reg_hash != PSA_ALG_ANY_HASH && reg_hash != PSA_ALG_SIGN_GET_HASH(alg)) {
return PSA_ERROR_NOT_SUPPORTED;
}
} else if (PSA_ALG_IS_RSA_PKCS1V15_SIGN(registered_alg)) {
if (!PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg)) {
return PSA_ERROR_NOT_SUPPORTED;
}
/* If registered with a specific hash, check it matches */
psa_algorithm_t reg_hash = PSA_ALG_SIGN_GET_HASH(registered_alg);
if (reg_hash != PSA_ALG_ANY_HASH && reg_hash != PSA_ALG_SIGN_GET_HASH(alg)) {
return PSA_ERROR_NOT_SUPPORTED;
}
} else if (registered_alg != alg) {
return PSA_ERROR_NOT_SUPPORTED;
}
/* Check key type matches */
if (attributes) {
psa_key_type_t req_type = psa_get_key_type(attributes);
/* Accept both key pair and public key for the same family */
psa_key_type_t reg_base = PSA_KEY_TYPE_IS_KEY_PAIR(cbs->key_type)
? PSA_KEY_TYPE_KEY_PAIR_OF_PUBLIC_KEY(PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(cbs->key_type))
: cbs->key_type;
psa_key_type_t req_base = PSA_KEY_TYPE_IS_KEY_PAIR(req_type)
? PSA_KEY_TYPE_KEY_PAIR_OF_PUBLIC_KEY(PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(req_type))
: req_type;
if (reg_base != req_base) {
return PSA_ERROR_NOT_SUPPORTED;
}
/* Check key size matches */
size_t req_bits = psa_get_key_bits(attributes);
if (req_bits != 0 && req_bits != cbs->key_bits) {
return PSA_ERROR_NOT_SUPPORTED;
}
}
return PSA_SUCCESS;
}
/* Transparent verify operations */
psa_status_t secure_element_transparent_verify_hash_start(
secure_element_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)
{
if (!operation || !attributes || !key_buffer || !hash || !signature) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Validate against registered SE capabilities */
psa_status_t status = validate_request(alg, attributes);
if (status != PSA_SUCCESS) {
return status;
}
size_t key_len = PSA_BITS_TO_BYTES(psa_get_key_bits(attributes));
if (key_len == 0 || key_len > SECURE_ELEMENT_MAX_KEY_BYTES) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Verify key buffer can hold the uncompressed public key (0x04 || X || Y) */
if (key_buffer_size < 1 + 2 * key_len) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Validate hash length matches key component length */
if (hash_length != key_len) {
return PSA_ERROR_NOT_SUPPORTED;
}
if (signature_length != 2 * key_len) {
return PSA_ERROR_INVALID_SIGNATURE;
}
/* Handle public key format - must be uncompressed (0x04 || X || Y) */
if (key_buffer[0] != UNCOMPRESSED_POINT_FORMAT) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (key_buffer_size != 2 * key_len + 1) {
return PSA_ERROR_INVALID_ARGUMENT;
}
memset(operation, 0, sizeof(secure_element_transparent_verify_hash_operation_t));
operation->key_len = key_len;
operation->sha_len = hash_length;
/* Big-endian format, matching PSA format */
memcpy(operation->sha, hash, key_len);
memcpy(operation->r, signature, key_len);
memcpy(operation->s, signature + key_len, key_len);
/* Extract public key coordinates (skip 0x04 format byte) */
memcpy(operation->qx, key_buffer + 1, key_len);
memcpy(operation->qy, key_buffer + 1 + key_len, key_len);
return PSA_SUCCESS;
}
psa_status_t secure_element_transparent_verify_hash_complete(secure_element_transparent_verify_hash_operation_t *operation)
{
if (!operation) {
return PSA_ERROR_INVALID_ARGUMENT;
}
const secure_element_callbacks_t *cbs = se_get_callbacks();
if (!cbs || !cbs->verify) {
ESP_LOGE(TAG, "Secure element verify callback not registered");
return PSA_ERROR_NOT_SUPPORTED;
}
size_t key_len = operation->key_len;
/* Construct public key (X || Y) */
uint8_t pubkey[2 * SECURE_ELEMENT_MAX_KEY_BYTES];
memcpy(pubkey, operation->qx, key_len);
memcpy(pubkey + key_len, operation->qy, key_len);
/* Construct signature (R || S) */
uint8_t sig[2 * SECURE_ELEMENT_MAX_KEY_BYTES];
memcpy(sig, operation->r, key_len);
memcpy(sig + key_len, operation->s, key_len);
/* Verify using registered SE callback */
bool is_verified = false;
psa_status_t status = cbs->verify(operation->sha, operation->sha_len,
sig, 2 * key_len,
pubkey, 2 * key_len,
&is_verified);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "SE verify callback failed: 0x%04x", (unsigned)status);
return status;
}
if (!is_verified) {
return PSA_ERROR_INVALID_SIGNATURE;
}
return PSA_SUCCESS;
}
psa_status_t secure_element_transparent_verify_hash_abort(secure_element_transparent_verify_hash_operation_t *operation)
{
if (operation) {
memset(operation, 0, sizeof(secure_element_transparent_verify_hash_operation_t));
}
return PSA_SUCCESS;
}
psa_status_t secure_element_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)
{
secure_element_transparent_verify_hash_operation_t operation;
psa_status_t status = secure_element_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 = secure_element_transparent_verify_hash_complete(&operation);
if (status != PSA_SUCCESS) {
return status;
}
return secure_element_transparent_verify_hash_abort(&operation);
}
/* Opaque sign operations */
psa_status_t secure_element_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(secure_element_opaque_key_t) || data_length < sizeof(secure_element_opaque_key_t)) {
return PSA_ERROR_BUFFER_TOO_SMALL;
}
/* Validate the key attributes match what the SE supports */
const secure_element_callbacks_t *cbs = se_get_callbacks();
if (!cbs) {
return PSA_ERROR_NOT_SUPPORTED;
}
psa_status_t status = validate_request(psa_get_key_algorithm(attributes), attributes);
if (status != PSA_SUCCESS) {
return status;
}
memcpy(key_buffer, data, sizeof(secure_element_opaque_key_t));
*key_buffer_length = sizeof(secure_element_opaque_key_t);
*bits = psa_get_key_bits(attributes);
return PSA_SUCCESS;
}
psa_status_t secure_element_opaque_sign_hash_start(
secure_element_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(secure_element_opaque_key_t)) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Validate against registered SE capabilities */
psa_status_t status = validate_request(alg, attributes);
if (status != PSA_SUCCESS) {
return status;
}
size_t component_len = PSA_BITS_TO_BYTES(psa_get_key_bits(attributes));
if (component_len == 0 || component_len > SECURE_ELEMENT_MAX_KEY_BYTES) {
return PSA_ERROR_INVALID_ARGUMENT;
}
/* Validate hash length matches key component length */
if (hash_length != component_len) {
return PSA_ERROR_INVALID_ARGUMENT;
}
memset(operation, 0, sizeof(secure_element_opaque_sign_hash_operation_t));
operation->key_len = component_len;
memcpy(operation->sha, hash, component_len);
memcpy(&operation->opaque_key, key_buffer, sizeof(secure_element_opaque_key_t));
operation->alg = alg;
operation->sha_len = hash_length;
return PSA_SUCCESS;
}
psa_status_t secure_element_opaque_sign_hash_complete(
secure_element_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;
}
const secure_element_callbacks_t *cbs = se_get_callbacks();
if (!cbs || !cbs->sign) {
ESP_LOGE(TAG, "Secure element sign callback not registered");
return PSA_ERROR_NOT_SUPPORTED;
}
/* Sign using registered SE callback */
uint8_t sig[2 * SECURE_ELEMENT_MAX_KEY_BYTES];
size_t sig_len = 0;
psa_status_t status = cbs->sign(operation->opaque_key.slot_id,
operation->sha, operation->sha_len,
sig, sizeof(sig), &sig_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "SE sign callback failed: 0x%04x", (unsigned)status);
return status;
}
if (sig_len == 0 || sig_len > sizeof(sig)) {
ESP_LOGE(TAG, "SE returned invalid signature length: %zu", sig_len);
return PSA_ERROR_GENERIC_ERROR;
}
if (sig_len > signature_size) {
return PSA_ERROR_BUFFER_TOO_SMALL;
}
/* Copy signature to output (R || S format, big-endian - matches PSA) */
memcpy(signature, sig, sig_len);
*signature_length = sig_len;
return PSA_SUCCESS;
}
psa_status_t secure_element_opaque_sign_hash_abort(secure_element_opaque_sign_hash_operation_t *operation)
{
if (operation) {
memset(operation, 0, sizeof(secure_element_opaque_sign_hash_operation_t));
}
return PSA_SUCCESS;
}
psa_status_t secure_element_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)
{
secure_element_opaque_sign_hash_operation_t operation;
psa_status_t status = secure_element_opaque_sign_hash_start(
&operation, attributes, key_buffer, key_buffer_size, alg, hash, hash_length);
if (status != PSA_SUCCESS) {
secure_element_opaque_sign_hash_abort(&operation);
return status;
}
status = secure_element_opaque_sign_hash_complete(&operation, signature, signature_size, signature_length);
if (status != PSA_SUCCESS) {
secure_element_opaque_sign_hash_abort(&operation);
return status;
}
return secure_element_opaque_sign_hash_abort(&operation);
}
psa_status_t secure_element_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)
{
if (!attributes || !key_buffer || !data || !data_length) {
return PSA_ERROR_INVALID_ARGUMENT;
}
if (key_buffer_size < sizeof(secure_element_opaque_key_t)) {
return PSA_ERROR_INVALID_ARGUMENT;
}
const secure_element_opaque_key_t *opaque_key = (const secure_element_opaque_key_t *) key_buffer;
const secure_element_callbacks_t *cbs = se_get_callbacks();
if (!cbs || !cbs->export_pubkey) {
ESP_LOGE(TAG, "Secure element export_pubkey callback not registered");
return PSA_ERROR_NOT_SUPPORTED;
}
/* Get key length from registered capabilities */
size_t key_len = PSA_BITS_TO_BYTES(cbs->key_bits);
/* 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;
}
/* Export public key using registered SE callback */
uint8_t pubkey[2 * SECURE_ELEMENT_MAX_KEY_BYTES];
size_t pubkey_len = 0;
psa_status_t status = cbs->export_pubkey(opaque_key->slot_id, pubkey, sizeof(pubkey), &pubkey_len);
if (status != PSA_SUCCESS) {
ESP_LOGE(TAG, "SE export_pubkey callback failed: 0x%04x", (unsigned)status);
return status;
}
/* Callback must have written exactly 2 * key_len bytes (X || Y) - reject anything else
* to avoid copying uninitialized stack memory into the caller's buffer. */
if (pubkey_len != 2 * key_len) {
ESP_LOGE(TAG, "SE export_pubkey returned %u bytes, expected %u",
(unsigned)pubkey_len, (unsigned)(2 * key_len));
return PSA_ERROR_HARDWARE_FAILURE;
}
/* Format: uncompressed point (0x04 followed by x and y coordinates) */
data[0] = UNCOMPRESSED_POINT_FORMAT;
memcpy(data + 1, pubkey, key_len); /* X coordinate */
memcpy(data + 1 + key_len, pubkey + key_len, key_len); /* Y coordinate */
*data_length = required_size;
return PSA_SUCCESS;
}
size_t secure_element_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(secure_element_opaque_key_t);
}
#endif /* CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED */
+3
View File
@@ -0,0 +1,3 @@
# Renamed ATCA/SE ECDSA options to generic secure element (v6.0)
CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED
CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY CONFIG_MBEDTLS_SECURE_ELEMENT_DRIVER_ENABLED
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -206,15 +206,6 @@ void esp_transport_ssl_set_common_name(esp_transport_handle_t t, const char *com
*/
void esp_transport_ssl_set_ciphersuites_list(esp_transport_handle_t t, const int *ciphersuites_list);
/**
* @brief Set the ssl context to use secure element (atecc608a) for client(device) private key and certificate
*
* @note Recommended to be used with ESP32 series interfaced to ATECC608A based secure element
*
* @param t ssl transport
*/
void esp_transport_ssl_use_secure_element(esp_transport_handle_t t);
/**
* @brief Set the ds_data handle in ssl context.(used for the digital signature operation)
*
@@ -223,6 +214,26 @@ void esp_transport_ssl_use_secure_element(esp_transport_handle_t t);
*/
void esp_transport_ssl_set_ds_data(esp_transport_handle_t t, void *ds_data);
/**
* @brief Set unified client key configuration for mutual authentication
*
* This function provides a unified way to configure client private keys
* from various sources (buffer, ECDSA peripheral, secure element, etc.)
* using the esp_key_config_t structure.
*
* @note This function stores the pointer to config, rather than making a copy.
* So the config must remain valid until after the connection is cleaned up.
*
* @note When client_key is set, it takes precedence over legacy key configuration
* functions (set_client_key_data, set_client_key_ecdsa_peripheral, etc.)
*
* @param t ssl transport
* @param[in] client_key Pointer to the unified key configuration
*
* @see esp_key_config_t for configuration options
*/
void esp_transport_ssl_set_client_key_config(esp_transport_handle_t t, const esp_key_config_t *client_key);
/**
* @brief Set PSK key and hint for PSK server/client verification in esp-tls component.
* Important notes:
+6 -8
View File
@@ -502,14 +502,6 @@ void esp_transport_ssl_set_ciphersuites_list(esp_transport_handle_t t, const int
ssl->cfg.ciphersuites_list = ciphersuites_list;
}
#ifdef CONFIG_ESP_TLS_USE_SECURE_ELEMENT
void esp_transport_ssl_use_secure_element(esp_transport_handle_t t)
{
GET_SSL_FROM_TRANSPORT_OR_RETURN(ssl, t);
ssl->cfg.use_secure_element = true;
}
#endif
#ifdef CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
void esp_transport_ssl_crt_bundle_attach(esp_transport_handle_t t, esp_err_t ((*crt_bundle_attach)(void *conf)))
{
@@ -575,6 +567,12 @@ void esp_transport_ssl_set_ds_data(esp_transport_handle_t t, void *ds_data)
}
#endif
void esp_transport_ssl_set_client_key_config(esp_transport_handle_t t, const esp_key_config_t *client_key)
{
GET_SSL_FROM_TRANSPORT_OR_RETURN(ssl, t);
ssl->cfg.client_key = client_key;
}
void esp_transport_ssl_set_keep_alive(esp_transport_handle_t t, esp_transport_keep_alive_t *keep_alive_cfg)
{
GET_SSL_FROM_TRANSPORT_OR_RETURN(ssl, t);