/* SPDX-License-Identifier: GPL-3.0-only */ #include "alloc.h" typedef void *httpd_handle_t; typedef int (*httpd_open_func_t)(httpd_handle_t, int); typedef void esp_https_server_user_cb(void *); typedef struct { unsigned char secret[37]; } esp_tls_t; typedef void *esp_tls_error_handle_t; typedef struct { int last_error, esp_tls_error_code, esp_tls_flags; } esp_https_server_last_error_t; typedef struct { int user_cb_state; esp_tls_t *tls; } esp_https_server_user_cb_arg_t; typedef struct { const unsigned char *cacert_buf, *servercert_buf, *serverkey_buf; unsigned cacert_bytes, servercert_bytes, serverkey_bytes; void *userdata; const char **alpn_protos; unsigned tls_handshake_timeout_ms; bool use_secure_element; } esp_tls_cfg_server_t; typedef struct { void *global_transport_ctx, *global_user_ctx; void (*global_transport_ctx_free_fn)(void *), (*global_user_ctx_free_fn)(void *); httpd_open_func_t open_fn; int server_port, ctrl_port; } httpd_config_t; struct httpd_ssl_config { httpd_config_t httpd; bool session_tickets, use_secure_element, use_ecdsa_peripheral; const unsigned char *cacert_pem, *servercert, *prvtkey_pem; unsigned cacert_len, servercert_len, prvtkey_len, tls_handshake_timeout_ms; void *ssl_userdata; const char **alpn_protos; esp_https_server_user_cb *user_cb; int transport_mode, port_secure, port_insecure; }; struct httpd_data { httpd_config_t config; int msg_fd; struct { int status; } hd_td; void *transport; void (*close_fn)(void *); }; struct httpd_ctrl_data { int hc_msg; }; #define HTTPD_SSL_TRANSPORT_SECURE 1 #define HTTPD_SSL_USER_CB_SESS_CLOSE 2 #define HTTPD_SSL_USER_CB_SESS_CREATE 3 #define HTTPS_SERVER_EVENT_ERROR 4 #define HTTPS_SERVER_EVENT_ON_CONNECTED 5 #define HTTPS_SERVER_EVENT_DISCONNECTED 6 #define HTTPS_SERVER_EVENT_START 7 #define HTTPS_SERVER_EVENT_STOP 8 #define HTTP_SERVER_EVENT_STOP 9 #define HTTPD_CTRL_SHUTDOWN 10 #define THREAD_STOPPED 11 static bool start_failure, stop_failure, handshake_failure; static unsigned deletes, creates, closes; static struct httpd_data *active; static void http_dispatch_event_to_event_loop(int id, const void *v, size_t n) {} static void esp_http_server_dispatch_event(int id, const void *v, size_t n) {} static int esp_tls_cfg_server_session_tickets_init(esp_tls_cfg_server_t *cfg) { return 0; } static void esp_tls_cfg_server_session_tickets_free(esp_tls_cfg_server_t *cfg) {} static esp_tls_t *esp_tls_init(void) { esp_tls_t *tls = calloc(1, sizeof(*tls)); if (tls) { memset(tls, 0xb6, sizeof(*tls)); mark_secret(tls); } return tls; } static int esp_tls_server_session_create(esp_tls_cfg_server_t *cfg, int fd, esp_tls_t *tls) { return handshake_failure ? -1 : 0; } static void esp_tls_server_session_delete(esp_tls_t *tls) { assert(tls); ++deletes; memset(tls, 0, sizeof(*tls)); free(tls); } static int esp_tls_get_error_handle(esp_tls_t *tls, esp_tls_error_handle_t *e) { return -1; } static int esp_tls_get_and_clear_last_error(esp_tls_error_handle_t e, int *a, int *b) { return 0; } static void *httpd_get_global_transport_ctx(httpd_handle_t h) { return ((struct httpd_data *)h)->config.global_transport_ctx; } static void httpd_sess_set_transport_ctx(httpd_handle_t h, int fd, void *ctx, void (*fn)(void *)) { struct httpd_data *hd = h; assert(!hd->transport); hd->transport = ctx; hd->close_fn = fn; } static int httpd_ssl_send(void) { return 0; } static int httpd_ssl_recv(void) { return 0; } static int httpd_ssl_pending(void) { return 0; } static void httpd_sess_set_send_override(httpd_handle_t h, int fd, int (*fn)(void)) {} static void httpd_sess_set_recv_override(httpd_handle_t h, int fd, int (*fn)(void)) {} static void httpd_sess_set_pending_override(httpd_handle_t h, int fd, int (*fn)(void)) {} static int httpd_start(httpd_handle_t *h, httpd_config_t *cfg) { if (start_failure) return ESP_FAIL; struct httpd_data *hd = calloc(1, sizeof(*hd)); if (!hd) return ESP_ERR_NO_MEM; hd->config = *cfg; *h = hd; active = hd; return ESP_OK; } static int cs_send_to_ctrl_sock(int fd, int port, void *msg, size_t n) { return stop_failure ? -1 : 0; } static void httpd_os_thread_sleep(int ms) { if (active->transport) { active->close_fn(active->transport); active->transport = NULL; } active->hd_td.status = THREAD_STOPPED; } static void httpd_delete(struct httpd_data *hd) { assert(!hd->transport); free(hd); active = NULL; } static void user_callback(void *arg) { esp_https_server_user_cb_arg_t *a = arg; if (a->user_cb_state == HTTPD_SSL_USER_CB_SESS_CREATE) ++creates; if (a->user_cb_state == HTTPD_SSL_USER_CB_SESS_CLOSE) ++closes; } /* SDK_FUNCTIONS */ static struct httpd_ssl_config config(void) { static unsigned char ca[13], cert[19], key[23]; memset(ca, 1, sizeof(ca)); memset(cert, 2, sizeof(cert)); memset(key, 3, sizeof(key)); return (struct httpd_ssl_config){.transport_mode=HTTPD_SSL_TRANSPORT_SECURE, .cacert_pem=ca, .cacert_len=sizeof(ca), .servercert=cert, .servercert_len=sizeof(cert), .prvtkey_pem=key, .prvtkey_len=sizeof(key), .user_cb=user_callback}; } int main(void) { secret_size = 23; for (unsigned fail = 1; fail <= 6; ++fail) { struct httpd_ssl_config cfg = config(); httpd_handle_t h = NULL; calls = 0; fail_at = fail; assert(httpd_ssl_start(&h, &cfg) != ESP_OK); assert(!h && live == 0); } fail_at = 0; struct httpd_ssl_config cfg = config(); httpd_handle_t h = NULL; start_failure = true; unsigned wipes = wiped_frees; assert(httpd_ssl_start(&h, &cfg) != ESP_OK && live == 0); assert(wiped_frees == wipes + 1); assert(!cfg.httpd.global_transport_ctx && !cfg.httpd.global_transport_ctx_free_fn); assert(!cfg.httpd.open_fn); /* no stale HTTPS wrapper on a retry */ start_failure = false; assert(httpd_ssl_start(&h, &cfg) == ESP_OK); assert(httpd_ssl_stop(h) == ESP_OK && live == 0); h = NULL; for (unsigned missing = 0; missing < 2; ++missing) { cfg = config(); if (missing) cfg.prvtkey_pem = NULL; else cfg.servercert = NULL; assert(httpd_ssl_start(&h, &cfg) != ESP_OK && live == 0); } cfg = config(); assert(httpd_ssl_start(&h, &cfg) == ESP_OK); unsigned baseline = live; for (unsigned fail = 1; fail <= 2; ++fail) { calls = 0; fail_at = fail; unsigned before = deletes; assert(httpd_ssl_open(h, 42) == ESP_ERR_NO_MEM); assert(live == baseline && !active->transport); assert(deletes == before + (fail == 2)); } fail_at = 0; handshake_failure = true; unsigned before = deletes; assert(httpd_ssl_open(h, 42) != ESP_OK && live == baseline); assert(deletes == before + 1); handshake_failure = false; assert(httpd_ssl_open(h, 42) == ESP_OK && creates == 1); void *retained = active->transport; unsigned retained_live = live; stop_failure = true; before = deletes; wipes = wiped_frees; assert(httpd_ssl_stop(h) != ESP_OK); assert(active->transport == retained && live == retained_live); assert(deletes == before && wiped_frees == wipes && closes == 0); stop_failure = false; assert(httpd_ssl_stop(h) == ESP_OK && live == 0 && closes == 1); assert(deletes == before + 1 && wiped_frees == wipes + 2); assert(httpd_ssl_stop(NULL) == ESP_ERR_INVALID_ARG); cfg = config(); cfg.transport_mode = 0; start_failure = true; assert(httpd_ssl_start(&h, &cfg) != ESP_OK && live == 0); puts("HTTPS allocation/handshake/start/stop ownership and wipe matrix PASS"); }