Skip to content

Mbed TLS#

The aes70::mbedtls helpers (header aes70/mbedtls.hpp) wrap Mbed TLS so AES70 traffic runs over TLS. They support TLS-PSK (psk_context) and certificate TLS (cert_context), including mutual TLS (mTLS), when those features are enabled in the linked Mbed TLS build. Mbed TLS is not included with libaes70; you supply and link it. See Third-party libraries.

Layering and lifecycle match the OpenSSL helpers; see OpenSSL TLS for the encrypt < io < decrypt < Connection model. APIs differ where Mbed TLS is naturally different.

Prerequisites#

  • Mbed TLS 3.x or 4.x.
  • Link MbedTLS::mbedtls (pulls mbedx509 / tfpsacrypto or equivalent).
  • A platform TCP integration (e.g. libuv) as for plaintext AES70.
  • Call path must run psa_crypto_init() before TLS work on Mbed TLS 4.x. The helpers call it once via detail::ensure_psa_crypto(). On 3.x, RNG is provided by CTR-DRBG (mbedtls_ctr_drbg_random) instead.

Feature detection#

aes70/mbedtls/features.hpp inspects the active Mbed TLS config (via mbedtls/ssl.h) and defines:

Macro When set Exposes
AES70_MBEDTLS_PSK PSK handshake APIs present in the build psk_context
AES70_MBEDTLS_CERT MBEDTLS_X509_CRT_PARSE_C (certificate TLS) cert_context, pem.hpp helpers

Detection uses MBEDTLS_SSL_HANDSHAKE_WITH_PSK_ENABLED or MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED for PSK, and MBEDTLS_X509_CRT_PARSE_C for certificates.

#include <aes70/mbedtls.hpp> always pulls the shared TLS session / port / connector helpers. PSK and cert context headers are included only when the corresponding macro is defined, so a reduced Mbed TLS (e.g. ESP-IDF without PSK) still compiles; those types are simply unavailable.

Guard optional call sites with #ifdef AES70_MBEDTLS_PSK / #ifdef AES70_MBEDTLS_CERT.

Layering#

encrypt < io < decrypt < Connection
  • Device: wrap the AES70 port with aes70::mbedtls::port<Port, Context>.
  • Controller: wrap an IO connector_factory with aes70::mbedtls::connector_factory<IoFactory, Connection, Context>.

Connection and Context are both required (e.g. aes70::controller::connection and psk_context / cert_context).

Context contract#

A TLS context must provide:

  • mbedtls_ssl_config *ssl_config()
  • bool tls_is_server() const
  • const char *hostname() const (SNI / cert verify name; may be nullptr for PSK)

It must be copyable enough to live in port and reconnecting controller factories (shared_ptr impl).

Memory BIO / non-blocking IO#

Mbed TLS has no BIO_s_mem. ssl_session uses mbedtls_ssl_set_bio over inbound/outbound aes70::buffer<>s:

  • Empty recv returns MBEDTLS_ERR_SSL_WANT_READ (not 0 — 0 means EOF).
  • Send appends to the outbound buffer for the encrypt layer to drain into TCP.

After WANT_READ / WANT_WRITE from mbedtls_ssl_write, the next write uses the same length (tracked as in_flight_write_len_).

Plaintext from mbedtls_ssl_read is copied into a scratch buffer of size AES70_MBEDTLS_READ_CHUNK_SIZE (default 2 KiB). Override at compile time for tighter RAM. This is independent of Mbed TLS’s own record buffers (MBEDTLS_SSL_IN_CONTENT_LEN / OUT_CONTENT_LEN in mbedtls_config.h), which dominate per-connection TLS memory on embedded builds.

TLS-PSK#

Requires AES70_MBEDTLS_PSK.

aes70::mbedtls::psk_config cfg;
cfg.key.assign(32, 0x42);  // demo only
cfg.min_version = MBEDTLS_SSL_VERSION_TLS1_2;
cfg.max_version = MBEDTLS_SSL_VERSION_TLS1_3;

auto server_tls = aes70::mbedtls::psk_context::make_server(cfg);
auto client_tls = aes70::mbedtls::psk_context::make_client(cfg);

using Port = aes70::mbedtls::port<aes70::port<Device>, aes70::mbedtls::psk_context>;
using Factory = aes70::mbedtls::connector_factory<libuv::tcp::connector_factory,
    aes70::controller::connection, aes70::mbedtls::psk_context>;

Optional cfg.ciphersuites is a list of Mbed TLS ciphersuite IDs ending in 0.

Full working code: examples/mbedtls/psk.

Certificate TLS#

Requires AES70_MBEDTLS_CERT.

cert_config takes PEM (NUL-terminated) or DER in memory. Use pem.hpp helpers for PEM files. Clients that verify the peer must set hostname (Mbed TLS 4.x refuses cert auth without it).

Mode Server Client
Server cert only certificate + private_key, verify_peer=false ca_certificates, verify_peer=true, hostname
mTLS cert + key + ca_certificates, verify_peer=true cert + key + ca_certificates, verify_peer=true, hostname

Examples: examples/mbedtls/server_cert, examples/mbedtls/mtls (demo PEMs via examples/mbedtls/certs/gen_certs.sh).

Errors#

Controller channels (same idea as OpenSSL):

  1. IO factory on_error — transport failures.
  2. Mbed TLS factory on_error — TLS-layer failures (negative Mbed TLS return codes).
  3. observe_connection on_failure — connect failed or closed before OCP.1 ready.

Pitfalls#

  • Entropy / PSA: product owns platform entropy seeding for embedded targets; desktop OS RNG via PSA is usually enough after psa_crypto_init.
  • Feature flags: a system Mbed TLS build may omit PSK or TLS 1.3 — use AES70_MBEDTLS_PSK / check MBEDTLS_SSL_PROTO_TLS1_3. On ESP-IDF enable CONFIG_MBEDTLS_PSK_MODES and CONFIG_MBEDTLS_KEY_EXCHANGE_PSK.
  • Stack: handshake can be stack-heavy on MCUs; prefer desktop examples first.
  • Close: mbedtls_ssl_close_notify is not wired into connection close() (same as OpenSSL helpers).
  • Shared config: do not mutate mbedtls_ssl_config after connections exist.

Examples#

Example Mode Default port
examples/mbedtls/psk TLS-PSK 65133
examples/mbedtls/server_cert Server cert only 65134
examples/mbedtls/mtls Mutual TLS 65135