diff --git a/components/esp_rom/esp32c2/ld/esp32c2.rom.ld b/components/esp_rom/esp32c2/ld/esp32c2.rom.ld index a1b3805d9c..6ceb29731e 100644 --- a/components/esp_rom/esp32c2/ld/esp32c2.rom.ld +++ b/components/esp_rom/esp32c2/ld/esp32c2.rom.ld @@ -2106,7 +2106,6 @@ sta_recv_mgmt = 0x40002094; ieee80211_send_setup = 0x40002098; ieee80211_send_probereq = 0x4000209c; sta_auth_shared = 0x400020a4; -sta_auth_sae = 0x400020a8; cnx_coexist_timeout_process = 0x400020ac; ieee80211_alloc_challenge = 0x400020b0; cnx_assoc_timeout = 0x400020b4; diff --git a/components/esp_wifi/include/esp_wifi_types.h b/components/esp_wifi/include/esp_wifi_types.h index 5068a5511f..e0419acc02 100644 --- a/components/esp_wifi/include/esp_wifi_types.h +++ b/components/esp_wifi/include/esp_wifi_types.h @@ -254,7 +254,8 @@ typedef struct { uint32_t mbo_enabled:1; /**< Whether MBO is enabled for the connection */ 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 reserved:27; /**< Reserved for future feature set */ + uint32_t sae_pwe_h2e:2; /**< Whether SAE hash to element is enabled */ + uint32_t reserved:25; /**< Reserved for future feature set */ } wifi_sta_config_t; /** @brief Configuration data for ESP32 AP or STA. diff --git a/components/esp_wifi/lib b/components/esp_wifi/lib index 81ab3ab615..ec0327abd2 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 81ab3ab615a7b7741a466958b90f38cdb9413ad2 +Subproject commit ec0327abd2d420062224d3a741f3cfd394bd8aae diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-bignum.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-bignum.c index 550d510819..643f29d13b 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-bignum.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-bignum.c @@ -46,6 +46,21 @@ cleanup: } +struct crypto_bignum * crypto_bignum_init_uint(unsigned int val) +{ + + mbedtls_mpi *bn = os_zalloc(sizeof(mbedtls_mpi)); + if (bn == NULL) { + return NULL; + } + + mbedtls_mpi_init(bn); + mbedtls_mpi_lset(bn, val); + + return (struct crypto_bignum *)bn; +} + + void crypto_bignum_deinit(struct crypto_bignum *n, int clear) { mbedtls_mpi_free((mbedtls_mpi *)n); @@ -163,6 +178,39 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, } +int crypto_bignum_sqrmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c) +{ + int res; + struct crypto_bignum *tmp = crypto_bignum_init(); + if (!tmp) { + return -1; + } + + res = mbedtls_mpi_copy((mbedtls_mpi *) tmp,(const mbedtls_mpi *) a); + res = crypto_bignum_mulmod(a,tmp,b,c); + + crypto_bignum_deinit(tmp, 0); + return res ? -1 : 0; +} + + +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r) +{ + int res; + res = mbedtls_mpi_copy((mbedtls_mpi *) r,(const mbedtls_mpi *) a); + if (res) { + return -1; + } + + res = mbedtls_mpi_shift_r((mbedtls_mpi *)r, n); + return res ? -1 : 0; + +} + + int crypto_bignum_cmp(const struct crypto_bignum *a, const struct crypto_bignum *b) { diff --git a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c index f9be1d19a6..9f5e2e8558 100644 --- a/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c +++ b/components/wpa_supplicant/esp_supplicant/src/crypto/crypto_mbedtls-ec.c @@ -115,6 +115,11 @@ size_t crypto_ec_prime_len(struct crypto_ec *e) return mbedtls_mpi_size(&e->group.P); } +size_t crypto_ec_order_len(struct crypto_ec *e) +{ + return mbedtls_mpi_size(&e->group.N); +} + size_t crypto_ec_prime_len_bits(struct crypto_ec *e) { @@ -152,6 +157,12 @@ const struct crypto_bignum *crypto_ec_get_order(struct crypto_ec *e) } +const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) &e->group.B; +} + + void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) { mbedtls_ecp_point_free((mbedtls_ecp_point *) p); diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_common.c b/components/wpa_supplicant/esp_supplicant/src/esp_common.c index 42c5fa1370..d25dc0fa24 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_common.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_common.c @@ -57,7 +57,7 @@ static int handle_action_frm(u8 *frame, size_t len, return 0; } -#endif +#endif /* CONFIG_SUPPLICANT_TASK */ #if defined(CONFIG_WPA_11KV_SUPPORT) static void handle_rrm_frame(struct wpa_supplicant *wpa_s, u8 *sender, @@ -99,7 +99,7 @@ static int mgmt_rx_action(u8 *frame, size_t len, u8 *sender, u32 rssi, u8 channe return 0; } -#endif +#endif /* defined(CONFIG_WPA_11KV_SUPPORT) */ #ifdef CONFIG_SUPPLICANT_TASK static void btm_rrm_task(void *pvParameters) @@ -153,7 +153,7 @@ static void btm_rrm_task(void *pvParameters) /* At this point, we completed */ os_task_delete(NULL); } -#endif +#endif /* CONFIG_SUPPLICANT_TASK */ static void clear_bssid_flag(struct wpa_supplicant *wpa_s) { @@ -192,7 +192,7 @@ static void register_mgmt_frames(struct wpa_supplicant *wpa_s) if (wpa_s->subtype) wpa_s->type |= 1 << WLAN_FC_STYPE_ACTION; -#endif +#endif /* CONFIG_WPA_11KV_SUPPORT */ #ifdef CONFIG_IEEE80211R /* register auth/assoc frames if FT is enabled */ @@ -200,7 +200,7 @@ static void register_mgmt_frames(struct wpa_supplicant *wpa_s) wpa_s->type |= (1 << WLAN_FC_STYPE_AUTH) | (1 << WLAN_FC_STYPE_ASSOC_RESP) | (1 << WLAN_FC_STYPE_REASSOC_RESP); -#endif +#endif /* CONFIG_IEEE80211R */ esp_wifi_register_mgmt_frame_internal(wpa_s->type, wpa_s->subtype); } @@ -240,7 +240,7 @@ static void supplicant_sta_disconn_handler(void* arg, esp_event_base_t event_bas #ifdef CONFIG_WPA_11KV_SUPPORT wpas_rrm_reset(wpa_s); wpas_clear_beacon_rep_data(wpa_s); -#endif +#endif /* CONFIG_WPA_11KV_SUPPORT */ if (wpa_s->current_bss) { wpa_s->current_bss = NULL; } @@ -280,7 +280,7 @@ static int handle_assoc_frame(u8 *frame, size_t len, } return 0; } -#endif +#endif /* CONFIG_IEEE80211R */ static int ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender, u32 rssi, u8 channel, u64 current_tsf) @@ -300,16 +300,16 @@ static int ieee80211_handle_rx_frm(u8 type, u8 *frame, size_t len, u8 *sender, case WLAN_FC_STYPE_REASSOC_RESP: ret = handle_assoc_frame(frame, len, sender, rssi, channel); break; -#endif +#endif /* CONFIG_IEEE80211R */ #if defined(CONFIG_WPA_11KV_SUPPORT) case WLAN_FC_STYPE_ACTION: #ifdef CONFIG_SUPPLICANT_TASK ret = handle_action_frm(frame, len, sender, rssi, channel); -#else +#else /* CONFIG_SUPPLICANT_TASK */ ret = mgmt_rx_action(frame, len, sender, rssi, channel); -#endif +#endif /* CONFIG_SUPPLICANT_TASK */ break; -#endif +#endif /* defined(CONFIG_WPA_11KV_SUPPORT) */ default: ret = -1; break; @@ -341,7 +341,7 @@ bool mbo_bss_profile_match(u8 *bssid) return true; } -#endif +#endif /* CONFIG_MBO */ int esp_supplicant_common_init(struct wpa_funcs *wpa_cb) { @@ -369,11 +369,11 @@ int esp_supplicant_common_init(struct wpa_funcs *wpa_cb) ret = -1; goto err; } -#endif +#endif /* CONFIG_SUPPLICANT_TASK */ #ifdef CONFIG_WPA_11KV_SUPPORT wpas_rrm_reset(wpa_s); wpas_clear_beacon_rep_data(wpa_s); -#endif +#endif /* CONFIG_WPA_11KV_SUPPORT */ esp_scan_init(wpa_s); esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, @@ -385,7 +385,7 @@ int esp_supplicant_common_init(struct wpa_funcs *wpa_cb) wpa_s->subtype = 0; #ifdef CONFIG_IEEE80211R wpa_s->type |= (1 << WLAN_FC_STYPE_ASSOC_RESP) | (1 << WLAN_FC_STYPE_REASSOC_RESP) | (1 << WLAN_FC_STYPE_AUTH); -#endif +#endif /* CONFIG_IEEE80211R */ if (esp_wifi_register_mgmt_frame_internal(wpa_s->type, wpa_s->subtype) != ESP_OK) { ret = -1; goto err; @@ -393,7 +393,7 @@ int esp_supplicant_common_init(struct wpa_funcs *wpa_cb) wpa_cb->wpa_sta_rx_mgmt = ieee80211_handle_rx_frm; #ifdef CONFIG_MBO dl_list_init(&wpa_s->bss_tmp_disallowed); -#endif +#endif /* CONFIG_MBO */ return 0; err: esp_supplicant_common_deinit(); @@ -408,7 +408,7 @@ void esp_supplicant_common_deinit(void) #ifdef CONFIG_WPA_11KV_SUPPORT wpas_rrm_reset(wpa_s); wpas_clear_beacon_rep_data(wpa_s); -#endif +#endif /* CONFIG_WPA_11KV_SUPPORT */ esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &supplicant_sta_conn_handler); esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, @@ -428,7 +428,7 @@ void esp_supplicant_common_deinit(void) s_supplicant_api_lock = NULL; } } -#endif +#endif /* CONFIG_SUPPLICANT_TASK */ } #ifdef CONFIG_WPA_11KV_SUPPORT @@ -555,7 +555,7 @@ static size_t get_rm_enabled_ie(uint8_t *ie, size_t len) *pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | #ifdef SCAN_CACHE_SUPPORTED WLAN_RRM_CAPS_BEACON_REPORT_TABLE | -#endif +#endif /* SCAN_CACHE_SUPPORTED */ WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE; os_memcpy(ie, rrm_ie, sizeof(rrm_ie)); @@ -618,7 +618,7 @@ static uint8_t get_operating_class_ie(uint8_t *ie, size_t len) return op_class_ie_len + 2; } -#endif +#endif /* CONFIG_MBO */ static uint8_t get_extended_caps_ie(uint8_t *ie, size_t len) { @@ -642,7 +642,7 @@ static uint8_t get_extended_caps_ie(uint8_t *ie, size_t len) return ext_caps_ie_len + 2; } -#endif +#endif /* CONFIG_WPA_11KV_SUPPORT */ void esp_set_scan_ie(void) { @@ -664,12 +664,12 @@ void esp_set_scan_ie(void) ie_len = get_mbo_oce_scan_ie(pos, len); pos += ie_len; len -= ie_len; -#endif +#endif /* CONFIG_MBO */ esp_wifi_unset_appie_internal(WIFI_APPIE_PROBEREQ); esp_wifi_set_appie_internal(WIFI_APPIE_PROBEREQ, ie, SCAN_IE_LEN - len, 0); os_free(ie); #undef SCAN_IE_LEN -#endif +#endif /* CONFIG_WPA_11KV_SUPPORT */ } #ifdef CONFIG_IEEE80211R @@ -703,52 +703,8 @@ static size_t add_mdie(uint8_t *bssid, uint8_t *ie, size_t len) return mdie_len; } -#endif +#endif /* CONFIG_IEEE80211R */ -void esp_set_assoc_ie(uint8_t *bssid, const u8 *ies, size_t ies_len, bool mdie) -{ -#define ASSOC_IE_LEN 128 - uint8_t *ie, *pos; - size_t len = ASSOC_IE_LEN, ie_len; - - ie = os_malloc(ASSOC_IE_LEN + ies_len); - if (!ie) { - wpa_printf(MSG_ERROR, "failed to allocate ie"); - return; - } - pos = ie; -#ifdef CONFIG_WPA_11KV_SUPPORT - ie_len = get_extended_caps_ie(pos, len); - pos += ie_len; - len -= ie_len; - ie_len = get_rm_enabled_ie(pos, len); - pos += ie_len; - len -= ie_len; -#ifdef CONFIG_MBO - ie_len = get_operating_class_ie(pos, len); - pos += ie_len; - len -= ie_len; - ie_len = get_mbo_oce_assoc_ie(pos, len); - pos += ie_len; - len -= ie_len; -#endif -#endif -#ifdef CONFIG_IEEE80211R - if (mdie) { - ie_len = add_mdie(bssid, pos, len); - pos += ie_len; - len -= ie_len; - } -#endif - if (ies_len) { - os_memcpy(pos, ies, ies_len); - pos += ies_len; - len -= ies_len; - } - esp_wifi_set_appie_internal(WIFI_APPIE_ASSOC_REQ, ie, ASSOC_IE_LEN - len, 0); - os_free(ie); -#undef ASSOC_IE_LEN -} #ifdef CONFIG_IEEE80211R int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md, @@ -768,7 +724,7 @@ int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md, return 0; } -#endif +#endif /* CONFIG_IEEE80211R */ void esp_get_tx_power(uint8_t *tx_power) { @@ -846,8 +802,8 @@ int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data) } return 0; } -#endif -#else +#endif /* CONFIG_SUPPLICANT_TASK */ +#else /* defined(CONFIG_WPA_11KV_SUPPORT) || defined(CONFIG_IEEE80211R) */ int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb, void *cb_ctx) { @@ -866,11 +822,61 @@ int esp_mbo_update_non_pref_chan(struct non_pref_chan_s *non_pref_chan) return -1; } void esp_set_scan_ie(void) { } -void esp_set_assoc_ie(uint8_t *bssid, const u8 *ies, size_t ies_len, bool mdie) { } int esp_supplicant_common_init(struct wpa_funcs *wpa_cb) { wpa_cb->wpa_sta_rx_mgmt = NULL; return 0; } void esp_supplicant_common_deinit(void) { } -#endif +#endif /* defined(CONFIG_WPA_11KV_SUPPORT) || defined(CONFIG_IEEE80211R) */ + +#if defined(CONFIG_WPA_11KV_SUPPORT) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WPA3_SAE) +void esp_set_assoc_ie(uint8_t *bssid, const u8 *ies, size_t ies_len, bool mdie) +{ +#define ASSOC_IE_LEN 128 + uint8_t *ie, *pos; + size_t len = ASSOC_IE_LEN; +#if defined(CONFIG_WPA_11KV_SUPPORT) || defined(CONFIG_IEEE80211R) + size_t ie_len; +#endif /* defined(CONFIG_WPA_11KV_SUPPORT) || defined(CONFIG_IEEE80211R) */ + ie = os_malloc(ASSOC_IE_LEN + ies_len); + if (!ie) { + wpa_printf(MSG_ERROR, "failed to allocate ie"); + return; + } + pos = ie; +#ifdef CONFIG_WPA_11KV_SUPPORT + ie_len = get_extended_caps_ie(pos, len); + pos += ie_len; + len -= ie_len; + ie_len = get_rm_enabled_ie(pos, len); + pos += ie_len; + len -= ie_len; +#ifdef CONFIG_MBO + ie_len = get_operating_class_ie(pos, len); + pos += ie_len; + len -= ie_len; + ie_len = get_mbo_oce_assoc_ie(pos, len); + pos += ie_len; + len -= ie_len; +#endif /* CONFIG_MBO */ +#endif /* CONFIG_WPA_11KV_SUPPORT */ +#ifdef CONFIG_IEEE80211R + if (mdie) { + ie_len = add_mdie(bssid, pos, len); + pos += ie_len; + len -= ie_len; + } +#endif /* CONFIG_IEEE80211R */ + if (ies_len) { + os_memcpy(pos, ies, ies_len); + pos += ies_len; + len -= ies_len; + } + esp_wifi_set_appie_internal(WIFI_APPIE_ASSOC_REQ, ie, ASSOC_IE_LEN - len, 0); + os_free(ie); +#undef ASSOC_IE_LEN +} +#else +void esp_set_assoc_ie(uint8_t *bssid, const u8 *ies, size_t ies_len, bool mdie) { } +#endif /* defined(CONFIG_WPA_11KV_SUPPORT) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WPA3_SAE) */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h index 778af824c1..07c133144a 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wifi_driver.h @@ -111,6 +111,7 @@ typedef struct { size_t num_pmkid; const u8 *pmkid; int mgmt_group_cipher; + uint8_t rsnxe_capa; } wifi_wpa_ie_t; struct wpa_funcs { @@ -137,6 +138,7 @@ struct wpa_funcs { void (*wpa_config_done)(void); uint8_t *(*owe_build_dhie)(uint16_t group); int (*owe_process_assoc_resp)(const u8 *rsn_ie, size_t rsn_len, const uint8_t *dh_ie, size_t dh_len); + int (*wpa_sta_set_ap_rsnxe)(const u8 *rsnxe, size_t rsnxe_ie_len); }; struct wpa2_funcs { @@ -277,5 +279,7 @@ 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_get_config_sae_pwe_h2e_internal(void); +uint8_t esp_wifi_get_use_h2e_internal(void); #endif /* _ESP_WIFI_DRIVER_H_ */ diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c index 3be8bef2f6..e5cedb6b50 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa3.c @@ -1,16 +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-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #ifdef CONFIG_WPA3_SAE @@ -19,6 +11,7 @@ #include "esp_wifi_driver.h" #include "rsn_supp/wpa.h" +static struct sae_pt *g_sae_pt; static struct sae_data g_sae_data; static struct wpabuf *g_sae_token = NULL; static struct wpabuf *g_sae_commit = NULL; @@ -30,7 +23,13 @@ static esp_err_t wpa3_build_sae_commit(u8 *bssid) int default_group = IANA_SECP256R1; u32 len = 0; u8 own_addr[ETH_ALEN]; - const u8 *pw; + const u8 *pw = (const u8 *)esp_wifi_sta_get_prof_password_internal(); + struct wifi_ssid *ssid = esp_wifi_sta_get_prof_ssid_internal(); + uint8_t use_pt = esp_wifi_get_use_h2e_internal(); + + if (use_pt && !g_sae_pt) { + g_sae_pt = sae_derive_pt(g_allowed_groups, ssid->ssid, ssid->len, pw, strlen((const char *)pw), NULL); + } if (wpa_sta_cur_pmksa_matches_akm()) { wpa_printf(MSG_INFO, "wpa3: Skip SAE and use cached PMK instead"); @@ -59,8 +58,16 @@ static esp_err_t wpa3_build_sae_commit(u8 *bssid) return ESP_FAIL; } - pw = (const u8 *)esp_wifi_sta_get_prof_password_internal(); - if (sae_prepare_commit(own_addr, bssid, pw, strlen((const char *)pw), NULL, &g_sae_data) < 0) { + if (use_pt && + sae_prepare_commit_pt(&g_sae_data, g_sae_pt, + own_addr, bssid, NULL) < 0) { + wpa_printf(MSG_ERROR, "wpa3: failed to prepare SAE commit!"); + return ESP_FAIL; + } + if (!use_pt && + sae_prepare_commit(own_addr, bssid, pw, + strlen((const char *)pw), + &g_sae_data) < 0) { wpa_printf(MSG_ERROR, "wpa3: failed to prepare SAE commit!"); return ESP_FAIL; } @@ -128,6 +135,10 @@ void esp_wpa3_free_sae_data(void) g_sae_confirm = NULL; } sae_clear_data(&g_sae_data); + if (g_sae_pt) { + sae_deinit_pt(g_sae_pt); + g_sae_pt = NULL; + } } static u8 *wpa3_build_sae_msg(u8 *bssid, u32 sae_msg_type, size_t *sae_msg_len) @@ -167,11 +178,23 @@ static int wpa3_parse_sae_commit(u8 *buf, u32 len, u16 status) if (status == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ) { if (g_sae_token) wpabuf_free(g_sae_token); - g_sae_token = wpabuf_alloc_copy(buf + 2, len - 2); + if (g_sae_data.h2e) { + if ((buf[2] != WLAN_EID_EXTENSION) || + (buf[3] == 0) || + (buf[3] > len - 4) || + (buf[4] != WLAN_EID_EXT_ANTI_CLOGGING_TOKEN)) { + wpa_printf(MSG_ERROR, "Invalid SAE anti-clogging token container header"); + return ESP_FAIL; + } + g_sae_token = wpabuf_alloc_copy(buf + 5, len - 5); + } else { + g_sae_token = wpabuf_alloc_copy(buf + 2, len - 2); + } return ESP_OK; } - ret = sae_parse_commit(&g_sae_data, buf, len, NULL, 0, g_allowed_groups); + ret = sae_parse_commit(&g_sae_data, buf, len, NULL, 0, g_allowed_groups, + status == WLAN_STATUS_SAE_HASH_TO_ELEMENT); if (ret) { wpa_printf(MSG_ERROR, "wpa3: could not parse commit(%d)", ret); return ret; diff --git a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c index 1dd5592e9f..8c3e4e9efd 100644 --- a/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c +++ b/components/wpa_supplicant/esp_supplicant/src/esp_wpa_main.c @@ -220,6 +220,7 @@ int wpa_parse_wpa_ie_wrapper(const u8 *wpa_ie, size_t wpa_ie_len, wifi_wpa_ie_t data->capabilities = ie.capabilities; data->pmkid = ie.pmkid; data->mgmt_group_cipher = cipher_type_map_supp_to_public(ie.mgmt_group_cipher); + data->rsnxe_capa = ie.rsnxe_capa; return ret; } @@ -341,6 +342,7 @@ int esp_supplicant_init(void) wpa_cb->wpa_config_bss = NULL;//wpa_config_bss; wpa_cb->wpa_michael_mic_failure = wpa_michael_mic_failure; wpa_cb->wpa_config_done = wpa_config_done; + wpa_cb->wpa_sta_set_ap_rsnxe = wpa_sm_set_ap_rsnxe; esp_wifi_register_wpa3_cb(wpa_cb); #ifdef CONFIG_OWE_STA diff --git a/components/wpa_supplicant/src/common/ieee802_11_defs.h b/components/wpa_supplicant/src/common/ieee802_11_defs.h index a378d91e3b..1742eed0c5 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_defs.h +++ b/components/wpa_supplicant/src/common/ieee802_11_defs.h @@ -157,6 +157,7 @@ #define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 #define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123 +#define WLAN_STATUS_SAE_HASH_TO_ELEMENT 126 /* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -228,6 +229,7 @@ #define WLAN_EID_FILS_INDICATION 240 #define WLAN_EID_DILS 241 #define WLAN_EID_FRAGMENT 242 +#define WLAN_EID_RSNX 244 #define WLAN_EID_EXTENSION 255 /* Element ID Extension (EID 255) values */ @@ -249,6 +251,12 @@ #define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33 #define WLAN_EID_EXT_HE_CAPABILITIES 35 #define WLAN_EID_EXT_HE_OPERATION 36 +#define WLAN_EID_EXT_REJECTED_GROUPS 92 +#define WLAN_EID_EXT_ANTI_CLOGGING_TOKEN 93 + +/* Extended RSN Capabilities */ +/* bits 0-3: Field length (n-1) */ +#define WLAN_RSNX_CAPAB_SAE_H2E 5 #define WLAN_EXT_CAPAB_BSS_TRANSITION 19 diff --git a/components/wpa_supplicant/src/common/sae.c b/components/wpa_supplicant/src/common/sae.c index a075592f8b..b7a80774de 100644 --- a/components/wpa_supplicant/src/common/sae.c +++ b/components/wpa_supplicant/src/common/sae.c @@ -10,6 +10,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/const_time.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "crypto/random.h" @@ -35,6 +36,7 @@ int sae_set_group(struct sae_data *sae, int group) sae->group = group; tmp->prime_len = crypto_ec_prime_len(tmp->ec); tmp->prime = crypto_ec_get_prime(tmp->ec); + tmp->order_len = crypto_ec_order_len(tmp->ec); tmp->order = crypto_ec_get_order(tmp->ec); return ESP_OK; } @@ -59,6 +61,7 @@ int sae_set_group(struct sae_data *sae, int group) } tmp->prime = tmp->prime_buf; + tmp->order_len = tmp->dh->order_len; tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, tmp->dh->order_len); if (tmp->order_buf == NULL) { @@ -73,7 +76,7 @@ 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); + os_free(tmp); return ESP_FAIL; } @@ -94,6 +97,8 @@ 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->own_rejected_groups); + wpabuf_free(tmp->peer_rejected_groups); os_free(tmp->pw_id); bin_clear_free(tmp, sizeof(*tmp)); sae->tmp = NULL; @@ -105,6 +110,7 @@ void sae_clear_data(struct sae_data *sae) return; sae_clear_temp_data(sae); crypto_bignum_deinit(sae->peer_commit_scalar, 0); + crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0); os_memset(sae, 0, sizeof(*sae)); } @@ -413,13 +419,12 @@ static int get_random_qr_qnr(const u8 *prime, size_t prime_len, static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, - size_t password_len, const char *identifier) + size_t password_len) { u8 counter, k = 40; u8 addrs[2 * ETH_ALEN]; const u8 *addr[3]; size_t len[3]; - size_t num_elem; u8 dummy_password[32]; size_t dummy_password_len; int pwd_seed_odd = 0; @@ -451,13 +456,10 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); - if (identifier) - wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", - identifier); /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) - * base = password [|| identifier] + * base = password * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), * base || counter) */ @@ -465,15 +467,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, addr[0] = password; len[0] = password_len; - num_elem = 1; - if (identifier) { - addr[num_elem] = (const u8 *) identifier; - len[num_elem] = os_strlen(identifier); - num_elem++; - } - addr[num_elem] = &counter; - len[num_elem] = sizeof(counter); - num_elem++; + addr[1] = &counter; + len[1] = sizeof(counter); /* * Continue for at least k iterations to protect against side-channel @@ -491,7 +486,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, } wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); - if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, + if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, pwd_seed) < 0) break; @@ -550,13 +545,12 @@ fail: static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, - size_t password_len, const char *identifier) + size_t password_len) { u8 counter; u8 addrs[2 * ETH_ALEN]; const u8 *addr[3]; size_t len[3]; - size_t num_elem; int found = 0; if (sae->tmp->pwe_ffc == NULL) { @@ -571,21 +565,14 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), - * password [|| identifier] || counter) + * password || counter) */ sae_pwd_seed_key(addr1, addr2, addrs); addr[0] = password; len[0] = password_len; - num_elem = 1; - if (identifier) { - addr[num_elem] = (const u8 *) identifier; - len[num_elem] = os_strlen(identifier); - num_elem++; - } - addr[num_elem] = &counter; - len[num_elem] = sizeof(counter); - num_elem++; + addr[1] = &counter; + len[1] = sizeof(counter); for (counter = 1; !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; @@ -598,7 +585,7 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, } wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); - if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, + if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); @@ -613,6 +600,711 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, return found ? 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) +{ + if (hash_len == 32) + return hmac_sha256_vector(salt, salt_len, num_elem, addr, len, + prk); + 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) +{ + size_t info_len = os_strlen(info); + + if (hash_len == 32) + return hmac_sha256_kdf(prk, prk_len, NULL, + (const u8 *) info, info_len, + okm, okm_len); + return -1; +} + +static int sswu_curve_param(int group, int *z) +{ + switch (group) { + case 19: + *z = -10; + return 0; + } + + return -1; +} + + +static void debug_print_bignum(const char *title, const struct crypto_bignum *a, + size_t prime_len) +{ + u8 *bin; + + bin = os_malloc(prime_len); + if (bin && crypto_bignum_to_bin(a, bin, prime_len, prime_len) >= 0) + wpa_hexdump_key(MSG_DEBUG, title, bin, prime_len); + else + wpa_printf(MSG_DEBUG, "Could not print bignum (%s)", title); + bin_clear_free(bin, prime_len); +} + +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, + *x1a, *x1b, *y = NULL; + struct crypto_bignum *x1 = NULL, *x2, *gx1, *gx2, *v = NULL; + struct crypto_bignum *tmp = NULL; + unsigned int m_is_zero, is_qr, is_eq; + size_t prime_len; + u8 bin[SAE_MAX_ECC_PRIME_LEN]; + u8 bin1[SAE_MAX_ECC_PRIME_LEN]; + u8 bin2[SAE_MAX_ECC_PRIME_LEN]; + u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN]; + struct crypto_ec_point *p = NULL; + + if (sswu_curve_param(group, &z_int) < 0) + return NULL; + + prime = crypto_ec_get_prime(ec); + prime_len = crypto_ec_prime_len(ec); + 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); + + u2 = crypto_bignum_init(); + t1 = crypto_bignum_init(); + t2 = crypto_bignum_init(); + z = crypto_bignum_init_uint(abs(z_int)); + t = crypto_bignum_init(); + zero = crypto_bignum_init_uint(0); + one = crypto_bignum_init_uint(1); + two = crypto_bignum_init_uint(2); + three = crypto_bignum_init_uint(3); + x1a = crypto_bignum_init(); + x1b = crypto_bignum_init(); + x2 = crypto_bignum_init(); + gx1 = crypto_bignum_init(); + gx2 = crypto_bignum_init(); + tmp = crypto_bignum_init(); + if (!u2 || !t1 || !t2 || !z || !t || !zero || !one || !two || !three || + !x1a || !x1b || !x2 || !gx1 || !gx2 || !tmp) + goto fail; + + if (z_int < 0 && crypto_bignum_sub(prime, z, z) < 0) + goto fail; + + /* m = z^2 * u^4 + z * u^2 */ + /* --> tmp = z * u^2, m = tmp^2 + tmp */ + + /* u2 = u^2 + * t1 = z * u2 + * t2 = t1^2 + * m = t1 = t1 + t2 */ + 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) + goto fail; + debug_print_bignum("SSWU: m", t1, prime_len); + + /* l = CEQ(m, 0) + * t = CSEL(l, 0, inverse(m); where inverse(x) is calculated as + * x^(p-2) modulo p which will handle m == 0 case correctly */ + /* TODO: Make sure crypto_bignum_is_zero() is constant time */ + m_is_zero = const_time_eq(crypto_bignum_is_zero(t1), 1); + /* t = m^(p-2) modulo p */ + if (crypto_bignum_sub(prime, two, t2) < 0 || + crypto_bignum_exptmod(t1, t2, prime, t) < 0) + goto fail; + debug_print_bignum("SSWU: t", t, prime_len); + + /* b / (z * a) */ + if (crypto_bignum_mulmod(z, a, prime, t1) < 0 || + crypto_bignum_inverse(t1, prime, t1) < 0 || + crypto_bignum_mulmod(b, t1, prime, x1a) < 0) + goto fail; + debug_print_bignum("SSWU: x1a = b / (z * a)", x1a, prime_len); + + /* (-b/a) * (1 + t) */ + 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_mulmod(t1, t2, prime, x1b) < 0) + goto fail; + debug_print_bignum("SSWU: x1b = (-b/a) * (1 + t)", x1b, prime_len); + + /* x1 = CSEL(CEQ(m, 0), x1a, x1b) */ + if (crypto_bignum_to_bin(x1a, bin1, sizeof(bin1), prime_len) < 0 || + crypto_bignum_to_bin(x1b, bin2, sizeof(bin2), prime_len) < 0) + goto fail; + const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin); + x1 = crypto_bignum_init_set(bin, prime_len); + if (!x1) + goto fail; + debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len); + + /* 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) + goto fail; + debug_print_bignum("SSWU: gx1 = x1^3 + a * x1 + b", gx1, prime_len); + + /* x2 = z * u^2 * x1 */ + if (crypto_bignum_mulmod(z, u2, prime, t1) < 0 || + crypto_bignum_mulmod(t1, x1, prime, x2) < 0) + goto fail; + debug_print_bignum("SSWU: x2 = z * u^2 * x1", x2, prime_len); + + /* 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) + goto fail; + debug_print_bignum("SSWU: gx2 = x2^3 + a * x2 + b", gx2, prime_len); + + /* l = gx1 is a quadratic residue modulo p + * --> gx1^((p-1)/2) modulo p is zero or one */ + if (crypto_bignum_sub(prime, one, t1) < 0 || + crypto_bignum_rshift(t1, 1, t1) < 0 || + crypto_bignum_exptmod(gx1, t1, prime, tmp) < 0) + goto fail; + debug_print_bignum("SSWU: gx1^((p-1)/2) modulo p", t1, prime_len); + is_qr = const_time_eq(crypto_bignum_is_zero(tmp) | + crypto_bignum_is_one(tmp), 1); + + /* v = CSEL(l, gx1, gx2) */ + if (crypto_bignum_to_bin(gx1, bin1, sizeof(bin1), prime_len) < 0 || + crypto_bignum_to_bin(gx2, bin2, sizeof(bin2), prime_len) < 0) + goto fail; + const_time_select_bin(is_qr, bin1, bin2, prime_len, bin); + v = crypto_bignum_init_set(bin, prime_len); + if (!v) + goto fail; + debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len); + + /* x = CSEL(l, x1, x2) */ + if (crypto_bignum_to_bin(x1, bin1, sizeof(bin1), prime_len) < 0 || + crypto_bignum_to_bin(x2, bin2, sizeof(bin2), prime_len) < 0) + goto fail; + 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 = 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) + goto fail; + debug_print_bignum("SSWU: y = sqrt(v)", y, prime_len); + + /* l = CEQ(LSB(u), LSB(y)) */ + if (crypto_bignum_to_bin(u, bin1, sizeof(bin1), prime_len) < 0 || + crypto_bignum_to_bin(y, bin2, sizeof(bin2), prime_len) < 0) + goto fail; + is_eq = const_time_eq(bin1[prime_len - 1] & 0x01, + bin2[prime_len - 1] & 0x01); + + /* P = CSEL(l, (x,y), (x, p-y)) */ + if (crypto_bignum_sub(prime, y, t1) < 0) + goto fail; + debug_print_bignum("SSWU: p - y", t1, prime_len); + if (crypto_bignum_to_bin(y, bin1, sizeof(bin1), prime_len) < 0 || + crypto_bignum_to_bin(t1, bin2, sizeof(bin2), prime_len) < 0) + goto fail; + const_time_select_bin(is_eq, bin1, bin2, prime_len, &x_y[prime_len]); + + /* output P */ + wpa_hexdump_key(MSG_DEBUG, "SSWU: P.x", x_y, prime_len); + wpa_hexdump_key(MSG_DEBUG, "SSWU: P.y", &x_y[prime_len], prime_len); + p = crypto_ec_point_from_bin(ec, x_y); + +fail: + crypto_bignum_deinit(tmp, 0); + crypto_bignum_deinit(u2, 1); + crypto_bignum_deinit(t1, 1); + crypto_bignum_deinit(t2, 1); + crypto_bignum_deinit(z, 0); + crypto_bignum_deinit(t, 1); + crypto_bignum_deinit(x1a, 1); + crypto_bignum_deinit(x1b, 1); + crypto_bignum_deinit(x1, 1); + crypto_bignum_deinit(x2, 1); + crypto_bignum_deinit(gx1, 1); + crypto_bignum_deinit(gx2, 1); + crypto_bignum_deinit(y, 1); + crypto_bignum_deinit(v, 1); + crypto_bignum_deinit(zero, 0); + crypto_bignum_deinit(one, 0); + crypto_bignum_deinit(two, 0); + crypto_bignum_deinit(three, 0); + forced_memzero(bin, sizeof(bin)); + forced_memzero(bin1, sizeof(bin1)); + forced_memzero(bin2, sizeof(bin2)); + forced_memzero(x_y, sizeof(x_y)); + 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) +{ + const u8 *addr[2]; + size_t len[2]; + size_t num_elem; + + /* pwd-seed = HKDF-Extract(ssid, password [ || identifier ]) */ + addr[0] = password; + len[0] = password_len; + num_elem = 1; + wpa_hexdump_ascii(MSG_DEBUG, "SAE: SSID", ssid, ssid_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", + password, password_len); + if (identifier) { + wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", + identifier); + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + if (hkdf_extract(hash_len, ssid, ssid_len, num_elem, addr, len, + pwd_seed) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, hash_len); + return 0; +} + + +size_t sae_ecc_prime_len_2_hash_len(size_t prime_len) +{ + if (prime_len <= 256 / 8) + return 32; + if (prime_len <= 384 / 8) + return 48; + return 64; +} + +static struct crypto_ec_point * +sae_derive_pt_ecc(struct crypto_ec *ec, int group, + const u8 *ssid, size_t ssid_len, + const u8 *password, size_t password_len, + const char *identifier) +{ + u8 pwd_seed[64]; + u8 pwd_value[SAE_MAX_ECC_PRIME_LEN * 2]; + size_t pwd_value_len, hash_len, prime_len; + const struct crypto_bignum *prime; + struct crypto_bignum *bn = NULL; + struct crypto_ec_point *p1 = NULL, *p2 = NULL, *pt = NULL; + + prime = crypto_ec_get_prime(ec); + prime_len = crypto_ec_prime_len(ec); + if (prime_len > SAE_MAX_ECC_PRIME_LEN) + goto fail; + hash_len = sae_ecc_prime_len_2_hash_len(prime_len); + + /* len = olen(p) + ceil(olen(p)/2) */ + pwd_value_len = prime_len + (prime_len + 1) / 2; + + if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, + identifier, pwd_seed) < 0) + goto fail; + + /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u1 P1", len) + */ + if (hkdf_expand(hash_len, pwd_seed, hash_len, + "SAE Hash to Element u1 P1", pwd_value, pwd_value_len) < + 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u1 P1)", + pwd_value, pwd_value_len); + + /* u1 = pwd-value modulo p */ + bn = crypto_bignum_init_set(pwd_value, pwd_value_len); + if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 || + crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), + prime_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "SAE: u1", pwd_value, prime_len); + + /* P1 = SSWU(u1) */ + p1 = sswu(ec, group, bn); + if (!p1) + goto fail; + + /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u2 P2", len) + */ + if (hkdf_expand(hash_len, pwd_seed, hash_len, + "SAE Hash to Element u2 P2", pwd_value, + pwd_value_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u2 P2)", + pwd_value, pwd_value_len); + + /* u2 = pwd-value modulo p */ + crypto_bignum_deinit(bn, 1); + bn = crypto_bignum_init_set(pwd_value, pwd_value_len); + if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 || + crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), + prime_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "SAE: u2", pwd_value, prime_len); + + /* P2 = SSWU(u2) */ + p2 = sswu(ec, group, bn); + if (!p2) + goto fail; + + /* PT = elem-op(P1, P2) */ + pt = crypto_ec_point_init(ec); + if (!pt) + goto fail; + if (crypto_ec_point_add(ec, p1, p2, pt) < 0) { + crypto_ec_point_deinit(pt, 1); + pt = NULL; + } + +fail: + forced_memzero(pwd_seed, sizeof(pwd_seed)); + forced_memzero(pwd_value, sizeof(pwd_value)); + crypto_bignum_deinit(bn, 1); + crypto_ec_point_deinit(p1, 1); + crypto_ec_point_deinit(p2, 1); + return pt; +} + + +size_t sae_ffc_prime_len_2_hash_len(size_t prime_len) +{ + if (prime_len <= 2048 / 8) + return 32; + if (prime_len <= 3072 / 8) + return 48; + return 64; +} + +static struct crypto_bignum * +sae_derive_pt_ffc(const struct dh_group *dh, int group, + const u8 *ssid, size_t ssid_len, + const u8 *password, size_t password_len, + const char *identifier) +{ + size_t hash_len, prime_len, pwd_value_len; + struct crypto_bignum *prime, *order; + struct crypto_bignum *one = NULL, *two = NULL, *bn = NULL, *tmp = NULL, + *pt = NULL; + u8 pwd_seed[64]; + u8 pwd_value[SAE_MAX_PRIME_LEN + SAE_MAX_PRIME_LEN / 2]; + + prime = crypto_bignum_init_set(dh->prime, dh->prime_len); + order = crypto_bignum_init_set(dh->order, dh->order_len); + if (!prime || !order) + goto fail; + prime_len = dh->prime_len; + if (prime_len > SAE_MAX_PRIME_LEN) + goto fail; + hash_len = sae_ffc_prime_len_2_hash_len(prime_len); + + /* len = olen(p) + ceil(olen(p)/2) */ + pwd_value_len = prime_len + (prime_len + 1) / 2; + if (pwd_value_len > sizeof(pwd_value)) + goto fail; + + if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, + identifier, pwd_seed) < 0) + goto fail; + + /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element", len) */ + if (hkdf_expand(hash_len, pwd_seed, hash_len, + "SAE Hash to Element", pwd_value, pwd_value_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", + pwd_value, pwd_value_len); + + /* pwd-value = (pwd-value modulo (p-2)) + 2 */ + bn = crypto_bignum_init_set(pwd_value, pwd_value_len); + one = crypto_bignum_init_uint(1); + two = crypto_bignum_init_uint(2); + tmp = crypto_bignum_init(); + if (!bn || !one || !two || !tmp || + crypto_bignum_sub(prime, two, tmp) < 0 || + crypto_bignum_mod(bn, tmp, bn) < 0 || + crypto_bignum_add(bn, two, bn) < 0 || + crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), + prime_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value(reduced)", + pwd_value, prime_len); + + /* PT = pwd-value^((p-1)/q) modulo p */ + pt = crypto_bignum_init(); + if (!pt || + crypto_bignum_sub(prime, one, tmp) < 0 || + crypto_bignum_div(tmp, order, tmp) < 0 || + crypto_bignum_exptmod(bn, tmp, prime, pt) < 0) { + crypto_bignum_deinit(pt, 1); + pt = NULL; + goto fail; + } + debug_print_bignum("SAE: PT", pt, prime_len); + +fail: + forced_memzero(pwd_seed, sizeof(pwd_seed)); + forced_memzero(pwd_value, sizeof(pwd_value)); + crypto_bignum_deinit(bn, 1); + crypto_bignum_deinit(tmp, 1); + crypto_bignum_deinit(one, 0); + crypto_bignum_deinit(two, 0); + crypto_bignum_deinit(prime, 0); + crypto_bignum_deinit(order, 0); + 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, + const char *identifier) +{ + struct sae_pt *pt; + + wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group); + + if (ssid_len > 32) + return NULL; + + pt = os_zalloc(sizeof(*pt)); + if (!pt) + return NULL; + + pt->group = group; + pt->ec = crypto_ec_init(group); + if (pt->ec) { + pt->ecc_pt = sae_derive_pt_ecc(pt->ec, group, ssid, ssid_len, + password, password_len, + identifier); + if (!pt->ecc_pt) { + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); + goto fail; + } + + return pt; + } + + pt->dh = dh_groups_get(group); + if (!pt->dh) { + wpa_printf(MSG_DEBUG, "SAE: Unsupported group %d", group); + goto fail; + } + + pt->ffc_pt = sae_derive_pt_ffc(pt->dh, group, ssid, ssid_len, + password, password_len, identifier); + if (!pt->ffc_pt) { + wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); + goto fail; + } + + return pt; +fail: + sae_deinit_pt(pt); + 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) +{ + struct sae_pt *pt = NULL, *last = NULL, *tmp; + int default_groups[] = { 19, 0 }; + int i; + + if (!groups) + groups = default_groups; + for (i = 0; groups[i] > 0; i++) { + tmp = sae_derive_pt_group(groups[i], ssid, ssid_len, password, + password_len, identifier); + if (!tmp) + continue; + + if (last) + last->next = tmp; + else + pt = tmp; + last = tmp; + } + + return pt; +} + +static void sae_max_min_addr(const u8 *addr[], size_t len[], + const u8 *addr1, const u8 *addr2) +{ + len[0] = ETH_ALEN; + len[1] = ETH_ALEN; + if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { + addr[0] = addr1; + addr[1] = addr2; + } else { + addr[0] = addr2; + addr[1] = addr1; + } +} + +struct crypto_ec_point * +sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt, + const u8 *addr1, const u8 *addr2) +{ + u8 bin[SAE_MAX_ECC_PRIME_LEN * 2]; + size_t prime_len; + const u8 *addr[2]; + size_t len[2]; + u8 salt[64], hash[64]; + size_t hash_len; + const struct crypto_bignum *order; + struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL; + struct crypto_ec_point *pwe = NULL; + + wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT"); + prime_len = crypto_ec_prime_len(pt->ec); + if (crypto_ec_point_to_bin(pt->ec, pt->ecc_pt, + bin, bin + prime_len) < 0) + return NULL; + wpa_hexdump_key(MSG_DEBUG, "SAE: PT.x", bin, prime_len); + wpa_hexdump_key(MSG_DEBUG, "SAE: PT.y", bin + prime_len, prime_len); + + sae_max_min_addr(addr, len, addr1, addr2); + + /* val = H(0^n, + * MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */ + wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))"); + hash_len = sae_ecc_prime_len_2_hash_len(prime_len); + os_memset(salt, 0, hash_len); + if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len); + + /* val = val modulo (q - 1) + 1 */ + order = crypto_ec_get_order(pt->ec); + tmp = crypto_bignum_init(); + val = crypto_bignum_init_set(hash, hash_len); + one = crypto_bignum_init_uint(1); + if (!tmp || !val || !one || + crypto_bignum_sub(order, one, tmp) < 0 || + crypto_bignum_mod(val, tmp, val) < 0 || + crypto_bignum_add(val, one, val) < 0) + goto fail; + debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len); + + /* PWE = scalar-op(val, PT) */ + pwe = crypto_ec_point_init(pt->ec); + if (!pwe || + crypto_ec_point_mul(pt->ec, pt->ecc_pt, val, pwe) < 0 || + crypto_ec_point_to_bin(pt->ec, pwe, bin, bin + prime_len) < 0) { + crypto_ec_point_deinit(pwe, 1); + pwe = NULL; + goto fail; + } + wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.x", bin, prime_len); + wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.y", bin + prime_len, prime_len); + +fail: + crypto_bignum_deinit(tmp, 1); + crypto_bignum_deinit(val, 1); + crypto_bignum_deinit(one, 0); + return pwe; +} + +struct crypto_bignum * +sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt, + const u8 *addr1, const u8 *addr2) +{ + size_t prime_len; + const u8 *addr[2]; + size_t len[2]; + u8 salt[64], hash[64]; + size_t hash_len; + struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL; + struct crypto_bignum *pwe = NULL, *order = NULL, *prime = NULL; + + wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT"); + prime = crypto_bignum_init_set(pt->dh->prime, pt->dh->prime_len); + order = crypto_bignum_init_set(pt->dh->order, pt->dh->order_len); + if (!prime || !order) + goto fail; + prime_len = pt->dh->prime_len; + + sae_max_min_addr(addr, len, addr1, addr2); + + /* val = H(0^n, + * MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */ + wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))"); + hash_len = sae_ffc_prime_len_2_hash_len(prime_len); + os_memset(salt, 0, hash_len); + if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len); + + /* val = val modulo (q - 1) + 1 */ + tmp = crypto_bignum_init(); + val = crypto_bignum_init_set(hash, hash_len); + one = crypto_bignum_init_uint(1); + if (!tmp || !val || !one || + crypto_bignum_sub(order, one, tmp) < 0 || + crypto_bignum_mod(val, tmp, val) < 0 || + crypto_bignum_add(val, one, val) < 0) + goto fail; + debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len); + + /* PWE = scalar-op(val, PT) */ + pwe = crypto_bignum_init(); + if (!pwe || crypto_bignum_exptmod(pt->ffc_pt, val, prime, pwe) < 0) { + crypto_bignum_deinit(pwe, 1); + pwe = NULL; + goto fail; + } + debug_print_bignum("SAE: PWE", pwe, prime_len); + +fail: + crypto_bignum_deinit(tmp, 1); + crypto_bignum_deinit(val, 1); + crypto_bignum_deinit(one, 0); + crypto_bignum_deinit(prime, 0); + crypto_bignum_deinit(order, 0); + return pwe; +} + +void sae_deinit_pt(struct sae_pt *pt) +{ + struct sae_pt *prev; + + while (pt) { + crypto_ec_point_deinit(pt->ecc_pt, 1); + crypto_bignum_deinit(pt->ffc_pt, 1); + crypto_ec_deinit(pt->ec); + prev = pt; + pt = pt->next; + os_free(prev); + } +} + static int sae_derive_commit_element_ecc(struct sae_data *sae, struct crypto_bignum *mask) { @@ -710,18 +1402,71 @@ fail: int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, - const char *identifier, struct sae_data *sae) + struct sae_data *sae) { if (sae->tmp == NULL || (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, - password_len, - identifier) < 0) || + password_len) < 0) || (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, - password_len, - identifier) < 0) || - sae_derive_commit(sae) < 0) + password_len) < 0)) return ESP_FAIL; - return ESP_OK; + + sae->h2e = 0; + return sae_derive_commit(sae); +} + +int sae_prepare_commit_pt(struct sae_data *sae, struct sae_pt *pt, + const u8 *addr1, const u8 *addr2, + int *rejected_groups) +{ + if (!sae->tmp) + return -1; + + while (pt) { + if (pt->group == sae->group) + break; + pt = pt->next; + } + if (!pt) { + wpa_printf(MSG_INFO, "SAE: Could not find PT for group %u", + sae->group); + return -1; + } + + sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0; + wpabuf_free(sae->tmp->own_rejected_groups); + sae->tmp->own_rejected_groups = NULL; + if (rejected_groups) { + int count, i; + struct wpabuf *groups; + + count = int_array_len(rejected_groups); + groups = wpabuf_alloc(count * 2); + if (!groups) + return -1; + for (i = 0; i < count; i++) + wpabuf_put_le16(groups, rejected_groups[i]); + sae->tmp->own_rejected_groups = groups; + } + + if (pt->ec) { + crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1); + sae->tmp->pwe_ecc = sae_derive_pwe_from_pt_ecc(pt, addr1, + addr2); + if (!sae->tmp->pwe_ecc) + return -1; + } + + if (pt->dh) { + crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); + sae->tmp->pwe_ffc = sae_derive_pwe_from_pt_ffc(pt, addr1, + addr2); + if (!sae->tmp->pwe_ffc) + return -1; + } + + sae->h2e = 1; + return sae_derive_commit(sae); } static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) @@ -796,49 +1541,113 @@ 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) +{ + if (hash_len == 32) + return sha256_prf(k, hash_len, label, + context, context_len, out, out_len); + return -1; +} + static int sae_derive_keys(struct sae_data *sae, const u8 *k) { - u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; - u8 keyseed[SHA256_MAC_LEN]; - u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; + u8 zero[SAE_MAX_HASH_LEN], val[SAE_MAX_PRIME_LEN]; + const u8 *salt; + struct wpabuf *rejected_groups = NULL; + u8 keyseed[SAE_MAX_HASH_LEN]; + u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN]; struct crypto_bignum *tmp; int ret = -1; + size_t hash_len, salt_len, prime_len = sae->tmp->prime_len; + const u8 *addr[1]; + size_t len[1]; tmp = crypto_bignum_init(); if (tmp == NULL) goto fail; - /* keyseed = H(<0>32, k) - * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK", + /* keyseed = H(salt, k) + * KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK", * (commit-scalar + peer-commit-scalar) modulo r) * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) */ + if (!sae->h2e) + hash_len = SHA256_MAC_LEN; + else if (sae->tmp->dh) + hash_len = sae_ffc_prime_len_2_hash_len(prime_len); + else + hash_len = sae_ecc_prime_len_2_hash_len(prime_len); + if (sae->h2e && (sae->tmp->own_rejected_groups || + sae->tmp->peer_rejected_groups)) { + struct wpabuf *own, *peer; - os_memset(null_key, 0, sizeof(null_key)); - if (hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, - keyseed) < 0) + own = sae->tmp->own_rejected_groups; + peer = sae->tmp->peer_rejected_groups; + salt_len = 0; + if (own) + salt_len += wpabuf_len(own); + if (peer) + salt_len += wpabuf_len(peer); + rejected_groups = wpabuf_alloc(salt_len); + if (!rejected_groups) + goto fail; + if (sae->tmp->own_addr_higher) { + if (own) + wpabuf_put_buf(rejected_groups, own); + if (peer) + wpabuf_put_buf(rejected_groups, peer); + } else { + if (peer) + wpabuf_put_buf(rejected_groups, peer); + if (own) + wpabuf_put_buf(rejected_groups, own); + } + salt = wpabuf_head(rejected_groups); + salt_len = wpabuf_len(rejected_groups); + } else { + os_memset(zero, 0, hash_len); + salt = zero; + salt_len = hash_len; + } + wpa_hexdump(MSG_DEBUG, "SAE: salt for keyseed derivation", + salt, salt_len); + addr[0] = k; + len[0] = prime_len; + if (hkdf_extract(hash_len, salt, salt_len, 1, addr, len, keyseed) < 0) goto fail; + wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, hash_len); - wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); - - crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, - tmp); - crypto_bignum_mod(tmp, sae->tmp->order, tmp); - crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); + if (crypto_bignum_add(sae->tmp->own_commit_scalar, + sae->peer_commit_scalar, tmp) < 0 || + crypto_bignum_mod(tmp, sae->tmp->order, tmp) < 0) + goto fail; + /* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit + * string that is needed for KCK, PMK, and PMKID derivation, but it + * seems to make most sense to encode the + * (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); wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); - if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", - val, sae->tmp->prime_len, keys, sizeof(keys)) < 0) + + if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK", + val, sae->tmp->order_len, + keys, hash_len + SAE_PMK_LEN) < 0) goto fail; forced_memzero(keyseed, sizeof(keyseed)); - os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); - os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); + os_memcpy(sae->tmp->kck, keys, hash_len); + os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN); os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); forced_memzero(keys, sizeof(keys)); - wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); + wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", + sae->tmp->kck, SAE_KCK_LEN); wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); ret = 0; fail: + wpabuf_free(rejected_groups); crypto_bignum_deinit(tmp, 0); return ret; } @@ -855,7 +1664,7 @@ int sae_process_commit(struct sae_data *sae) } int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token, const char *identifier) + const struct wpabuf *token, const char *identifier) { u8 *pos; @@ -863,7 +1672,7 @@ int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, return ESP_FAIL; wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ - if (token) { + if (!sae->h2e && token) { wpabuf_put_buf(buf, token); wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", wpabuf_head(token), wpabuf_len(token)); @@ -908,6 +1717,25 @@ int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", identifier); } + if (sae->h2e && sae->tmp->own_rejected_groups) { + wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups", + sae->tmp->own_rejected_groups); + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, + 1 + wpabuf_len(sae->tmp->own_rejected_groups)); + wpabuf_put_u8(buf, WLAN_EID_EXT_REJECTED_GROUPS); + wpabuf_put_buf(buf, sae->tmp->own_rejected_groups); + } + + if (sae->h2e && token) { + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + wpabuf_len(token)); + wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN); + wpabuf_put_buf(buf, token); + wpa_hexdump_buf(MSG_DEBUG, + "SAE: Anti-clogging token (in container)", + token); + } return ESP_OK; } @@ -963,30 +1791,45 @@ static int sae_is_password_id_elem(const u8 *pos, const u8 *end) return ret; } + +static int sae_is_rejected_groups_elem(const u8 *pos, const u8 *end) +{ + return end - pos >= 3 && + pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 2 && + end - pos - 2 >= pos[1] && + pos[2] == WLAN_EID_EXT_REJECTED_GROUPS; +} + + +static int sae_is_token_container_elem(const u8 *pos, const u8 *end) +{ + return end - pos >= 3 && + pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 && + end - pos - 2 >= pos[1] && + pos[2] == WLAN_EID_EXT_ANTI_CLOGGING_TOKEN; +} + + static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, const u8 *end, const u8 **token, - size_t *token_len) + size_t *token_len, int h2e) { size_t scalar_elem_len, tlen; - const u8 *elem; if (token) *token = NULL; if (token_len) *token_len = 0; + if (h2e) + return; /* No Anti-Clogging Token field outside container IE */ + scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; if (scalar_elem_len >= (size_t) (end - *pos)) return; /* No extra data beyond peer scalar and element */ - /* It is a bit difficult to parse this now that there is an - * optional variable length Anti-Clogging Token field and - * optional variable length Password Identifier element in the - * frame. We are sending out fixed length Anti-Clogging Token - * fields, so use that length as a requirement for the received - * token and check for the presence of possible Password - * Identifier element based on the element header information. - */ tlen = end - (*pos + scalar_elem_len); if (tlen < SHA256_MAC_LEN) { @@ -996,21 +1839,6 @@ static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, return; } - elem = *pos + scalar_elem_len; - if (sae_is_password_id_elem(elem, end)) { - /* Password Identifier element takes out all available - * extra octets, so there can be no Anti-Clogging token in - * this frame. */ - return; - } - - elem += SHA256_MAC_LEN; - if (sae_is_password_id_elem(elem, end)) { - /* Password Identifier element is included in the end, so - * remove its length from the Anti-Clogging token field. */ - tlen -= 2 + elem[1]; - } - wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); if (token) *token = *pos; @@ -1019,6 +1847,22 @@ static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, *pos += tlen; } + +static void sae_parse_token_container(struct sae_data *sae, + const u8 *pos, const u8 *end, + const u8 **token, size_t *token_len) +{ + wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", + pos, end - pos); + if (!sae_is_token_container_elem(pos, end)) + return; + *token = pos + 3; + *token_len = pos[1] - 1; + wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token (in container)", + *token, *token_len); +} + + static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, const u8 *end) { @@ -1039,8 +1883,9 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, * shall be dropped if the peer-scalar is identical to the one used in * the existing protocol instance. */ - if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && - crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { + if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar_accepted && + crypto_bignum_cmp(sae->peer_commit_scalar_accepted, + peer_scalar) == 0) { wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " "peer-commit-scalar"); crypto_bignum_deinit(peer_scalar, 0); @@ -1169,23 +2014,36 @@ static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, } static int sae_parse_password_identifier(struct sae_data *sae, - const u8 *pos, const u8 *end) + const u8 **pos, const u8 *end) { + const u8 *epos; + u8 len; + wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", - pos, end - pos); - if (!sae_is_password_id_elem(pos, end)) { + *pos, end - *pos); + if (!sae_is_password_id_elem(*pos, end)) { if (sae->tmp->pw_id) { wpa_printf(MSG_DEBUG, "SAE: No Password Identifier included, but expected one (%s)", sae->tmp->pw_id); return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; } + os_free(sae->tmp->pw_id); + sae->tmp->pw_id = NULL; return WLAN_STATUS_SUCCESS; /* No Password Identifier */ } + epos = *pos; + epos++; /* skip IE type */ + len = *epos++; /* IE length */ + if (len > end - epos || len < 1) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + epos++; /* skip ext ID */ + len--; + if (sae->tmp->pw_id && - (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) || - os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) { + (len != os_strlen(sae->tmp->pw_id) || + os_memcmp(sae->tmp->pw_id, epos, len) != 0)) { wpa_printf(MSG_DEBUG, "SAE: The included Password Identifier does not match the expected one (%s)", sae->tmp->pw_id); @@ -1193,16 +2051,50 @@ static int sae_parse_password_identifier(struct sae_data *sae, } os_free(sae->tmp->pw_id); - sae->tmp->pw_id = os_malloc(pos[1]); + sae->tmp->pw_id = os_malloc(len + 1); if (!sae->tmp->pw_id) return WLAN_STATUS_UNSPECIFIED_FAILURE; - os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1); - sae->tmp->pw_id[pos[1] - 1] = '\0'; + os_memcpy(sae->tmp->pw_id, epos, len); + sae->tmp->pw_id[len] = '\0'; + *pos = epos + len; return WLAN_STATUS_SUCCESS; } + +static int sae_parse_rejected_groups(struct sae_data *sae, + const u8 **pos, const u8 *end) +{ + const u8 *epos; + u8 len; + + wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", + *pos, end - *pos); + if (!sae_is_rejected_groups_elem(*pos, end)) + return WLAN_STATUS_SUCCESS; + + epos = *pos; + epos++; /* skip IE type */ + len = *epos++; /* IE length */ + if (len > end - epos || len < 1) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + epos++; /* skip ext ID */ + len--; + + wpabuf_free(sae->tmp->peer_rejected_groups); + sae->tmp->peer_rejected_groups = wpabuf_alloc(len); + if (!sae->tmp->peer_rejected_groups) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_put_data(sae->tmp->peer_rejected_groups, epos, len); + wpa_hexdump_buf(MSG_DEBUG, "SAE: Received Rejected Groups list", + sae->tmp->peer_rejected_groups); + *pos = epos + len; + return WLAN_STATUS_SUCCESS; +} + + u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, - const u8 **token, size_t *token_len, int *allowed_groups) + const u8 **token, size_t *token_len, int *allowed_groups, + int h2e) { const u8 *pos = data, *end = data + len; u16 res; @@ -1216,7 +2108,7 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, pos += 2; /* Optional Anti-Clogging Token */ - sae_parse_commit_token(sae, &pos, end, token, token_len); + sae_parse_commit_token(sae, &pos, end, token, token_len, h2e); /* commit-scalar */ res = sae_parse_commit_scalar(sae, &pos, end); @@ -1229,10 +2121,21 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, return res; /* Optional Password Identifier element */ - res = sae_parse_password_identifier(sae, pos, end); + res = sae_parse_password_identifier(sae, &pos, end); if (res != WLAN_STATUS_SUCCESS) return res; + /* Conditional Rejected Groups element */ + if (h2e) { + res = sae_parse_rejected_groups(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + } + + /* Optional Anti-Clogging Token Container element */ + if (h2e) + sae_parse_token_container(sae, pos, end, token, token_len); + /* * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as * the values we sent which would be evidence of a reflection attack. @@ -1259,12 +2162,13 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, return SAE_SILENTLY_DISCARD; } -static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, - const struct crypto_bignum *scalar1, - const u8 *element1, size_t element1_len, - const struct crypto_bignum *scalar2, - const u8 *element2, size_t element2_len, - u8 *confirm) + +static int sae_cn_confirm(struct sae_data *sae, const u8 *sc, + const struct crypto_bignum *scalar1, + const u8 *element1, size_t element1_len, + const struct crypto_bignum *scalar2, + const u8 *element2, size_t element2_len, + u8 *confirm) { const u8 *addr[5]; size_t len[5]; @@ -1278,41 +2182,42 @@ static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) */ + if (crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), + sae->tmp->prime_len) < 0 || + crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), + sae->tmp->prime_len) < 0) + return ESP_FAIL; addr[0] = sc; len[0] = 2; - crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), - sae->tmp->prime_len); addr[1] = scalar_b1; len[1] = sae->tmp->prime_len; addr[2] = element1; len[2] = element1_len; - crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), - sae->tmp->prime_len); addr[3] = scalar_b2; len[3] = sae->tmp->prime_len; addr[4] = element2; len[4] = element2_len; - hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, - confirm); + return hkdf_extract(SAE_KCK_LEN, sae->tmp->kck, SAE_KCK_LEN, + 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, - const struct crypto_bignum *scalar2, - const struct crypto_ec_point *element2, - u8 *confirm) + const struct crypto_bignum *scalar1, + const struct crypto_ec_point *element1, + const struct crypto_bignum *scalar2, + const struct crypto_ec_point *element2, + u8 *confirm) { u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; if (crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, - element_b1 + sae->tmp->prime_len) < 0) { + element_b1 + sae->tmp->prime_len) < 0) { wpa_printf(MSG_ERROR, "SAE: failed bignum op while deriving ec point"); return ESP_FAIL; } if (crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, - element_b2 + sae->tmp->prime_len) < 0) { + element_b2 + sae->tmp->prime_len) < 0) { wpa_printf(MSG_ERROR, "SAE: failed bignum op while deriving ec point"); return ESP_FAIL; } @@ -1343,8 +2248,9 @@ static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, return ESP_FAIL; } - sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, - scalar2, element_b2, sae->tmp->prime_len, confirm); + if (sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, + scalar2, element_b2, sae->tmp->prime_len, confirm) < 0) + return -1; return ESP_OK; } @@ -1363,19 +2269,19 @@ int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) if (sae->tmp->ec) { if (sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, - sae->tmp->own_commit_element_ecc, - sae->peer_commit_scalar, - sae->tmp->peer_commit_element_ecc, - wpabuf_put(buf, SHA256_MAC_LEN))) { + sae->tmp->own_commit_element_ecc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ecc, + wpabuf_put(buf, SHA256_MAC_LEN))) { wpa_printf(MSG_ERROR, "SAE: failed generate SAE confirm (ecc)"); return ESP_FAIL; } } else { if (sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, - sae->tmp->own_commit_element_ffc, - sae->peer_commit_scalar, - sae->tmp->peer_commit_element_ffc, - wpabuf_put(buf, SHA256_MAC_LEN))) { + sae->tmp->own_commit_element_ffc, + sae->peer_commit_scalar, + sae->tmp->peer_commit_element_ffc, + wpabuf_put(buf, SHA256_MAC_LEN))) { wpa_printf(MSG_ERROR, "SAE: failed generate SAE confirm (ffc)"); return ESP_FAIL; } @@ -1385,16 +2291,18 @@ int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) { - u8 verifier[SHA256_MAC_LEN]; + u8 verifier[SAE_MAX_HASH_LEN]; + size_t hash_len= SAE_KCK_LEN; - if (len < 2 + SHA256_MAC_LEN) { + if (len < 2 + hash_len) { wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); return ESP_FAIL; } wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); - if (sae->tmp == NULL) { + if (sae->tmp == NULL || !sae->peer_commit_scalar || + !sae->tmp->own_commit_scalar) { wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); return ESP_FAIL; } @@ -1419,12 +2327,12 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) } } - if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) { + if (os_memcmp(verifier, data + 2, hash_len) != 0) { wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", - data + 2, SHA256_MAC_LEN); + data + 2, hash_len); wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", - verifier, SHA256_MAC_LEN); + verifier, hash_len); return ESP_FAIL; } diff --git a/components/wpa_supplicant/src/common/sae.h b/components/wpa_supplicant/src/common/sae.h index 24437c0d02..f71a6ad1f4 100644 --- a/components/wpa_supplicant/src/common/sae.h +++ b/components/wpa_supplicant/src/common/sae.h @@ -21,8 +21,9 @@ #define SAE_KEYSEED_KEY_LEN 32 #define SAE_MAX_PRIME_LEN 512 #define SAE_MAX_ECC_PRIME_LEN 66 -#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN) -#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN) +#define SAE_MAX_HASH_LEN 64 +#define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN + 255) +#define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_HASH_LEN) /* Special value returned by sae_parse_commit() */ #define SAE_SILENTLY_DISCARD 65535 @@ -45,6 +46,20 @@ struct sae_temporary_data { struct crypto_bignum *prime_buf; struct crypto_bignum *order_buf; char *pw_id; + int order_len; + struct wpabuf *own_rejected_groups; + struct wpabuf *peer_rejected_groups; + unsigned int own_addr_higher:1; +}; + +struct sae_pt { + struct sae_pt *next; + int group; + struct crypto_ec *ec; + struct crypto_ec_point *ecc_pt; + + const struct dh_group *dh; + struct crypto_bignum *ffc_pt; }; enum { @@ -66,6 +81,8 @@ struct sae_data { unsigned int sync; /* protocol instance variable: Sync */ u16 rc; /* protocol instance variable: Rc (received send-confirm) */ struct sae_temporary_data *tmp; + struct crypto_bignum *peer_commit_scalar_accepted; + unsigned int h2e:1; }; int sae_set_group(struct sae_data *sae, int group); @@ -74,16 +91,32 @@ 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, - const char *identifier, struct sae_data *sae); + struct sae_data *sae); +int sae_prepare_commit_pt(struct sae_data *sae, struct sae_pt *pt, + const u8 *addr1, const u8 *addr2, + int *rejected_groups); int sae_process_commit(struct sae_data *sae); int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token, const char *identifier); + const struct wpabuf *token, const char *identifier); u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, - const u8 **token, size_t *token_len, int *allowed_groups); + const u8 **token, size_t *token_len, int *allowed_groups, + int h2e); int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group); const char * sae_state_txt(enum sae_state state); +size_t sae_ecc_prime_len_2_hash_len(size_t prime_len); +size_t sae_ffc_prime_len_2_hash_len(size_t prime_len); +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); +struct crypto_ec_point * +sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt, + const u8 *addr1, const u8 *addr2); +struct crypto_bignum * +sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt, + const u8 *addr1, const u8 *addr2); +void sae_deinit_pt(struct sae_pt *pt); #endif /* SAE_H */ #endif /* CONFIG_WPA3_SAE */ diff --git a/components/wpa_supplicant/src/common/wpa_common.c b/components/wpa_supplicant/src/common/wpa_common.c index 34d3c73756..6d74532c64 100644 --- a/components/wpa_supplicant/src/common/wpa_common.c +++ b/components/wpa_supplicant/src/common/wpa_common.c @@ -365,6 +365,25 @@ int wpa_cipher_valid_mgmt_group(int cipher) cipher == WPA_CIPHER_BIP_GMAC_256; } +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_get_config_sae_pwe_h2e_internal(); + memset(data, 0, sizeof(*data)); + + if (rsnxe_ie_len < 1) { + return -1; + } + rsnxe_capa = rsnxe_ie[2]; + if (sae_pwe == 1 && !(rsnxe_capa & BIT(WLAN_RSNX_CAPAB_SAE_H2E))){ + wpa_printf(MSG_ERROR, "SAE H2E required, but not supported by the AP"); + return -1; + } + data->rsnxe_capa = rsnxe_capa; + return 0; +} + /** * wpa_parse_wpa_ie_rsn - Parse RSN IE * @rsn_ie: Buffer containing RSN IE @@ -941,7 +960,8 @@ int wpa_compare_rsn_ie(int ft_initial_assoc, ie1d.group_cipher == ie2d.group_cipher && ie1d.key_mgmt == ie2d.key_mgmt && ie1d.capabilities == ie2d.capabilities && - ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher) + ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher && + ie1d.rsnxe_capa == ie2d.rsnxe_capa) return 0; } #endif /* CONFIG_IEEE80211R */ diff --git a/components/wpa_supplicant/src/common/wpa_common.h b/components/wpa_supplicant/src/common/wpa_common.h index f232f73cf1..86e158505c 100644 --- a/components/wpa_supplicant/src/common/wpa_common.h +++ b/components/wpa_supplicant/src/common/wpa_common.h @@ -337,6 +337,7 @@ struct wpa_ie_data { size_t num_pmkid; const u8 *pmkid; int mgmt_group_cipher; + uint8_t rsnxe_capa; }; struct rsn_sppamsdu_sup { @@ -372,6 +373,8 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); +int wpa_parse_wpa_ie_rsnxe(const u8 *rsnxe_ie, size_t rsnxe_ie_len, + struct wpa_ie_data *data); int wpa_compare_rsn_ie(int ft_initial_assoc, const u8 *ie1, size_t ie1len, diff --git a/components/wpa_supplicant/src/crypto/crypto.h b/components/wpa_supplicant/src/crypto/crypto.h index 8c6c0a1c74..f7215536d1 100644 --- a/components/wpa_supplicant/src/crypto/crypto.h +++ b/components/wpa_supplicant/src/crypto/crypto.h @@ -654,6 +654,26 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, const struct crypto_bignum *c, struct crypto_bignum *d); +/** + * crypto_bignum_sqrmod - c = a^2 (mod b) + * @a: Bignum + * @b: Bignum + * @c: Bignum; used to store the result of a^2 % b + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_sqrmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *c); +/** + * crypto_bignum_rshift - r = a >> n + * @a: Bignum + * @n: Number of bits + * @r: Bignum; used to store the result of a >> n + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r); + /** * crypto_bignum_cmp - Compare two bignums * @a: Bignum @@ -730,6 +750,13 @@ size_t crypto_ec_prime_len(struct crypto_ec *e); */ size_t crypto_ec_prime_len_bits(struct crypto_ec *e); +/** + * crypto_ec_order_len - Get length of the order in octets + * @e: EC context from crypto_ec_init() + * Returns: Length of the order defining the group + */ +size_t crypto_ec_order_len(struct crypto_ec *e); + /** * crypto_ec_get_prime - Get prime defining an EC group * @e: EC context from crypto_ec_init() @@ -750,6 +777,14 @@ const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e); * Internal data structure for EC implementation to represent a point. The * contents is specific to the used crypto library. */ + +/** + * crypto_ec_get_b - Get 'b' coeffiecient of an EC group's curve + * @e: EC context from crypto_ec_init() + * Returns: 'b' coefficient (bignum) of the group + */ +const struct crypto_bignum * crypto_ec_get_b(struct crypto_ec *e); + struct crypto_ec_point; /** diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.c b/components/wpa_supplicant/src/rsn_supp/wpa.c index ba9986e80c..7a889bd584 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa.c @@ -650,6 +650,8 @@ void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; int res; + u8 *kde, *kde_buf = NULL; + size_t kde_len; wpa_sm_set_state(WPA_FIRST_HALF_4WAY_HANDSHAKE); @@ -705,16 +707,30 @@ void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, sm->tptk_set = 1; sm->ptk_set = 0; sm->key_install = true; + kde = sm->assoc_wpa_ie; + kde_len = sm->assoc_wpa_ie_len; + kde_buf = os_malloc(kde_len + + sm->assoc_rsnxe_len); + if (!kde_buf) + goto failed; + os_memcpy(kde_buf, kde, kde_len); + kde = kde_buf; + + if (sm->assoc_rsnxe && sm->assoc_rsnxe_len) { + os_memcpy(kde + kde_len, sm->assoc_rsnxe, sm->assoc_rsnxe_len); + kde_len += sm->assoc_rsnxe_len; + } if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, - sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, - ptk)) + kde, kde_len, ptk)) goto failed; + os_free(kde_buf); memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); return; failed: + os_free(kde_buf); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -1104,6 +1120,22 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, return -1; } + if (sm->proto == WPA_PROTO_RSN && + ((sm->ap_rsnxe && !ie->rsnxe) || + (!sm->ap_rsnxe && ie->rsnxe) || + (sm->ap_rsnxe && ie->rsnxe && + (sm->ap_rsnxe_len != ie->rsnxe_len || + os_memcmp(sm->ap_rsnxe, ie->rsnxe, sm->ap_rsnxe_len) != 0)))) { + wpa_printf(MSG_INFO, + "WPA: RSNXE mismatch between Beacon/ProbeResp and EAPOL-Key msg 3/4"); + wpa_hexdump(MSG_INFO, "RSNXE in Beacon/ProbeResp", + sm->ap_rsnxe, sm->ap_rsnxe_len); + wpa_hexdump(MSG_INFO, "RSNXE in EAPOL-Key msg 3/4", + ie->rsnxe, ie->rsnxe_len); + wpa_sm_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); + return -1; + } + return 0; } @@ -2209,6 +2241,8 @@ void wpa_sm_deinit(void) { struct wpa_sm *sm = &gWpaSm; pmksa_cache_deinit(sm->pmksa); + os_free(sm->ap_rsnxe); + os_free(sm->assoc_rsnxe); } @@ -2263,6 +2297,8 @@ int wpa_set_bss(char *macddr, char * bssid, u8 pairwise_cipher, u8 group_cipher, int res = 0; struct wpa_sm *sm = &gWpaSm; bool use_pmk_cache = true; + u8 assoc_rsnxe[20]; + size_t assoc_rsnxe_len = sizeof(assoc_rsnxe); /* Incase AP has changed it's SSID, don't try with PMK caching for SAE connection */ /* Ideally we should use network_ctx for this purpose however currently network profile block @@ -2357,7 +2393,14 @@ int wpa_set_bss(char *macddr, char * bssid, u8 pairwise_cipher, u8 group_cipher, if (res < 0) return -1; sm->assoc_wpa_ie_len = res; - esp_set_assoc_ie((uint8_t *)bssid, NULL, 0, true); + res = wpa_gen_rsnxe(sm ,assoc_rsnxe, assoc_rsnxe_len); + if (res < 0) + return -1; + assoc_rsnxe_len = res; + res = wpa_sm_set_assoc_rsnxe(sm, assoc_rsnxe, assoc_rsnxe_len); + if (res < 0) + return -1; + esp_set_assoc_ie((uint8_t *)bssid, assoc_rsnxe, assoc_rsnxe_len, true); os_memset(sm->ssid, 0, sizeof(sm->ssid)); os_memcpy(sm->ssid, ssid, ssid_len); sm->ssid_len = ssid_len; @@ -2611,6 +2654,52 @@ struct wpa_sm * get_wpa_sm(void) return &gWpaSm; } +int wpa_sm_set_ap_rsnxe(const u8 *ie, size_t len) +{ + struct wpa_sm *sm = &gWpaSm; + if (!sm) + return -1; + + os_free(sm->ap_rsnxe); + if (!ie || len == 0) { + wpa_hexdump(MSG_DEBUG, "WPA: set AP RSNXE", ie, len); + sm->ap_rsnxe = NULL; + sm->ap_rsnxe_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "WPA: set AP RSNXE", ie, len); + sm->ap_rsnxe = os_memdup(ie, len); + if (!sm->ap_rsnxe) + return -1; + + sm->ap_rsnxe_len = len; + } + + sm->sae_pwe = esp_wifi_get_config_sae_pwe_h2e_internal(); + return 0; +} + + +int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len) +{ + if (!sm) + return -1; + + os_free(sm->assoc_rsnxe); + if (!ie || len == 0) { + sm->assoc_rsnxe = NULL; + sm->assoc_rsnxe_len = 0; + } else { + wpa_hexdump(MSG_DEBUG, "RSN: set own RSNXE", ie, len); + sm->assoc_rsnxe = os_memdup(ie, len); + if (!sm->assoc_rsnxe) + return -1; + + sm->assoc_rsnxe_len = len; + } + + return 0; +} + #ifdef CONFIG_OWE_STA struct wpabuf *owe_build_assoc_req(struct wpa_sm *sm, u16 group) { diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.h b/components/wpa_supplicant/src/rsn_supp/wpa.h index ddb15a328a..908d3b9263 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa.h +++ b/components/wpa_supplicant/src/rsn_supp/wpa.h @@ -54,6 +54,10 @@ int wpa_sm_set_key(struct install_key *sm, enum wpa_alg alg, u8 *key, size_t key_len, enum key_flag key_flag); +int wpa_sm_set_ap_rsnxe(const u8 *ie, size_t len); + +int wpa_sm_set_assoc_rsnxe(struct wpa_sm *sm, const u8 *ie, size_t len); + #ifdef CONFIG_IEEE80211R int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len); diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_i.h b/components/wpa_supplicant/src/rsn_supp/wpa_i.h index da55b32081..8fb4d40d9f 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_i.h +++ b/components/wpa_supplicant/src/rsn_supp/wpa_i.h @@ -48,11 +48,14 @@ struct wpa_sm { void *network_ctx; int rsn_enabled; /* Whether RSN is enabled in configuration */ + int sae_pwe; /* SAE PWE generation options */ int countermeasures; /*TKIP countermeasures state flag, 1:in countermeasures state*/ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ size_t assoc_wpa_ie_len; + u8 *assoc_rsnxe; /* Own RSNXE from (Re)AssocReq */ + size_t assoc_rsnxe_len; u8 eapol_version; @@ -64,8 +67,8 @@ struct wpa_sm { unsigned int proto; enum wpa_states wpa_state; - u8 *ap_wpa_ie, *ap_rsn_ie; - size_t ap_wpa_ie_len, ap_rsn_ie_len; + u8 *ap_wpa_ie, *ap_rsn_ie, *ap_rsnxe; + size_t ap_wpa_ie_len, ap_rsn_ie_len, ap_rsnxe_len; bool key_install; diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_ie.c b/components/wpa_supplicant/src/rsn_supp/wpa_ie.c index 341beb837a..7e4102addd 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_ie.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa_ie.c @@ -35,6 +35,8 @@ int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, { if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN) { return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data); + } else if (wpa_ie_len >=1 && wpa_ie[0] == WLAN_EID_RSNX){ + return wpa_parse_wpa_ie_rsnxe(wpa_ie, wpa_ie_len, data); } else if (wpa_ie[0] == WLAN_EID_WAPI) { return 0; } @@ -293,6 +295,34 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) } +int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len) +{ + u8 *pos = rsnxe; + u16 capab = 0; + size_t flen; + + if (wpa_key_mgmt_sae(sm->key_mgmt) && + (sm->sae_pwe == 1 || sm->sae_pwe == 2)) { + capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E); + } + + flen = (capab & 0xff00) ? 2 : 1; + if (!capab) + return 0; /* no supported extended RSN capabilities */ + if (rsnxe_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 - rsnxe; +} + /** * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs * @pos: Pointer to the IE header @@ -393,6 +423,11 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, ie->rsn_ie_len = pos[1] + 2; wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", ie->rsn_ie, ie->rsn_ie_len); + } 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) diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_ie.h b/components/wpa_supplicant/src/rsn_supp/wpa_ie.h index 98ba648794..5a9b77de0e 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_ie.h +++ b/components/wpa_supplicant/src/rsn_supp/wpa_ie.h @@ -37,12 +37,14 @@ struct wpa_eapol_ie_parse { const u8 *reassoc_deadline; const u8 *key_lifetime; #endif /* CONFIG_IEEE80211R */ + const u8 *rsnxe; + size_t rsnxe_len; }; int wpa_supplicant_parse_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie); int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len); - +int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len); int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); diff --git a/components/wpa_supplicant/src/utils/const_time.h b/components/wpa_supplicant/src/utils/const_time.h new file mode 100644 index 0000000000..ab8f611ef6 --- /dev/null +++ b/components/wpa_supplicant/src/utils/const_time.h @@ -0,0 +1,191 @@ +/* + * Helper functions for constant time operations + * Copyright (c) 2019, The Linux Foundation + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * These helper functions can be used to implement logic that needs to minimize + * externally visible differences in execution path by avoiding use of branches, + * avoiding early termination or other time differences, and forcing same memory + * access pattern regardless of values. + */ + +#ifndef CONST_TIME_H +#define CONST_TIME_H + + +#if defined(__clang__) +#define NO_UBSAN_UINT_OVERFLOW \ + __attribute__((no_sanitize("unsigned-integer-overflow"))) +#else +#define NO_UBSAN_UINT_OVERFLOW +#endif + + +/** + * const_time_fill_msb - Fill all bits with MSB value + * @val: Input value + * Returns: Value with all the bits set to the MSB of the input val + */ +static inline unsigned int const_time_fill_msb(unsigned int val) +{ + /* Move the MSB to LSB and multiple by -1 to fill in all bits. */ + return (val >> (sizeof(val) * 8 - 1)) * ~0U; +} + + +/* Returns: -1 if val is zero; 0 if val is not zero */ +static inline unsigned int const_time_is_zero(unsigned int val) + NO_UBSAN_UINT_OVERFLOW +{ + /* Set MSB to 1 for 0 and fill rest of bits with the MSB value */ + return const_time_fill_msb(~val & (val - 1)); +} + + +/* Returns: -1 if a == b; 0 if a != b */ +static inline unsigned int const_time_eq(unsigned int a, unsigned int b) +{ + return const_time_is_zero(a ^ b); +} + + +/* Returns: -1 if a == b; 0 if a != b */ +static inline u8 const_time_eq_u8(unsigned int a, unsigned int b) +{ + return (u8) const_time_eq(a, b); +} + + +/** + * const_time_eq_bin - Constant time memory comparison + * @a: First buffer to compare + * @b: Second buffer to compare + * @len: Number of octets to compare + * Returns: -1 if buffers are equal, 0 if not + * + * This function is meant for comparing passwords or hash values where + * difference in execution time or memory access pattern could provide external + * observer information about the location of the difference in the memory + * buffers. The return value does not behave like memcmp(), i.e., + * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike + * memcmp(), the execution time of const_time_eq_bin() does not depend on the + * contents of the compared memory buffers, but only on the total compared + * length. + */ +static inline unsigned int const_time_eq_bin(const void *a, const void *b, + size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + size_t i; + u8 res = 0; + + for (i = 0; i < len; i++) + res |= aa[i] ^ bb[i]; + + return const_time_is_zero(res); +} + + +/** + * const_time_select - Constant time unsigned int selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline unsigned int const_time_select(unsigned int mask, + unsigned int true_val, + unsigned int false_val) +{ + return (mask & true_val) | (~mask & false_val); +} + + +/** + * const_time_select_int - Constant time int selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline int const_time_select_int(unsigned int mask, int true_val, + int false_val) +{ + return (int) const_time_select(mask, (unsigned int) true_val, + (unsigned int) false_val); +} + + +/** + * const_time_select_u8 - Constant time u8 selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val) +{ + return (u8) const_time_select(mask, true_val, false_val); +} + + +/** + * const_time_select_s8 - Constant time s8 selection + * @mask: 0 (false) or -1 (true) to identify which value to select + * @true_val: Value to select for the true case + * @false_val: Value to select for the false case + * Returns: true_val if mask == -1, false_val if mask == 0 + */ +static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val) +{ + return (s8) const_time_select(mask, (unsigned int) true_val, + (unsigned int) false_val); +} + + +/** + * const_time_select_bin - Constant time binary buffer selection copy + * @mask: 0 (false) or -1 (true) to identify which value to copy + * @true_val: Buffer to copy for the true case + * @false_val: Buffer to copy for the false case + * @len: Number of octets to copy + * @dst: Destination buffer for the copy + * + * This function copies the specified buffer into the destination buffer using + * operations with identical memory access pattern regardless of which buffer + * is being copied. + */ +static inline void const_time_select_bin(u8 mask, const u8 *true_val, + const u8 *false_val, size_t len, + u8 *dst) +{ + size_t i; + + for (i = 0; i < len; i++) + dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]); +} + + +static inline int const_time_memcmp(const void *a, const void *b, size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + int diff, res = 0; + unsigned int mask; + + if (len == 0) + return 0; + do { + len--; + diff = (int) aa[len] - (int) bb[len]; + mask = const_time_is_zero((unsigned int) diff); + res = const_time_select_int(mask, res, diff); + } while (len); + + return res; +} + +#endif /* CONST_TIME_H */ diff --git a/components/wpa_supplicant/test/test_sae.c b/components/wpa_supplicant/test/test_sae.c index a9616e34c9..8dd06d889f 100644 --- a/components/wpa_supplicant/test/test_sae.c +++ b/components/wpa_supplicant/test/test_sae.c @@ -74,7 +74,7 @@ TEST_CASE("Test SAE functionality with ECC group", "[wpa3_sae]") TEST_ASSERT(sae_set_group(&sae, IANA_SECP256R1) == 0); - TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd, strlen((const char *)pwd), NULL, &sae) == 0); + TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd, strlen((const char *)pwd), &sae) == 0); buf = wpabuf_alloc2(SAE_COMMIT_MAX_LEN); @@ -84,7 +84,7 @@ TEST_CASE("Test SAE functionality with ECC group", "[wpa3_sae]") /* Parsing commit created by self will be detected as reflection attack*/ TEST_ASSERT(sae_parse_commit(&sae, - wpabuf_mhead(buf), buf->used, NULL, 0, default_groups) == SAE_SILENTLY_DISCARD); + wpabuf_mhead(buf), buf->used, NULL, 0, default_groups, 0) == SAE_SILENTLY_DISCARD); wpabuf_free2(buf); sae_clear_temp_data(&sae); @@ -112,10 +112,10 @@ TEST_CASE("Test SAE functionality with ECC group", "[wpa3_sae]") TEST_ASSERT(sae_set_group(&sae2, IANA_SECP256R1) == 0); /* STA1 prepares for commit*/ - TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd, strlen((const char *)pwd), NULL, &sae1) == 0); + TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd, strlen((const char *)pwd), &sae1) == 0); /* STA2 prepares for commit*/ - TEST_ASSERT(sae_prepare_commit(addr2, addr1, pwd, strlen((const char *)pwd), NULL, &sae2) == 0); + TEST_ASSERT(sae_prepare_commit(addr2, addr1, pwd, strlen((const char *)pwd), &sae2) == 0); /* STA1 creates commit msg buffer*/ buf1 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN); @@ -135,11 +135,11 @@ TEST_CASE("Test SAE functionality with ECC group", "[wpa3_sae]") /* STA1 parses STA2 commit*/ TEST_ASSERT(sae_parse_commit(&sae1, - wpabuf_mhead(buf2), buf2->used, NULL, 0, default_groups) == 0); + wpabuf_mhead(buf2), buf2->used, NULL, 0, default_groups, 0) == 0); /* STA2 parses STA1 commit*/ TEST_ASSERT(sae_parse_commit(&sae2, - wpabuf_mhead(buf1), buf1->used, NULL, 0, default_groups) == 0); + wpabuf_mhead(buf1), buf1->used, NULL, 0, default_groups, 0) == 0); /* STA1 processes commit*/ TEST_ASSERT(sae_process_commit(&sae1) == 0); @@ -200,10 +200,10 @@ TEST_CASE("Test SAE functionality with ECC group", "[wpa3_sae]") TEST_ASSERT(sae_set_group(&sae2, IANA_SECP256R1) == 0); /* STA1 prepares for commit*/ - TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd1, strlen((const char *)pwd1), NULL, &sae1) == 0); + TEST_ASSERT(sae_prepare_commit(addr1, addr2, pwd1, strlen((const char *)pwd1), &sae1) == 0); /* STA2 prepares for commit*/ - TEST_ASSERT(sae_prepare_commit(addr2, addr1, pwd2, strlen((const char *)pwd2), NULL, &sae2) == 0); + TEST_ASSERT(sae_prepare_commit(addr2, addr1, pwd2, strlen((const char *)pwd2), &sae2) == 0); /* STA1 creates commit msg buffer*/ buf1 = wpabuf_alloc2(SAE_COMMIT_MAX_LEN); @@ -220,11 +220,11 @@ TEST_CASE("Test SAE functionality with ECC group", "[wpa3_sae]") /* STA1 parses STA2 commit*/ TEST_ASSERT(sae_parse_commit(&sae1, - wpabuf_mhead(buf2), buf2->used, NULL, 0, default_groups) == 0); + wpabuf_mhead(buf2), buf2->used, NULL, 0, default_groups, 0) == 0); /* STA2 parses STA1 commit*/ TEST_ASSERT(sae_parse_commit(&sae2, - wpabuf_mhead(buf1), buf1->used, NULL, 0, default_groups) == 0); + wpabuf_mhead(buf1), buf1->used, NULL, 0, default_groups, 0) == 0); /* STA1 processes commit*/ TEST_ASSERT(sae_process_commit(&sae1) == 0); diff --git a/examples/wifi/getting_started/station/main/station_example_main.c b/examples/wifi/getting_started/station/main/station_example_main.c index 663533cf6a..9c00dd1eb9 100644 --- a/examples/wifi/getting_started/station/main/station_example_main.c +++ b/examples/wifi/getting_started/station/main/station_example_main.c @@ -115,6 +115,7 @@ void wifi_init_sta(void) * However these modes are deprecated and not advisable to be used. Incase your Access point * doesn't support WPA2, these mode can be enabled by commenting below line */ .threshold.authmode = ESP_WIFI_SCAN_AUTH_MODE_THRESHOLD, + .sae_pwe_h2e = 2, }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index 4314a67cea..f7a32f383c 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1500,7 +1500,6 @@ components/wifi_provisioning/src/wifi_config.c components/wifi_provisioning/src/wifi_provisioning_priv.h 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.c 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