From 6b76228fcbc7c6a828cf972edb68aa36dcd4330c Mon Sep 17 00:00:00 2001 From: Nachiket Kukade Date: Wed, 29 Apr 2020 18:45:29 +0530 Subject: [PATCH] wpa_supplicant: Add SAE handshake support for WPA3-PSK Under WPA3-Personal, SAE authentication is used to derive PMK which is more secure and immune to offline dictionary attacks. 1. Add modules to generate SAE commit/confirm for the handshake 2. Add modules that build and parse SAE data in Auth frames 3. Add WPA3 association and key mgmt definitions 4. Invert y-bit while solving for ECC co-ordinate - Once an X co-ordinate is obtained, solving for Y co-ordinate using an elliptical curve equation results in 2 possible values, Y and (P - Y), where p is the prime number. The co-ordinates are used for deriving keys in SAE handshake. As par the 802.11 spec if LSB of X is same as LSB of Y then Y is chosen, (P - Y) otherwise. This is not what is implemented, so fix this behavior to obtain the correct Y co-ordinate. --- components/esp_wifi/include/esp_wifi_types.h | 1 + components/wpa_supplicant/CMakeLists.txt | 1 + .../src/common/ieee802_11_defs.h | 27 ++ components/wpa_supplicant/src/common/sae.c | 259 ++++++++++++++---- components/wpa_supplicant/src/common/sae.h | 21 +- .../wpa_supplicant/src/common/wpa_common.c | 4 + .../wpa_supplicant/src/common/wpa_common.h | 1 + .../src/crypto/crypto_mbedtls.c | 10 +- .../src/esp_supplicant/esp_wifi_driver.h | 8 +- .../src/esp_supplicant/esp_wpa3.c | 165 +++++++++++ .../src/esp_supplicant/esp_wpa3_i.h | 23 ++ .../src/esp_supplicant/esp_wpa_main.c | 6 +- components/wpa_supplicant/src/rsn_supp/wpa.c | 6 + .../wpa_supplicant/src/rsn_supp/wpa_ie.c | 4 + 14 files changed, 464 insertions(+), 72 deletions(-) create mode 100644 components/wpa_supplicant/src/esp_supplicant/esp_wpa3.c create mode 100644 components/wpa_supplicant/src/esp_supplicant/esp_wpa3_i.h diff --git a/components/esp_wifi/include/esp_wifi_types.h b/components/esp_wifi/include/esp_wifi_types.h index a07cfde08..cdffd6a90 100644 --- a/components/esp_wifi/include/esp_wifi_types.h +++ b/components/esp_wifi/include/esp_wifi_types.h @@ -56,6 +56,7 @@ typedef enum { WIFI_AUTH_WPA2_PSK, /**< authenticate mode : WPA2_PSK */ WIFI_AUTH_WPA_WPA2_PSK, /**< authenticate mode : WPA_WPA2_PSK */ WIFI_AUTH_WPA2_ENTERPRISE, /**< authenticate mode : WPA2_ENTERPRISE */ + WIFI_AUTH_WPA3_PSK, /**< authenticate mode : WPA3_PSK */ WIFI_AUTH_MAX } wifi_auth_mode_t; diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index eed582d85..909282528 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -50,6 +50,7 @@ set(srcs "port/os_xtensa.c" "src/esp_supplicant/esp_wpa_main.c" "src/esp_supplicant/esp_wpas_glue.c" "src/esp_supplicant/esp_wps.c" + "src/esp_supplicant/esp_wpa3.c" "src/rsn_supp/pmksa_cache.c" "src/rsn_supp/wpa.c" "src/rsn_supp/wpa_ie.c" diff --git a/components/wpa_supplicant/src/common/ieee802_11_defs.h b/components/wpa_supplicant/src/common/ieee802_11_defs.h index 209ae9362..9260d22f0 100644 --- a/components/wpa_supplicant/src/common/ieee802_11_defs.h +++ b/components/wpa_supplicant/src/common/ieee802_11_defs.h @@ -159,6 +159,7 @@ #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95 #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 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ #define WLAN_REASON_UNSPECIFIED 1 @@ -225,6 +226,32 @@ #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 #define WLAN_EID_MMIE 76 #define WLAN_EID_VENDOR_SPECIFIC 221 +#define WLAN_EID_CAG_NUMBER 237 +#define WLAN_EID_AP_CSN 239 +#define WLAN_EID_FILS_INDICATION 240 +#define WLAN_EID_DILS 241 +#define WLAN_EID_FRAGMENT 242 +#define WLAN_EID_EXTENSION 255 + +/* Element ID Extension (EID 255) values */ +#define WLAN_EID_EXT_ASSOC_DELAY_INFO 1 +#define WLAN_EID_EXT_FILS_REQ_PARAMS 2 +#define WLAN_EID_EXT_FILS_KEY_CONFIRM 3 +#define WLAN_EID_EXT_FILS_SESSION 4 +#define WLAN_EID_EXT_FILS_HLP_CONTAINER 5 +#define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6 +#define WLAN_EID_EXT_KEY_DELIVERY 7 +#define WLAN_EID_EXT_FILS_WRAPPED_DATA 8 +#define WLAN_EID_EXT_FTM_SYNC_INFO 9 +#define WLAN_EID_EXT_EXTENDED_REQUEST 10 +#define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11 +#define WLAN_EID_EXT_FILS_PUBLIC_KEY 12 +#define WLAN_EID_EXT_FILS_NONCE 13 +#define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14 +#define WLAN_EID_EXT_OWE_DH_PARAM 32 +#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33 +#define WLAN_EID_EXT_HE_CAPABILITIES 35 +#define WLAN_EID_EXT_HE_OPERATION 36 /* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */ diff --git a/components/wpa_supplicant/src/common/sae.c b/components/wpa_supplicant/src/common/sae.c index ebae4c41e..3283c334f 100644 --- a/components/wpa_supplicant/src/common/sae.c +++ b/components/wpa_supplicant/src/common/sae.c @@ -35,18 +35,20 @@ void bin_clear_free(void *bin, size_t len) } } -int sae_set_group(struct sae_data *sae, u16 group) +int sae_set_group(struct sae_data *sae, int group) { - struct sae_temporary_data *tmp; + struct sae_temporary_data *tmp; - sae_clear_data(sae); - tmp = sae->tmp = os_zalloc(sizeof(*tmp)); - if (tmp == NULL) - return -1; + sae_clear_data(sae); + tmp = sae->tmp = os_zalloc(sizeof(*tmp)); + if (tmp == NULL) + return -1; - /* First, check if this is an ECC group */ + /* First, check if this is an ECC group */ tmp->ec = crypto_ec_init(group); if (tmp->ec) { + wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", + group); sae->group = group; tmp->prime_len = crypto_ec_prime_len(tmp->ec); tmp->prime = crypto_ec_get_prime(tmp->ec); @@ -57,6 +59,8 @@ int sae_set_group(struct sae_data *sae, u16 group) /* Not an ECC group, check FFC */ tmp->dh = dh_groups_get(group); if (tmp->dh) { + wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", + group); sae->group = group; tmp->prime_len = tmp->dh->prime_len; if (tmp->prime_len > SAE_MAX_PRIME_LEN) { @@ -84,6 +88,8 @@ int sae_set_group(struct sae_data *sae, u16 group) } /* Unsupported group */ + wpa_printf(MSG_DEBUG, + "SAE: Group %d not supported by the crypto library", group); return -1; } @@ -105,6 +111,7 @@ void sae_clear_temp_data(struct sae_data *sae) crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); wpabuf_free(tmp->anti_clogging_token); + os_free(tmp->pw_id); bin_clear_free(tmp, sizeof(*tmp)); sae->tmp = NULL; } @@ -423,12 +430,13 @@ 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) + size_t password_len, const char *identifier) { u8 counter, k = 40; u8 addrs[2 * ETH_ALEN]; - const u8 *addr[2]; - size_t len[2]; + 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; @@ -460,10 +468,13 @@ 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 + * base = password [|| identifier] * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), * base || counter) */ @@ -471,8 +482,15 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, addr[0] = password; len[0] = password_len; - addr[1] = &counter; - len[1] = sizeof(counter); + 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++; /* * Continue for at least k iterations to protect against side-channel @@ -490,8 +508,8 @@ 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), 2, addr, len, - pwd_seed) < 0) + if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, + addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ecc(sae, pwd_seed, @@ -516,7 +534,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, crypto_bignum_deinit(x_cand, 1); } } - + if (!x) { wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); res = -1; @@ -549,12 +567,13 @@ fail: static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, - size_t password_len) + size_t password_len, const char *identifier) { u8 counter; u8 addrs[2 * ETH_ALEN]; - const u8 *addr[2]; - size_t len[2]; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem; int found = 0; if (sae->tmp->pwe_ffc == NULL) { @@ -569,14 +588,21 @@ 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 || counter) + * password [|| identifier] || counter) */ sae_pwd_seed_key(addr1, addr2, addrs); addr[0] = password; len[0] = password_len; - addr[1] = &counter; - len[1] = sizeof(counter); + 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++; for (counter = 1; !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; @@ -589,8 +615,8 @@ 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), 2, addr, len, - pwd_seed) < 0) + if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, + addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); if (res < 0) @@ -697,13 +723,15 @@ fail: int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, - struct sae_data *sae) + const char *identifier, struct sae_data *sae) { if (sae->tmp == NULL || (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, - password_len) < 0) || + password_len, + identifier) < 0) || (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, - password_len) < 0) || + password_len, + identifier) < 0) || sae_derive_commit(sae) < 0) return -1; return 0; @@ -838,7 +866,7 @@ int sae_process_commit(struct sae_data *sae) } void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token) + const struct wpabuf *token, const char *identifier) { u8 *pos; @@ -872,6 +900,16 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", pos, sae->tmp->prime_len); } + + if (identifier) { + /* Password Identifier element */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + os_strlen(identifier)); + wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); + wpabuf_put_str(buf, identifier); + wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", + identifier); + } } u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) @@ -915,25 +953,71 @@ u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) return WLAN_STATUS_SUCCESS; } +static int sae_is_password_id_elem(const u8 *pos, const u8 *end) +{ + int ret = end - pos >= 3 && + pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 && + end - pos - 2 >= pos[1] && + pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; + + return ret; +} + static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, const u8 *end, const u8 **token, size_t *token_len) { - if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) { - size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * - sae->tmp->prime_len); - wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); - if (token) - *token = *pos; - if (token_len) - *token_len = tlen; - *pos += tlen; - } else { - if (token) - *token = NULL; - if (token_len) - *token_len = 0; + size_t scalar_elem_len, tlen; + const u8 *elem; + + if (token) + *token = NULL; + if (token_len) + *token_len = 0; + + 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) { + wpa_printf(MSG_DEBUG, + "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", + (unsigned int) tlen); + 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; + if (token_len) + *token_len = tlen; + *pos += tlen; } static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, @@ -973,7 +1057,6 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - crypto_bignum_deinit(sae->peer_commit_scalar, 0); sae->peer_commit_scalar = peer_scalar; wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", @@ -983,12 +1066,12 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, return WLAN_STATUS_SUCCESS; } -static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, +static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, const u8 *end) { u8 prime[SAE_MAX_ECC_PRIME_LEN]; - if (2 * sae->tmp->prime_len > end - pos) { + if (2 * sae->tmp->prime_len > end - *pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -999,8 +1082,8 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; /* element x and y coordinates < p */ - if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 || - os_memcmp(pos + sae->tmp->prime_len, prime, + if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || + os_memcmp(*pos + sae->tmp->prime_len, prime, sae->tmp->prime_len) >= 0) { wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " "element"); @@ -1008,13 +1091,13 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, } wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", - pos, sae->tmp->prime_len); + *pos, sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", - pos + sae->tmp->prime_len, sae->tmp->prime_len); + *pos + sae->tmp->prime_len, sae->tmp->prime_len); crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); sae->tmp->peer_commit_element_ecc = - crypto_ec_point_from_bin(sae->tmp->ec, pos); + crypto_ec_point_from_bin(sae->tmp->ec, *pos); if (sae->tmp->peer_commit_element_ecc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1024,26 +1107,28 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; } + *pos += 2 * sae->tmp->prime_len; + return WLAN_STATUS_SUCCESS; } -static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, +static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, const u8 *end) { struct crypto_bignum *res, *one; const u8 one_bin[1] = { 0x01 }; - if (sae->tmp->prime_len > end - pos) { + if (sae->tmp->prime_len > end - *pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } - wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos, + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, sae->tmp->prime_len); crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); sae->tmp->peer_commit_element_ffc = - crypto_bignum_init_set(pos, sae->tmp->prime_len); + crypto_bignum_init_set(*pos, sae->tmp->prime_len); if (sae->tmp->peer_commit_element_ffc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* 1 < element < p - 1 */ @@ -1071,10 +1156,12 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, } crypto_bignum_deinit(res, 0); + *pos += sae->tmp->prime_len; + return WLAN_STATUS_SUCCESS; } -static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, +static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, const u8 *end) { if (sae->tmp->dh) @@ -1082,12 +1169,47 @@ static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, return sae_parse_commit_element_ecc(sae, pos, end); } +static int sae_parse_password_identifier(struct sae_data *sae, + const u8 *pos, const u8 *end) +{ + wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", + 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 */ + } + + 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)) { + wpa_printf(MSG_DEBUG, + "SAE: The included Password Identifier does not match the expected one (%s)", + sae->tmp->pw_id); + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + } + + os_free(sae->tmp->pw_id); + sae->tmp->pw_id = os_malloc(pos[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'; + 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 *pos = data, *end = data + len; u16 res; - + /* Check Finite Cyclic Group */ if (end - pos < 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1105,7 +1227,12 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, return res; /* commit-element */ - res = sae_parse_commit_element(sae, pos, end); + res = sae_parse_commit_element(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* Optional Password Identifier element */ + res = sae_parse_password_identifier(sae, pos, end); if (res != WLAN_STATUS_SUCCESS) return res; @@ -1220,7 +1347,8 @@ void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) /* Send-Confirm */ sc = wpabuf_put(buf, 0); wpabuf_put_le16(buf, sae->send_confirm); - sae->send_confirm++; + if (sae->send_confirm < 0xffff) + sae->send_confirm++; if (sae->tmp->ec) sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, @@ -1277,4 +1405,19 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) return 0; } +const char * sae_state_txt(enum sae_state state) +{ + switch (state) { + case SAE_NOTHING: + return "Nothing"; + case SAE_COMMITTED: + return "Committed"; + case SAE_CONFIRMED: + return "Confirmed"; + case SAE_ACCEPTED: + return "Accepted"; + } + return "?"; +} + #endif /* CONFIG_WPA3_SAE */ diff --git a/components/wpa_supplicant/src/common/sae.h b/components/wpa_supplicant/src/common/sae.h index 41c7ac0cb..b0160c458 100644 --- a/components/wpa_supplicant/src/common/sae.h +++ b/components/wpa_supplicant/src/common/sae.h @@ -5,14 +5,12 @@ * This software may be distributed under the terms of the BSD license. * See README for more details. */ - #ifdef CONFIG_WPA3_SAE #ifndef SAE_H #define SAE_H #include "esp_err.h" - #include "utils/includes.h" #include "utils/common.h" #include "utils/wpa_debug.h" @@ -47,6 +45,7 @@ struct sae_temporary_data { struct crypto_bignum *prime_buf; struct crypto_bignum *order_buf; struct wpabuf *anti_clogging_token; + char *pw_id; }; enum { @@ -54,32 +53,38 @@ enum { SAE_MSG_CONFIRM = 2, }; +enum sae_state { + SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED +}; + struct sae_data { - enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; + enum sae_state state; u16 send_confirm; u8 pmk[SAE_PMK_LEN]; u8 pmkid[SAE_PMKID_LEN]; struct crypto_bignum *peer_commit_scalar; - u16 group; - int sync; + int group; + unsigned int sync; /* protocol instance variable: Sync */ + u16 rc; /* protocol instance variable: Rc (received send-confirm) */ struct sae_temporary_data *tmp; }; -int sae_set_group(struct sae_data *sae, u16 group); +int sae_set_group(struct sae_data *sae, int group); void sae_clear_temp_data(struct sae_data *sae); void sae_clear_data(struct sae_data *sae); int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, - struct sae_data *sae); + const char *identifier, struct sae_data *sae); int sae_process_commit(struct sae_data *sae); void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token); + 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); void 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); #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 1eee98a0c..765590042 100644 --- a/components/wpa_supplicant/src/common/wpa_common.c +++ b/components/wpa_supplicant/src/common/wpa_common.c @@ -58,6 +58,10 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK) return WPA_KEY_MGMT_FT_PSK; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPA3_SAE + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE) + return WPA_KEY_MGMT_SAE; +#endif /* CONFIG_WPA3_SAE */ #ifdef CONFIG_IEEE80211W if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256) return WPA_KEY_MGMT_IEEE8021X_SHA256; diff --git a/components/wpa_supplicant/src/common/wpa_common.h b/components/wpa_supplicant/src/common/wpa_common.h index 07d549aac..12a13e1a0 100644 --- a/components/wpa_supplicant/src/common/wpa_common.h +++ b/components/wpa_supplicant/src/common/wpa_common.h @@ -54,6 +54,7 @@ #endif /* CONFIG_IEEE80211R */ #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) +#define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) diff --git a/components/wpa_supplicant/src/crypto/crypto_mbedtls.c b/components/wpa_supplicant/src/crypto/crypto_mbedtls.c index 88c0e732a..0f50cd8d0 100644 --- a/components/wpa_supplicant/src/crypto/crypto_mbedtls.c +++ b/components/wpa_supplicant/src/crypto/crypto_mbedtls.c @@ -478,9 +478,11 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, * such that p ≡ 3 (mod 4) * y_ = (y2 ^ ((p+1)/4)) mod p * - * if y_bit: y = p-y_ - * else y = y_` + * if LSB of both x and y are same: y = y_ + * else y = p - y_ + * y_bit is LSB of x */ + y_bit = (y_bit != 0); y_sqr = (mbedtls_mpi *) crypto_ec_point_compute_y_sqr(e, x); @@ -490,9 +492,9 @@ int crypto_ec_point_solve_y_coord(struct crypto_ec *e, MBEDTLS_MPI_CHK(mbedtls_mpi_div_int(&temp, NULL, &temp, 4)); MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(y, y_sqr, &temp, &e->group.P, NULL)); - if (y_bit) { + if (y_bit != mbedtls_mpi_get_bit(y, 0)) MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(y, &e->group.P, y)); - } + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&((mbedtls_ecp_point* )p)->X, (const mbedtls_mpi*) x)); MBEDTLS_MPI_CHK(mbedtls_mpi_lset(&((mbedtls_ecp_point *)p)->Z, 1)); } else { diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h b/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h index c081a2b7f..b542ac940 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h +++ b/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h @@ -64,7 +64,8 @@ enum { WPA_AUTH_CCKM = 0x06, WPA2_AUTH_CCKM = 0x07, WPA2_AUTH_PSK_SHA256= 0x08, - WPA2_AUTH_INVALID = 0x09, + WPA3_AUTH_PSK = 0x09, + WPA2_AUTH_INVALID = 0x0a, }; typedef enum { @@ -121,6 +122,10 @@ struct wpa_funcs { int (*wpa_parse_wpa_ie)(const u8 *wpa_ie, size_t wpa_ie_len, wifi_wpa_ie_t *data); int (*wpa_config_bss)(u8 *bssid); int (*wpa_michael_mic_failure)(u16 is_unicast); +#ifdef CONFIG_WPA3_SAE + u8 *(*wpa3_build_sae_msg)(u8 *bssid, u32 type, u32 *len); + int (*wpa3_parse_sae_msg)(u8 *buf, u32 len, u32 type); +#endif }; struct wpa2_funcs { @@ -209,6 +214,7 @@ int esp_wifi_ipc_internal(wifi_ipc_config_t *cfg, bool sync); int esp_wifi_register_wpa2_cb_internal(struct wpa2_funcs *cb); int esp_wifi_unregister_wpa2_cb_internal(void); bool esp_wifi_sta_prof_is_wpa2_internal(void); +bool esp_wifi_sta_prof_is_wpa3_internal(void); esp_err_t esp_wifi_sta_wpa2_ent_disable_internal(wifi_wpa2_param_t *param); esp_err_t esp_wifi_sta_wpa2_ent_enable_internal(wifi_wpa2_param_t *param); esp_err_t esp_wifi_set_wpa2_ent_state_internal(wpa2_ent_eap_state_t state); diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_wpa3.c b/components/wpa_supplicant/src/esp_supplicant/esp_wpa3.c new file mode 100644 index 000000000..d98436a1b --- /dev/null +++ b/components/wpa_supplicant/src/esp_supplicant/esp_wpa3.c @@ -0,0 +1,165 @@ +// 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. + +#ifdef CONFIG_WPA3_SAE + +#include "../common/sae.h" +#include "esp_wifi_driver.h" +#include "rsn_supp/wpa.h" + +static struct sae_data g_sae_data; + +int g_allowed_groups[] = { IANA_SECP256R1, 0 }; + +static struct wpabuf *wpa3_build_sae_commit(u8 *bssid) +{ + int default_group = IANA_SECP256R1; + struct wpabuf *buf; + u8 own_addr[ETH_ALEN]; + const u8 *pw; + memset(&g_sae_data, 0, sizeof(g_sae_data)); + + if (sae_set_group(&g_sae_data, default_group)) { + wpa_printf(MSG_ERROR, "wpa3: could not set SAE group %d", default_group); + return NULL; + } + + esp_wifi_get_macaddr_internal(WIFI_IF_STA, own_addr); + if (!bssid) { + wpa_printf(MSG_ERROR, "wpa3: cannot prepare SAE commit with no BSSID!"); + return NULL; + } + + 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) { + wpa_printf(MSG_ERROR, "wpa3: failed to prepare SAE commit!"); + return NULL; + } + + // For reuse PWE after retry case + // memcpy(g_sae_data.tmp->bssid, bssid, ETH_ALEN); + + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + sae_write_commit(&g_sae_data, buf, NULL, NULL); //no token + g_sae_data.state = SAE_COMMITTED; + + return buf; +} + +static struct wpabuf *wpa3_build_sae_confirm(void) +{ + struct wpabuf *buf; + + if (g_sae_data.state != SAE_COMMITTED) + return NULL; + + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + sae_write_confirm(&g_sae_data, buf); + g_sae_data.state = SAE_CONFIRMED; + + return buf; +} + +static u8 *wpa3_build_sae_msg(u8 *bssid, u32 sae_msg_type, u32 *sae_msg_len) +{ + struct wpabuf *buf = NULL; + + switch (sae_msg_type) { + case SAE_MSG_COMMIT: + buf = wpa3_build_sae_commit(bssid); + break; + case SAE_MSG_CONFIRM: + buf = wpa3_build_sae_confirm(); + break; + default: + break; + } + + if (buf) { + *sae_msg_len = (u32)wpabuf_len(buf); + return wpabuf_mhead_u8(buf); + } else + return NULL; +} + +static int wpa3_parse_sae_commit(u8 *buf, u32 len) +{ + int ret; + + if (g_sae_data.state != SAE_COMMITTED) { + wpa_printf(MSG_ERROR, "wpa3: failed to parse SAE commit in state(%d)!", + g_sae_data.state); + return -1; + } + + ret = sae_parse_commit(&g_sae_data, buf, len, NULL, 0, g_allowed_groups); + if (ret) { + wpa_printf(MSG_ERROR, "wpa3: could not parse commit(%d)", ret); + return -1; + } + + ret = sae_process_commit(&g_sae_data); + if (ret) { + wpa_printf(MSG_ERROR, "wpa3: could not process commit(%d)", ret); + return -1; + } + + return 0; +} + +static int wpa3_parse_sae_confirm(u8 *buf, u32 len) +{ + if (g_sae_data.state != SAE_CONFIRMED) { + wpa_printf(MSG_ERROR, "wpa3: failed to parse SAE commit in state(%d)!", + g_sae_data.state); + return -1; + } + + sae_check_confirm(&g_sae_data, buf, len); + g_sae_data.state = SAE_ACCEPTED; + + wpa_set_pmk(g_sae_data.pmk); + memcpy(esp_wifi_sta_get_ap_info_prof_pmk_internal(), g_sae_data.pmk, PMK_LEN); + + return 0; +} + +static int wpa3_parse_sae_msg(u8 *buf, u32 len, u32 sae_msg_type) +{ + int ret = 0; + + switch (sae_msg_type) { + case SAE_MSG_COMMIT: + ret = wpa3_parse_sae_commit(buf, len); + break; + case SAE_MSG_CONFIRM: + ret = wpa3_parse_sae_confirm(buf, len); + break; + default: + wpa_printf(MSG_ERROR, "wpa3: Invalid SAE msg type(%d)!", + sae_msg_type); + ret = -1; + break; + } + + return ret; +} + +void esp_wifi_register_wpa3_cb(struct wpa_funcs *wpa_cb) +{ + wpa_cb->wpa3_build_sae_msg = wpa3_build_sae_msg; + wpa_cb->wpa3_parse_sae_msg = wpa3_parse_sae_msg; +} + +#endif /* CONFIG_WPA3_SAE */ diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_wpa3_i.h b/components/wpa_supplicant/src/esp_supplicant/esp_wpa3_i.h new file mode 100644 index 000000000..99c9d84f0 --- /dev/null +++ b/components/wpa_supplicant/src/esp_supplicant/esp_wpa3_i.h @@ -0,0 +1,23 @@ + +// 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. + +#ifndef ESP_WPA3_H +#define ESP_WPA3_H + +#include "esp_wifi_driver.h" + +void esp_wifi_register_wpa3_cb(struct wpa_funcs *wpa_cb); + +#endif /* ESP_WPA3_H */ diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c b/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c index 434b666b4..fd160738e 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c +++ b/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c @@ -32,6 +32,7 @@ #include "esp_wifi_driver.h" #include "esp_private/wifi.h" +#include "esp_wpa3_i.h" void wpa_install_key(enum wpa_alg alg, u8 *addr, int key_idx, int set_tx, u8 *seq, size_t seq_len, u8 *key, size_t key_len, int key_entry_valid) @@ -74,7 +75,7 @@ void wpa_config_profile() { if (esp_wifi_sta_prof_is_wpa_internal()) { wpa_set_profile(WPA_PROTO_WPA, esp_wifi_sta_get_prof_authmode_internal()); - } else if (esp_wifi_sta_prof_is_wpa2_internal()) { + } else if (esp_wifi_sta_prof_is_wpa2_internal() || esp_wifi_sta_prof_is_wpa3_internal()) { wpa_set_profile(WPA_PROTO_RSN, esp_wifi_sta_get_prof_authmode_internal()); } else { WPA_ASSERT(0); @@ -201,6 +202,9 @@ int esp_supplicant_init(void) wpa_cb->wpa_parse_wpa_ie = wpa_parse_wpa_ie_wrapper; wpa_cb->wpa_config_bss = NULL;//wpa_config_bss; wpa_cb->wpa_michael_mic_failure = wpa_michael_mic_failure; +#ifdef CONFIG_WPA3_SAE + esp_wifi_register_wpa3_cb(wpa_cb); +#endif /* CONFIG_WPA3_SAE */ esp_wifi_register_wpa_cb_internal(wpa_cb); diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.c b/components/wpa_supplicant/src/rsn_supp/wpa.c index 12d4fbb66..97fe59bf0 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa.c @@ -2077,6 +2077,10 @@ void wpa_set_profile(u32 wpa_proto, u8 auth_mode) sm->key_mgmt = WPA_KEY_MGMT_PSK; /* fixed to PSK for now */ } else if (auth_mode == WPA2_AUTH_PSK_SHA256) { sm->key_mgmt = WPA_KEY_MGMT_PSK_SHA256; + } else if (auth_mode == WPA3_AUTH_PSK) { + sm->key_mgmt = WPA_KEY_MGMT_SAE; /* for WPA3 PSK */ + } else { + sm->key_mgmt = WPA_KEY_MGMT_PSK; /* fixed to PSK for now */ } } @@ -2143,6 +2147,8 @@ wpa_set_passphrase(char * passphrase, u8 *ssid, size_t ssid_len) * Here only handle passphrase string. Need extra step to handle 32B, 64Hex raw * PMK. */ + if (sm->key_mgmt == WPA_KEY_MGMT_SAE) + return; /* This is really SLOW, so just re cacl while reset param */ if (esp_wifi_sta_get_reset_param_internal() != 0) { diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_ie.c b/components/wpa_supplicant/src/rsn_supp/wpa_ie.c index dc1a1616b..9aaf3bc4a 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_ie.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa_ie.c @@ -204,6 +204,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_WPA3_SAE + } else if (key_mgmt == WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); +#endif /* CONFIG_WPA3_SAE */ } else { wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", key_mgmt);