From 57927c7c22241cc27c2ced062826398229c861d0 Mon Sep 17 00:00:00 2001 From: user Date: Sat, 14 Feb 2026 20:10:14 +0100 Subject: [PATCH] fix: Address P2 security audit findings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VULN-012: Split STATUS into minimal (unauthed: hostname, uptime, rssi, version, motion, presence) and full (authed: all internals, build info, target IP, heap, NVS stats) - VULN-011: Remove mDNS service advertisement and hardcoded "ESP32 CSI Sensor" instance name; use hostname only - VULN-021: Increase HMAC tag from 64 bits (16 hex) to 128 bits (32 hex) — BREAKING: client scripts must update HMAC computation - VULN-023: Enable PMF (802.11w) in sdkconfig.defaults to prevent deauth attacks at protocol level --- get-started/csi_recv_router/main/app_main.c | 70 +++++++++++-------- .../csi_recv_router/sdkconfig.defaults | 6 ++ 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/get-started/csi_recv_router/main/app_main.c b/get-started/csi_recv_router/main/app_main.c index 2fb7541..2aed6c3 100644 --- a/get-started/csi_recv_router/main/app_main.c +++ b/get-started/csi_recv_router/main/app_main.c @@ -1455,8 +1455,8 @@ static struct { static int s_auth_nonce_idx = 0; /** - * Verify HMAC-signed command. Format: "HMAC:<16hex>::" - * HMAC = truncated SHA-256(secret, ":") + * Verify HMAC-signed command. Format: "HMAC:<32hex>::" + * HMAC = first 16 bytes of SHA-256(secret, ":") as 32 hex chars * Timestamp must be within 5s of device uptime (replay protection). * Recently used timestamp+HMAC pairs are cached to reject exact replays. * Returns pointer to actual command on success, or NULL on failure @@ -1475,18 +1475,18 @@ static const char *auth_verify(const char *input, char *reply, size_t reply_size return NULL; } - /* Parse: HMAC:<16 hex chars>:: */ - if (strlen(input) < 5 + 16 + 1) { + /* Parse: HMAC:<32 hex chars>:: */ + if (strlen(input) < 5 + 32 + 1) { snprintf(reply, reply_size, "ERR AUTH malformed"); return NULL; } - if (input[5 + 16] != ':') { + if (input[5 + 32] != ':') { snprintf(reply, reply_size, "ERR AUTH malformed"); return NULL; } /* payload = ":" */ - const char *payload = input + 5 + 16 + 1; + const char *payload = input + 5 + 32 + 1; const char *cmd_sep = strchr(payload, ':'); if (!cmd_sep) { snprintf(reply, reply_size, "ERR AUTH malformed (need timestamp:cmd)"); @@ -1515,15 +1515,15 @@ static const char *auth_verify(const char *input, char *reply, size_t reply_size mbedtls_md_hmac_finish(&ctx, hmac); mbedtls_md_free(&ctx); - /* Format first 8 bytes as 16 hex chars */ - char expected[17]; - for (int i = 0; i < 8; i++) { + /* Format first 16 bytes as 32 hex chars (128-bit tag) */ + char expected[33]; + for (int i = 0; i < 16; i++) { snprintf(expected + i * 2, 3, "%02x", hmac[i]); } /* Constant-time comparison (prevents timing side-channel) */ volatile uint8_t diff = 0; - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 32; i++) { diff |= (uint8_t)input[5 + i] ^ (uint8_t)expected[i]; } if (diff != 0) { @@ -1703,7 +1703,7 @@ static void powertest_task(void *arg) vTaskDelete(NULL); } -static int cmd_handle(const char *cmd, char *reply, size_t reply_size) +static int cmd_handle(const char *cmd, char *reply, size_t reply_size, bool authed) { /* REBOOT */ if (strncmp(cmd, "REBOOT", 6) == 0) { @@ -1744,13 +1744,21 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size) return strlen(reply); } - /* STATUS */ + /* STATUS — minimal without auth, full with auth */ if (strncmp(cmd, "STATUS", 6) == 0) { int64_t up = esp_timer_get_time() / 1000000LL; int days = (int)(up / 86400); int hours = (int)((up % 86400) / 3600); int mins = (int)((up % 3600) / 60); - uint32_t heap = esp_get_free_heap_size(); + + char uptime_str[32]; + if (days > 0) { + snprintf(uptime_str, sizeof(uptime_str), "%dd%dh%dm", days, hours, mins); + } else if (hours > 0) { + snprintf(uptime_str, sizeof(uptime_str), "%dh%dm", hours, mins); + } else { + snprintf(uptime_str, sizeof(uptime_str), "%dm", mins); + } wifi_ap_record_t ap; int rssi = 0; @@ -1762,6 +1770,20 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size) const esp_app_desc_t *app_desc = esp_app_get_description(); + if (!authed) { + /* Minimal: no build info, no target, no internals */ + snprintf(reply, reply_size, + "OK STATUS hostname=%s uptime=%s uptime_s=%lld rssi=%d channel=%d" + " version=%s motion=%d presence=%d", + s_hostname, uptime_str, (long long)up, rssi, channel, + app_desc->version, s_motion_detected ? 1 : 0, + s_presence_detected ? 1 : 0); + return strlen(reply); + } + + /* Full status (authenticated) */ + uint32_t heap = esp_get_free_heap_size(); + float chip_temp = 0.0f; #if SOC_TEMP_SENSOR_SUPPORTED if (s_temp_handle) { @@ -1771,15 +1793,6 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size) int actual_rate = (up > 0) ? (int)((uint64_t)s_csi_count / (uint64_t)up) : 0; - char uptime_str[32]; - if (days > 0) { - snprintf(uptime_str, sizeof(uptime_str), "%dd%dh%dm", days, hours, mins); - } else if (hours > 0) { - snprintf(uptime_str, sizeof(uptime_str), "%dh%dm", hours, mins); - } else { - snprintf(uptime_str, sizeof(uptime_str), "%dm", mins); - } - const char *csi_mode_str = (s_csi_mode == CSI_MODE_COMPACT) ? "compact" : (s_csi_mode == CSI_MODE_HYBRID) ? "hybrid" : "raw"; @@ -2596,8 +2609,8 @@ static void cmd_task(void *arg) rx_buf[len] = '\0'; /* Log command (redact HMAC token) */ - if (strncmp(rx_buf, "HMAC:", 5) == 0 && strlen(rx_buf) > 22) { - ESP_LOGI(TAG, "CMD rx: HMAC:****:%s", rx_buf + 22); + if (strncmp(rx_buf, "HMAC:", 5) == 0 && strlen(rx_buf) > 38) { + ESP_LOGI(TAG, "CMD rx: HMAC:****:%s", rx_buf + 38); } else { ESP_LOGI(TAG, "CMD rx: \"%s\"", rx_buf); } @@ -2622,7 +2635,7 @@ static void cmd_task(void *arg) } else if (!authed && cmd_requires_auth(cmd)) { reply_len = snprintf(reply_buf, sizeof(reply_buf), "ERR AUTH required"); } else { - reply_len = cmd_handle(cmd, reply_buf, sizeof(reply_buf)); + reply_len = cmd_handle(cmd, reply_buf, sizeof(reply_buf), authed); } sendto(sock, reply_buf, reply_len, 0, (struct sockaddr *)&src_addr, src_len); @@ -2726,12 +2739,11 @@ void app_main() } #endif - /* mDNS: announce as .local with _esp-csi._udp service */ + /* mDNS: announce as .local — generic service type to reduce fingerprinting */ ESP_ERROR_CHECK(mdns_init()); mdns_hostname_set(s_hostname); - mdns_instance_name_set("ESP32 CSI Sensor"); - mdns_service_add(NULL, "_esp-csi", "_udp", s_target_port, NULL, 0); - ESP_LOGI(TAG, "mDNS hostname: %s.local (_esp-csi._udp:%d)", s_hostname, s_target_port); + mdns_instance_name_set(s_hostname); + ESP_LOGI(TAG, "mDNS hostname: %s.local", s_hostname); /* Watchdog: 30s timeout, auto-reboot on hang */ esp_task_wdt_config_t wdt_cfg = { diff --git a/get-started/csi_recv_router/sdkconfig.defaults b/get-started/csi_recv_router/sdkconfig.defaults index 1ff04a1..587adf6 100644 --- a/get-started/csi_recv_router/sdkconfig.defaults +++ b/get-started/csi_recv_router/sdkconfig.defaults @@ -87,3 +87,9 @@ CONFIG_FREERTOS_USE_TICKLESS_IDLE=y # WiFi Authentication (reject open/WEP APs) # CONFIG_EXAMPLE_WIFI_AUTH_WPA2_WPA3_PSK=y + +# +# Protected Management Frames (802.11w) — prevent deauth attacks +# +CONFIG_ESP_WIFI_PMF_ENABLED=y +CONFIG_ESP_WIFI_PMF_REQUIRED=y