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(pullsmbedx509/tfpsacryptoor 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 viadetail::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_factorywithaes70::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() constconst char *hostname() const(SNI / cert verify name; may benullptrfor 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(not0—0means 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):
- IO factory
on_error— transport failures. - Mbed TLS factory
on_error— TLS-layer failures (negative Mbed TLS return codes). observe_connectionon_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/ checkMBEDTLS_SSL_PROTO_TLS1_3. On ESP-IDF enableCONFIG_MBEDTLS_PSK_MODESandCONFIG_MBEDTLS_KEY_EXCHANGE_PSK. - Stack: handshake can be stack-heavy on MCUs; prefer desktop examples first.
- Close:
mbedtls_ssl_close_notifyis not wired into connectionclose()(same as OpenSSL helpers). - Shared config: do not mutate
mbedtls_ssl_configafter 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 |