17#if DETWS_ENABLE_TLS && defined(ARDUINO)
22#include <esp_system.h>
25#include <mbedtls/error.h>
26#include <mbedtls/pk.h>
27#include <mbedtls/platform.h>
28#include <mbedtls/sha256.h>
29#include <mbedtls/ssl.h>
30#include <mbedtls/version.h>
31#include <mbedtls/x509_crt.h>
32#if DETWS_ENABLE_TLS_RESUMPTION
33#include <mbedtls/ssl_ticket.h>
34#if !defined(MBEDTLS_SSL_TICKET_C) || !defined(MBEDTLS_SSL_SESSION_TICKETS)
35#error "DETWS_ENABLE_TLS_RESUMPTION needs an mbedTLS build with MBEDTLS_SSL_TICKET_C + MBEDTLS_SSL_SESSION_TICKETS"
67#if DETWS_TLS_ARENA_IN_PSRAM && defined(ARDUINO)
69#if !defined(CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY)
71 "DETWS_TLS_ARENA_IN_PSRAM needs a framework built with CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y. The stock arduino-esp32 core ships it OFF, so EXT_RAM_BSS_ATTR silently no-ops and the arena would stay in internal RAM. Rebuild the core (see tools/psram/README.md) or unset DETWS_TLS_ARENA_IN_PSRAM."
73#if defined(EXT_RAM_BSS_ATTR)
74#define DETWS_TLS_ARENA_ATTR EXT_RAM_BSS_ATTR
75#elif defined(EXT_RAM_ATTR)
76#define DETWS_TLS_ARENA_ATTR EXT_RAM_ATTR
78#error "DETWS_TLS_ARENA_IN_PSRAM: no EXT_RAM_BSS_ATTR/EXT_RAM_ATTR from the framework (unexpected)."
81#define DETWS_TLS_ARENA_ATTR
92DETWS_TLS_ARENA_ATTR
static TlsPoolCtx s_pool;
100static const size_t POOL_HDR = (
sizeof(PoolBlk) + (TLS_ALIGN - 1)) & ~(
size_t)(TLS_ALIGN - 1);
102static void pool_init()
104 PoolBlk *b = (PoolBlk *)s_pool.arena;
105 b->size =
sizeof(s_pool.arena) - POOL_HDR;
109 s_pool.inited =
true;
112static void pool_coalesce()
114 uint8_t *p = s_pool.arena;
115 uint8_t *end = s_pool.arena +
sizeof(s_pool.arena);
118 PoolBlk *b = (PoolBlk *)p;
119 uint8_t *next = p + POOL_HDR + b->size;
120 if (!b->used && next < end)
122 PoolBlk *nb = (PoolBlk *)next;
125 b->size += POOL_HDR + nb->size;
133static void *pool_calloc(
size_t n,
size_t size)
137 if (n != 0 && size > (
size_t)-1 / n)
139 size_t want = n * size;
140 want = (want + (TLS_ALIGN - 1)) & ~(size_t)(TLS_ALIGN - 1);
144 uint8_t *p = s_pool.arena;
145 uint8_t *end = s_pool.arena +
sizeof(s_pool.arena);
148 PoolBlk *b = (PoolBlk *)p;
149 if (!b->used && b->size >= want)
152 if (b->size >= want + POOL_HDR + TLS_ALIGN)
154 PoolBlk *nb = (PoolBlk *)(p + POOL_HDR + want);
155 nb->size = b->size - want - POOL_HDR;
160 s_pool.used += b->size;
161 if (s_pool.used > s_pool.peak)
162 s_pool.peak = s_pool.used;
163 void *payload = p + POOL_HDR;
164 memset(payload, 0, b->size);
167 p += POOL_HDR + b->size;
172static void pool_free(
void *ptr)
176 PoolBlk *b = (PoolBlk *)((uint8_t *)ptr - POOL_HDR);
180 if (s_pool.used >= b->size)
181 s_pool.used -= b->size;
192struct TlsServerReadyCtx
196static TlsServerReadyCtx s_srv_ready;
203 mbedtls_ssl_config conf;
204 mbedtls_x509_crt cert;
205 mbedtls_pk_context key;
209#if DETWS_ENABLE_TLS_RESUMPTION
210 mbedtls_ssl_ticket_context ticket_ctx;
213static TlsServerCtx s_srv;
217 mbedtls_ssl_context ssl;
229static TlsConnsCtx s_conns;
231static TlsConn *find(uint8_t slot)
234 if (s_conns.conns[i].active && s_conns.conns[i].slot == slot)
235 return &s_conns.conns[i];
242static int tls_rng(
void *ctx,
unsigned char *out,
size_t len)
245 esp_fill_random(out, len);
252static int server_bio_recv(
void *ctx,
unsigned char *buf,
size_t len)
254 TlsConn *e = (TlsConn *)ctx;
255 size_t n = det_conn_read(e->slot, buf, len);
257 return MBEDTLS_ERR_SSL_WANT_READ;
266static int server_bio_send(
void *ctx,
const unsigned char *buf,
size_t len)
268 TlsConn *e = (TlsConn *)ctx;
270 return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
272 size_t avail = tcp_sndbuf(e->pcb);
274 return MBEDTLS_ERR_SSL_WANT_WRITE;
283 return MBEDTLS_ERR_SSL_WANT_WRITE;
291static void tls_apply_max_frag_len(mbedtls_ssl_config *conf)
293#if DETWS_TLS_MAX_FRAG_LEN && defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
294#if DETWS_TLS_MAX_FRAG_LEN <= 512
295 mbedtls_ssl_conf_max_frag_len(conf, MBEDTLS_SSL_MAX_FRAG_LEN_512);
296#elif DETWS_TLS_MAX_FRAG_LEN <= 1024
297 mbedtls_ssl_conf_max_frag_len(conf, MBEDTLS_SSL_MAX_FRAG_LEN_1024);
298#elif DETWS_TLS_MAX_FRAG_LEN <= 2048
299 mbedtls_ssl_conf_max_frag_len(conf, MBEDTLS_SSL_MAX_FRAG_LEN_2048);
301 mbedtls_ssl_conf_max_frag_len(conf, MBEDTLS_SSL_MAX_FRAG_LEN_4096);
322static void tls_apply_curve_pref(mbedtls_ssl_config *conf)
324#if MBEDTLS_VERSION_MAJOR >= 3
325 static const uint16_t kGroupPref[] = {
326#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
327 MBEDTLS_SSL_IANA_TLS_GROUP_X25519,
329#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
330 MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1,
332#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED)
333 MBEDTLS_SSL_IANA_TLS_GROUP_SECP384R1,
335#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED)
336 MBEDTLS_SSL_IANA_TLS_GROUP_SECP521R1,
340 mbedtls_ssl_conf_groups(conf, kGroupPref);
342 static const mbedtls_ecp_group_id kCurvePref[] = {
343#if defined(MBEDTLS_ECP_DP_CURVE25519_ENABLED)
344 MBEDTLS_ECP_DP_CURVE25519,
346#if defined(MBEDTLS_ECP_DP_SECP256R1_ENABLED)
347 MBEDTLS_ECP_DP_SECP256R1,
349#if defined(MBEDTLS_ECP_DP_SECP384R1_ENABLED)
350 MBEDTLS_ECP_DP_SECP384R1,
352#if defined(MBEDTLS_ECP_DP_SECP521R1_ENABLED)
353 MBEDTLS_ECP_DP_SECP521R1,
357 mbedtls_ssl_conf_curves(conf, kCurvePref);
364bool det_tls_global_init(
const uint8_t *cert,
size_t cert_len,
const uint8_t *key,
size_t key_len)
366 if (s_srv_ready.ready)
374 mbedtls_platform_set_calloc_free(pool_calloc, pool_free);
376 mbedtls_x509_crt_init(&s_srv.cert);
377 mbedtls_pk_init(&s_srv.key);
378 mbedtls_ssl_config_init(&s_srv.conf);
380 if (mbedtls_x509_crt_parse(&s_srv.cert, cert, cert_len) != 0)
383#if MBEDTLS_VERSION_MAJOR >= 3
384 if (mbedtls_pk_parse_key(&s_srv.key, key, key_len,
nullptr, 0, tls_rng,
nullptr) != 0)
387 if (mbedtls_pk_parse_key(&s_srv.key, key, key_len,
nullptr, 0) != 0)
391 if (mbedtls_ssl_config_defaults(&s_srv.conf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM,
392 MBEDTLS_SSL_PRESET_DEFAULT) != 0)
395 mbedtls_ssl_conf_rng(&s_srv.conf, tls_rng,
nullptr);
396 tls_apply_max_frag_len(&s_srv.conf);
397 tls_apply_curve_pref(&s_srv.conf);
398#if MBEDTLS_VERSION_MAJOR >= 3
399 mbedtls_ssl_conf_min_tls_version(&s_srv.conf, MBEDTLS_SSL_VERSION_TLS1_2);
401 mbedtls_ssl_conf_min_version(&s_srv.conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
404 if (mbedtls_ssl_conf_own_cert(&s_srv.conf, &s_srv.cert, &s_srv.key) != 0)
407#if DETWS_ENABLE_HTTP2
410 static const char *s_alpn[] = {
"h2",
"http/1.1",
nullptr};
411 if (mbedtls_ssl_conf_alpn_protocols(&s_srv.conf, s_alpn) != 0)
415#if DETWS_ENABLE_TLS_RESUMPTION
420 mbedtls_ssl_ticket_init(&s_srv.ticket_ctx);
421 if (mbedtls_ssl_ticket_setup(&s_srv.ticket_ctx, tls_rng,
nullptr, MBEDTLS_CIPHER_AES_256_GCM,
424 mbedtls_ssl_conf_session_tickets_cb(&s_srv.conf, mbedtls_ssl_ticket_write, mbedtls_ssl_ticket_parse,
429 s_conns.conns[i].active =
false;
431 s_srv_ready.ready =
true;
437 return s_srv_ready.ready;
440const char *det_tls_alpn(uint8_t slot)
442 TlsConn *c = find(slot);
443 return c ? mbedtls_ssl_get_alpn_protocol(&c->ssl) : nullptr;
446bool det_tls_conn_begin(uint8_t slot)
448 if (!s_srv_ready.ready)
450 TlsConn *e =
nullptr;
453 if (!s_conns.conns[i].active)
455 e = &s_conns.conns[i];
465 e->established =
false;
466 mbedtls_ssl_init(&e->ssl);
467 if (mbedtls_ssl_setup(&e->ssl, &s_srv.conf) != 0)
469 mbedtls_ssl_free(&e->ssl);
473 mbedtls_ssl_set_bio(&e->ssl, e, server_bio_send, server_bio_recv,
nullptr);
477int det_tls_handshake(uint8_t slot)
479 TlsConn *e = find(slot);
482 int ret = mbedtls_ssl_handshake(&e->ssl);
485 e->established =
true;
488 if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
493bool det_tls_established(uint8_t slot)
495 TlsConn *e = find(slot);
496 return e && e->established;
499int det_tls_read(uint8_t slot, uint8_t *buf,
size_t len)
501 TlsConn *e = find(slot);
504 int ret = mbedtls_ssl_read(&e->ssl, buf, len);
507 if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
512int det_tls_write(uint8_t slot,
const void *data,
size_t len)
514 TlsConn *e = find(slot);
517 const unsigned char *p = (
const unsigned char *)data;
522 int ret = mbedtls_ssl_write(&e->ssl, p + sent, len - sent);
529 if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
540void det_tls_conn_end(uint8_t slot)
542 TlsConn *e = find(slot);
545 mbedtls_ssl_close_notify(&e->ssl);
546 mbedtls_ssl_free(&e->ssl);
548 e->established =
false;
552void det_tls_conn_free(uint8_t slot)
554 TlsConn *e = find(slot);
557 mbedtls_ssl_free(&e->ssl);
559 e->established =
false;
563size_t det_tls_arena_peak()
569bool det_tls_set_client_ca(
const uint8_t *ca,
size_t ca_len)
571 if (!s_srv_ready.ready || !ca)
573 mbedtls_x509_crt_init(&s_srv.ca);
574 if (mbedtls_x509_crt_parse(&s_srv.ca, ca, ca_len) != 0)
578 mbedtls_ssl_conf_ca_chain(&s_srv.conf, &s_srv.ca,
nullptr);
579 mbedtls_ssl_conf_authmode(&s_srv.conf, MBEDTLS_SSL_VERIFY_REQUIRED);
583int det_tls_peer_subject(uint8_t slot,
char *out,
size_t out_len)
585 if (!out || out_len == 0)
588 TlsConn *e = find(slot);
589 if (!e || !e->established)
591 const mbedtls_x509_crt *peer = mbedtls_ssl_get_peer_cert(&e->ssl);
594 int n = mbedtls_x509_dn_gets(out, out_len, &peer->subject);
599#if DETWS_ENABLE_CLIENT_TLS
609struct TlsClientAuthCtx
614 bool pin_set =
false;
616static TlsClientAuthCtx s_cli;
620static void client_arena_ensure()
625 mbedtls_platform_set_calloc_free(pool_calloc, pool_free);
629void det_tls_client_set_ca(
const uint8_t *ca,
size_t ca_len)
631 client_arena_ensure();
633 mbedtls_x509_crt_free(&s_cli.ca);
634 s_cli.ca_set =
false;
635 if (!ca || ca_len == 0)
637 mbedtls_x509_crt_init(&s_cli.ca);
638 s_cli.ca_set = (mbedtls_x509_crt_parse(&s_cli.ca, ca, ca_len) == 0);
640 mbedtls_x509_crt_free(&s_cli.ca);
643void det_tls_client_set_pin(
const uint8_t sha256[32])
647 s_cli.pin_set =
false;
650 memcpy(s_cli.pin, sha256, 32);
651 s_cli.pin_set =
true;
654void det_tls_client_clear_verify()
657 mbedtls_x509_crt_free(&s_cli.ca);
658 s_cli.ca_set =
false;
659 s_cli.pin_set =
false;
663static bool ct_eq32(
const uint8_t *a,
const uint8_t *b)
666 for (
int i = 0; i < 32; i++)
667 d |= (uint8_t)(a[i] ^ b[i]);
673static int client_conf_apply(mbedtls_ssl_config *conf)
675 if (mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
676 MBEDTLS_SSL_PRESET_DEFAULT) != 0)
678 mbedtls_ssl_conf_rng(conf, tls_rng,
nullptr);
679 tls_apply_max_frag_len(conf);
680 tls_apply_curve_pref(conf);
683 mbedtls_ssl_conf_ca_chain(conf, &s_cli.ca,
nullptr);
684 mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_REQUIRED);
687 mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE);
688#if MBEDTLS_VERSION_MAJOR >= 3
689 mbedtls_ssl_conf_min_tls_version(conf, MBEDTLS_SSL_VERSION_TLS1_2);
691 mbedtls_ssl_conf_min_version(conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
693#if DETWS_ENABLE_TLS_RESUMPTION
696 mbedtls_ssl_conf_session_tickets(conf, MBEDTLS_SSL_SESSION_TICKETS_ENABLED);
703static bool client_pin_ok(mbedtls_ssl_context *ssl)
707 const mbedtls_x509_crt *peer = mbedtls_ssl_get_peer_cert(ssl);
711#if MBEDTLS_VERSION_MAJOR >= 3
712 int hret = mbedtls_sha256(peer->raw.p, peer->raw.len, hash, 0);
714 int hret = mbedtls_sha256_ret(peer->raw.p, peer->raw.len, hash, 0);
716 return (hret == 0) && ct_eq32(hash, s_cli.pin);
719#if DETWS_ENABLE_HTTP_CLIENT_TLS
728int det_tls_client_run(
const char *host,
const uint8_t *req,
size_t reqlen, uint8_t *out,
size_t out_cap,
729 size_t *out_len, det_tls_bio_send_fn send_fn, det_tls_bio_recv_fn recv_fn, uint32_t deadline_ms)
733 if (!req || !out || out_cap == 0 || !send_fn || !recv_fn)
736 client_arena_ensure();
738 mbedtls_ssl_context ssl;
739 mbedtls_ssl_config conf;
740 mbedtls_ssl_init(&ssl);
741 mbedtls_ssl_config_init(&conf);
747 if (client_conf_apply(&conf) != 0)
749 if (mbedtls_ssl_setup(&ssl, &conf) != 0)
752 mbedtls_ssl_set_hostname(&ssl, host);
753 mbedtls_ssl_set_bio(&ssl,
nullptr, send_fn, recv_fn,
nullptr);
756 while ((ret = mbedtls_ssl_handshake(&ssl)) != 0)
758 if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
760 if ((int32_t)(deadline_ms - millis()) <= 0)
769#ifdef DETWS_HTTP_CLIENT_DEBUG
770 printf(
"[tls] handshake ret=-0x%04x arena_peak=%u\n", (
unsigned)(-ret), (
unsigned)det_tls_arena_peak());
776 if (!client_pin_ok(&ssl))
778#ifdef DETWS_HTTP_CLIENT_DEBUG
779 printf(
"[tls] cert pin mismatch\n");
787 while (sent < reqlen)
789 ret = mbedtls_ssl_write(&ssl, req + sent, reqlen - sent);
795 if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
797 if ((int32_t)(deadline_ms - millis()) <= 0)
806 while (total < out_cap)
808 ret = mbedtls_ssl_read(&ssl, out + total, out_cap - total);
811 total += (size_t)ret;
814 if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
816 if ((int32_t)(deadline_ms - millis()) <= 0)
825 rc = (total > 0) ? 0 : -1;
828 mbedtls_ssl_close_notify(&ssl);
829 mbedtls_ssl_free(&ssl);
830 mbedtls_ssl_config_free(&conf);
843 mbedtls_ssl_context ssl;
844 mbedtls_ssl_config conf;
846#if DETWS_ENABLE_TLS_RESUMPTION
847 mbedtls_ssl_session saved;
848 bool saved_valid =
false;
851static TlsCsessCtx s_csess;
853#if DETWS_ENABLE_TLS_RESUMPTION
858void det_tls_csess_forget_session()
860 if (s_csess.saved_valid)
861 mbedtls_ssl_session_free(&s_csess.saved);
862 s_csess.saved_valid =
false;
865void det_tls_csess_forget_session()
870bool det_tls_csess_begin(
const char *host, det_tls_bio_send_fn send_fn, det_tls_bio_recv_fn recv_fn)
872 if (!send_fn || !recv_fn)
876 client_arena_ensure();
877 mbedtls_ssl_init(&s_csess.ssl);
878 mbedtls_ssl_config_init(&s_csess.conf);
879 if (client_conf_apply(&s_csess.conf) != 0 || mbedtls_ssl_setup(&s_csess.ssl, &s_csess.conf) != 0)
881 mbedtls_ssl_free(&s_csess.ssl);
882 mbedtls_ssl_config_free(&s_csess.conf);
886 mbedtls_ssl_set_hostname(&s_csess.ssl, host);
887#if DETWS_ENABLE_TLS_RESUMPTION
890 if (s_csess.saved_valid)
891 mbedtls_ssl_set_session(&s_csess.ssl, &s_csess.saved);
893 mbedtls_ssl_set_bio(&s_csess.ssl,
nullptr, send_fn, recv_fn,
nullptr);
894 s_csess.active =
true;
898int det_tls_csess_handshake()
902 int ret = mbedtls_ssl_handshake(&s_csess.ssl);
905 if (!client_pin_ok(&s_csess.ssl))
907#if DETWS_ENABLE_TLS_RESUMPTION
909 det_tls_csess_forget_session();
910 mbedtls_ssl_session_init(&s_csess.saved);
911 s_csess.saved_valid = (mbedtls_ssl_get_session(&s_csess.ssl, &s_csess.saved) == 0);
915 if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
920int det_tls_csess_read(uint8_t *buf,
size_t len)
924 int ret = mbedtls_ssl_read(&s_csess.ssl, buf, len);
927 if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
932int det_tls_csess_write(
const uint8_t *data,
size_t len)
940 int ret = mbedtls_ssl_write(&s_csess.ssl, data + sent, len - sent);
947 if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE)
959void det_tls_csess_end()
963 mbedtls_ssl_close_notify(&s_csess.ssl);
964 mbedtls_ssl_free(&s_csess.ssl);
965 mbedtls_ssl_config_free(&s_csess.conf);
966 s_csess.active =
false;
#define MAX_TLS_CONNS
Maximum simultaneous TLS connections (each holds mbedTLS record buffers).
#define DETWS_TLS_ARENA_SIZE
Bytes of the static BSS arena mbedTLS allocates from (DETWS_ENABLE_TLS).
#define DETWS_TLS_TICKET_LIFETIME_S
Session-ticket lifetime / key-rotation period in seconds (see DETWS_ENABLE_TLS_RESUMPTION).
struct tcp_pcb * pcb
lwIP PCB; null when slot is free.
TcpConn conn_pool[CONN_POOL_SLOTS]
Static pool of connection contexts. Defined in tcp.cpp. Sized CONN_POOL_SLOTS: MAX_CONNS TCP slots pl...
bool det_conn_raw_send(struct tcp_pcb *pcb, const void *data, u16_t len)
Write raw bytes straight to pcb (no TLS), context-safe.
Layer 4 (Transport) - TCP connection pool, ring buffers, and lwIP integration.
Deterministic TLS engine: mbedTLS over a static memory pool (DETWS_ENABLE_TLS).