esp_wifi: WPA3-SAE support for softAP

This commit is contained in:
Shreyas Sheth 2023-02-28 12:25:05 +08:00 committed by Jiang Jiang Jian
parent ad19981af8
commit 2b8e40e760
44 changed files with 2773 additions and 249 deletions

View File

@ -250,6 +250,14 @@ menu "Wi-Fi"
help
Select this option to enable SAE-PK
config ESP_WIFI_SOFTAP_SAE_SUPPORT
bool "Enable WPA3 Personal(SAE) SoftAP"
default y
depends on ESP_WIFI_ENABLE_WPA3_SAE
depends on ESP_WIFI_SOFTAP_SUPPORT
help
Select this option to enable SAE support in softAP mode.
config ESP_WIFI_ENABLE_WPA3_OWE_STA
bool "Enable OWE STA"
default y

View File

@ -287,6 +287,7 @@ typedef struct {
wifi_cipher_type_t pairwise_cipher; /**< pairwise cipher of SoftAP, group cipher will be derived using this. cipher values are valid starting from WIFI_CIPHER_TYPE_TKIP, enum values before that will be considered as invalid and default cipher suites(TKIP+CCMP) will be used. Valid cipher suites in softAP mode are WIFI_CIPHER_TYPE_TKIP, WIFI_CIPHER_TYPE_CCMP and WIFI_CIPHER_TYPE_TKIP_CCMP. */
bool ftm_responder; /**< Enable FTM Responder mode */
wifi_pmf_config_t pmf_cfg; /**< Configuration for Protected Management Frame */
wifi_sae_pwe_method_t sae_pwe_h2e; /**< Configuration for SAE PWE derivation method */
} wifi_ap_config_t;
/** @brief STA configuration settings for the device */
@ -307,8 +308,8 @@ typedef struct {
uint32_t ft_enabled:1; /**< Whether FT is enabled for the connection */
uint32_t owe_enabled:1; /**< Whether OWE is enabled for the connection */
uint32_t transition_disable:1; /**< Whether to enable transition disable feature */
uint32_t reserved:26; /**< Reserved for future feature set */
wifi_sae_pwe_method_t sae_pwe_h2e; /**< Whether SAE hash to element is enabled */
uint32_t reserved:26; /**< Reserved for future feature set */
wifi_sae_pwe_method_t sae_pwe_h2e; /**< Configuration for SAE PWE derivation method */
wifi_sae_pk_mode_t sae_pk_mode; /**< SAE-PK mode */
uint8_t failure_retry_cnt; /**< Number of connection retries station will do before moving to next AP. scan_method should be set as WIFI_ALL_CHANNEL_SCAN to use this config.
Note: Enabling this may cause connection time to increase incase best AP doesn't behave properly. */

@ -1 +1 @@
Subproject commit adf9880a99c6fba80a5f0d376f17de5c91151ff3
Subproject commit 194ce926518a254714850e9bd4f12469d89867c2

View File

@ -4,7 +4,10 @@ set(srcs "port/os_xtensa.c"
"src/ap/ieee802_1x.c"
"src/ap/wpa_auth.c"
"src/ap/wpa_auth_ie.c"
"src/ap/pmksa_cache_auth.c"
"src/ap/sta_info.c"
"src/ap/ieee802_11.c"
"src/ap/comeback_token.c"
"src/common/sae.c"
"src/common/dragonfly.c"
"src/common/wpa_common.c"
@ -234,6 +237,9 @@ endif()
if(CONFIG_ESP_WIFI_ENABLE_SAE_PK)
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_SAE_PK)
endif()
if(CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT)
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_SAE)
endif()
if(CONFIG_ESP_WIFI_WPS_STRICT)
target_compile_definitions(${COMPONENT_LIB} PRIVATE CONFIG_WPS_STRICT)
endif()

View File

@ -317,10 +317,10 @@ int crypto_bignum_addmod(const struct crypto_bignum *a,
struct crypto_bignum *tmp = crypto_bignum_init();
int ret = -1;
if (mbedtls_mpi_add_mpi((mbedtls_mpi *) tmp, (const mbedtls_mpi *) b, (const mbedtls_mpi *) c) < 0)
if (mbedtls_mpi_add_mpi((mbedtls_mpi *) tmp, (const mbedtls_mpi *) a, (const mbedtls_mpi *) b) < 0)
goto fail;
if (mbedtls_mpi_mod_mpi( (mbedtls_mpi *) a, (const mbedtls_mpi *) tmp, (const mbedtls_mpi *) d) < 0)
if (mbedtls_mpi_mod_mpi( (mbedtls_mpi *) d, (const mbedtls_mpi *) tmp, (const mbedtls_mpi *) c) < 0)
goto fail;
ret = 0;

View File

@ -17,9 +17,14 @@
#include "ap/wpa_auth_i.h"
#include "esp_wifi_driver.h"
#include "esp_wifi_types.h"
#include "esp_wpa3_i.h"
struct hostapd_data *global_hapd;
#ifdef CONFIG_SAE
extern SemaphoreHandle_t g_wpa3_hostap_auth_api_lock;
#endif /* CONFIG_SAE */
struct hostapd_data *hostapd_get_hapd_data(void)
{
return global_hapd;
@ -33,6 +38,7 @@ void *hostap_init(void)
u16 spp_attrubute = 0;
u8 pairwise_cipher;
wifi_pmf_config_t pmf_cfg;
uint8_t authmode;
hapd = (struct hostapd_data *)os_zalloc(sizeof(struct hostapd_data));
@ -46,7 +52,6 @@ void *hostap_init(void)
os_free(hapd);
return NULL;
}
hapd->conf->max_num_sta = MAX_STA_COUNT;
auth_conf = (struct wpa_auth_config *)os_zalloc(sizeof(struct wpa_auth_config));
@ -56,15 +61,23 @@ void *hostap_init(void)
hapd = NULL;
return NULL;
}
if (esp_wifi_ap_get_prof_authmode_internal() == WIFI_AUTH_WPA_PSK) {
hapd->conf->sae_pwe = esp_wifi_get_config_sae_pwe_h2e_internal(WIFI_IF_AP);
auth_conf->sae_pwe = hapd->conf->sae_pwe;
authmode = esp_wifi_ap_get_prof_authmode_internal();
if (authmode == WIFI_AUTH_WPA_PSK) {
auth_conf->wpa = WPA_PROTO_WPA;
}
if (esp_wifi_ap_get_prof_authmode_internal() == WIFI_AUTH_WPA2_PSK) {
if (authmode == WIFI_AUTH_WPA2_PSK) {
auth_conf->wpa = WPA_PROTO_RSN;
}
if (esp_wifi_ap_get_prof_authmode_internal() == WIFI_AUTH_WPA_WPA2_PSK) {
if (authmode == WIFI_AUTH_WPA_WPA2_PSK) {
auth_conf->wpa = WPA_PROTO_RSN | WPA_PROTO_WPA;
}
if (authmode == WIFI_AUTH_WPA3_PSK || authmode == WIFI_AUTH_WPA2_WPA3_PSK) {
auth_conf->wpa = WPA_PROTO_RSN;
}
pairwise_cipher = esp_wifi_ap_get_prof_pairwise_cipher_internal();
@ -73,7 +86,7 @@ void *hostap_init(void)
esp_wifi_get_pmf_config_internal(&pmf_cfg, WIFI_IF_AP);
if (pmf_cfg.required) {
pairwise_cipher = WIFI_CIPHER_TYPE_CCMP;
pairwise_cipher = WIFI_CIPHER_TYPE_CCMP;
}
#endif /* CONFIG_IEEE80211W */
@ -98,15 +111,24 @@ void *hostap_init(void)
auth_conf->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
auth_conf->eapol_version = EAPOL_VERSION;
hapd->conf->sae_anti_clogging_threshold = SAE_ANTI_CLOGGING_THRESHOLD;
#ifdef CONFIG_IEEE80211W
if (pmf_cfg.required && pmf_cfg.capable) {
auth_conf->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
auth_conf->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
wpa_printf(MSG_DEBUG, "%s :pmf required", __func__);
auth_conf->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED;
auth_conf->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
wpa_printf(MSG_DEBUG, "%s :pmf required", __func__);
} else if (pmf_cfg.capable && !pmf_cfg.required) {
auth_conf->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
auth_conf->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
wpa_printf(MSG_DEBUG, "%s : pmf optional", __func__);
auth_conf->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
auth_conf->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
wpa_printf(MSG_DEBUG, "%s : pmf optional", __func__);
}
if (authmode == WIFI_AUTH_WPA2_WPA3_PSK) {
auth_conf->wpa_key_mgmt |= WPA_KEY_MGMT_SAE;
}
if (authmode == WIFI_AUTH_WPA3_PSK) {
auth_conf->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
}
#endif /* CONFIG_IEEE80211W */
@ -116,6 +138,7 @@ void *hostap_init(void)
memcpy(hapd->conf->ssid.ssid, ssid->ssid, ssid->len);
hapd->conf->ssid.ssid_len = ssid->len;
hapd->conf->wpa_key_mgmt = auth_conf->wpa_key_mgmt;
hapd->conf->ssid.wpa_passphrase = (char *)os_zalloc(64);
if (hapd->conf->ssid.wpa_passphrase == NULL) {
os_free(auth_conf);
@ -124,7 +147,24 @@ void *hostap_init(void)
hapd = NULL;
return NULL;
}
memcpy(hapd->conf->ssid.wpa_passphrase, esp_wifi_ap_get_prof_password_internal(), strlen((char *)esp_wifi_ap_get_prof_password_internal()));
#ifdef CONFIG_SAE
if (authmode == WIFI_AUTH_WPA3_PSK ||
authmode == WIFI_AUTH_WPA2_WPA3_PSK) {
if (wpa3_hostap_auth_init(hapd) != 0) {
os_free(hapd->conf->ssid.wpa_passphrase);
os_free(auth_conf);
os_free(hapd->conf);
os_free(hapd);
hapd = NULL;
return NULL;
}
}
#endif /* CONFIG_SAE */
os_memcpy(hapd->conf->ssid.wpa_passphrase, esp_wifi_ap_get_prof_password_internal(), strlen((char *)esp_wifi_ap_get_prof_password_internal()));
hapd->conf->max_num_sta = esp_wifi_ap_get_max_sta_conn();
hapd->conf->ap_max_inactivity = 5 * 60;
hostapd_setup_wpa_psk(hapd->conf);
@ -139,6 +179,41 @@ void *hostap_init(void)
return (void *)hapd;
}
void hostapd_cleanup(struct hostapd_data *hapd)
{
if (hapd == NULL) {
return;
}
if(hapd->wpa_auth) {
wpa_deinit(hapd->wpa_auth);
hapd->wpa_auth = NULL;
}
if (hapd->conf) {
hostapd_config_free_bss(hapd->conf);
hapd->conf = NULL;
}
#ifdef CONFIG_SAE
struct hostapd_sae_commit_queue *q, *tmp;
if (dl_list_empty(&hapd->sae_commit_queue)) {
dl_list_for_each_safe(q, tmp, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
dl_list_del(&q->list);
os_free(q);
}
}
#endif /* CONFIG_SAE */
os_free(hapd);
global_hapd = NULL;
}
bool hostap_deinit(void *data)
{
struct hostapd_data *hapd = (struct hostapd_data *)data;
@ -146,30 +221,95 @@ bool hostap_deinit(void *data)
if (hapd == NULL) {
return true;
}
if (hapd->wpa_auth != NULL) {
if (hapd->wpa_auth->wpa_ie != NULL) {
os_free(hapd->wpa_auth->wpa_ie);
}
if (hapd->wpa_auth->group != NULL) {
os_free(hapd->wpa_auth->group);
}
os_free(hapd->wpa_auth);
}
if (hapd->conf != NULL) {
if (hapd->conf->ssid.wpa_psk != NULL) {
os_free(hapd->conf->ssid.wpa_psk);
}
if (hapd->conf->ssid.wpa_passphrase != NULL) {
os_free(hapd->conf->ssid.wpa_passphrase);
}
os_free(hapd->conf);
}
os_free(hapd);
esp_wifi_unset_appie_internal(WIFI_APPIE_WPA);
global_hapd = NULL;
#ifdef CONFIG_SAE
uint8_t authmode;
authmode = esp_wifi_ap_get_prof_authmode_internal();
if (authmode == WIFI_AUTH_WPA3_PSK ||
authmode == WIFI_AUTH_WPA2_WPA3_PSK) {
wpa3_hostap_auth_deinit();
/* Wait till lock is released by wpa3 task */
if (WPA3_HOSTAP_AUTH_API_LOCK() == pdTRUE) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
os_mutex_delete(g_wpa3_hostap_auth_api_lock);
g_wpa3_hostap_auth_api_lock = NULL;
}
}
#endif /* CONFIG_SAE */
hostapd_cleanup(hapd);
return true;
}
int esp_wifi_build_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
{
u8 *pos = eid;
u16 capab = 0;
size_t flen;
if (!(hapd->wpa_auth->conf.wpa & WPA_PROTO_RSN)) {
return 0;
}
if (wpa_key_mgmt_sae(hapd->wpa_auth->conf.wpa_key_mgmt) &&
(hapd->conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT
|| hapd->conf->sae_pwe == SAE_PWE_BOTH)) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
}
flen = (capab & 0xff00) ? 2 : 1;
if (len < 2 + flen || !capab) {
return 0; /* no supported extended RSN capabilities */
}
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
*pos++ = WLAN_EID_RSNX;
*pos++ = flen;
*pos++ = capab & 0x00ff;
capab >>= 8;
if (capab) {
*pos++ = capab;
}
return pos - eid;
}
u16 esp_send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 status_code, bool omit_rsnxe, int subtype)
{
#define ASSOC_RESP_LENGTH 20
u8 buf[ASSOC_RESP_LENGTH];
wifi_mgmt_frm_req_t *reply = NULL;
int send_len = 0;
int res = WLAN_STATUS_SUCCESS;
if (!omit_rsnxe) {
send_len = esp_wifi_build_rsnxe(hapd, buf, ASSOC_RESP_LENGTH);
}
esp_wifi_set_appie_internal(WIFI_APPIE_ASSOC_RESP, buf, send_len, 0);
reply = os_zalloc(sizeof(wifi_mgmt_frm_req_t) + sizeof(uint16_t));
if (!reply) {
wpa_printf(MSG_ERROR, "failed to allocate memory for assoc response");
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto done;
}
reply->ifx = WIFI_IF_AP;
reply->subtype = subtype;
os_memcpy(reply->da, addr, ETH_ALEN);
reply->data_len = sizeof(uint16_t);
((uint16_t *)reply->data)[0] = status_code;
if (esp_wifi_send_mgmt_frm_internal(reply) != 0) {
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
wpa_printf(MSG_INFO, "esp_send_assoc_resp_failed: send failed");
}
#undef ASSOC_RESP_LENGTH
done:
os_free(reply);
return res;
}

View File

@ -15,6 +15,9 @@ extern "C" {
#ifdef CONFIG_ESP_WIFI_SOFTAP_SUPPORT
void *hostap_init(void);
bool hostap_deinit(void *data);
u16 esp_send_assoc_resp(struct hostapd_data *data, struct sta_info *sta,
const u8 *addr, u16 status_code, bool omit_rsnxe,
int subtype);
#endif
#ifdef __cplusplus

View File

@ -123,8 +123,8 @@ struct wpa_funcs {
bool (*wpa_sta_in_4way_handshake)(void);
void *(*wpa_ap_init)(void);
bool (*wpa_ap_deinit)(void *data);
bool (*wpa_ap_join)(void **sm, u8 *bssid, u8 *wpa_ie, u8 wpa_ie_len, bool *pmf_enable);
bool (*wpa_ap_remove)(void *sm);
bool (*wpa_ap_join)(void **sm, u8 *bssid, u8 *wpa_ie, u8 wpa_ie_len, u8* rsnxe, u8 rsnxe_len, bool *pmf_enable, int subtype);
bool (*wpa_ap_remove)(void *sta_info);
uint8_t *(*wpa_ap_get_wpa_ie)(uint8_t *len);
bool (*wpa_ap_rx_eapol)(void *hapd_data, void *sm, u8 *data, size_t data_len);
void (*wpa_ap_get_peer_spp_msg)(void *sm, bool *spp_cap, bool *spp_req);
@ -134,6 +134,7 @@ struct wpa_funcs {
int (*wpa_michael_mic_failure)(u16 is_unicast);
uint8_t *(*wpa3_build_sae_msg)(uint8_t *bssid, uint32_t type, size_t *len);
int (*wpa3_parse_sae_msg)(uint8_t *buf, size_t len, uint32_t type, uint16_t status);
int (*wpa3_hostap_handle_auth)(uint8_t *buf, size_t len, uint32_t type, uint16_t status, uint8_t *bssid);
int (*wpa_sta_rx_mgmt)(u8 type, u8 *frame, size_t len, u8 *sender, u32 rssi, u8 channel, u64 current_tsf);
void (*wpa_config_done)(void);
uint8_t *(*owe_build_dhie)(uint16_t group);
@ -190,9 +191,13 @@ typedef struct {
} wifi_wpa_igtk_t;
typedef struct {
#ifndef ETH_ALEN
#define ETH_ALEN 6
#endif
wifi_interface_t ifx;
uint8_t subtype;
uint32_t data_len;
uint8_t da[ETH_ALEN];
uint8_t data[0];
} wifi_mgmt_frm_req_t;
@ -279,10 +284,13 @@ esp_err_t esp_wifi_remain_on_channel(uint8_t ifx, uint8_t type, uint8_t channel,
bool esp_wifi_is_mbo_enabled_internal(uint8_t if_index);
void esp_wifi_get_pmf_config_internal(wifi_pmf_config_t *pmf_cfg, uint8_t ifx);
bool esp_wifi_is_ft_enabled_internal(uint8_t if_index);
uint8_t esp_wifi_sta_get_config_sae_pwe_h2e_internal(void);
uint8_t esp_wifi_sta_get_use_h2e_internal(void);
uint8_t esp_wifi_sta_get_config_sae_pk_internal(void);
void esp_wifi_sta_disable_sae_pk_internal(void);
void esp_wifi_sta_disable_wpa2_authmode_internal(void);
uint8_t esp_wifi_ap_get_max_sta_conn(void);
uint8_t esp_wifi_get_config_sae_pwe_h2e_internal(uint8_t ifx);
bool esp_wifi_ap_notify_node_sae_auth_done(uint8_t *mac);
bool esp_wifi_ap_is_sta_sae_reauth_node(uint8_t *mac);
#endif /* _ESP_WIFI_DRIVER_H_ */

View File

@ -1,15 +1,20 @@
/*
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifdef CONFIG_WPA3_SAE
#include "common/sae.h"
#include "common/ieee802_11_defs.h"
#include "esp_wifi_driver.h"
#include "rsn_supp/wpa.h"
#include "ap/hostapd.h"
#include "ap/ieee802_11.h"
#include "ap/sta_info.h"
#include "esp_wpa3_i.h"
#include "endian.h"
#include "esp_hostap.h"
#ifdef CONFIG_SAE_PK
#include "common/bss.h"
@ -309,3 +314,311 @@ void esp_wifi_register_wpa3_cb(struct wpa_funcs *wpa_cb)
}
#endif /* CONFIG_WPA3_SAE */
#ifdef CONFIG_SAE
static TaskHandle_t g_wpa3_hostap_task_hdl = NULL;
static QueueHandle_t g_wpa3_hostap_evt_queue = NULL;
SemaphoreHandle_t g_wpa3_hostap_auth_api_lock = NULL;
int wpa3_hostap_post_evt(uint32_t evt_id, uint32_t data)
{
wpa3_hostap_auth_event_t *evt = os_zalloc(sizeof(wpa3_hostap_auth_event_t));
if (evt == NULL) {
return ESP_FAIL;
}
evt->id = evt_id;
evt->data = data;
if (g_wpa3_hostap_auth_api_lock) {
WPA3_HOSTAP_AUTH_API_LOCK();
if (g_wpa3_hostap_evt_queue == NULL) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
os_free(evt);
return ESP_FAIL;
}
} else {
os_free(evt);
return ESP_FAIL;
}
if (evt->id == SIG_WPA3_RX_CONFIRM || evt->id == SIG_TASK_DEL) {
/* prioritising confirm for completing handshake for committed sta */
if (os_queue_send_to_front(g_wpa3_hostap_evt_queue, &evt, 0) != pdPASS) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
os_free(evt);
return ESP_FAIL;
}
} else {
if (os_queue_send(g_wpa3_hostap_evt_queue, &evt, 0) != pdPASS) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
os_free(evt);
return ESP_FAIL;
}
}
if (evt_id != SIG_TASK_DEL) {
/* For SIG_TASK_DEL, WPA3_HOSTAP_AUTH_API_UNLOCK will be after clean up of hostapd_data */
WPA3_HOSTAP_AUTH_API_UNLOCK();
}
return ESP_OK;
}
static void wpa3_process_rx_commit(wpa3_hostap_auth_event_t *evt)
{
struct hostapd_sae_commit_queue *frm;
struct hostapd_data *hapd = (struct hostapd_data *)esp_wifi_get_hostap_private_internal();
struct sta_info *sta = NULL;
int ret;
frm = dl_list_first(&hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list);
if (!frm) {
return;
}
dl_list_del(&frm->list);
wpa_printf(MSG_DEBUG, "SAE: Process next available message from queue");
sta = ap_get_sta(hapd, frm->bssid);
if (!sta) {
sta = ap_sta_add(hapd, frm->bssid);
if (!sta) {
wpa_printf(MSG_DEBUG, "ap_sta_add() failed");
ret = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
if (esp_send_sae_auth_reply(hapd, frm->bssid, frm->bssid, WLAN_AUTH_SAE,
frm->auth_transaction, ret, NULL,
0) != 0) {
wpa_printf(MSG_INFO, "esp_send_sae_auth_reply: send failed");
}
goto free;
}
}
if (sta->lock && os_mutex_lock(sta->lock)) {
sta->sae_commit_processing = true;
ret = handle_auth_sae(hapd, sta, frm->msg, frm->len, frm->bssid, frm->auth_transaction, frm->status);
if (sta->remove_pending) {
ap_free_sta(hapd, sta);
goto free;
}
sta->sae_commit_processing = false;
os_mutex_unlock(sta->lock);
uint16_t aid = 0;
if (ret != WLAN_STATUS_SUCCESS &&
ret != WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ) {
if (esp_wifi_ap_get_sta_aid(frm->bssid, &aid) == ESP_OK && aid == 0) {
esp_wifi_ap_deauth_internal(frm->bssid, ret);
}
}
}
free:
os_free(frm);
}
static void wpa3_process_rx_confirm(wpa3_hostap_auth_event_t *evt)
{
struct hostapd_data *hapd = (struct hostapd_data *)esp_wifi_get_hostap_private_internal();
struct sta_info *sta = NULL;
int ret = WLAN_STATUS_SUCCESS;
struct sae_hostap_confirm_data *frm = (struct sae_hostap_confirm_data *)evt->data;
if (!frm) {
return;
}
sta = ap_get_sta(hapd, frm->bssid);
if (!sta) {
os_free(frm);
return;
}
if (sta->lock && os_mutex_lock(sta->lock)) {
ret = handle_auth_sae(hapd, sta, frm->msg, frm->len, frm->bssid, frm->auth_transaction, frm->status);
if (sta->remove_pending) {
ap_free_sta(hapd, sta);
goto done;
}
if (ret == WLAN_STATUS_SUCCESS) {
if (esp_wifi_ap_notify_node_sae_auth_done(frm->bssid) != true) {
ap_free_sta(hapd, sta);
goto done;
}
}
os_mutex_unlock(sta->lock);
if (ret != WLAN_STATUS_SUCCESS) {
uint16_t aid = 0;
if (esp_wifi_ap_get_sta_aid(frm->bssid, &aid) == ESP_OK && aid == 0) {
esp_wifi_ap_deauth_internal(frm->bssid, ret);
}
}
}
done:
os_free(frm);
}
static void esp_wpa3_hostap_task(void *pvParameters)
{
wpa3_hostap_auth_event_t *evt;
bool task_del = false;
while (1) {
if (os_queue_recv(g_wpa3_hostap_evt_queue, &evt, portMAX_DELAY) == pdTRUE) {
switch (evt->id) {
case SIG_WPA3_RX_COMMIT: {
wpa3_process_rx_commit(evt);
break;
}
case SIG_WPA3_RX_CONFIRM: {
wpa3_process_rx_confirm(evt);
break;
}
case SIG_TASK_DEL:
task_del = true;
break;
default:
break;
}
os_free(evt);
if (task_del) {
break;
}
}
}
uint32_t items_in_queue = os_queue_msg_waiting(g_wpa3_hostap_evt_queue);
while(items_in_queue--) {
/* Free events posted to queue */
os_queue_recv(g_wpa3_hostap_evt_queue, &evt, portMAX_DELAY);
if (evt->id == SIG_WPA3_RX_CONFIRM) {
os_free((void *)evt->data);
}
os_free(evt);
}
os_queue_delete(g_wpa3_hostap_evt_queue);
g_wpa3_hostap_evt_queue = NULL;
if (g_wpa3_hostap_auth_api_lock) {
WPA3_HOSTAP_AUTH_API_UNLOCK();
}
/* At this point, task is deleted*/
os_task_delete(NULL);
}
int wpa3_hostap_auth_init(void *data)
{
int ret = ESP_OK;
if (g_wpa3_hostap_evt_queue) {
wpa_printf(MSG_ERROR, "esp_wpa3_hostap_task has already been initialised");
return ret;
}
g_wpa3_hostap_auth_api_lock = os_semphr_create(1, 0);
if (!g_wpa3_hostap_auth_api_lock) {
wpa_printf(MSG_ERROR, "wpa3_hostap_auth_init: failed to create WPA3 hostap auth API lock");
return ESP_FAIL;
}
g_wpa3_hostap_evt_queue = os_queue_create(10, sizeof(wpa3_hostap_auth_event_t));
if (!g_wpa3_hostap_evt_queue) {
wpa_printf(MSG_ERROR, "wpa3_hostap_auth_init: failed to create queue");
ret = ESP_FAIL;
goto init_done;
}
if (os_task_create(esp_wpa3_hostap_task, "esp_wpa3_hostap_task",
WPA3_HOSTAP_HANDLE_AUTH_TASK_STACK_SIZE, NULL,
WPA3_HOSTAP_HANDLE_AUTH_TASK_PRIORITY,
&g_wpa3_hostap_task_hdl) != pdPASS) {
wpa_printf(MSG_ERROR, "wpa3_hostap_auth_init: failed to create task");
os_queue_delete(g_wpa3_hostap_evt_queue);
ret = ESP_FAIL;
goto init_done;
}
struct hostapd_data *hapd = (struct hostapd_data *)data;
dl_list_init(&hapd->sae_commit_queue);
init_done:
WPA3_HOSTAP_AUTH_API_UNLOCK();
return ret;
}
bool wpa3_hostap_auth_deinit(void)
{
if (wpa3_hostap_post_evt(SIG_TASK_DEL, 0) != 0) {
wpa_printf(MSG_ERROR, "failed to send task delete event");
return false;
} else {
return true;
}
}
static int wpa3_hostap_handle_auth(u8 *buf, size_t len, u32 auth_transaction, u16 status, u8 *bssid)
{
struct hostapd_data *hapd = (struct hostapd_data *)esp_wifi_get_hostap_private_internal();
struct sta_info *sta = ap_get_sta(hapd, bssid);
if (auth_transaction == SAE_MSG_COMMIT) {
if (sta && sta->sae_commit_processing) {
/* Ignore commit msg as we are already processing commit msg for this station */
return ESP_OK;
}
return auth_sae_queue(hapd, buf, len, bssid, status, auth_transaction);
}
if (sta && auth_transaction == SAE_MSG_CONFIRM) {
struct sae_hostap_confirm_data *frm = os_malloc(sizeof(struct sae_hostap_confirm_data) + len);
if (!frm) {
wpa_printf(MSG_ERROR, "failed to allocate memory for confirm event");
return ESP_FAIL;
}
frm->len = len;
os_memcpy(frm->bssid, bssid, ETH_ALEN);
frm->auth_transaction = auth_transaction;
frm->status = status;
os_memcpy(frm->msg, buf, len);
if (wpa3_hostap_post_evt(SIG_WPA3_RX_CONFIRM, (u32)frm) != 0) {
wpa_printf(MSG_ERROR, "failed to queue confirm build event");
os_free(frm);
return ESP_FAIL;
}
}
return ESP_OK;
}
int esp_send_sae_auth_reply(struct hostapd_data *hapd,
const u8 *dst, const u8 *bssid,
u16 auth_alg, u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len)
{
int reply_res = ESP_FAIL;
ies_len += 3 * sizeof(uint16_t);
wifi_mgmt_frm_req_t *req = os_zalloc(sizeof(*req) + ies_len);
if (!req) {
wpa_printf(MSG_ERROR, "failed to send sae auth reply");
return reply_res;
}
((uint16_t *)req->data)[0] = htole16(auth_alg);
((uint16_t *)req->data)[1] = htole16(auth_transaction);
((uint16_t *)req->data)[2] = htole16(resp);
os_memcpy(&((uint16_t *)req->data)[3], ies, ies_len - 3 * sizeof(uint16_t));
req->ifx = WIFI_IF_AP;
req->subtype = WLAN_FC_STYPE_AUTH;
req->data_len = ies_len;
os_memcpy(req->da, bssid, ETH_ALEN);
if (esp_wifi_send_mgmt_frm_internal(req) != 0) {
wpa_printf(MSG_INFO, "%s: send failed", __func__);
} else {
reply_res = ESP_OK;
}
os_free(req);
return reply_res;
}
void esp_wifi_register_wpa3_ap_cb(struct wpa_funcs *wpa_cb)
{
wpa_cb->wpa3_hostap_handle_auth = wpa3_hostap_handle_auth;
}
#endif /* CONFIG_SAE */

View File

@ -1,18 +1,8 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ESP_WPA3_H
#define ESP_WPA3_H
@ -36,4 +26,48 @@ static inline void esp_wpa3_free_sae_data(void)
}
#endif /* CONFIG_WPA3_SAE */
#ifdef CONFIG_SAE
enum SIG_WPA3_TASK {
SIG_WPA3_RX_COMMIT,
SIG_WPA3_RX_CONFIRM,
SIG_TASK_DEL,
SIG_TASK_MAX
};
typedef struct {
uint32_t id;
uint32_t data;
} wpa3_hostap_auth_event_t;
struct sae_hostap_confirm_data {
size_t len;
u8 bssid[ETH_ALEN];
u32 auth_transaction;
u16 status;
u8 msg[];
};
#define WPA3_HOSTAP_HANDLE_AUTH_TASK_STACK_SIZE (6144)
#define WPA3_HOSTAP_HANDLE_AUTH_TASK_PRIORITY (19)
#define WPA3_HOSTAP_AUTH_API_LOCK() os_semphr_take(g_wpa3_hostap_auth_api_lock, OS_BLOCK)
#define WPA3_HOSTAP_AUTH_API_UNLOCK() os_semphr_give(g_wpa3_hostap_auth_api_lock)
int wpa3_hostap_post_evt(uint32_t evt_id, uint32_t data);
void esp_wifi_register_wpa3_ap_cb(struct wpa_funcs *wpa_cb);
int wpa3_hostap_auth_init(void *data);
bool wpa3_hostap_auth_deinit(void);
int esp_send_sae_auth_reply(struct hostapd_data *hapd,
const u8 *dst, const u8 *bssid,
u16 auth_alg, u16 auth_transaction, u16 resp,
const u8 *ies, size_t ies_len);
#else /* CONFIG_SAE */
static inline void esp_wifi_register_wpa3_ap_cb(struct wpa_funcs *wpa_cb)
{
wpa_cb->wpa3_hostap_handle_auth = NULL;
}
#endif /* CONFIG_SAE */
#endif /* ESP_WPA3_H */

View File

@ -180,6 +180,7 @@ void wpa_ap_get_peer_spp_msg(void *sm_data, bool *spp_cap, bool *spp_req)
bool wpa_deattach(void)
{
esp_wpa3_free_sae_data();
esp_wifi_sta_wpa2_ent_disable();
wpa_sm_deinit();
return true;
@ -275,7 +276,7 @@ static int check_n_add_wps_sta(struct hostapd_data *hapd, struct sta_info *sta_i
}
#endif
static bool hostap_sta_join(void **sta, u8 *bssid, u8 *wpa_ie, u8 wpa_ie_len, bool *pmf_enable)
static bool hostap_sta_join(void **sta, u8 *bssid, u8 *wpa_ie, u8 wpa_ie_len,u8 *rsnxe, u8 rsnxe_len, bool *pmf_enable, int subtype)
{
struct sta_info *sta_info;
struct hostapd_data *hapd = hostapd_get_hapd_data();
@ -284,7 +285,7 @@ static bool hostap_sta_join(void **sta, u8 *bssid, u8 *wpa_ie, u8 wpa_ie_len, bo
return 0;
}
if (*sta) {
if (*sta && !esp_wifi_ap_is_sta_sae_reauth_node(bssid)) {
ap_free_sta(hapd, *sta);
}
sta_info = ap_sta_add(hapd, bssid);
@ -298,7 +299,7 @@ static bool hostap_sta_join(void **sta, u8 *bssid, u8 *wpa_ie, u8 wpa_ie_len, bo
return true;
}
#endif
if (wpa_ap_join(sta_info, bssid, wpa_ie, wpa_ie_len, pmf_enable)) {
if (wpa_ap_join(sta_info, bssid, wpa_ie, wpa_ie_len, rsnxe, rsnxe_len, pmf_enable, subtype)) {
*sta = sta_info;
return true;
}
@ -345,6 +346,7 @@ int esp_supplicant_init(void)
wpa_cb->wpa_config_done = wpa_config_done;
wpa_cb->wpa_sta_set_ap_rsnxe = wpa_sm_set_ap_rsnxe;
esp_wifi_register_wpa3_ap_cb(wpa_cb);
esp_wifi_register_wpa3_cb(wpa_cb);
#ifdef CONFIG_OWE_STA
esp_wifi_register_owe_cb(wpa_cb);

View File

@ -95,7 +95,7 @@ int hostapd_send_eapol(const u8 *source, const u8 *sta_addr,
void wpa_supplicant_transition_disable(void *sm, u8 bitmap)
{
wpa_printf(MSG_INFO, "TRANSITION_DISABLE %02x", bitmap);
wpa_printf(MSG_DEBUG, "TRANSITION_DISABLE %02x", bitmap);
if (bitmap & TRANSITION_DISABLE_WPA3_PERSONAL) {
esp_wifi_sta_disable_wpa2_authmode_internal();

View File

@ -346,11 +346,15 @@ extern const wifi_osi_funcs_t *wifi_funcs;
#define os_mutex_lock(a) wifi_funcs->_mutex_lock((a))
#define os_mutex_unlock(a) wifi_funcs->_mutex_unlock((a))
#define os_recursive_mutex_create() wifi_funcs->_recursive_mutex_create()
#define os_mutex_create() wifi_funcs->_mutex_create();
#define os_mutex_delete(a) wifi_funcs->_mutex_delete(a)
#define os_queue_create(a, b) wifi_funcs->_queue_create((a), (b))
#define os_queue_delete(a) wifi_funcs->_queue_delete(a)
#define os_queue_send(a, b, c) wifi_funcs->_queue_send((a), (b), (c))
#define os_queue_send_to_front(a, b, c) wifi_funcs->_queue_send_to_front((a), (b), (c))
#define os_queue_recv(a, b, c) wifi_funcs->_queue_recv((a), (b), (c))
#define os_queue_msg_waiting(a) wifi_funcs->_queue_msg_waiting((a))
#define os_task_create(a,b,c,d,e,f) wifi_funcs->_task_create((a), (b), (c), (d), (e), (f))
#define os_task_delete(a) wifi_funcs->_task_delete((a))

View File

@ -12,6 +12,7 @@
#include "crypto/sha1.h"
#include "common/ieee802_11_defs.h"
#include "common/eapol_common.h"
#include "common/sae.h"
#include "ap/wpa_auth.h"
#include "ap/ap_config.h"
#include "utils/wpa_debug.h"
@ -144,10 +145,39 @@ static int hostapd_derive_psk(struct hostapd_ssid *ssid)
}
int hostapd_setup_sae_pt(struct hostapd_bss_config *conf)
{
#ifdef CONFIG_SAE
struct hostapd_ssid *ssid = &conf->ssid;
if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK ||
!wpa_key_mgmt_sae(conf->wpa_key_mgmt)))
return 0; /* PT not needed */
sae_deinit_pt(ssid->pt);
ssid->pt = NULL;
if (ssid->wpa_passphrase) {
ssid->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
ssid->ssid_len,
(const u8 *) ssid->wpa_passphrase,
os_strlen(ssid->wpa_passphrase),
NULL);
if (!ssid->pt)
return -1;
}
#endif /* CONFIG_SAE */
return 0;
}
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
{
struct hostapd_ssid *ssid = &conf->ssid;
if (hostapd_setup_sae_pt(conf) < 0)
return -1;
if (ssid->wpa_passphrase != NULL) {
if (ssid->wpa_psk != NULL) {
wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
@ -245,3 +275,25 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
return NULL;
}
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l)
{
struct hostapd_wpa_psk *psk, *tmp;
for (psk = *l; psk;) {
tmp = psk;
psk = psk->next;
bin_clear_free(tmp, sizeof(*tmp));
}
*l = NULL;
}
void hostapd_config_free_bss(struct hostapd_bss_config *conf)
{
hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
str_clear_free(conf->ssid.wpa_passphrase);
#ifdef CONFIG_SAE
sae_deinit_pt(conf->ssid.pt);
#endif /* CONFIG_SAE */
os_free(conf);
}

View File

@ -53,6 +53,7 @@ struct hostapd_ssid {
struct hostapd_wpa_psk *wpa_psk;
char *wpa_passphrase;
struct sae_pt *pt;
struct hostapd_wep_keys wep;
@ -299,6 +300,12 @@ struct hostapd_bss_config {
char *dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */
unsigned int sae_anti_clogging_threshold;
enum sae_pwe sae_pwe;
unsigned int sae_sync;
int *sae_groups;
#define SAE_ANTI_CLOGGING_THRESHOLD 2 /* max number of commit msg allowed to queue without anti-clogging token request */
};
@ -367,13 +374,17 @@ void hostapd_config_free(struct hostapd_config *conf);
int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
const u8 *addr, int *vlan_id);
int hostapd_rate_found(int *list, int rate);
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
void hostapd_config_free_bss(struct hostapd_bss_config *conf);
int hostapd_wep_key_cmp(struct hostapd_wep_keys *a,
struct hostapd_wep_keys *b);
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *prev_psk);
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
struct sta_info;
bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie, uint8_t wpa_ie_len, bool *pmf_enable);
bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie,
uint8_t wpa_ie_len,uint8_t *rsnxe, uint8_t rsnxe_len,
bool *pmf_enable, int subtype);
bool wpa_ap_remove(void* sta_info);
#endif /* HOSTAPD_CONFIG_H */

View File

@ -0,0 +1,142 @@
/*
* hostapd / Comeback token mechanism for SAE
* Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "hostapd.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "comeback_token.h"
#ifdef CONFIG_SAE
int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
{
u8 hash[SHA256_MAC_LEN];
if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
addr, ETH_ALEN, hash) < 0)
return -1;
*idx = hash[0];
return 0;
}
int check_comeback_token(const u8 *comeback_key,
u16 *comeback_pending_idx, const u8 *addr,
const u8 *token, size_t token_len)
{
u8 mac[SHA256_MAC_LEN];
const u8 *addrs[2];
size_t len[2];
u16 token_idx;
u8 idx;
if (token_len != SHA256_MAC_LEN ||
comeback_token_hash(comeback_key, addr, &idx) < 0) {
return -1;
}
token_idx = comeback_pending_idx[idx];
if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
wpa_printf(MSG_DEBUG,
"Comeback: Invalid anti-clogging token from "
MACSTR " - token_idx 0x%04x, expected 0x%04x",
MAC2STR(addr), WPA_GET_BE16(token), token_idx);
return -1;
}
addrs[0] = addr;
len[0] = ETH_ALEN;
addrs[1] = token;
len[1] = 2;
if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
2, addrs, len, mac) < 0 ||
os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0) {
return -1;
}
comeback_pending_idx[idx] = 0; /* invalidate used token */
return 0;
}
struct wpabuf *
auth_build_token_req(struct os_reltime *last_comeback_key_update,
u8 *comeback_key, u16 comeback_idx,
u16 *comeback_pending_idx, size_t idx_len,
int group, const u8 *addr, int h2e)
{
struct wpabuf *buf;
u8 *token;
struct os_reltime now;
u8 idx[2];
const u8 *addrs[2];
size_t len[2];
u8 p_idx;
u16 token_idx;
os_get_time(&now);
if (!os_reltime_initialized(last_comeback_key_update) ||
os_reltime_expired(&now, last_comeback_key_update, 60) ||
comeback_idx == 0xffff) {
if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0) {
return NULL;
}
wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
comeback_key, COMEBACK_KEY_SIZE);
*last_comeback_key_update = now;
comeback_idx = 0;
os_memset(comeback_pending_idx, 0, idx_len);
}
buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
if (buf == NULL) {
return NULL;
}
if (group)
wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
if (h2e) {
/* Encapsulate Anti-clogging Token field in a container IE */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
}
if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
wpabuf_free(buf);
return NULL;
}
token_idx = comeback_pending_idx[p_idx];
if (!token_idx) {
comeback_idx++;
token_idx = comeback_idx;
comeback_pending_idx[p_idx] = token_idx;
}
WPA_PUT_BE16(idx, token_idx);
token = wpabuf_put(buf, SHA256_MAC_LEN);
addrs[0] = addr;
len[0] = ETH_ALEN;
addrs[1] = idx;
len[1] = sizeof(idx);
if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
2, addrs, len, token) < 0) {
wpabuf_free(buf);
return NULL;
}
WPA_PUT_BE16(token, token_idx);
return buf;
}
#endif /* CONFIG_SAE */

View File

@ -0,0 +1,21 @@
/*
* hostapd / Comeback token mechanism for SAE
* Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef COMEBACK_TOKEN_H
#define COMEBACK_TOKEN_H
int check_comeback_token(const u8 *comeback_key,
u16 *comeback_pending_idx, const u8 *addr,
const u8 *token, size_t token_len);
struct wpabuf *
auth_build_token_req(struct os_reltime *last_comeback_key_update,
u8 *comeback_key, u16 comeback_idx,
u16 *comeback_pending_idx, size_t idx_len,
int group, const u8 *addr, int h2e);
#endif /* COMEBACK_TOKEN_H */

View File

@ -63,6 +63,15 @@ struct hostapd_frame_info {
int ssi_signal; /* dBm */
};
struct hostapd_sae_commit_queue {
struct dl_list list;
size_t len;
u8 bssid[ETH_ALEN];
u32 auth_transaction;
u16 status;
u8 msg[];
};
#ifdef CONFIG_WPS
enum hapd_wps_status {
WPS_SUCCESS_STATUS = 1,
@ -131,6 +140,20 @@ struct hostapd_data {
int noa_start;
int noa_duration;
#endif /* CONFIG_P2P */
#ifdef CONFIG_SAE
#define COMEBACK_KEY_SIZE 8
#define COMEBACK_PENDING_IDX_SIZE 256
/** Key used for generating SAE anti-clogging tokens */
u8 comeback_key[COMEBACK_KEY_SIZE];
struct os_reltime last_comeback_key_update;
u16 comeback_idx;
u16 comeback_pending_idx[COMEBACK_PENDING_IDX_SIZE];
int dot11RSNASAERetransPeriod;
struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
#endif /* CONFIG_SAE */
#ifdef CONFIG_INTERWORKING
size_t gas_frag_limit;
#endif /* CONFIG_INTERWORKING */

View File

@ -0,0 +1,702 @@
/*
* hostapd / IEEE 802.11 Management
* Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "common/sae.h"
#include "common/ieee802_11_defs.h"
#include "esp_wifi_driver.h"
#include "ap/wpa_auth.h"
#include "ap/hostapd.h"
#include "ap/sta_info.h"
#include "crypto/sha256.h"
#include "ap/pmksa_cache_auth.h"
#include "ap/comeback_token.h"
#include "crypto/random.h"
#include "esp_wpa3_i.h"
#ifdef CONFIG_SAE
static void sae_set_state(struct sta_info *sta, enum sae_state state,
const char *reason)
{
wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
sae_state_txt(sta->sae->state), sae_state_txt(state),
MAC2STR(sta->addr), reason);
sta->sae->state = state;
}
static const char * sae_get_password(struct hostapd_data *hapd,
struct sta_info *sta,
const char *rx_id,
struct sae_pt **s_pt)
{
const char *password = NULL;
struct sae_pt *pt = NULL;
if (!password) {
password = hapd->conf->ssid.wpa_passphrase;
pt = hapd->conf->ssid.pt;
}
if (s_pt) {
*s_pt = pt;
}
return password;
}
static struct wpabuf *auth_build_sae_commit(struct hostapd_data *hapd,
struct sta_info *sta, int update, int status_code)
{
struct wpabuf *buf;
const char *password = NULL;
const char *rx_id = NULL;
int use_pt = 0;
struct sae_pt *pt = NULL;
if (sta->sae->tmp) {
rx_id = sta->sae->tmp->pw_id;
use_pt = sta->sae->h2e;
}
if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
use_pt = 1;
else if (status_code == WLAN_STATUS_SUCCESS)
use_pt = 0;
else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
use_pt = 1;
password = sae_get_password(hapd, sta, rx_id, &pt);
if (!password || (use_pt && !pt)) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
}
if (update && use_pt &&
sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr,
NULL, NULL) < 0) {
return NULL;
}
if (update && !use_pt &&
sae_prepare_commit(hapd->own_addr, sta->addr,
(u8 *) password, os_strlen((const char *)password),
sta->sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
return NULL;
}
buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
(rx_id ? 3 + os_strlen(rx_id) : 0));
if (buf &&
sae_write_commit(sta->sae, buf, sta->sae->tmp ?
sta->sae->tmp->anti_clogging_token : NULL,
rx_id) < 0) {
wpabuf_free(buf);
buf = NULL;
}
return buf;
}
static struct wpabuf *auth_build_sae_confirm(struct hostapd_data *hapd,
struct sta_info *sta)
{
struct wpabuf *buf;
buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
if (buf == NULL) {
return NULL;
}
if (sae_write_confirm(sta->sae, buf) < 0) {
wpabuf_free(buf);
return NULL;
}
return buf;
}
static int auth_sae_send_commit(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *bssid, int update, int status_code)
{
struct wpabuf *data;
int reply_res;
u16 status;
data = auth_build_sae_commit(hapd, sta, update, status_code);
if (!data && sta->sae->tmp && sta->sae->tmp->pw_id) {
return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
}
if (data == NULL) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (sta->sae->tmp && sta->sae->h2e) {
status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
} else {
status = WLAN_STATUS_SUCCESS;
}
#ifdef ESP_SUPPLICANT
if (sta->remove_pending) {
reply_res = -1;
} else {
reply_res = esp_send_sae_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1,
status, wpabuf_head(data),
wpabuf_len(data));
}
#endif /* ESP_SUPPLICANT */
wpabuf_free(data);
return reply_res;
}
static int auth_sae_send_confirm(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *bssid)
{
struct wpabuf *data;
int reply_res;
data = auth_build_sae_confirm(hapd, sta);
if (data == NULL) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
#ifdef ESP_SUPPLICANT
if (sta->remove_pending) {
reply_res = -1;
} else {
reply_res = esp_send_sae_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2,
WLAN_STATUS_SUCCESS, wpabuf_head(data),
wpabuf_len(data));
}
#endif /* ESP_SUPPLICANT */
wpabuf_free(data);
return reply_res;
}
static int use_sae_anti_clogging(struct hostapd_data *hapd)
{
struct sta_info *sta;
unsigned int open = 0;
if (hapd->conf->sae_anti_clogging_threshold == 0) {
return 1;
}
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (sta->sae &&
(sta->sae->state == SAE_COMMITTED ||
sta->sae->state != SAE_CONFIRMED)) {
open++;
}
if (open >= hapd->conf->sae_anti_clogging_threshold) {
return 1;
}
}
/* In addition to already existing open SAE sessions, check whether
* there are enough pending commit messages in the processing queue to
* potentially result in too many open sessions. */
if (open + dl_list_len(&hapd->sae_commit_queue) >=
hapd->conf->sae_anti_clogging_threshold) {
return 1;
}
return 0;
}
static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
{
if (sta->sae->sync > hapd->conf->sae_sync) {
sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
sta->sae->sync = 0;
return -1;
}
return 0;
}
void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
sta->flags |= WLAN_STA_AUTH;
#ifdef ESP_SUPPLICANT
sta->sae_commit_processing = false;
#endif /* ESP_SUPPLICANT */
sta->auth_alg = WLAN_AUTH_SAE;
sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
sta->sae->pmk, sta->sae->pmkid, false);
}
static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *bssid, u8 auth_transaction, u16 status_code,
int allow_reuse, int *sta_removed)
{
int ret = WLAN_STATUS_SUCCESS;
*sta_removed = 0;
if (auth_transaction != 1 && auth_transaction != 2) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
auth_transaction);
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
if (sta->sae->tmp) {
sta->sae->h2e =
(status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT);
}
ret = auth_sae_send_commit(hapd, sta, bssid,
!allow_reuse, status_code);
if (ret) {
return ret;
}
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
if (sae_process_commit(sta->sae) < 0) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sta->sae->sync = 0;
} else {
wpa_printf(MSG_DEBUG, "SAE confirm before commit");
}
break;
case SAE_COMMITTED:
if (auth_transaction == 1) {
if (sae_process_commit(sta->sae) < 0) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret) {
return ret;
}
sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
sta->sae->sync = 0;
} else {
/*
* For instructure BSS, send the postponed Confirm from
* Nothing -> Confirmed transition that was reduced to
* Nothing -> Committed above.
*/
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret) {
return ret;
}
sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
/*
* Since this was triggered on Confirm RX, run another
* step to get to Accepted without waiting for
* additional events.
*/
return sae_sm_step(hapd, sta, bssid, auth_transaction,
WLAN_STATUS_SUCCESS, 0, sta_removed);
}
break;
case SAE_CONFIRMED:
if (auth_transaction == 1) {
if (sae_check_big_sync(hapd, sta)) {
return WLAN_STATUS_SUCCESS;
}
sta->sae->sync++;
ret = auth_sae_send_commit(hapd, sta, bssid, 1,
status_code);
if (ret) {
return ret;
}
if (sae_process_commit(sta->sae) < 0) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret) {
return ret;
}
} else {
sta->sae->send_confirm = 0xffff;
sae_accept_sta(hapd, sta);
}
break;
case SAE_ACCEPTED:
if (auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
ret = auth_sae_send_commit(hapd, sta, bssid, 1,
status_code);
if (ret) {
return ret;
}
if (sae_process_commit(sta->sae) < 0) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
sta->sae->sync = 0;
} else {
if (sae_check_big_sync(hapd, sta)) {
return WLAN_STATUS_SUCCESS;
}
sta->sae->sync++;
ret = auth_sae_send_confirm(hapd, sta, bssid);
sae_clear_temp_data(sta->sae);
if (ret) {
return ret;
}
}
break;
default:
wpa_printf(MSG_ERROR, "SAE: invalid state %d",
sta->sae->state);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
return WLAN_STATUS_SUCCESS;
}
static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
{
enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
status_code == WLAN_STATUS_SUCCESS) ||
(sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
(status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)) ||
(sae_pwe == SAE_PWE_BOTH &&
(status_code == WLAN_STATUS_SUCCESS ||
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT));
}
int handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf, size_t len, u8 *bssid,
u16 auth_transaction, u16 status)
{
int resp = WLAN_STATUS_SUCCESS;
struct wpabuf *data = NULL;
int *groups = hapd->conf->sae_groups;
int default_group[] = { IANA_SECP256R1, 0};
const u8 *pos, *end;
int sta_removed = 0;
if (!groups) {
groups = default_group;
}
if (!sta->sae) {
if (auth_transaction != 1 ||
!sae_status_success(hapd, status)) {
wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
status);
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
sta->sae = os_zalloc(sizeof(*sta->sae));
if (!sta->sae) {
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto remove_sta;
}
sae_set_state(sta, SAE_NOTHING, "Init");
sta->sae->sync = 0;
}
if (auth_transaction == 1) {
const u8 *token = NULL;
size_t token_len = 0;
int allow_reuse = 0;
if (!sae_status_success(hapd, status)) {
goto remove_sta;
}
if (sta->sae->state == SAE_COMMITTED) {
/* This is needed in the infrastructure BSS case to
* address a sequence where a STA entry may remain in
* hostapd across two attempts to do SAE authentication
* by the same STA. The second attempt may end up trying
* to use a different group and that would not be
* allowed if we remain in Committed state with the
* previously set parameters. */
pos = buf;
end = buf + len;
if (end - pos >= (int) sizeof(le16) &&
sae_group_allowed(sta->sae, groups,
WPA_GET_LE16(pos)) ==
WLAN_STATUS_SUCCESS) {
/* Do not waste resources deriving the same PWE
* again since the same group is reused. */
sae_set_state(sta, SAE_NOTHING,
"Allow previous PWE to be reused");
allow_reuse = 1;
} else {
sae_set_state(sta, SAE_NOTHING,
"Clear existing state to allow restart");
sae_clear_data(sta->sae);
}
}
resp = sae_parse_commit(sta->sae, buf, len, &token, &token_len, default_group,
status == WLAN_STATUS_SAE_HASH_TO_ELEMENT);
if (resp == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message from " MACSTR " due to reflection attack",
MAC2STR(sta->addr));
resp = WLAN_STATUS_SUCCESS;
goto remove_sta;
}
if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
sae_set_state(sta, SAE_NOTHING,
"Unknown Password Identifier");
goto remove_sta;
}
if (token &&
check_comeback_token(hapd->comeback_key,
hapd->comeback_pending_idx, sta->addr,
token, token_len) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
"incorrect token from " MACSTR,
MAC2STR(sta->addr));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto remove_sta;
}
if (resp != WLAN_STATUS_SUCCESS) {
goto reply;
}
if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) {
int h2e = 0;
wpa_printf(MSG_DEBUG,
"SAE: Request anti-clogging token from "
MACSTR, MAC2STR(sta->addr));
if (sta->sae->tmp)
h2e = sta->sae->h2e;
if (status == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
h2e = 1;
data = auth_build_token_req(
&hapd->last_comeback_key_update,
hapd->comeback_key,
hapd->comeback_idx,
hapd->comeback_pending_idx,
sizeof(hapd->comeback_pending_idx),
sta->sae->group,
sta->addr, h2e);
resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
#ifdef ESP_SUPPLICANT
sta->sae_commit_processing = false;
#endif /* ESP_SUPPLICANT */
goto reply;
}
resp = sae_sm_step(hapd, sta, bssid, auth_transaction,
status, allow_reuse, &sta_removed);
} else if (auth_transaction == 2) {
if (status != WLAN_STATUS_SUCCESS) {
goto remove_sta;
}
if (sta->sae->state >= SAE_CONFIRMED) {
const u8 *var;
size_t var_len;
u16 peer_send_confirm;
var = buf;
var_len = len;
if (var_len < 2) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
peer_send_confirm = WPA_GET_LE16(var);
if (sta->sae->state == SAE_ACCEPTED &&
(peer_send_confirm <= sta->sae->rc ||
peer_send_confirm == 0xffff)) {
wpa_printf(MSG_DEBUG,
"SAE: Silently ignore unexpected Confirm from peer "
MACSTR
" (peer-send-confirm=%u Rc=%u)",
MAC2STR(sta->addr),
peer_send_confirm, sta->sae->rc);
return 0;
}
if (sae_check_confirm(sta->sae, buf, len) < 0) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
sta->sae->rc = peer_send_confirm;
}
resp = sae_sm_step(hapd, sta, bssid, auth_transaction,
status, 0, &sta_removed);
} else {
wpa_printf(MSG_ERROR, "unexpected SAE authentication transaction %u (status=%u )", auth_transaction, status);
if (status != WLAN_STATUS_SUCCESS) {
resp = -1;
goto remove_sta;
}
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
}
reply:
if (!sta_removed && resp != WLAN_STATUS_SUCCESS) {
pos = buf;
end = buf + len;
/* Copy the Finite Cyclic Group field from the request if we
* rejected it as unsupported group. */
if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
!data && end - pos >= 2) {
data = wpabuf_alloc_copy(pos, 2);
}
#ifdef ESP_SUPPLICANT
if (!sta->remove_pending) {
esp_send_sae_auth_reply(hapd, bssid, bssid, WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0);
}
#endif /* ESP_SUPPLICANT */
}
remove_sta:
wpabuf_free(data);
return resp;
}
int auth_sae_queue(struct hostapd_data *hapd,
u8 *buf, size_t len, u8 *bssid, u16 status, u32 auth_transaction)
{
struct hostapd_sae_commit_queue *q, *q2;
unsigned int queue_len;
queue_len = dl_list_len(&hapd->sae_commit_queue);
if (queue_len >= 5) {
wpa_printf(MSG_DEBUG,
"SAE: No more room in message queue - drop the new frame from "
MACSTR, MAC2STR(bssid));
return 0;
}
wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
MACSTR " for processing (queue_len %u)", MAC2STR(bssid),
queue_len);
q = os_zalloc(sizeof(*q) + len);
if (!q) {
return -1;
}
q->len = len;
os_memcpy(q->msg, buf, len);
os_memcpy(q->bssid, bssid, ETH_ALEN);
q->auth_transaction = auth_transaction;
q->status = status;
/* Check whether there is already a queued Authentication frame from the
* same station with the same transaction number and if so, replace that
* queue entry with the new one. This avoids issues with a peer that
* sends multiple times (e.g., due to frequent SAE retries). There is no
* point in us trying to process the old attempts after a new one has
* obsoleted them. */
dl_list_for_each(q2, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
if (os_memcmp(bssid, q2->bssid, ETH_ALEN) == 0 &&
auth_transaction == q2->auth_transaction) {
wpa_printf(MSG_DEBUG,
"SAE: Replace queued message from same STA with same transaction number");
dl_list_add(&q2->list, &q->list);
dl_list_del(&q2->list);
os_free(q2);
goto queued;
}
}
/* No pending identical entry, so add to the end of the queue */
dl_list_add_tail(&hapd->sae_commit_queue, &q->list);
queued:
#ifdef ESP_SUPPLICANT
/* posting event to the task to handle commit */
if (wpa3_hostap_post_evt(SIG_WPA3_RX_COMMIT, 0) != 0) {
wpa_printf(MSG_ERROR, "failed to queue commit build event");
return -1;
}
return 0;
#endif /* ESP_SUPPLICANT */
}
#endif /* CONFIG_SAE */
u16 wpa_res_to_status_code(enum wpa_validate_result res)
{
switch (res) {
case WPA_IE_OK:
return WLAN_STATUS_SUCCESS;
case WPA_INVALID_IE:
return WLAN_STATUS_INVALID_IE;
case WPA_INVALID_GROUP:
return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
case WPA_INVALID_PAIRWISE:
return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
case WPA_INVALID_AKMP:
return WLAN_STATUS_AKMP_NOT_VALID;
case WPA_NOT_ENABLED:
return WLAN_STATUS_INVALID_IE;
case WPA_ALLOC_FAIL:
return WLAN_STATUS_UNSPECIFIED_FAILURE;
case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
case WPA_INVALID_MGMT_GROUP_CIPHER:
return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
case WPA_INVALID_MDIE:
return WLAN_STATUS_INVALID_MDIE;
case WPA_INVALID_PROTO:
return WLAN_STATUS_INVALID_IE;
case WPA_INVALID_PMKID:
return WLAN_STATUS_INVALID_PMKID;
case WPA_DENIED_OTHER_REASON:
return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
}
return WLAN_STATUS_INVALID_IE;
}

View File

@ -0,0 +1,20 @@
/*
* hostapd / IEEE 802.11 Management
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_11_H
#define IEEE802_11_H
enum wpa_validate_result;
int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr);
int auth_sae_queue(struct hostapd_data *hapd, u8 *buf, size_t len, u8 *bssid, u16 status, u32 auth_transaction);
int handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf, size_t len, u8 *bssid,
u16 auth_transaction, u16 status);
u16 wpa_res_to_status_code(enum wpa_validate_result res);
#endif /* IEEE802_11_H */

View File

@ -0,0 +1,441 @@
/*
* hostapd - PMKSA cache for IEEE 802.11i RSN
* Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "sta_info.h"
#include "ap_config.h"
#include "pmksa_cache_auth.h"
#include "common/eapol_common.h"
#include "common/ieee802_11_defs.h"
#include "ap/pmksa_cache_auth.h"
#include "ap/ieee802_1x.h"
static const int pmksa_cache_max_entries = 10;
static const int dot11RSNAConfigPMKLifetime = 8640000;
struct rsn_pmksa_cache {
#define PMKID_HASH_SIZE 128
#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
struct rsn_pmksa_cache_entry *pmksa;
int pmksa_count;
void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
void *ctx;
};
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
{
os_free(entry->vlan_desc);
os_free(entry->identity);
wpabuf_free(entry->cui);
bin_clear_free(entry, sizeof(*entry));
}
void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos, *prev;
unsigned int hash;
pmksa->pmksa_count--;
pmksa->free_cb(entry, pmksa->ctx);
/* unlink from hash list */
hash = PMKID_HASH(entry->pmkid);
pos = pmksa->pmkid[hash];
prev = NULL;
while (pos) {
if (pos == entry) {
if (prev != NULL)
prev->hnext = entry->hnext;
else
pmksa->pmkid[hash] = entry->hnext;
break;
}
prev = pos;
pos = pos->hnext;
}
/* unlink from entry list */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
if (pos == entry) {
if (prev != NULL)
prev->next = entry->next;
else
pmksa->pmksa = entry->next;
break;
}
prev = pos;
pos = pos->next;
}
_pmksa_cache_free_entry(entry);
}
/**
* pmksa_cache_auth_flush - Flush all PMKSA cache entries
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
*/
void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa)
{
while (pmksa->pmksa) {
wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for "
MACSTR, MAC2STR(pmksa->pmksa->spa));
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
}
}
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
struct os_reltime now;
os_get_reltime(&now);
while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
MACSTR, MAC2STR(pmksa->pmksa->spa));
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
}
pmksa_cache_set_expiration(pmksa);
}
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
{
int sec;
struct os_reltime now;
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
if (pmksa->pmksa == NULL)
return;
os_get_reltime(&now);
sec = pmksa->pmksa->expiration - now.sec;
if (sec < 0)
sec = 0;
eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
}
static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
struct eapol_state_machine *eapol)
{
if (eapol == NULL)
return;
}
static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos, *prev;
int hash;
/* Add the new entry; order by expiration time */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
if (pos->expiration > entry->expiration)
break;
prev = pos;
pos = pos->next;
}
if (prev == NULL) {
entry->next = pmksa->pmksa;
pmksa->pmksa = entry;
} else {
entry->next = prev->next;
prev->next = entry;
}
hash = PMKID_HASH(entry->pmkid);
entry->hnext = pmksa->pmkid[hash];
pmksa->pmkid[hash] = entry;
pmksa->pmksa_count++;
if (prev == NULL)
pmksa_cache_set_expiration(pmksa);
wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
MAC2STR(entry->spa));
wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
}
/**
* pmksa_cache_auth_add - Add a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @pmk: The new pairwise master key
* @pmk_len: PMK length in bytes, usually PMK_LEN (32)
* @pmkid: Calculated PMKID
* @kck: Key confirmation key or %NULL if not yet derived
* @kck_len: KCK length in bytes
* @aa: Authenticator address
* @spa: Supplicant address
* @session_timeout: Session timeout
* @eapol: Pointer to EAPOL state machine data
* @akmp: WPA_KEY_MGMT_* used in key derivation
* Returns: Pointer to the added PMKSA cache entry or %NULL on error
*
* This function create a PMKSA entry for a new PMK and adds it to the PMKSA
* cache. If an old entry is already in the cache for the same Supplicant,
* this entry will be replaced with the new entry. PMKID will be calculated
* based on the PMK.
*/
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
const u8 *kck, size_t kck_len,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp)
{
struct rsn_pmksa_cache_entry *entry;
entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
aa, spa, session_timeout, eapol,
akmp);
if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
return NULL;
return entry;
}
/**
* pmksa_cache_auth_create_entry - Create a PMKSA cache entry
* @pmk: The new pairwise master key
* @pmk_len: PMK length in bytes, usually PMK_LEN (32)
* @pmkid: Calculated PMKID
* @kck: Key confirmation key or %NULL if not yet derived
* @kck_len: KCK length in bytes
* @aa: Authenticator address
* @spa: Supplicant address
* @session_timeout: Session timeout
* @eapol: Pointer to EAPOL state machine data
* @akmp: WPA_KEY_MGMT_* used in key derivation
* Returns: Pointer to the added PMKSA cache entry or %NULL on error
*
* This function creates a PMKSA entry.
*/
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp)
{
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
if (pmk_len > PMK_LEN_MAX)
return NULL;
if (wpa_key_mgmt_suite_b(akmp) && !kck)
return NULL;
entry = os_zalloc(sizeof(*entry));
if (entry == NULL)
return NULL;
os_memcpy(entry->pmk, pmk, pmk_len);
entry->pmk_len = pmk_len;
if (pmkid)
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
else if (wpa_key_mgmt_suite_b(akmp))
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
else
rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
os_get_reltime(&now);
entry->expiration = now.sec;
if (session_timeout > 0)
entry->expiration += session_timeout;
else
entry->expiration += dot11RSNAConfigPMKLifetime;
entry->akmp = akmp;
os_memcpy(entry->spa, spa, ETH_ALEN);
pmksa_cache_from_eapol_data(entry, eapol);
return entry;
}
/**
* pmksa_cache_auth_add_entry - Add a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @entry: Pointer to PMKSA cache entry
*
* This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
* already in the cache for the same Supplicant, this entry will be replaced
* with the new entry. PMKID will be calculated based on the PMK.
*/
int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos;
if (entry == NULL)
return -1;
/* Replace an old entry for the same STA (if found) with the new entry
*/
pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
if (pos)
pmksa_cache_free_entry(pmksa, pos);
if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
/* Remove the oldest entry to make room for the new entry */
wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
"entry (for " MACSTR ") to make room for new one",
MAC2STR(pmksa->pmksa->spa));
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
}
pmksa_cache_link_entry(pmksa, entry);
return 0;
}
/**
* pmksa_cache_auth_deinit - Free all entries in PMKSA cache
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
*/
void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
{
struct rsn_pmksa_cache_entry *entry, *prev;
int i;
if (pmksa == NULL)
return;
entry = pmksa->pmksa;
while (entry) {
prev = entry;
entry = entry->next;
_pmksa_cache_free_entry(prev);
}
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
pmksa->pmksa_count = 0;
pmksa->pmksa = NULL;
for (i = 0; i < PMKID_HASH_SIZE; i++)
pmksa->pmkid[i] = NULL;
os_free(pmksa);
}
/**
* pmksa_cache_auth_get - Fetch a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @spa: Supplicant address or %NULL to match any
* @pmkid: PMKID 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_auth_get(struct rsn_pmksa_cache *pmksa,
const u8 *spa, const u8 *pmkid)
{
struct rsn_pmksa_cache_entry *entry;
if (pmkid) {
for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
entry = entry->hnext) {
if ((spa == NULL ||
os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
return entry;
}
} else {
for (entry = pmksa->pmksa; entry; entry = entry->next) {
if (spa == NULL ||
os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
return entry;
}
}
return NULL;
}
/**
* pmksa_cache_auth_init - Initialize PMKSA cache
* @free_cb: Callback function to be called when a PMKSA cache entry is freed
* @ctx: Context pointer for free_cb function
* Returns: Pointer to PMKSA cache data or %NULL on failure
*/
struct rsn_pmksa_cache *
pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx), void *ctx)
{
struct rsn_pmksa_cache *pmksa;
pmksa = os_zalloc(sizeof(*pmksa));
if (pmksa) {
pmksa->free_cb = free_cb;
pmksa->ctx = ctx;
}
return pmksa;
}
/**
* pmksa_cache_auth_list - Dump text list of entries in PMKSA cache
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @buf: Buffer for the list
* @len: Length of the buffer
* Returns: Number of bytes written to buffer
*
* This function is used to generate a text format representation of the
* current PMKSA cache contents for the ctrl_iface PMKSA command.
*/
int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
{
int i, ret;
char *pos = buf;
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
os_get_reltime(&now);
ret = os_snprintf(pos, buf + len - pos,
"Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
i = 0;
entry = pmksa->pmksa;
while (entry) {
ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
i, MAC2STR(entry->spa));
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
PMKID_LEN);
ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
(int) (entry->expiration - now.sec),
entry->opportunistic);
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
entry = entry->next;
}
return pos - buf;
}

View File

@ -0,0 +1,77 @@
/*
* hostapd - PMKSA cache for IEEE 802.11i RSN
* Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef PMKSA_CACHE_H
#define PMKSA_CACHE_H
#include "ap/ieee802_1x.h"
/**
* struct rsn_pmksa_cache_entry - PMKSA cache entry
*/
struct rsn_pmksa_cache_entry {
struct rsn_pmksa_cache_entry *next, *hnext;
u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */
u8 spa[ETH_ALEN];
u8 *identity;
size_t identity_len;
struct wpabuf *cui;
u8 eap_type_authsrv;
struct vlan_description *vlan_desc;
int opportunistic;
u64 acct_multi_session_id;
};
struct rsn_pmksa_cache;
struct radius_das_attrs;
struct rsn_pmksa_cache *
pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx), void *ctx);
void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
const u8 *spa, const u8 *pmkid);
struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa,
const u8 *pmkid);
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
const u8 *kck, size_t kck_len,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp);
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp);
int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry);
struct rsn_pmksa_cache_entry *
pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
const struct rsn_pmksa_cache_entry *old_entry,
const u8 *aa, const u8 *pmkid);
void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
struct rsn_pmksa_cache_entry *entry,
struct eapol_state_machine *eapol);
void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry);
int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
struct radius_das_attrs *attr);
int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
char *buf, size_t len);
#endif /* PMKSA_CACHE_H */

View File

@ -11,6 +11,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "crypto/crypto.h"
#include "hostapd.h"
#include "ieee802_1x.h"
@ -104,6 +105,15 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
hapd->num_sta--;
#ifdef CONFIG_SAE
sae_clear_data(sta->sae);
os_free(sta->sae);
if (sta->lock) {
os_mutex_unlock(sta->lock);
os_mutex_delete(sta->lock);
sta->lock = NULL;
}
#endif /* CONFIG_SAE */
wpa_auth_sta_deinit(sta->wpa_sm);
#ifdef CONFIG_WPS_REGISTRAR
if (ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta))
@ -162,6 +172,11 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
hapd->sta_list = sta;
hapd->num_sta++;
ap_sta_hash_add(hapd, sta);
#ifdef CONFIG_SAE
sta->sae_commit_processing = false;
sta->remove_pending = false;
sta->lock = os_mutex_create();
#endif /* CONFIG_SAE */
return sta;
}

View File

@ -50,6 +50,7 @@ struct sta_info {
char *identity; /* User-Name from RADIUS */
u16 auth_alg;
#ifdef CONFIG_INTERWORKING
#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
struct gas_dialog_info *gas_dialog;
@ -57,10 +58,17 @@ struct sta_info {
#endif /* CONFIG_INTERWORKING */
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
#ifdef ESP_SUPPLICANT
#ifdef CONFIG_SAE
enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state;
u16 sae_send_confirm;
void *lock;
struct sae_data *sae;
bool sae_commit_processing; /* halt queuing commit while we are
* processing commit for that station */
bool remove_pending; /* Flag to indicate to free station when
* whose mutex is taken by task */
#endif /* CONFIG_SAE */
#endif /* ESP_SUPPLICANT */
};

View File

@ -11,6 +11,9 @@
#include "utils/eloop.h"
#include "utils/state_machine.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "ap/sta_info.h"
#include "ap/ieee802_11.h"
#include "ap/wpa_auth.h"
#include "ap/wpa_auth_i.h"
#include "ap/wpa_auth_ie.h"
@ -20,7 +23,9 @@
#include "ap/ap_config.h"
#include "ap/sta_info.h"
#include "common/wpa_common.h"
#include "ap/pmksa_cache_auth.h"
#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/sha1.h"
@ -32,6 +37,7 @@
#include "esp_private/wifi.h"
#include "esp_wpas_glue.h"
#include "esp_wps_i.h"
#include "esp_hostap.h"
#define STATE_MACHINE_DATA struct wpa_state_machine
#define STATE_MACHINE_DEBUG_PREFIX "WPA"
@ -127,6 +133,20 @@ static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
return NULL;
}
#ifdef CONFIG_SAE
struct sta_info *sta = ap_get_sta(hapd, addr);
if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
if (!sta->sae || prev_psk)
return NULL;
return sta->sae->pmk;
}
if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
wpa_printf(MSG_DEBUG,
"No PSK for STA trying to use SAE with PMKSA caching");
return NULL;
}
#endif /*CONFIG_SAE*/
return (u8*)hostapd_get_psk(hapd->conf, addr, prev_psk);
}
@ -246,6 +266,23 @@ static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
wpa_sm_step(sm);
}
static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
{
if (sm->pmksa == ctx)
sm->pmksa = NULL;
return 0;
}
static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
void *ctx)
{
struct wpa_authenticator *wpa_auth = ctx;
wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
}
static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
@ -351,6 +388,17 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
return NULL;
}
wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
wpa_auth);
if (wpa_auth->pmksa == NULL) {
wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
os_free(wpa_auth->group);
os_free(wpa_auth->wpa_ie);
os_free(wpa_auth);
return NULL;
}
#ifdef CONFIG_IEEE80211R_AP
wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
if (wpa_auth->ft_pmk_cache == NULL) {
@ -446,7 +494,13 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
{
#ifdef ESP_SUPPLICANT
if (esp_wifi_ap_is_sta_sae_reauth_node(sm->addr)) {
wpa_printf( MSG_DEBUG, "deinit old sm=%p\n", sm);
}
#else /* ESP_SUPPLICANT */
wpa_printf( MSG_DEBUG, "deinit sm=%p\n", sm);
#endif /* ESP_SUPPLICANT */
if (sm == NULL)
return;
@ -652,15 +706,21 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *s
sm->pairwise == WPA_CIPHER_GCMP) {
if (wpa_use_aes_cmac(sm) &&
!wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
!wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
return;
}
if (!wpa_use_aes_cmac(sm) &&
!wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
return;
}
}
if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED){
return;
}
}
if (key_info & WPA_KEY_INFO_REQUEST) {
@ -882,6 +942,21 @@ continue_processing:
wpa_sm_step(sm);
}
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, const u8 *pmkid, bool cache_pmksa)
{
if (cache_pmksa)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN);
if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
NULL, 0,
wpa_auth->addr, addr, 0, NULL,
WPA_KEY_MGMT_SAE))
return 0;
return -1;
}
static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
const u8 *gnonce, u8 *gtk, size_t gtk_len)
@ -937,6 +1012,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
if (force_version)
version = force_version;
else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_use_aes_cmac(sm))
version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
else if (sm->pairwise != WPA_CIPHER_TKIP)
@ -944,7 +1021,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
else
version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
wpa_printf( MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d "
"ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d "
@ -959,6 +1036,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
key_data_len = kde_len;
if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
pad_len = key_data_len % 8;
if (pad_len)
@ -1027,6 +1105,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, (key_data_len - 8) / 8, buf,
(u8 *) (key + 1))) {
@ -1423,6 +1502,13 @@ SM_STATE(WPA_PTK, INITPSK)
sm->xxkey_len = PMK_LEN;
#endif /* CONFIG_IEEE80211R_AP */
}
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sm) && sm->pmksa) {
wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->pmk_len = sm->pmksa->pmk_len;
}
#endif
sm->req_replay_counter_used = 0;
}
@ -1448,7 +1534,8 @@ SM_STATE(WPA_PTK, PTKSTART)
* one possible PSK for this STA.
*/
if (sm->wpa == WPA_VERSION_WPA2 &&
wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
(wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
wpa_key_mgmt_sae(sm->wpa_key_mgmt))) {
pmkid = buf;
pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
@ -1493,6 +1580,10 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
struct wpa_ptk PTK;
int ok = 0;
const u8 *pmk = NULL;
u16 key_data_length;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
struct wpa_eapol_ie_parse kde;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = FALSE;
@ -1502,7 +1593,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
* WPA-PSK: iterate through possible PSKs and select the one matching
* the packet */
for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
wpa_printf( MSG_DEBUG, "wpa psk");
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk);
if (pmk == NULL){
@ -1513,6 +1605,11 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
pmk = sm->PMK;
}
if (!pmk && sm->pmksa) {
wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache");
pmk = sm->pmksa->pmk;
}
wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
@ -1535,6 +1632,19 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
return;
}
hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
key = (struct wpa_eapol_key *) (hdr + 1);
key_data_length = WPA_GET_BE16(key->key_data_length);
if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
sizeof(*key))
return;
if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length, &kde) < 0) {
wpa_printf(MSG_DEBUG,
"received EAPOL-Key msg 2/4 with invalid Key Data contents");
return;
}
#ifdef CONFIG_IEEE80211R_AP
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
@ -1553,6 +1663,21 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
}
#endif /* CONFIG_IEEE80211R_AP */
if ((!sm->rsnxe && kde.rsnxe) ||
(sm->rsnxe && !kde.rsnxe) ||
(sm->rsnxe && kde.rsnxe &&
(sm->rsnxe_len != kde.rsnxe_len ||
os_memcmp(sm->rsnxe, kde.rsnxe, sm->rsnxe_len) != 0))) {
wpa_printf(MSG_DEBUG,
"RSNXE from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
wpa_hexdump(MSG_DEBUG, "RSNXE in AssocReq",
sm->rsnxe, sm->rsnxe_len);
wpa_hexdump(MSG_DEBUG, "RSNXE in EAPOL-Key msg 2/4",
kde.rsnxe, kde.rsnxe_len);
wpa_sta_disconnect(sm->wpa_auth, sm->addr);
return;
}
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(resend_eapol_handle, (void*)(sm->index), NULL);
@ -1664,6 +1789,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
/* WPA-only STA, remove RSN IE */
wpa_ie = wpa_ie + wpa_ie[1] + 2;
if (wpa_ie[0] == WLAN_EID_RSNX)
wpa_ie = wpa_ie + wpa_ie[1] + 2;
wpa_ie_len = wpa_ie[1] + 2;
}
if (sm->wpa == WPA_VERSION_WPA2) {
@ -1888,9 +2015,13 @@ SM_STEP(WPA_PTK)
}
break;
case WPA_PTK_INITPSK:
if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL))
if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) {
SM_ENTER(WPA_PTK, PTKSTART);
else {
#ifdef CONFIG_SAE
} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
SM_ENTER(WPA_PTK, PTKSTART);
#endif /* CONFIG_SAE */
} else {
SM_ENTER(WPA_PTK, DISCONNECT);
}
break;
@ -2359,11 +2490,35 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
return 0;
}
bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie, uint8_t wpa_ie_len, bool *pmf_enable)
void wpa_deinit(struct wpa_authenticator *wpa_auth)
{
struct wpa_group *group, *prev;
pmksa_cache_auth_deinit(wpa_auth->pmksa);
if (wpa_auth->wpa_ie != NULL) {
os_free(wpa_auth->wpa_ie);
}
if (wpa_auth->group != NULL) {
group = wpa_auth->group;
while (group) {
prev = group;
group = group->next;
bin_clear_free(prev, sizeof(*prev));
}
}
os_free(wpa_auth);
}
bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie,
uint8_t wpa_ie_len, uint8_t *rsnxe, uint8_t rsnxe_len,
bool *pmf_enable, int subtype)
{
struct hostapd_data *hapd = (struct hostapd_data*)esp_wifi_get_hostap_private_internal();
enum wpa_validate_result status_code = WPA_IE_OK;
int resp = WLAN_STATUS_SUCCESS;
bool omit_rsnxe = false;
if (!sta || !bssid || !wpa_ie){
if (!sta || !bssid || !wpa_ie) {
return false;
}
@ -2377,15 +2532,28 @@ bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie, uint8_t
wpa_printf( MSG_DEBUG, "init wpa sm=%p", sta->wpa_sm);
if (sta->wpa_sm == NULL) {
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto send_resp;
}
status_code = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, wpa_ie, wpa_ie_len, rsnxe, rsnxe_len);
resp = wpa_res_to_status_code(status_code);
send_resp:
if (!rsnxe) {
omit_rsnxe = true;
}
if (esp_send_assoc_resp(hapd, sta, bssid, resp, omit_rsnxe, subtype) != WLAN_STATUS_SUCCESS) {
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
}
if (resp != WLAN_STATUS_SUCCESS) {
return false;
}
if (wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, wpa_ie, wpa_ie_len)) {
return false;
}
//Check whether AP uses Management Frame Protection for this connection
*pmf_enable = wpa_auth_uses_mfp(sta->wpa_sm);
//Check whether AP uses Management Frame Protection for this connection
*pmf_enable = wpa_auth_uses_mfp(sta->wpa_sm);
}
wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
@ -2416,11 +2584,23 @@ bool wpa_ap_remove(void* sta_info)
if (!sta_info || !hapd) {
return false;
}
struct sta_info *sta = NULL;
sta = (struct sta_info*)sta_info;
#ifdef CONFIG_SAE
if (sta->lock) {
if (os_mutex_lock(sta->lock)) {
ap_free_sta(hapd, sta);
} else {
sta->remove_pending = true;
}
return true;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_WPS_REGISTRAR
wpa_printf(MSG_DEBUG, "wps_status=%d", wps_get_status());
if (wps_get_status() == WPS_STATUS_PENDING) {
struct sta_info *sta = (struct sta_info *)sta_info;
u8 *addr = os_malloc(ETH_ALEN);
if (!addr) {
@ -2430,7 +2610,29 @@ bool wpa_ap_remove(void* sta_info)
eloop_register_timeout(0, 10000, ap_free_sta_timeout, hapd, addr);
} else
#endif
ap_free_sta(hapd, sta_info);
ap_free_sta(hapd, sta);
return true;
}
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr)
{
struct rsn_pmksa_cache_entry *pmksa;
if (wpa_auth == NULL || wpa_auth->pmksa == NULL)
return;
pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
if (pmksa) {
wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
MACSTR " based on request", MAC2STR(sta_addr));
pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
}
}
int wpa_auth_uses_sae(struct wpa_state_machine *sm)
{
if (sm == NULL)
return 0;
return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
}

View File

@ -161,6 +161,7 @@ struct wpa_auth_config {
#endif /* CONFIG_IEEE80211R */
int disable_gtk;
int ap_mlme;
enum sae_pwe sae_pwe;
struct rsn_sppamsdu_sup spp_sup;
};
@ -213,16 +214,19 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth);
int wpa_reconfig(struct wpa_authenticator *wpa_auth,
struct wpa_auth_config *conf);
enum {
enum wpa_validate_result{
WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
WPA_INVALID_MDIE, WPA_INVALID_PROTO
WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID,
WPA_DENIED_OTHER_REASON
};
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
enum wpa_validate_result
wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len/*,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len/*,
const u8 *mdie, size_t mdie_len*/);
int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
struct wpa_state_machine *
@ -290,5 +294,10 @@ int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
int wpa_auth_uses_sae(struct wpa_state_machine *sm);
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, const u8 *pmkid,bool cache_pmksa);
void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr);
#endif /* WPA_AUTH_H */

View File

@ -58,6 +58,8 @@ struct wpa_state_machine {
u8 ANonce[WPA_NONCE_LEN];
u8 SNonce[WPA_NONCE_LEN];
u8 PMK[PMK_LEN];
unsigned int pmk_len;
u8 pmkid[PMKID_LEN];
struct wpa_ptk PTK;
Boolean PTK_valid;
Boolean pairwise_set;
@ -94,6 +96,8 @@ struct wpa_state_machine {
u8 *wpa_ie;
size_t wpa_ie_len;
u8 *rsnxe;
size_t rsnxe_len;
enum {
WPA_VERSION_NO_WPA = 0 /* WPA not used */,
@ -102,6 +106,7 @@ struct wpa_state_machine {
} wpa;
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
struct rsn_pmksa_cache_entry *pmksa;
#ifdef CONFIG_IEEE80211R_AP
u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
@ -171,14 +176,16 @@ struct wpa_ft_pmk_cache;
struct wpa_authenticator {
struct wpa_group *group;
u8 dot11RSNAPMKIDUsed[PMKID_LEN];
struct wpa_auth_config conf;
u8 *wpa_ie;
size_t wpa_ie_len;
struct rsn_pmksa_cache *pmksa;
u8 addr[ETH_ALEN];
#ifdef CONFIG_IEEE80211R
struct rsn_pmksa_cache *pmksa;
struct wpa_ft_pmk_cache *ft_pmk_cache;
#endif
@ -187,6 +194,7 @@ struct wpa_authenticator {
int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
const u8 *pmkid);
int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len);
void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int key_info,
const u8 *key_rsc, const u8 *nonce,

View File

@ -14,6 +14,7 @@
#include "ap/wpa_auth_i.h"
#include "common/wpa_common.h"
#include "utils/wpa_debug.h"
#include "ap/pmksa_cache_auth.h"
#ifdef CONFIG_RSN_TESTING
int rsn_testing = 0;
@ -298,6 +299,36 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
}
int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
{
u8 *pos = buf;
u16 capab = 0;
size_t flen;
if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
(conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
conf->sae_pwe == SAE_PWE_BOTH)) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
}
flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
return 0; /* no supported extended RSN capabilities */
if (len < 2 + flen)
return -1;
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
*pos++ = WLAN_EID_RSNX;
*pos++ = flen;
*pos++ = capab & 0x00ff;
capab >>= 8;
if (capab)
*pos++ = capab;
return pos - buf;
}
int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
{
u8 *pos, buf[128];
@ -311,6 +342,11 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
if (res < 0)
return res;
pos += res;
res = wpa_write_rsnxe(&wpa_auth->conf, pos,
buf + sizeof(buf) - pos);
if (res < 0)
return res;
pos += res;
}
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
@ -355,14 +391,18 @@ u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
return pos;
}
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
enum wpa_validate_result
wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len/*,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len/*,
const u8 *mdie, size_t mdie_len*/)
{
struct wpa_ie_data data = {0};
int ciphers, key_mgmt, res, version;
u32 selector;
size_t i;
const u8 *pmkid = NULL;
if (wpa_auth == NULL || sm == NULL)
return WPA_NOT_ENABLED;
@ -569,6 +609,29 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else
sm->wpa = WPA_VERSION_WPA;
sm->pmksa = NULL;
for (i = 0; i < data.num_pmkid; i++) {
wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
&data.pmkid[i * PMKID_LEN], PMKID_LEN);
sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
&data.pmkid[i * PMKID_LEN]);
if (sm->pmksa) {
pmkid = sm->pmksa->pmkid;
break;
}
}
if (sm->pmksa && pmkid) {
wpa_printf(MSG_DEBUG, "PMKID found from PMKSA cache");
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
}
#ifdef CONFIG_SAE
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
!sm->pmksa) {
wpa_printf(MSG_DEBUG, "No PMKSA cache entry found for SAE");
return WPA_INVALID_PMKID;
}
#endif /* CONFIG_SAE */
if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
os_free(sm->wpa_ie);
sm->wpa_ie = os_malloc(wpa_ie_len);
@ -577,6 +640,20 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
sm->wpa_ie_len = wpa_ie_len;
if (rsnxe && rsnxe_len) {
if (!sm->rsnxe || sm->rsnxe_len < rsnxe_len) {
os_free(sm->rsnxe);
sm->rsnxe = os_malloc(rsnxe_len);
if (!sm->rsnxe)
return WPA_ALLOC_FAIL;
}
os_memcpy(sm->rsnxe, rsnxe, rsnxe_len);
sm->rsnxe_len = rsnxe_len;
} else {
os_free(sm->rsnxe);
sm->rsnxe = NULL;
sm->rsnxe_len = 0;
}
return WPA_IE_OK;
}
@ -676,6 +753,11 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
ie->ftie = pos;
ie->ftie_len = pos[1] + 2;
#endif /* CONFIG_IEEE80211R_AP */
} else if (*pos == WLAN_EID_RSNX) {
ie->rsnxe = pos;
ie->rsnxe_len = pos[1] + 2;
wpa_hexdump(MSG_DEBUG, "WPA: RSNXE in EAPOL-Key",
ie->rsnxe, ie->rsnxe_len);
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, end, ie);
if (ret < 0)

View File

@ -29,6 +29,8 @@ struct wpa_eapol_ie_parse {
const u8 *ftie;
size_t ftie_len;
#endif /* CONFIG_IEEE80211R */
const u8 *rsnxe;
size_t rsnxe_len;
};
int wpa_parse_kde_ies(const u8 *buf, size_t len,

View File

@ -371,4 +371,12 @@ enum set_band {
WPA_SETBAND_2G
};
enum sae_pwe {
SAE_PWE_HUNT_AND_PECK = 0,
SAE_PWE_HASH_TO_ELEMENT = 1,
SAE_PWE_BOTH = 2,
SAE_PWE_FORCE_HUNT_AND_PECK = 3,
SAE_PWE_NOT_SET = 4,
};
#endif /* DEFS_H */

View File

@ -72,6 +72,7 @@
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
#define WLAN_AUTH_FT 2
#define WLAN_AUTH_SAE 3
#define WLAN_AUTH_LEAP 128
#define WLAN_AUTH_CHALLENGE_LEN 128

View File

@ -78,10 +78,10 @@ int sae_set_group(struct sae_data *sae, int group)
/* Unsupported group */
wpa_printf(MSG_DEBUG,
"SAE: Group %d not supported by the crypto library", group);
os_free(tmp);
return ESP_FAIL;
}
void sae_clear_temp_data(struct sae_data *sae)
{
struct sae_temporary_data *tmp;
@ -99,6 +99,7 @@ void sae_clear_temp_data(struct sae_data *sae)
crypto_ec_point_deinit(tmp->pwe_ecc, 1);
crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
wpabuf_free(tmp->anti_clogging_token);
wpabuf_free(tmp->own_rejected_groups);
wpabuf_free(tmp->peer_rejected_groups);
os_free(tmp->pw_id);
@ -116,54 +117,6 @@ void sae_clear_data(struct sae_data *sae)
os_memset(sae, 0, sizeof(*sae));
}
static void buf_shift_right(u8 *buf, size_t len, size_t bits)
{
size_t i;
for (i = len - 1; i > 0; i--)
buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
buf[0] >>= bits;
}
static struct crypto_bignum * sae_get_rand(struct sae_data *sae)
{
u8 val[SAE_MAX_PRIME_LEN];
int iter = 0;
struct crypto_bignum *bn = NULL;
int order_len_bits = crypto_bignum_bits(sae->tmp->order);
size_t order_len = (order_len_bits + 7) / 8;
if (order_len > sizeof(val))
return NULL;
for (;;) {
if (iter++ > 100 || random_get_bytes(val, order_len) < 0)
return NULL;
if (order_len_bits % 8)
buf_shift_right(val, order_len, 8 - order_len_bits % 8);
bn = crypto_bignum_init_set(val, order_len);
if (bn == NULL)
return NULL;
if (crypto_bignum_is_zero(bn) ||
crypto_bignum_is_one(bn) ||
crypto_bignum_cmp(bn, sae->tmp->order) >= 0) {
crypto_bignum_deinit(bn, 0);
continue;
}
break;
}
forced_memzero(val, order_len);
return bn;
}
static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae)
{
crypto_bignum_deinit(sae->tmp->sae_rand, 1);
sae->tmp->sae_rand = sae_get_rand(sae);
if (sae->tmp->sae_rand == NULL)
return NULL;
return sae_get_rand(sae);
}
static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
{
@ -185,6 +138,8 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
struct crypto_bignum *y_sqr, *x_cand;
int res;
size_t bits;
int cmp_prime;
unsigned int in_range;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
@ -198,8 +153,13 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
pwd_value, sae->tmp->prime_len);
if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
return ESP_OK;
cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len);
/* Create a const_time mask for selection based on prf result
* being smaller than prime. */
in_range = const_time_fill_msb((unsigned int) cmp_prime);
/* The algorithm description would skip the next steps if
* cmp_prime >= 0 (return 0 here), but go through them regardless to
* minimize externally observable differences in behavior. */
x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
if (!x_cand)
@ -213,9 +173,13 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
y_sqr);
crypto_bignum_deinit(y_sqr, 1);
return res;
if (res < 0) {
return res;
}
return const_time_select_int(in_range, res, 0);
}
/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
* pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
@ -275,8 +239,8 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
b = crypto_bignum_init_set(exp, sizeof(exp));
if (b == NULL ||
crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
crypto_bignum_div(b, sae->tmp->order, b) < 0)
goto fail;
crypto_bignum_div(b, sae->tmp->order, b) < 0)
goto fail;
}
@ -299,21 +263,23 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
is_val = crypto_bignum_is_one(pwe);
res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
fail:
crypto_bignum_deinit(a, 1);
crypto_bignum_deinit(b, 1);
return res;
}
static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
size_t password_len)
{
u8 counter, k = 40;
u8 counter, k;
u8 addrs[2 * ETH_ALEN];
const u8 *addr[3];
size_t len[3];
u8 *dummy_password, *tmp_password;
const u8 *addr[2];
size_t len[2];
u8 *stub_password, *tmp_password;
int pwd_seed_odd = 0;
u8 prime[SAE_MAX_ECC_PRIME_LEN];
size_t prime_len;
@ -330,10 +296,10 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
os_memset(x_bin, 0, sizeof(x_bin));
dummy_password = os_malloc(password_len);
stub_password = os_malloc(password_len);
tmp_password = os_malloc(password_len);
if (!dummy_password || !tmp_password ||
random_get_bytes(dummy_password, password_len) < 0)
if (!stub_password || !tmp_password ||
random_get_bytes(stub_password, password_len) < 0)
goto fail;
prime_len = sae->tmp->prime_len;
@ -366,6 +332,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
addr[1] = &counter;
len[1] = sizeof(counter);
k = dragonfly_min_pwe_loop_iter(sae->group);
/*
* Continue for at least k iterations to protect against side-channel
* attacks that attempt to determine the number of iterations required
@ -381,14 +349,14 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
}
wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
const_time_select_bin(found, dummy_password, password,
const_time_select_bin(found, stub_password, password,
password_len, tmp_password);
if (hmac_sha256_vector(addrs, sizeof(addrs), 2,
addr, len, pwd_seed) < 0)
break;
res = sae_test_pwd_seed_ecc(sae, pwd_seed,
prime, qr_bin, qnr_bin, x_cand_bin);
prime, qr_bin, qnr_bin, x_cand_bin);
const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
x_bin);
pwd_seed_odd = const_time_select_u8(
@ -410,16 +378,19 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
res, found);
}
if (!found) {
wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
res = -1;
goto fail;
}
x = crypto_bignum_init_set(x_bin, prime_len);
if (!x) {
res = -1;
goto fail;
}
/* y = sqrt(x^3 + ax + b) mod p
* if LSB(save) == LSB(y): PWE = (x, y)
* else: PWE = (x, p - y)
@ -456,7 +427,7 @@ fail:
crypto_bignum_deinit(qr, 0);
crypto_bignum_deinit(qnr, 0);
crypto_bignum_deinit(y, 1);
os_free(dummy_password);
os_free(stub_password);
bin_clear_free(tmp_password, password_len);
crypto_bignum_deinit(x, 1);
os_memset(x_bin, 0, sizeof(x_bin));
@ -464,11 +435,6 @@ fail:
return res;
}
static int sae_modp_group_require_masking(int group)
{
/* Groups for which pwd-value is likely to be >= p frequently */
return group == 22 || group == 23 || group == 24;
}
static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
@ -476,8 +442,8 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
{
u8 counter, k, sel_counter = 0;
u8 addrs[2 * ETH_ALEN];
const u8 *addr[3];
size_t len[3];
const u8 *addr[2];
size_t len[2];
u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
* mask */
u8 mask;
@ -509,7 +475,7 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
addr[1] = &counter;
len[1] = sizeof(counter);
k = sae_modp_group_require_masking(sae->group) ? 40 : 1;
k = dragonfly_min_pwe_loop_iter(sae->group);
for (counter = 1; counter <= k || !found; counter++) {
u8 pwd_seed[SHA256_MAC_LEN];
@ -542,8 +508,10 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
mask = const_time_eq_u8(res, 1);
found = const_time_select_u8(found, found, mask);
}
if (!found)
goto fail;
wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
fail:
@ -552,6 +520,7 @@ fail:
return sae->tmp->pwe_ffc ? 0 : -1;
}
static int hkdf_extract(size_t hash_len, const u8 *salt, size_t salt_len,
size_t num_elem, const u8 *addr[], const size_t len[],
u8 *prk)
@ -562,6 +531,7 @@ static int hkdf_extract(size_t hash_len, const u8 *salt, size_t salt_len,
return -1;
}
static int hkdf_expand(size_t hash_len, const u8 *prk, size_t prk_len,
const char *info, u8 *okm, size_t okm_len)
{
@ -574,6 +544,7 @@ static int hkdf_expand(size_t hash_len, const u8 *prk, size_t prk_len,
return -1;
}
static int sswu_curve_param(int group, int *z)
{
switch (group) {
@ -603,8 +574,8 @@ static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
const struct crypto_bignum *u)
{
int z_int;
const struct crypto_bignum *a, *b, *prime;
struct crypto_bignum *u2, *t1, *t2, *z, *t, *zero, *one, *two, *three,
const struct crypto_bignum *b, *prime;
struct crypto_bignum *a, *u2, *t1, *t2, *z, *t, *zero, *one, *two, *three,
*x1a, *x1b, *y = NULL;
struct crypto_bignum *x1 = NULL, *x2, *gx1, *gx2, *v = NULL;
struct crypto_bignum *tmp = NULL;
@ -621,6 +592,7 @@ static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
prime = crypto_ec_get_prime(ec);
prime_len = crypto_ec_prime_len(ec);
/* Value of 'a' defined for curve secp256r1 in 'y^2 = x^3 + ax + b' */
uint8_t buf[32] = {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc};
a = crypto_bignum_init_set(buf, 32);
b = crypto_ec_get_b(ec);
@ -657,8 +629,7 @@ static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
if (crypto_bignum_sqrmod(u, prime, u2) < 0 ||
crypto_bignum_mulmod(z, u2, prime, t1) < 0 ||
crypto_bignum_sqrmod(t1, prime, t2) < 0 ||
crypto_bignum_add(t1, t2, tmp) < 0 ||
crypto_bignum_mod(tmp, prime, t1) < 0)
crypto_bignum_addmod(t1, t2, prime, t1) < 0)
goto fail;
debug_print_bignum("SSWU: m", t1, prime_len);
@ -684,8 +655,7 @@ static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
if (crypto_bignum_sub(prime, b, t1) < 0 ||
crypto_bignum_inverse(a, prime, t2) < 0 ||
crypto_bignum_mulmod(t1, t2, prime, t1) < 0 ||
crypto_bignum_add(one, t, tmp) < 0 ||
crypto_bignum_mod(tmp, prime, t2) < 0 ||
crypto_bignum_addmod(one, t, prime, t2) < 0 ||
crypto_bignum_mulmod(t1, t2, prime, x1b) < 0)
goto fail;
debug_print_bignum("SSWU: x1b = (-b/a) * (1 + t)", x1b, prime_len);
@ -703,10 +673,8 @@ static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
/* gx1 = x1^3 + a * x1 + b */
if (crypto_bignum_exptmod(x1, three, prime, t1) < 0 ||
crypto_bignum_mulmod(a, x1, prime, t2) < 0 ||
crypto_bignum_add(t1, t2, tmp) < 0 ||
crypto_bignum_mod(tmp, prime, t1) < 0 ||
crypto_bignum_add(t1, b, tmp) < 0 ||
crypto_bignum_mod(tmp, prime, gx1) < 0)
crypto_bignum_addmod(t1, t2, prime, t1) < 0 ||
crypto_bignum_addmod(t1, b, prime, gx1) < 0)
goto fail;
debug_print_bignum("SSWU: gx1 = x1^3 + a * x1 + b", gx1, prime_len);
@ -719,10 +687,8 @@ static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
/* gx2 = x2^3 + a * x2 + b */
if (crypto_bignum_exptmod(x2, three, prime, t1) < 0 ||
crypto_bignum_mulmod(a, x2, prime, t2) < 0 ||
crypto_bignum_add(t1, t2, tmp) < 0 ||
crypto_bignum_mod(tmp, prime, t1) < 0 ||
crypto_bignum_add(t1, b, tmp) < 0 ||
crypto_bignum_mod(tmp, prime, gx2) < 0)
crypto_bignum_addmod(t1, t2, prime, t1) < 0 ||
crypto_bignum_addmod(t1, b, prime, gx2) < 0)
goto fail;
debug_print_bignum("SSWU: gx2 = x2^3 + a * x2 + b", gx2, prime_len);
@ -753,19 +719,9 @@ static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
const_time_select_bin(is_qr, bin1, bin2, prime_len, x_y);
wpa_hexdump_key(MSG_DEBUG, "SSWU: x = CSEL(l, x1, x2)", x_y, prime_len);
/* y = sqrt(v)
* For prime p such that p = 3 mod 4 --> v^((p+1)/4) */
if (crypto_bignum_to_bin(prime, bin1, sizeof(bin1), prime_len) < 0)
goto fail;
if ((bin1[prime_len - 1] & 0x03) != 3) {
wpa_printf(MSG_DEBUG, "SSWU: prime does not have p = 3 mod 4");
goto fail;
}
/* y = sqrt(v) */
y = crypto_bignum_init();
if (!y ||
crypto_bignum_add(prime, one, t1) < 0 ||
crypto_bignum_rshift(t1, 2, t1) < 0 ||
crypto_bignum_exptmod(v, t1, prime, y) < 0)
if (!y || dragonfly_sqrt(ec, v, y) < 0)
goto fail;
debug_print_bignum("SSWU: y = sqrt(v)", y, prime_len);
@ -791,6 +747,7 @@ static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
p = crypto_ec_point_from_bin(ec, x_y);
fail:
crypto_bignum_deinit(a, 0);
crypto_bignum_deinit(tmp, 0);
crypto_bignum_deinit(u2, 1);
crypto_bignum_deinit(t1, 1);
@ -816,6 +773,7 @@ fail:
return p;
}
static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
const char *identifier, u8 *pwd_seed)
@ -855,6 +813,7 @@ size_t sae_ecc_prime_len_2_hash_len(size_t prime_len)
return 64;
}
static struct crypto_ec_point *
sae_derive_pt_ecc(struct crypto_ec *ec, int group,
const u8 *ssid, size_t ssid_len,
@ -954,6 +913,7 @@ size_t sae_ffc_prime_len_2_hash_len(size_t prime_len)
return 64;
}
static struct crypto_bignum *
sae_derive_pt_ffc(const struct dh_group *dh, int group,
const u8 *ssid, size_t ssid_len,
@ -1031,6 +991,7 @@ fail:
return pt;
}
static struct sae_pt *
sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
@ -1084,6 +1045,7 @@ fail:
return NULL;
}
struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
const u8 *password, size_t password_len,
const char *identifier)
@ -1110,6 +1072,7 @@ struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
return pt;
}
static void sae_max_min_addr(const u8 *addr[], size_t len[],
const u8 *addr1, const u8 *addr2)
{
@ -1124,6 +1087,7 @@ static void sae_max_min_addr(const u8 *addr[], size_t len[],
}
}
struct crypto_ec_point *
sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2)
@ -1188,6 +1152,7 @@ fail:
return pwe;
}
struct crypto_bignum *
sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2)
@ -1247,6 +1212,7 @@ fail:
return pwe;
}
void sae_deinit_pt(struct sae_pt *pt)
{
struct sae_pt *prev;
@ -1261,6 +1227,7 @@ void sae_deinit_pt(struct sae_pt *pt)
}
}
static int sae_derive_commit_element_ecc(struct sae_data *sae,
struct crypto_bignum *mask)
{
@ -1283,6 +1250,7 @@ static int sae_derive_commit_element_ecc(struct sae_data *sae,
return ESP_OK;
}
static int sae_derive_commit_element_ffc(struct sae_data *sae,
struct crypto_bignum *mask)
{
@ -1305,57 +1273,30 @@ static int sae_derive_commit_element_ffc(struct sae_data *sae,
return ESP_OK;
}
static int sae_derive_commit(struct sae_data *sae)
{
struct crypto_bignum *mask = NULL;
int ret = -1;
unsigned int counter = 0;
int ret;
do {
counter++;
if (counter > 100) {
/*
* This cannot really happen in practice if the random
* number generator is working. Anyway, to avoid even a
* theoretical infinite loop, break out after 100
* attemps.
*/
crypto_bignum_deinit(mask, 1);
return ESP_FAIL;
}
if (mask) {
crypto_bignum_deinit(mask, 1);
}
mask = sae_get_rand_and_mask(sae);
if (mask == NULL) {
wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
return ESP_FAIL;
}
/* commit-scalar = (rand + mask) modulo r */
if (!sae->tmp->own_commit_scalar) {
sae->tmp->own_commit_scalar = crypto_bignum_init();
if (!sae->tmp->own_commit_scalar)
goto fail;
}
crypto_bignum_add(sae->tmp->sae_rand, mask,
sae->tmp->own_commit_scalar);
crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
sae->tmp->own_commit_scalar);
} while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) ||
crypto_bignum_is_one(sae->tmp->own_commit_scalar));
if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) ||
(sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0))
goto fail;
ret = 0;
fail:
mask = crypto_bignum_init();
if (!sae->tmp->sae_rand)
sae->tmp->sae_rand = crypto_bignum_init();
if (!sae->tmp->own_commit_scalar)
sae->tmp->own_commit_scalar = crypto_bignum_init();
ret = !mask || !sae->tmp->sae_rand || !sae->tmp->own_commit_scalar ||
dragonfly_generate_scalar(sae->tmp->order, sae->tmp->sae_rand,
mask,
sae->tmp->own_commit_scalar) < 0 ||
(sae->tmp->ec &&
sae_derive_commit_element_ecc(sae, mask) < 0) ||
(sae->tmp->dh &&
sae_derive_commit_element_ffc(sae, mask) < 0);
crypto_bignum_deinit(mask, 1);
return ret;
return ret ? -1 : 0;
}
int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
const u8 *password, size_t password_len,
struct sae_data *sae)
@ -1368,10 +1309,12 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
return ESP_FAIL;
sae->h2e = 0;
sae->pk = 0;
return sae_derive_commit(sae);
}
int sae_prepare_commit_pt(struct sae_data *sae, struct sae_pt *pt,
int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2,
int *rejected_groups, const struct sae_pk *pk)
{
@ -1430,6 +1373,7 @@ int sae_prepare_commit_pt(struct sae_data *sae, struct sae_pt *pt,
return sae_derive_commit(sae);
}
static int sae_derive_k_ecc(struct sae_data *sae, u8 *k)
{
struct crypto_ec_point *K;
@ -1465,6 +1409,7 @@ fail:
return ret;
}
static int sae_derive_k_ffc(struct sae_data *sae, u8 *k)
{
struct crypto_bignum *K;
@ -1502,6 +1447,7 @@ fail:
return ret;
}
static int sae_kdf_hash(size_t hash_len, const u8 *k, const char *label,
const u8 *context, size_t context_len,
u8 *out, size_t out_len)
@ -1512,6 +1458,7 @@ static int sae_kdf_hash(size_t hash_len, const u8 *k, const char *label,
return -1;
}
static int sae_derive_keys(struct sae_data *sae, const u8 *k)
{
u8 zero[SAE_MAX_HASH_LEN], val[SAE_MAX_PRIME_LEN];
@ -1594,7 +1541,10 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k)
* (commit-scalar + peer-commit-scalar) mod r part as a bit string by
* zero padding it from left to the length of the order (in full
* octets). */
crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len);
if (crypto_bignum_to_bin(tmp, val, sizeof(val),
sae->tmp->order_len) < 0) {
goto fail;
}
wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
#ifdef CONFIG_SAE_PK
@ -1641,6 +1591,7 @@ fail:
return ret;
}
int sae_process_commit(struct sae_data *sae)
{
u8 k[SAE_MAX_PRIME_LEN] = {0};
@ -1652,6 +1603,7 @@ int sae_process_commit(struct sae_data *sae)
return ESP_OK;
}
int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
const struct wpabuf *token, const char *identifier)
{
@ -1728,6 +1680,7 @@ int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
return ESP_OK;
}
u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
{
if (allowed_groups) {
@ -1769,15 +1722,14 @@ u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
return WLAN_STATUS_SUCCESS;
}
static int sae_is_password_id_elem(const u8 *pos, const u8 *end)
{
int ret = end - pos >= 3 &&
return end - pos >= 3 &&
pos[0] == WLAN_EID_EXTENSION &&
pos[1] >= 1 &&
end - pos - 2 >= pos[1] &&
pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER;
return ret;
}
@ -1899,6 +1851,7 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
return WLAN_STATUS_SUCCESS;
}
static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
@ -1931,8 +1884,10 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos,
crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
sae->tmp->peer_commit_element_ecc =
crypto_ec_point_from_bin(sae->tmp->ec, *pos);
if (sae->tmp->peer_commit_element_ecc == NULL)
if (!sae->tmp->peer_commit_element_ecc) {
wpa_printf(MSG_DEBUG, "SAE: Peer element is not a valid point");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
sae->tmp->peer_commit_element_ecc)) {
@ -1945,6 +1900,7 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos,
return WLAN_STATUS_SUCCESS;
}
static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
@ -1994,6 +1950,7 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos,
return WLAN_STATUS_SUCCESS;
}
static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos,
const u8 *end)
{
@ -2002,6 +1959,7 @@ static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos,
return sae_parse_commit_element_ecc(sae, pos, end);
}
static int sae_parse_password_identifier(struct sae_data *sae,
const u8 **pos, const u8 *end)
{
@ -2190,6 +2148,7 @@ static int sae_cn_confirm(struct sae_data *sae, const u8 *sc,
5, addr, len, confirm);
}
static int sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
const struct crypto_bignum *scalar1,
const struct crypto_ec_point *element1,
@ -2216,6 +2175,7 @@ static int sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
return ESP_OK;
}
static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
const struct crypto_bignum *scalar1,
const struct crypto_bignum *element1,
@ -2243,6 +2203,7 @@ static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
return ESP_OK;
}
int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
{
const u8 *sc;
@ -2251,10 +2212,10 @@ int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
return ESP_FAIL;
/* Send-Confirm */
sc = wpabuf_put(buf, 0);
wpabuf_put_le16(buf, sae->send_confirm);
if (sae->send_confirm < 0xffff)
sae->send_confirm++;
sc = wpabuf_put(buf, 0);
wpabuf_put_le16(buf, sae->send_confirm);
if (sae->tmp->ec) {
if (sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
@ -2278,11 +2239,15 @@ int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
return ESP_OK;
}
int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
{
u8 verifier[SAE_MAX_HASH_LEN];
size_t hash_len= SAE_KCK_LEN;
if (!sae->tmp)
return ESP_FAIL;
if (len < 2 + hash_len) {
wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
return ESP_FAIL;
@ -2322,7 +2287,7 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
}
}
if (os_memcmp(verifier, data + 2, hash_len) != 0) {
if (os_memcmp_const(verifier, data + 2, hash_len) != 0) {
wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
data + 2, hash_len);

View File

@ -17,6 +17,7 @@
#define SAE_KCK_LEN 32
#define SAE_PMK_LEN 32
#define SAE_PMK_LEN_MAX 64
#define SAE_PMKID_LEN 16
#define SAE_KEYSEED_KEY_LEN 32
#define SAE_MAX_PRIME_LEN 512
@ -57,6 +58,7 @@ struct sae_temporary_data {
const struct crypto_bignum *order;
struct crypto_bignum *prime_buf;
struct crypto_bignum *order_buf;
struct wpabuf *anti_clogging_token;
char *pw_id;
int order_len;
struct wpabuf *own_rejected_groups;
@ -125,7 +127,7 @@ void sae_clear_data(struct sae_data *sae);
int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
const u8 *password, size_t password_len,
struct sae_data *sae);
int sae_prepare_commit_pt(struct sae_data *sae, struct sae_pt *pt,
int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
const u8 *addr1, const u8 *addr2,
int *rejected_groups, const struct sae_pk *pk);
int sae_process_commit(struct sae_data *sae);

View File

@ -369,7 +369,7 @@ int wpa_parse_wpa_ie_rsnxe(const u8 *rsnxe_ie, size_t rsnxe_ie_len,
struct wpa_ie_data *data)
{
uint8_t rsnxe_capa = 0;
uint8_t sae_pwe = esp_wifi_sta_get_config_sae_pwe_h2e_internal();
uint8_t sae_pwe = esp_wifi_get_config_sae_pwe_h2e_internal(WIFI_IF_STA);
memset(data, 0, sizeof(*data));
if (rsnxe_ie_len < 1) {
@ -828,6 +828,28 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
/**
* wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used
* @akmp: WPA_KEY_MGMT_* used in key derivation
* Returns: 1 if AKM-defined Key Descriptor Version is used; 0 otherwise
*/
int wpa_use_akm_defined(int akmp){
int ret = 0;
if(wpa_key_mgmt_sae(akmp))
ret = 1;
return ret;
}
int wpa_use_aes_key_wrap(int akmp)
{
return akmp == WPA_KEY_MGMT_OSEN ||
wpa_key_mgmt_ft(akmp) ||
wpa_key_mgmt_sha256(akmp) ||
wpa_key_mgmt_sae(akmp) ||
wpa_key_mgmt_suite_b(akmp);
}
/**
* wpa_eapol_key_mic - Calculate EAPOL-Key MIC
* @key: EAPOL-Key Key Confirmation Key (KCK)

View File

@ -23,6 +23,7 @@
#define WPA_KEY_RSC_LEN 8
#define WPA_GMK_LEN 32
#define WPA_GTK_MAX_LEN 32
#define WPA_MAX_RSNXE_LEN 4
#define WPA_SELECTOR_LEN 4
#define WPA_VERSION 1
@ -61,6 +62,7 @@
#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
#define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
#define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
@ -446,5 +448,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
int rsn_cipher_put_suites(u8 *pos, int ciphers);
unsigned int wpa_mic_len(int akmp);
int wpa_use_akm_defined(int akmp);
int wpa_use_aes_key_wrap(int akmp);
#endif /* WPA_COMMON_H */

View File

@ -2690,7 +2690,7 @@ int wpa_sm_set_ap_rsnxe(const u8 *ie, size_t len)
sm->ap_rsnxe_len = len;
}
sm->sae_pwe = esp_wifi_sta_get_config_sae_pwe_h2e_internal();
sm->sae_pwe = esp_wifi_get_config_sae_pwe_h2e_internal(WIFI_IF_STA);
#ifdef CONFIG_SAE_PK
const u8 *pw = (const u8 *)esp_wifi_sta_get_prof_password_internal();
if (esp_wifi_sta_get_config_sae_pk_internal() != WPA3_SAE_PK_MODE_DISABLED &&

View File

@ -53,7 +53,7 @@ struct wpa_sm {
void *network_ctx;
int rsn_enabled; /* Whether RSN is enabled in configuration */
int sae_pwe; /* SAE PWE generation options */
enum sae_pwe sae_pwe; /* SAE PWE generation options */
bool sae_pk; /* whether SAE-PK is used */

View File

@ -302,7 +302,8 @@ int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len)
size_t flen;
if (wpa_key_mgmt_sae(sm->key_mgmt) &&
(sm->sae_pwe == 1 || sm->sae_pwe == 2 || sm->sae_pk)) {
(sm->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
sm->sae_pwe == SAE_PWE_BOTH)) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (sm->sae_pk) {

View File

@ -116,6 +116,15 @@ void inc_byte_array(u8 *counter, size_t len)
}
void buf_shift_right(u8 *buf, size_t len, size_t bits)
{
size_t i;
for (i = len - 1; i > 0; i--)
buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
buf[0] >>= bits;
}
void wpa_get_ntp_timestamp(u8 *buf)
{
struct os_time now;
@ -529,6 +538,12 @@ int os_reltime_expired(struct os_time *now,
}
int os_reltime_initialized(struct os_reltime *t)
{
return t->sec != 0 || t->usec != 0;
}
u8 rssi_to_rcpi(int rssi)
{
if (!rssi)

View File

@ -379,6 +379,7 @@ int hwaddr_aton2(const char *txt, u8 *addr);
int hex2byte(const char *hex);
int hexstr2bin(const char *hex, u8 *buf, size_t len);
void inc_byte_array(u8 *counter, size_t len);
void buf_shift_right(u8 *buf, size_t len, size_t bits);
void wpa_get_ntp_timestamp(u8 *buf);
int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
@ -392,6 +393,7 @@ u8 rssi_to_rcpi(int rssi);
int os_reltime_expired(struct os_time *now,
struct os_time *ts,
os_time_t timeout_secs);
int os_reltime_initialized(struct os_reltime *t);
#ifdef CONFIG_NATIVE_WINDOWS
void wpa_unicode2ascii_inplace(TCHAR *str);

View File

@ -58,6 +58,170 @@ TEST_CASE("Test SAE functionality with ECC group", "[wpa3_sae]")
}
ESP_LOGI("SAE Test", "=========== Complete ============");
ESP_LOGI("SAE Test", "### Verification of Hunt and Peck, Hash to Element crypto operation using defined vectors ###");
{
struct sae_data sae;
/* IEEE Std 802.11-2020, Annex J.10 */
const u8 addr1[ETH_ALEN] = { 0x4d, 0x3f, 0x2f, 0xff, 0xe3, 0x87 };
const u8 addr2[ETH_ALEN] = { 0xa5, 0xd8, 0xaa, 0x95, 0x8e, 0x3c };
const char *ssid = "byteme";
const char *pw = "mekmitasdigoat";
const char *pwid = "psk4internet";
const u8 local_rand[] = {
0x99, 0x24, 0x65, 0xfd, 0x3d, 0xaa, 0x3c, 0x60,
0xaa, 0x65, 0x65, 0xb7, 0xf6, 0x2a, 0x2a, 0x7f,
0x2e, 0x12, 0xdd, 0x12, 0xf1, 0x98, 0xfa, 0xf4,
0xfb, 0xed, 0x89, 0xd7, 0xff, 0x1a, 0xce, 0x94
};
const u8 local_mask[] = {
0x95, 0x07, 0xa9, 0x0f, 0x77, 0x7a, 0x04, 0x4d,
0x6a, 0x08, 0x30, 0xb9, 0x1e, 0xa3, 0xd5, 0xdd,
0x70, 0xbe, 0xce, 0x44, 0xe1, 0xac, 0xff, 0xb8,
0x69, 0x83, 0xb5, 0xe1, 0xbf, 0x9f, 0xb3, 0x22
};
const u8 local_commit[] = {
0x13, 0x00, 0x2e, 0x2c, 0x0f, 0x0d, 0xb5, 0x24,
0x40, 0xad, 0x14, 0x6d, 0x96, 0x71, 0x14, 0xce,
0x00, 0x5c, 0xe1, 0xea, 0xb0, 0xaa, 0x2c, 0x2e,
0x5c, 0x28, 0x71, 0xb7, 0x74, 0xf6, 0xc2, 0x57,
0x5c, 0x65, 0xd5, 0xad, 0x9e, 0x00, 0x82, 0x97,
0x07, 0xaa, 0x36, 0xba, 0x8b, 0x85, 0x97, 0x38,
0xfc, 0x96, 0x1d, 0x08, 0x24, 0x35, 0x05, 0xf4,
0x7c, 0x03, 0x53, 0x76, 0xd7, 0xac, 0x4b, 0xc8,
0xd7, 0xb9, 0x50, 0x83, 0xbf, 0x43, 0x82, 0x7d,
0x0f, 0xc3, 0x1e, 0xd7, 0x78, 0xdd, 0x36, 0x71,
0xfd, 0x21, 0xa4, 0x6d, 0x10, 0x91, 0xd6, 0x4b,
0x6f, 0x9a, 0x1e, 0x12, 0x72, 0x62, 0x13, 0x25,
0xdb, 0xe1
};
const u8 peer_commit[] = {
0x13, 0x00, 0x59, 0x1b, 0x96, 0xf3, 0x39, 0x7f,
0xb9, 0x45, 0x10, 0x08, 0x48, 0xe7, 0xb5, 0x50,
0x54, 0x3b, 0x67, 0x20, 0xd8, 0x83, 0x37, 0xee,
0x93, 0xfc, 0x49, 0xfd, 0x6d, 0xf7, 0xe0, 0x8b,
0x52, 0x23, 0xe7, 0x1b, 0x9b, 0xb0, 0x48, 0xd3,
0x87, 0x3f, 0x20, 0x55, 0x69, 0x53, 0xa9, 0x6c,
0x91, 0x53, 0x6f, 0xd8, 0xee, 0x6c, 0xa9, 0xb4,
0xa6, 0x8a, 0x14, 0x8b, 0x05, 0x6a, 0x90, 0x9b,
0xe0, 0x3e, 0x83, 0xae, 0x20, 0x8f, 0x60, 0xf8,
0xef, 0x55, 0x37, 0x85, 0x80, 0x74, 0xdb, 0x06,
0x68, 0x70, 0x32, 0x39, 0x98, 0x62, 0x99, 0x9b,
0x51, 0x1e, 0x0a, 0x15, 0x52, 0xa5, 0xfe, 0xa3,
0x17, 0xc2
};
const u8 kck[] = {
0x1e, 0x73, 0x3f, 0x6d, 0x9b, 0xd5, 0x32, 0x56,
0x28, 0x73, 0x04, 0x33, 0x88, 0x31, 0xb0, 0x9a,
0x39, 0x40, 0x6d, 0x12, 0x10, 0x17, 0x07, 0x3a,
0x5c, 0x30, 0xdb, 0x36, 0xf3, 0x6c, 0xb8, 0x1a
};
const u8 pmk[] = {
0x4e, 0x4d, 0xfa, 0xb1, 0xa2, 0xdd, 0x8a, 0xc1,
0xa9, 0x17, 0x90, 0xf9, 0x53, 0xfa, 0xaa, 0x45,
0x2a, 0xe5, 0xc6, 0x87, 0x3a, 0xb7, 0x5b, 0x63,
0x60, 0x5b, 0xa6, 0x63, 0xf8, 0xa7, 0xfe, 0x59
};
const u8 pmkid[] = {
0x87, 0x47, 0xa6, 0x00, 0xee, 0xa3, 0xf9, 0xf2,
0x24, 0x75, 0xdf, 0x58, 0xca, 0x1e, 0x54, 0x98
};
struct wpabuf *buf = NULL;
struct crypto_bignum *mask = NULL;
const u8 pwe_19_x[32] = {
0xc9, 0x30, 0x49, 0xb9, 0xe6, 0x40, 0x00, 0xf8,
0x48, 0x20, 0x16, 0x49, 0xe9, 0x99, 0xf2, 0xb5,
0xc2, 0x2d, 0xea, 0x69, 0xb5, 0x63, 0x2c, 0x9d,
0xf4, 0xd6, 0x33, 0xb8, 0xaa, 0x1f, 0x6c, 0x1e
};
const u8 pwe_19_y[32] = {
0x73, 0x63, 0x4e, 0x94, 0xb5, 0x3d, 0x82, 0xe7,
0x38, 0x3a, 0x8d, 0x25, 0x81, 0x99, 0xd9, 0xdc,
0x1a, 0x5e, 0xe8, 0x26, 0x9d, 0x06, 0x03, 0x82,
0xcc, 0xbf, 0x33, 0xe6, 0x14, 0xff, 0x59, 0xa0
};
int pt_groups[] = { 19 };
struct sae_pt *pt_info, *pt;
const u8 addr1b[ETH_ALEN] = { 0x00, 0x09, 0x5b, 0x66, 0xec, 0x1e };
const u8 addr2b[ETH_ALEN] = { 0x00, 0x0b, 0x6b, 0xd9, 0x02, 0x46 };
os_memset(&sae, 0, sizeof(sae));
buf = wpabuf_alloc2(1000);
TEST_ASSERT(buf != NULL);
TEST_ASSERT(sae_set_group(&sae, 19) == 0);
TEST_ASSERT(sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw), &sae) == 0);
/* Override local values based on SAE test vector */
crypto_bignum_deinit(sae.tmp->sae_rand, 1);
sae.tmp->sae_rand = crypto_bignum_init_set(local_rand, sizeof(local_rand));
mask = crypto_bignum_init_set(local_mask, sizeof(local_mask));
TEST_ASSERT(sae.tmp->sae_rand != NULL);
TEST_ASSERT(mask != NULL);
TEST_ASSERT(crypto_bignum_add(sae.tmp->sae_rand, mask, sae.tmp->own_commit_scalar) == 0)
TEST_ASSERT(crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order, sae.tmp->own_commit_scalar) == 0);
TEST_ASSERT(crypto_ec_point_mul(sae.tmp->ec, sae.tmp->pwe_ecc, mask, sae.tmp->own_commit_element_ecc) == 0);
TEST_ASSERT(crypto_ec_point_invert(sae.tmp->ec, sae.tmp->own_commit_element_ecc) == 0);
TEST_ASSERT(sae_write_commit(&sae, buf, NULL, NULL) == 0);
/* Check that output matches the test vector */
TEST_ASSERT(wpabuf_len(buf) == sizeof(local_commit));
ESP_LOG_BUFFER_HEXDUMP("SAE: Derived SAE Commit ", wpabuf_mhead_u8(buf), wpabuf_len(buf), ESP_LOG_INFO);
ESP_LOG_BUFFER_HEXDUMP("SAE: Predefined SAE Commit ", local_commit, sizeof(local_commit), ESP_LOG_INFO);
TEST_ASSERT(os_memcmp(wpabuf_head(buf), local_commit, sizeof(local_commit)) == 0);
TEST_ASSERT(sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL, NULL, 0) == 0);
TEST_ASSERT(sae_process_commit(&sae) == 0)
ESP_LOGI("SAE TEST", "### Compare derived KCK,PMK,PMKID with predefined vectors ###");
ESP_LOG_BUFFER_HEXDUMP("SAE: Derived KCK ", sae.tmp->kck, SAE_KCK_LEN, ESP_LOG_INFO);
ESP_LOG_BUFFER_HEXDUMP("SAE: Predefined KCK ", kck, SAE_KCK_LEN, ESP_LOG_INFO);
TEST_ASSERT(os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) == 0);
ESP_LOG_BUFFER_HEXDUMP("SAE: Derived PMK ", sae.pmk, SAE_PMK_LEN, ESP_LOG_INFO);
ESP_LOG_BUFFER_HEXDUMP("SAE: Predefined PMK ", pmk, SAE_PMK_LEN, ESP_LOG_INFO);
TEST_ASSERT(os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) == 0);
ESP_LOG_BUFFER_HEXDUMP("SAE: Derived PMKID ", sae.pmkid, SAE_PMKID_LEN, ESP_LOG_INFO);
ESP_LOG_BUFFER_HEXDUMP("SAE: Predefined PMKID ", pmkid, SAE_PMKID_LEN, ESP_LOG_INFO);
TEST_ASSERT(os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) == 0);
pt_info = sae_derive_pt(pt_groups,
(const u8 *) ssid, os_strlen(ssid),
(const u8 *) pw, os_strlen(pw), pwid);
TEST_ASSERT(pt_info != NULL);
struct crypto_ec_point *pwe;
u8 bin[SAE_MAX_ECC_PRIME_LEN * 2];
size_t prime_len = sizeof(pwe_19_x);
pt = pt_info;
pwe = sae_derive_pwe_from_pt_ecc(pt, addr1b, addr2b);
TEST_ASSERT(pwe != NULL);
TEST_ASSERT(crypto_ec_point_to_bin(pt->ec, pwe, bin, bin + prime_len) == 0);
ESP_LOGI("SAE TEST", "### Compare derived SAE: PT.x and SAE: PT.y with predefined vectors ###");
ESP_LOG_BUFFER_HEXDUMP("SAE: Derived SAE: PT.x ", bin, prime_len, ESP_LOG_INFO);
ESP_LOG_BUFFER_HEXDUMP("SAE: Predefined SAE: PT.x ", pwe_19_x, prime_len, ESP_LOG_INFO);
TEST_ASSERT(os_memcmp(pwe_19_x, bin, prime_len) == 0);
ESP_LOG_BUFFER_HEXDUMP("SAE: Derived SAE: PT.y ", bin + prime_len, prime_len, ESP_LOG_INFO);
ESP_LOG_BUFFER_HEXDUMP("SAE: Predefined SAE: PT.y ", pwe_19_y, prime_len, ESP_LOG_INFO);
TEST_ASSERT(os_memcmp(pwe_19_y, bin + prime_len, prime_len) == 0)
crypto_ec_point_deinit(pwe, 1);
sae_deinit_pt(pt_info);
sae_clear_data(&sae);
wpabuf_free(buf);
crypto_bignum_deinit(mask, 1);
}
ESP_LOGI("SAE Test", "=========== Complete ============");
ESP_LOGI("SAE Test", "### Beginning SAE commit msg formation and parsing ###");
{
/* Test SAE commit msg formation and parsing*/

View File

@ -75,9 +75,11 @@ Please refer to `Security <https://www.wi-fi.org/discover-wi-fi/security>`_ sect
Setting up WPA3 with {IDF_TARGET_NAME}
++++++++++++++++++++++++++++++++++++++
In IDF Menuconfig under Wi-Fi component, a config option "Enable WPA3-Personal" is provided to Enable/Disable WPA3. By default it is kept enabled, if disabled {IDF_TARGET_NAME} will not be able to establish a WPA3 connection. Currently, WPA3 is supported only in the Station mode. Additionally, since PMF is mandated by WPA3 protocol, PMF Mode should be set to either Optional or Required while setting WiFi config.
In IDF Menuconfig under Wi-Fi component, a config option "Enable WPA3-Personal" is provided to Enable/Disable WPA3. By default it is kept enabled, if disabled {IDF_TARGET_NAME} will not be able to establish a WPA3 connection. WPA3 is supported by station as well as softAP. Additionally, since PMF is mandated by WPA3 protocol, PMF Mode should be set to either Optional or Required while setting WiFi config.
Refer to `Protected Management Frames (PMF)`_ on how to set this mode.
After these settings are done, Station is ready to use WPA3-Personal. Application developers need not worry about the underlying security mode of the AP. WPA3-Personal is now the highest supported protocol in terms of security, so it will be automatically selected for the connection whenever available. For example, if an AP is configured to be in WPA3 Transition Mode, where it will advertise as both WPA2 and WPA3 capable, Station will choose WPA3 for the connection with above settings.
Note that Wi-Fi stack size requirement will increase 3kB when WPA3 is used.
To configure WPA3 for softAP you have set up authmode as WIFI_AUTH_WPA3_PSK in config. For WPA3 softAP PMF is mandatory.

View File

@ -66,9 +66,14 @@ void wifi_init_softap(void)
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
.password = EXAMPLE_ESP_WIFI_PASS,
.max_connection = EXAMPLE_MAX_STA_CONN,
.authmode = WIFI_AUTH_WPA_WPA2_PSK,
#ifdef CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT
.authmode = WIFI_AUTH_WPA3_PSK,
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
#else /* CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT */
.authmode = WIFI_AUTH_WPA2_PSK,
#endif
.pmf_cfg = {
.required = false,
.required = true,
},
},
};

View File

@ -1086,7 +1086,6 @@ components/wifi_provisioning/src/scheme_console.c
components/wifi_provisioning/src/wifi_config.c
components/wifi_provisioning/src/wifi_scan.c
components/wpa_supplicant/esp_supplicant/src/esp_scan_i.h
components/wpa_supplicant/esp_supplicant/src/esp_wpa3_i.h
components/wpa_supplicant/esp_supplicant/src/esp_wpa_err.h
components/wpa_supplicant/include/utils/wpa_debug.h
components/wpa_supplicant/include/utils/wpabuf.h