fix(mbedtls_cxx): Add support for mbedtls psa api

This commit is contained in:
David Cermak
2026-01-05 12:13:32 +01:00
parent d30246b74f
commit 884d8fe6f5
4 changed files with 151 additions and 20 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
idf_component_register(SRCS mbedtls_wrap.cpp
INCLUDE_DIRS include
REQUIRES tcp_transport)
REQUIRES tcp_transport esp_timer)
+17 -1
View File
@@ -1,3 +1,19 @@
# mbedtls_cxx
This is a simplified C++ wrapper of mbedTLS for performing TLS and DTLS handshake a communication. This component allows for overriding low level IO functions (`send()` and `recv()`) and thus supporting TLS over various physical channels.
This is a simplified C++ wrapper of mbedTLS for performing TLS and DTLS handshake and communication. This component allows for overriding low level IO functions (`send()` and `recv()`) and thus supporting TLS over various physical channels.
## mbedTLS Version Support
This wrapper supports both mbedTLS v3 (legacy API) and mbedTLS v4 (PSA Crypto API). The appropriate API is selected automatically at compile time based on the mbedTLS version.
### PSA Crypto Lifecycle (mbedTLS v4)
When using mbedTLS v4, this wrapper calls `psa_crypto_init()` during `Tls::init()`. This function is idempotent - it's safe to call multiple times, whether by this wrapper or by other code using mbedTLS directly.
**Important:** This wrapper does **not** call `mbedtls_psa_crypto_free()` on destruction. This is intentional to avoid breaking other code that may also be using PSA Crypto. If your application requires explicit cleanup of PSA resources (e.g., for memory leak detection), you should call `mbedtls_psa_crypto_free()` at application shutdown after all TLS operations and other mbedTLS usage is complete.
```cpp
// At application shutdown (if needed):
#include "psa/crypto_extra.h"
mbedtls_psa_crypto_free();
```
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -7,11 +7,10 @@
#include <utility>
#include <memory>
#include <mbedtls/timing.h>
#include <cstdint>
#include "mbedtls/version.h"
#include <mbedtls/ssl_cookie.h>
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
namespace idf::mbedtls_cxx {
@@ -19,6 +18,31 @@ namespace idf::mbedtls_cxx {
using const_buf = std::pair<const unsigned char *, std::size_t>;
using buf = std::pair<unsigned char *, std::size_t>;
// Determine mbedTLS major version (ESP-IDF may define MBEDTLS_MAJOR_VERSION via compile flags)
#if defined(MBEDTLS_MAJOR_VERSION)
#define MBEDTLS_CXX_MBEDTLS_MAJOR MBEDTLS_MAJOR_VERSION
#elif defined(MBEDTLS_VERSION_MAJOR)
#define MBEDTLS_CXX_MBEDTLS_MAJOR MBEDTLS_VERSION_MAJOR
#elif defined(MBEDTLS_VERSION_NUMBER)
#define MBEDTLS_CXX_MBEDTLS_MAJOR ((MBEDTLS_VERSION_NUMBER >> 24) & 0xFF)
#else
#define MBEDTLS_CXX_MBEDTLS_MAJOR 0
#endif
#if MBEDTLS_CXX_MBEDTLS_MAJOR >= 4
// mbedTLS v4: PSA-backed RNG, no mbedtls_timing helpers guaranteed available in ESP-IDF linkage.
struct dtls_timer_context {
int64_t start_us = 0;
uint32_t int_ms = 0;
uint32_t fin_ms = 0;
};
#else
// mbedTLS v3: legacy entropy/CTR_DRBG + timing helpers.
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include <mbedtls/timing.h>
#endif
struct TlsConfig {
bool is_dtls;
uint32_t timeout;
@@ -84,10 +108,15 @@ protected:
mbedtls_pk_context pk_key_{};
mbedtls_x509_crt ca_cert_{};
mbedtls_ssl_config conf_{};
mbedtls_ctr_drbg_context ctr_drbg_{};
#if MBEDTLS_CXX_MBEDTLS_MAJOR >= 4
dtls_timer_context timer_ {};
#else
mbedtls_ctr_drbg_context ctr_drbg_ {};
mbedtls_entropy_context entropy_{};
mbedtls_timing_delay_context timer_{};
mbedtls_ssl_cookie_ctx cookie_{};
bool rng_initialized_{false};
#endif
mbedtls_ssl_cookie_ctx cookie_ {};
const_buf client_id_{};
virtual void delay() {}
+98 -12
View File
@@ -1,23 +1,80 @@
/*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <mbedtls/timing.h>
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/ssl.h"
#include "mbedtls_wrap.hpp"
#if MBEDTLS_CXX_MBEDTLS_MAJOR >= 4
#include "esp_timer.h"
#include "psa/crypto.h"
namespace {
void timer_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms)
{
auto *ctx = static_cast<idf::mbedtls_cxx::dtls_timer_context *>(data);
if (fin_ms == 0) {
ctx->int_ms = 0;
ctx->fin_ms = 0;
ctx->start_us = 0;
return;
}
ctx->int_ms = int_ms;
ctx->fin_ms = fin_ms;
ctx->start_us = esp_timer_get_time();
}
int timer_get_delay(void *data)
{
auto *ctx = static_cast<idf::mbedtls_cxx::dtls_timer_context *>(data);
if (ctx->fin_ms == 0) {
// Timer cancelled or not set
return -1;
}
int64_t elapsed_ms = (esp_timer_get_time() - ctx->start_us) / 1000;
if (elapsed_ms >= static_cast<int64_t>(ctx->fin_ms)) {
return 2;
}
if (elapsed_ms >= static_cast<int64_t>(ctx->int_ms)) {
return 1;
}
return 0;
}
} // anonymous namespace
#endif // MBEDTLS_CXX_MBEDTLS_MAJOR >= 4
using namespace idf::mbedtls_cxx;
bool Tls::init(is_server server, do_verify verify, TlsConfig *config)
{
const char pers[] = "mbedtls_wrapper";
is_server_ = server == is_server{true};
is_dtls_ = config ? config->is_dtls : false;
uint32_t timeout = config ? config->timeout : 0;
#if MBEDTLS_CXX_MBEDTLS_MAJOR >= 4
psa_status_t status = psa_crypto_init();
if (status != PSA_SUCCESS) {
printf("psa_crypto_init() failed: %d\n", (int)status);
return false;
}
#else
const char pers[] = "mbedtls_cxx";
mbedtls_entropy_init(&entropy_);
mbedtls_ctr_drbg_seed(&ctr_drbg_, mbedtls_entropy_func, &entropy_, (const unsigned char *)pers, sizeof(pers));
mbedtls_ctr_drbg_init(&ctr_drbg_);
int ret_seed = mbedtls_ctr_drbg_seed(&ctr_drbg_, mbedtls_entropy_func, &entropy_,
reinterpret_cast<const unsigned char *>(pers), sizeof(pers));
if (ret_seed != 0) {
print_error("mbedtls_ctr_drbg_seed", ret_seed);
mbedtls_ctr_drbg_free(&ctr_drbg_);
mbedtls_entropy_free(&entropy_);
return false;
}
rng_initialized_ = true;
#endif
int endpoint = server == is_server{true} ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT;
int transport = is_dtls_ ? MBEDTLS_SSL_TRANSPORT_DATAGRAM : MBEDTLS_SSL_TRANSPORT_STREAM;
int ret = mbedtls_ssl_config_defaults(&conf_, endpoint, transport, MBEDTLS_SSL_PRESET_DEFAULT);
@@ -25,7 +82,10 @@ bool Tls::init(is_server server, do_verify verify, TlsConfig *config)
print_error("mbedtls_ssl_config_defaults", ret);
return false;
}
#if MBEDTLS_CXX_MBEDTLS_MAJOR < 4
// mbedTLS v3: TLS RNG must be configured explicitly
mbedtls_ssl_conf_rng(&conf_, mbedtls_ctr_drbg_random, &ctr_drbg_);
#endif
if (timeout) {
mbedtls_ssl_conf_read_timeout(&conf_, timeout);
}
@@ -54,7 +114,11 @@ bool Tls::init(is_server server, do_verify verify, TlsConfig *config)
}
if (timeout) {
#if MBEDTLS_CXX_MBEDTLS_MAJOR >= 4
mbedtls_ssl_set_timer_cb(&ssl_, &timer_, timer_set_delay, timer_get_delay);
#else
mbedtls_ssl_set_timer_cb(&ssl_, &timer_, mbedtls_timing_set_delay, mbedtls_timing_get_delay);
#endif
}
#if CONFIG_MBEDTLS_SSL_PROTO_DTLS
@@ -76,6 +140,13 @@ bool Tls::deinit()
::mbedtls_pk_free(&pk_key_);
::mbedtls_x509_crt_free(&public_cert_);
::mbedtls_x509_crt_free(&ca_cert_);
#if MBEDTLS_CXX_MBEDTLS_MAJOR < 4
if (rng_initialized_) {
::mbedtls_ctr_drbg_free(&ctr_drbg_);
::mbedtls_entropy_free(&entropy_);
rng_initialized_ = false;
}
#endif
return true;
}
@@ -93,8 +164,8 @@ int Tls::handshake()
int ret = 0;
mbedtls_ssl_set_bio(&ssl_, this, bio_write, bio_read, is_dtls_ ? bio_read_tout : nullptr);
while ( ( ret = mbedtls_ssl_handshake( &ssl_ ) ) != 0 ) {
if ( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) {
while ((ret = mbedtls_ssl_handshake(&ssl_)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
#if CONFIG_MBEDTLS_SSL_PROTO_DTLS
if (is_server_ && is_dtls_ && ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED) {
// hello verification requested -> restart the session with this client_id
@@ -104,7 +175,7 @@ int Tls::handshake()
continue;
}
#endif // MBEDTLS_SSL_PROTO_DTLS
print_error( "mbedtls_ssl_handshake returned", ret );
print_error("mbedtls_ssl_handshake returned", ret);
return -1;
}
delay();
@@ -132,12 +203,12 @@ int Tls::bio_read_tout(void *ctx, unsigned char *buf, size_t len, uint32_t timeo
int Tls::write(const unsigned char *buf, size_t len)
{
return mbedtls_ssl_write( &ssl_, buf, len );
return mbedtls_ssl_write(&ssl_, buf, len);
}
int Tls::read(unsigned char *buf, size_t len)
{
return mbedtls_ssl_read( &ssl_, buf, len );
return mbedtls_ssl_read(&ssl_, buf, len);
}
bool Tls::set_own_cert(const_buf crt, const_buf key)
@@ -147,7 +218,7 @@ bool Tls::set_own_cert(const_buf crt, const_buf key)
print_error("mbedtls_x509_crt_parse", ret);
return false;
}
ret = mbedtls_pk_parse_key(&pk_key_, key.first, key.second, nullptr, 0);
ret = this->mbedtls_pk_parse_key(&pk_key_, key.first, key.second, nullptr, 0);
if (ret < 0) {
print_error("mbedtls_pk_parse_keyfile", ret);
return false;
@@ -184,8 +255,13 @@ Tls::Tls()
int Tls::mbedtls_pk_parse_key(mbedtls_pk_context *ctx, const unsigned char *key, size_t keylen, const unsigned char *pwd, size_t pwdlen)
{
#if MBEDTLS_CXX_MBEDTLS_MAJOR >= 4
return ::mbedtls_pk_parse_key(ctx, key, keylen, pwd, pwdlen);
#else
// Pass nullptr for RNG since set_own_cert() may be called before init()
// and the RNG context won't be seeded yet. This is safe for unencrypted keys.
return ::mbedtls_pk_parse_key(ctx, key, keylen, pwd, pwdlen, nullptr, nullptr);
#endif
}
size_t Tls::get_available_bytes()
@@ -200,6 +276,12 @@ Tls::~Tls()
::mbedtls_pk_free(&pk_key_);
::mbedtls_x509_crt_free(&public_cert_);
::mbedtls_x509_crt_free(&ca_cert_);
#if MBEDTLS_CXX_MBEDTLS_MAJOR < 4
if (rng_initialized_) {
::mbedtls_ctr_drbg_free(&ctr_drbg_);
::mbedtls_entropy_free(&entropy_);
}
#endif
}
bool Tls::get_session()
@@ -241,7 +323,11 @@ bool Tls::is_session_loaded()
#if CONFIG_MBEDTLS_SSL_PROTO_DTLS
bool Tls::init_dtls_cookies()
{
#if MBEDTLS_CXX_MBEDTLS_MAJOR >= 4
int ret = mbedtls_ssl_cookie_setup(&cookie_);
#else
int ret = mbedtls_ssl_cookie_setup(&cookie_, mbedtls_ctr_drbg_random, &ctr_drbg_);
#endif
if (ret != 0) {
print_error("mbedtls_ssl_cookie_setup() failed", ret);
return false;