From 18cbdbf2b1998c374b7ef0c00eec4bf708ff6056 Mon Sep 17 00:00:00 2001 From: Aditi Date: Fri, 18 Oct 2024 04:30:56 +0530 Subject: [PATCH] feat(esp_wifi): Add improvements for privacy extension 1) Add support for MAC randomization in Active scan and connect 2) Add support for randomizaton of sequence numbers 3) Add support for randomization of dialog token for GAS frames --- components/esp_hw_support/include/esp_mac.h | 2 ++ components/esp_wifi/Kconfig | 15 +++++++++ components/esp_wifi/include/esp_wifi.h | 4 +++ components/esp_wifi/remote/Kconfig.wifi.in | 15 +++++++++ .../esp_wifi/remote/Kconfig.wifi_is_remote.in | 11 +++++++ .../remote/include/injected/esp_wifi.h | 4 +++ components/esp_wifi/src/wifi_default.c | 7 ++++ .../esp_supplicant/src/esp_dpp.c | 12 +++++++ .../esp_supplicant/src/esp_dpp_i.h | 1 - components/wpa_supplicant/src/common/dpp.c | 8 ++++- components/wpa_supplicant/src/common/dpp.h | 1 + .../wpa_supplicant/src/rsn_supp/pmksa_cache.c | 16 ++++++---- .../wpa_supplicant/src/rsn_supp/pmksa_cache.h | 8 +++-- components/wpa_supplicant/src/rsn_supp/wpa.c | 9 +++--- docs/en/api-guides/wifi-security.rst | 32 +++++++++++++++++++ 15 files changed, 130 insertions(+), 15 deletions(-) diff --git a/components/esp_hw_support/include/esp_mac.h b/components/esp_hw_support/include/esp_mac.h index 365e11337dd..01370a9b102 100644 --- a/components/esp_hw_support/include/esp_mac.h +++ b/components/esp_hw_support/include/esp_mac.h @@ -117,6 +117,8 @@ esp_err_t esp_efuse_mac_get_default(uint8_t *mac); * Then calculates the MAC address of the specific interface requested, * refer to ESP-IDF Programming Guide for the algorithm. * + * @note This function reads MAC address directly from efuse(and might be different from MAC address read using esp_wifi_get_mac() API). + * * The MAC address set by the esp_iface_mac_addr_set() function will not depend on the base MAC address. * * @param mac base MAC address, length: 6 bytes/8 bytes. diff --git a/components/esp_wifi/Kconfig b/components/esp_wifi/Kconfig index f593d696164..5815c7411b8 100644 --- a/components/esp_wifi/Kconfig +++ b/components/esp_wifi/Kconfig @@ -925,6 +925,21 @@ menu "Wi-Fi" Select this option to enable/disable support for station connections to hidden APs using passive scan when the country policy is set to auto. + config ESP_WIFI_STA_RANDOM_MAC_ENABLED + bool "Station Mac-Randomization enabled" + default y + help + Select this option to enable Mac Randomization for Station + + config ESP_WIFI_STA_RANDOM_MAC_AUTO_RESET_INTERVAL + int "Station Mac-Randomization Auto-Reset Interval time" + depends on ESP_WIFI_STA_RANDOM_MAC_ENABLED + range 1 24 + default 12 + help + Interval in hours to rotate the STA random MAC while not connected to an AP. + Values below 1 or above 24 are not allowed. + endif # wifi enabled endmenu # Wi-Fi diff --git a/components/esp_wifi/include/esp_wifi.h b/components/esp_wifi/include/esp_wifi.h index f1727019ae2..a41baa55d43 100644 --- a/components/esp_wifi/include/esp_wifi.h +++ b/components/esp_wifi/include/esp_wifi.h @@ -120,6 +120,8 @@ typedef struct { int espnow_max_encrypt_num; /**< Maximum encrypt number of peers supported by espnow */ int tx_hetb_queue_num; /**< WiFi TX HE TB QUEUE number for STA HE TB PPDU transmission */ bool dump_hesigb_enable; /**< enable dump sigb field */ + bool sta_random_mac; /**< STA MAC randomization. Supported on station interface only; softAP may be added in future */ + uint8_t sta_rmac_auto_reset_int;/**< STA random MAC auto-reset interval in hours (1-24) while not connected */ int magic; /**< WiFi init magic number, it should be the last field */ } wifi_init_config_t; @@ -347,6 +349,8 @@ extern wifi_osi_funcs_t g_wifi_osi_funcs; .espnow_max_encrypt_num = CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM, \ .tx_hetb_queue_num = WIFI_TX_HETB_QUEUE_NUM, \ .dump_hesigb_enable = WIFI_DUMP_HESIGB_ENABLED, \ + .sta_random_mac = CONFIG_ESP_WIFI_STA_RANDOM_MAC_ENABLED, \ + .sta_rmac_auto_reset_int = CONFIG_ESP_WIFI_STA_RANDOM_MAC_AUTO_RESET_INTERVAL, \ .magic = WIFI_INIT_CONFIG_MAGIC\ } diff --git a/components/esp_wifi/remote/Kconfig.wifi.in b/components/esp_wifi/remote/Kconfig.wifi.in index f0482f1c2cb..9b673b111e2 100644 --- a/components/esp_wifi/remote/Kconfig.wifi.in +++ b/components/esp_wifi/remote/Kconfig.wifi.in @@ -904,6 +904,21 @@ config WIFI_RMT_PASSIVE_HIDDEN_AP_SUPPORT Select this option to enable/disable support for station connections to hidden APs using passive scan when the country policy is set to auto. +config WIFI_RMT_STA_RANDOM_MAC_ENABLED + bool "Station Mac-Randomization enabled" + default y + help + Select this option to enable Mac Randomization for Station + +config WIFI_RMT_STA_RANDOM_MAC_AUTO_RESET_INTERVAL + int "Station Mac-Randomization Auto-Reset Interval time" + depends on WIFI_RMT_STA_RANDOM_MAC_ENABLED + range 1 24 + default 12 + help + Interval in hours to rotate the STA random MAC while not connected to an AP. + Values below 1 or above 24 are not allowed. + if !ESP_WIFI_ENABLED rsource "Kconfig.wifi_is_remote.in" endif # ESP_WIFI_ENABLED diff --git a/components/esp_wifi/remote/Kconfig.wifi_is_remote.in b/components/esp_wifi/remote/Kconfig.wifi_is_remote.in index 64b1e119b62..2cf67211300 100644 --- a/components/esp_wifi/remote/Kconfig.wifi_is_remote.in +++ b/components/esp_wifi/remote/Kconfig.wifi_is_remote.in @@ -512,3 +512,14 @@ if WIFI_RMT_PASSIVE_HIDDEN_AP_SUPPORT bool default WIFI_RMT_PASSIVE_HIDDEN_AP_SUPPORT endif + +if WIFI_RMT_STA_RANDOM_MAC_ENABLED + config ESP_WIFI_STA_RANDOM_MAC_ENABLED # ignore: multiple-definition + bool + default WIFI_RMT_STA_RANDOM_MAC_ENABLED +endif + +config ESP_WIFI_STA_RANDOM_MAC_AUTO_RESET_INTERVAL # ignore: multiple-definition + int + depends on WIFI_RMT_STA_RANDOM_MAC_ENABLED + default WIFI_RMT_STA_RANDOM_MAC_AUTO_RESET_INTERVAL diff --git a/components/esp_wifi/remote/include/injected/esp_wifi.h b/components/esp_wifi/remote/include/injected/esp_wifi.h index 0010fa69efe..31d9d4212a3 100644 --- a/components/esp_wifi/remote/include/injected/esp_wifi.h +++ b/components/esp_wifi/remote/include/injected/esp_wifi.h @@ -120,6 +120,8 @@ typedef struct { int espnow_max_encrypt_num; /**< Maximum encrypt number of peers supported by espnow */ int tx_hetb_queue_num; /**< WiFi TX HE TB QUEUE number for STA HE TB PPDU transmission */ bool dump_hesigb_enable; /**< enable dump sigb field */ + bool sta_random_mac; /**< STA MAC randomization. Supported on station interface only; softAP may be added in future */ + uint8_t sta_rmac_auto_reset_int;/**< STA random MAC auto-reset interval in hours (1-24) while not connected */ int magic; /**< WiFi init magic number, it should be the last field */ } wifi_init_config_t; @@ -347,6 +349,8 @@ extern wifi_osi_funcs_t g_wifi_osi_funcs; .espnow_max_encrypt_num = CONFIG_WIFI_RMT_ESPNOW_MAX_ENCRYPT_NUM, \ .tx_hetb_queue_num = WIFI_TX_HETB_QUEUE_NUM, \ .dump_hesigb_enable = WIFI_DUMP_HESIGB_ENABLED, \ + .sta_random_mac = CONFIG_WIFI_RMT_STA_RANDOM_MAC_ENABLED, \ + .sta_rmac_auto_reset_int = CONFIG_WIFI_RMT_STA_RANDOM_MAC_AUTO_RESET_INTERVAL, \ .magic = WIFI_INIT_CONFIG_MAGIC\ } diff --git a/components/esp_wifi/src/wifi_default.c b/components/esp_wifi/src/wifi_default.c index 330f2c5c34c..a7f23ed4076 100644 --- a/components/esp_wifi/src/wifi_default.c +++ b/components/esp_wifi/src/wifi_default.c @@ -119,6 +119,13 @@ static void wifi_default_action_sta_connected(void *arg, esp_event_base_t base, } } +#if CONFIG_ESP_WIFI_STA_RANDOM_MAC_ENABLED + /* Sync netif MAC when STA random MAC was set internally by the Wi-Fi driver */ + uint8_t mac[6]; + esp_wifi_get_mac(WIFI_IF_STA, mac); + esp_netif_set_mac(esp_netif, mac); +#endif + esp_netif_action_connected(s_wifi_netifs[WIFI_IF_STA], base, event_id, data); } } diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_dpp.c b/components/wpa_supplicant/esp_supplicant/src/esp_dpp.c index c93cdebc5e0..926ed8ea5b5 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_dpp.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_dpp.c @@ -620,6 +620,18 @@ static void esp_dpp_rx_action(void *data, void *user_ctx) public_action->v.pa_gas_resp.length == 8 && public_action->v.pa_gas_resp.status_code == 0) { + if (!s_dpp_ctx.dpp_auth || + public_action->v.pa_gas_resp.diag_token != + s_dpp_ctx.dpp_auth->gas_dialog_token) { + wpa_printf(MSG_DEBUG, + "DPP: GAS dialog token mismatch (rx=%u exp=%u) - drop", + public_action->v.pa_gas_resp.diag_token, + s_dpp_ctx.dpp_auth ? + s_dpp_ctx.dpp_auth->gas_dialog_token : 0); + os_free(rx_param); + return; + } + rx_param->vendor_data_len = rx_param->frm_len - (size_t)(public_action->v.pa_gas_resp.data + public_action->v.pa_gas_resp.length - diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_dpp_i.h b/components/wpa_supplicant/esp_supplicant/src/esp_dpp_i.h index 671dac7abb3..a0810f3ecd6 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_dpp_i.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_dpp_i.h @@ -33,7 +33,6 @@ struct dpp_bootstrap_params_t { struct esp_dpp_context_t { struct dpp_bootstrap_params_t bootstrap_params; struct dpp_authentication *dpp_auth; - int gas_dialog_token; struct dpp_global *dpp_global; wifi_config_t wifi_cfg; int id; diff --git a/components/wpa_supplicant/src/common/dpp.c b/components/wpa_supplicant/src/common/dpp.c index f0ecfd8175c..969c755fe9e 100644 --- a/components/wpa_supplicant/src/common/dpp.c +++ b/components/wpa_supplicant/src/common/dpp.c @@ -1368,6 +1368,7 @@ struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, const char *json) { struct wpabuf *buf, *conf_req; + u8 dialog_token; conf_req = dpp_build_conf_req_attr(auth, json); if (!conf_req) { @@ -1376,7 +1377,12 @@ struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, return NULL; } - buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); + do { + dialog_token = os_random() & 0xff; + } while (!dialog_token); + auth->gas_dialog_token = dialog_token; + + buf = gas_build_initial_req(dialog_token, 10 + 2 + wpabuf_len(conf_req)); if (!buf) { wpabuf_free(conf_req); return NULL; diff --git a/components/wpa_supplicant/src/common/dpp.h b/components/wpa_supplicant/src/common/dpp.h index 1fbc8326c58..7f0ff009d7d 100644 --- a/components/wpa_supplicant/src/common/dpp.h +++ b/components/wpa_supplicant/src/common/dpp.h @@ -311,6 +311,7 @@ struct dpp_authentication { int send_conn_status; int conn_status_requested; int akm_use_selector; + u8 gas_dialog_token; /* Dialog Token used in GAS Initial Request */ #ifdef CONFIG_TESTING_OPTIONS char *config_obj_override; diff --git a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c index 0914de2e7bd..323a02d4735 100644 --- a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c +++ b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.c @@ -165,6 +165,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, entry->reauth_time = now.sec + dot11RSNAConfigPMKLifetime / 100 * dot11RSNAConfigPMKReauthThreshold; entry->akmp = akmp; os_memcpy(entry->aa, aa, ETH_ALEN); + os_memcpy(entry->spa, spa, ETH_ALEN); entry->network_ctx = network_ctx; return pmksa_cache_add_entry(pmksa, entry); @@ -335,12 +336,13 @@ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) * pmksa_cache_get - Fetch a PMKSA cache entry * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @aa: Authenticator address or %NULL to match any + * @spa: Supplicant address or %NULL to skip SPA matching (not recommended) * @pmkid: PMKID or %NULL to match any * @network_ctx: Network context or %NULL to match any * Returns: Pointer to PMKSA cache entry or %NULL if no match was found */ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, - const u8 *aa, const u8 *pmkid, + const u8 *aa, const u8 *spa, const u8 *pmkid, const void *network_ctx) { if(!pmksa) @@ -348,6 +350,8 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; while (entry) { if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && + (spa == NULL || + os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && (pmkid == NULL || os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && (network_ctx == NULL || network_ctx == entry->network_ctx)) @@ -468,10 +472,10 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, sm->cur_pmksa = NULL; if (pmkid) - sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, + sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, sm->own_addr, pmkid, network_ctx); if (sm->cur_pmksa == NULL && bssid) - sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, + sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, sm->own_addr, NULL, network_ctx); if (sm->cur_pmksa == NULL && try_opportunistic && bssid) sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, @@ -504,7 +508,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) struct rsn_pmksa_cache_entry *entry; struct os_reltime now; ret = os_snprintf(pos, buf + len - pos, - "Index / AA / PMKID / expiration (in seconds) / " + "Index / AA / SPA / PMKID / expiration (in seconds) / " "opportunistic\n"); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; @@ -514,8 +518,8 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) os_get_reltime(&now); while (entry) { i++; - ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", - i, MAC2STR(entry->aa)); + ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " " MACSTR " ", + i, MAC2STR(entry->aa), MAC2STR(entry->spa)); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; diff --git a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h index 8923e27bd5d..b5cc5aabe6f 100644 --- a/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h +++ b/components/wpa_supplicant/src/rsn_supp/pmksa_cache.h @@ -20,6 +20,8 @@ struct rsn_pmksa_cache_entry { os_time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ u8 aa[ETH_ALEN]; + /** Supplicant MAC (SPA) this PMKSA was created for; used when matching cache */ + u8 spa[ETH_ALEN]; os_time_t reauth_time; @@ -52,7 +54,7 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx, struct wpa_sm *sm); void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, - const u8 *aa, const u8 *pmkid, + const u8 *aa, const u8 *spa, const u8 *pmkid, const void *network_ctx); int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * @@ -90,8 +92,8 @@ static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) } static inline struct rsn_pmksa_cache_entry * -pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, - const void *network_ctx) +pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, + const u8 *pmkid, const void *network_ctx) { return NULL; } diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.c b/components/wpa_supplicant/src/rsn_supp/wpa.c index 735fa6abcac..e503962c74a 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa.c @@ -400,8 +400,8 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * not have enough time to get the association information * event before receiving this 1/4 message, so try to find a * matching PMKSA cache entry here. */ - sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, - NULL); + sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, sm->own_addr, + pmkid, NULL); if (sm->cur_pmksa) { wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from PMKSA cache"); @@ -470,7 +470,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, sm->network_ctx, sm->key_mgmt); } if (!sm->cur_pmksa && pmkid && - pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) + pmksa_cache_get(sm->pmksa, src_addr, sm->own_addr, pmkid, NULL)) { wpa_printf( MSG_DEBUG, "RSN: the new PMK matches with the " @@ -2687,7 +2687,8 @@ int wpa_set_bss(uint8_t *macddr, uint8_t *bssid, uint8_t pairwise_cipher, uint8_ struct rsn_pmksa_cache_entry *pmksa = NULL; if (use_pmk_cache) { - pmksa = pmksa_cache_get(sm->pmksa, (const u8 *)bssid, NULL, NULL); + pmksa = pmksa_cache_get(sm->pmksa, (const u8 *)bssid, sm->own_addr, + NULL, NULL); if (pmksa && (pmksa->akmp != sm->key_mgmt)) { use_pmk_cache = false; } diff --git a/docs/en/api-guides/wifi-security.rst b/docs/en/api-guides/wifi-security.rst index 0040f106258..00cd7ec4321 100644 --- a/docs/en/api-guides/wifi-security.rst +++ b/docs/en/api-guides/wifi-security.rst @@ -171,3 +171,35 @@ A configuration option :ref:`CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA` and configurat For softap mode : A configuration option :ref:`CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_SOFTAP` from menuconfig should be enabled and configuration parameter `authmode` from :cpp:type:`wifi_ap_config_t` should be set to ``WIFI_AUTH_OWE``. SoftAP does not support OWE Transition Mode; configure ``WIFI_AUTH_OWE`` only. + +MAC Address Randomization +-------------------------- + +MAC addresses, used by devices to connect to Wi-Fi networks, can be captured and tracked because they are transmitted without encryption and due to their unique and static nature. {IDF_TARGET_NAME} supports the MAC randomization feature which enhances privacy by using a randomized MAC address, preventing devices from being consistently tracked when scanning or connecting to networks. + +To use this feature, enable configuration option :ref:`CONFIG_ESP_WIFI_STA_RANDOM_MAC_ENABLED` from menuconfig. + +{IDF_TARGET_NAME} also rotates the STA random MAC periodically while not connected, using menuconfig option :ref:`CONFIG_ESP_WIFI_STA_RANDOM_MAC_AUTO_RESET_INTERVAL` (valid range: 1 to 24 hours, default 12). + +.. note:: + + The `CONFIG_ESP_WIFI_STA_RANDOM_MAC_AUTO_RESET_INTERVAL` will only generate and set new random mac address when station is not connected to any AP. If the station is connected to any AP, the connection will not be interrupted and same random mac will be used. + + For every new connection request, new random mac will be generated and auto reset time interval will be reset if `CONFIG_ESP_WIFI_STA_RANDOM_MAC_ENABLED` is enabled. + + PMK caching is not supported when MAC randomization is enabled, as the device's identity changes with each connection attempt. + + +{IDF_TARGET_NAME} supports MAC randomization while scanning when + + - enable configuration option :ref:`CONFIG_ESP_WIFI_STA_RANDOM_MAC_ENABLED` from menuconfig + - scan_type is :cpp:enumerator:`WIFI_SCAN_TYPE_ACTIVE` + - station is not connected to any Access Point + +{IDF_TARGET_NAME} supports MAC randomization while connecting when + + - enable configuration option :ref:`CONFIG_ESP_WIFI_STA_RANDOM_MAC_ENABLED` from menuconfig + - new wifi configuration is set using esp_wifi_set_config() + + +To get the mac address, please use API :cpp:func:`esp_err_t esp_wifi_get_mac(wifi_interface_t ifx, uint8_t mac[6]);`