diff --git a/components/esp_wifi/include/esp_wifi_types.h b/components/esp_wifi/include/esp_wifi_types.h index 9adf79d561..3727d7c9ba 100644 --- a/components/esp_wifi/include/esp_wifi_types.h +++ b/components/esp_wifi/include/esp_wifi_types.h @@ -222,6 +222,14 @@ typedef struct { bool required; /**< Advertizes that Protected Management Frame is required. Device will not associate to non-PMF capable devices. */ } wifi_pmf_config_t; +/** Configuration for SAE PWE derivation */ +typedef enum { + WPA3_SAE_PWE_UNSPECIFIED, + WPA3_SAE_PWE_HUNT_AND_PECK, + WPA3_SAE_PWE_HASH_TO_ELEMENT, + WPA3_SAE_PWE_BOTH, +} wifi_sae_pwe_method_t; + /** @brief Soft-AP configuration settings for the ESP32 */ typedef struct { uint8_t ssid[32]; /**< SSID of ESP32 soft-AP. If ssid_len field is 0, this must be a Null terminated string. Otherwise, length is set according to ssid_len. */ @@ -251,6 +259,7 @@ typedef struct { uint32_t rm_enabled:1; /**< Whether Radio Measurements are enabled for the connection */ uint32_t btm_enabled:1; /**< Whether BSS Transition Management is enabled for the connection */ uint32_t reserved:30; /**< Reserved for future feature set */ + wifi_sae_pwe_method_t sae_pwe_h2e; /**< Whether SAE hash to element is enabled */ } 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 3bf3a0974d..603c80b581 160000 --- a/components/esp_wifi/lib +++ b/components/esp_wifi/lib @@ -1 +1 @@ -Subproject commit 3bf3a0974dc628c36149098f869029b1b4bdf170 +Subproject commit 603c80b5814f449af4acfef3dc9aa02351b7a5ce diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 02b2e6f3cf..83e2a1bf38 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -32,6 +32,7 @@ set(srcs "port/os_xtensa.c" "src/esp_supplicant/esp_hostap.c" "src/esp_supplicant/esp_wpa2.c" "src/esp_supplicant/esp_wpa_main.c" + "src/esp_supplicant/esp_common.c" "src/esp_supplicant/esp_wpas_glue.c" "src/esp_supplicant/esp_wps.c" "src/esp_supplicant/esp_wpa3.c" @@ -150,7 +151,6 @@ if(CONFIG_WPA_11KV_SUPPORT) "src/common/bss.c" "src/common/scan.c" "src/common/ieee802_11_common.c" - "src/esp_supplicant/esp_common.c" "src/esp_supplicant/esp_scan.c" ) else() diff --git a/components/wpa_supplicant/component.mk b/components/wpa_supplicant/component.mk index a53dee8782..e578b05a93 100644 --- a/components/wpa_supplicant/component.mk +++ b/components/wpa_supplicant/component.mk @@ -67,7 +67,6 @@ ifneq ($(CONFIG_WPA_11KV_SUPPORT), y) src/common/bss.o \ src/common/scan.o \ src/common/ieee802_11_common.o \ - src/esp_supplicant/esp_common.o \ src/esp_supplicant/esp_scan.o endif diff --git a/components/wpa_supplicant/port/include/supplicant_opt.h b/components/wpa_supplicant/port/include/supplicant_opt.h index 59b428d546..da75332769 100644 --- a/components/wpa_supplicant/port/include/supplicant_opt.h +++ b/components/wpa_supplicant/port/include/supplicant_opt.h @@ -29,9 +29,6 @@ #define DEBUG_PRINT #endif -#if CONFIG_WPA_11KV_SUPPORT -#define ROAMING_SUPPORT 1 -#endif #if CONFIG_WPA_SCAN_CACHE #define SCAN_CACHE_SUPPORTED diff --git a/components/wpa_supplicant/src/common/defs.h b/components/wpa_supplicant/src/common/defs.h index edd4639804..eabd2c0681 100644 --- a/components/wpa_supplicant/src/common/defs.h +++ b/components/wpa_supplicant/src/common/defs.h @@ -25,6 +25,9 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_CIPHER_AES_128_CMAC BIT(5) #define WPA_CIPHER_GCMP BIT(6) #define WPA_CIPHER_SMS4 BIT(10) +#define WPA_CIPHER_GCMP_256 BIT(11) +#define WPA_CIPHER_BIP_GMAC_128 BIT(12) +#define WPA_CIPHER_BIP_GMAC_256 BIT(13) #define WPA_KEY_MGMT_IEEE8021X BIT(0) #define WPA_KEY_MGMT_PSK BIT(1) diff --git a/components/wpa_supplicant/src/common/ieee802_11_defs.h b/components/wpa_supplicant/src/common/ieee802_11_defs.h index fefc820be3..4bc3c281df 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 @@ -227,6 +228,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 */ @@ -248,6 +250,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 af1da9d506..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,47 +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)); - hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, - keyseed); - wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); + 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); - 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; } @@ -853,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; @@ -861,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)); @@ -906,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; } @@ -961,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) { @@ -994,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; @@ -1017,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) { @@ -1037,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); @@ -1167,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); @@ -1191,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; @@ -1214,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); @@ -1227,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. @@ -1257,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]; @@ -1276,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; } @@ -1341,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; } @@ -1361,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; } @@ -1383,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; } @@ -1417,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 f995d0d2d4..821cb36494 100644 --- a/components/wpa_supplicant/src/common/wpa_common.c +++ b/components/wpa_supplicant/src/common/wpa_common.c @@ -98,6 +98,33 @@ static int wpa_key_mgmt_to_bitfield(const u8 *s) return 0; } #endif /* CONFIG_NO_WPA2 */ + +int wpa_cipher_valid_mgmt_group(int cipher) +{ + return cipher == WPA_CIPHER_AES_128_CMAC || + cipher == WPA_CIPHER_BIP_GMAC_128 || + 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_sta_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 @@ -436,7 +463,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 fcecef1648..46ffecf015 100644 --- a/components/wpa_supplicant/src/common/wpa_common.h +++ b/components/wpa_supplicant/src/common/wpa_common.h @@ -87,6 +87,8 @@ #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #endif /* CONFIG_IEEE80211W */ +#define WFA_KEY_DATA_TRANSITION_DISABLE RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x20) + #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1) #define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val)) @@ -290,6 +292,7 @@ struct wpa_ie_data { size_t num_pmkid; const u8 *pmkid; int mgmt_group_cipher; + uint8_t rsnxe_capa; }; struct rsn_sppamsdu_sup { @@ -301,7 +304,10 @@ const char * wpa_cipher_txt(int cipher); 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_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, u8 *mic); int wpa_compare_rsn_ie(int ft_initial_assoc, diff --git a/components/wpa_supplicant/src/crypto/crypto.h b/components/wpa_supplicant/src/crypto/crypto.h index 75234e229c..67ca466b5d 100644 --- a/components/wpa_supplicant/src/crypto/crypto.h +++ b/components/wpa_supplicant/src/crypto/crypto.h @@ -653,6 +653,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 @@ -729,6 +749,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() @@ -749,6 +776,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/crypto/crypto_mbedtls-bignum.c b/components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c index 550d510819..643f29d13b 100644 --- a/components/wpa_supplicant/src/crypto/crypto_mbedtls-bignum.c +++ b/components/wpa_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/src/crypto/crypto_mbedtls-ec.c b/components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c index d55805db4b..0cf0b9115e 100644 --- a/components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c +++ b/components/wpa_supplicant/src/crypto/crypto_mbedtls-ec.c @@ -107,6 +107,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) { @@ -144,6 +149,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/src/esp_supplicant/esp_common.c b/components/wpa_supplicant/src/esp_supplicant/esp_common.c index f74fa556c2..0ea50a1bfa 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_common.c +++ b/components/wpa_supplicant/src/esp_supplicant/esp_common.c @@ -3,7 +3,6 @@ * * SPDX-License-Identifier: Apache-2.0 */ - #include "utils/includes.h" #include "utils/common.h" #include "esp_event.h" @@ -23,6 +22,7 @@ struct wpa_supplicant g_wpa_supp; +#if defined(CONFIG_WPA_11KV_SUPPORT) static TaskHandle_t s_supplicant_task_hdl = NULL; static void *s_supplicant_evt_queue = NULL; static void *s_supplicant_api_lock = NULL; @@ -431,6 +431,93 @@ void esp_set_rm_enabled_ie(void) } } +static size_t get_rm_enabled_ie(uint8_t *ie, size_t len) +{ + uint8_t rrm_ie[7] = {0}; + uint8_t rrm_ie_len = 5; + uint8_t *pos = rrm_ie; + + if (!esp_wifi_is_rm_enabled_internal(WIFI_IF_STA)) { + return 0; + } + + *pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; + *pos++ = rrm_ie_len; + *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT; + + *pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | +#ifdef SCAN_CACHE_SUPPORTED + WLAN_RRM_CAPS_BEACON_REPORT_TABLE | +#endif + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE; + + os_memcpy(ie, rrm_ie, sizeof(rrm_ie)); + + return rrm_ie_len + 2; +} +#endif + +static uint8_t get_extended_caps_ie(uint8_t *ie, size_t len) +{ + uint8_t ext_caps_ie[5] = {0}; + uint8_t ext_caps_ie_len = 3; + uint8_t *pos = ext_caps_ie; + + if (!esp_wifi_is_btm_enabled_internal(WIFI_IF_STA)) { + return 0; + } + + *pos++ = WLAN_EID_EXT_CAPAB; + *pos++ = ext_caps_ie_len; + *pos++ = 0; + *pos++ = 0; +#define CAPAB_BSS_TRANSITION BIT(3) + *pos |= CAPAB_BSS_TRANSITION; +#undef CAPAB_BSS_TRANSITION + os_memcpy(ie, ext_caps_ie, sizeof(ext_caps_ie)); + + return ext_caps_ie_len + 2; +} + +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; + ie_len = get_extended_caps_ie(pos, len); + pos += ie_len; + len -= ie_len; +#ifdef CONFIG_WPA_11KV_SUPPORT + 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 + if (ies_len) { + os_memcpy(pos, ies, ies_len); + pos += ies_len; + len -= ies_len; + } + esp_wifi_unset_appie_internal(WIFI_APPIE_ASSOC_REQ); + esp_wifi_set_appie_internal(WIFI_APPIE_ASSOC_REQ, ie, ASSOC_IE_LEN - len, 0); + os_free(ie); +#undef ASSOC_IE_LEN +} + +#ifdef CONFIG_WPA_11KV_SUPPORT void esp_get_tx_power(uint8_t *tx_power) { #define DEFAULT_MAX_TX_POWER 19 /* max tx power is 19.5 dbm */ @@ -506,3 +593,4 @@ int esp_supplicant_post_evt(uint32_t evt_id, uint32_t data) } return 0; } +#endif diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_common_i.h b/components/wpa_supplicant/src/esp_supplicant/esp_common_i.h index 9d3350fa49..94a0db559f 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_common_i.h +++ b/components/wpa_supplicant/src/esp_supplicant/esp_common_i.h @@ -11,7 +11,7 @@ struct wpa_funcs; -#ifdef ROAMING_SUPPORT +#ifdef CONFIG_WPA_11KV_SUPPORT struct ieee_mgmt_frame { u8 sender[ETH_ALEN]; u8 channel; @@ -47,18 +47,7 @@ void esp_supplicant_common_deinit(void); #include "esp_wnm.h" static inline void esp_set_rm_enabled_ie(void) {} -int esp_rrm_send_neighbor_rep_request(neighbor_rep_request_cb cb, - void *cb_ctx) -{ - return -1; -} - -int esp_wnm_send_bss_transition_mgmt_query(enum btm_query_reason query_reason, - const char *btm_candidates, - int cand_list) -{ - return -1; -} #endif +void esp_set_assoc_ie(uint8_t *bssid, const u8 *ies, size_t ies_len, bool add_mdie); #endif 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 1bd67a5449..8c5b46487d 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h +++ b/components/wpa_supplicant/src/esp_supplicant/esp_wifi_driver.h @@ -113,6 +113,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 { int (*wpa3_parse_sae_msg)(uint8_t *buf, size_t len, uint32_t type, uint16_t status); int (*wpa_sta_rx_mgmt)(u8 type, u8 *frame, size_t len, u8 *sender, u32 rssi, u8 channel, u64 current_tsf); void (*wpa_config_done)(void); + int (*wpa_sta_set_ap_rsnxe)(const u8 *rsnxe, size_t rsnxe_ie_len); }; struct wpa2_funcs { @@ -274,5 +276,8 @@ esp_err_t esp_wifi_action_tx_req(uint8_t type, uint8_t channel, uint32_t wait_time_ms, const wifi_action_tx_req_t *req); esp_err_t esp_wifi_remain_on_channel(uint8_t ifx, uint8_t type, uint8_t channel, uint32_t wait_time_ms, wifi_action_rx_cb_t rx_cb); +uint8_t esp_wifi_sta_get_config_sae_pwe_h2e_internal(void); +uint8_t esp_wifi_sta_get_use_h2e_internal(void); +void esp_wifi_sta_disable_wpa2_authmode_internal(void); #endif /* _ESP_WIFI_DRIVER_H_ */ diff --git a/components/wpa_supplicant/src/esp_supplicant/esp_wpa3.c b/components/wpa_supplicant/src/esp_supplicant/esp_wpa3.c index 3be8bef2f6..fa5b27402f 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_wpa3.c +++ b/components/wpa_supplicant/src/esp_supplicant/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_sta_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/src/esp_supplicant/esp_wpa_main.c b/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c index 525e3d374f..6b848b7972 100644 --- a/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.c +++ b/components/wpa_supplicant/src/esp_supplicant/esp_wpa_main.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-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include "utils/includes.h" #include "utils/common.h" @@ -201,6 +193,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; } @@ -226,7 +219,7 @@ static void wpa_sta_disconnected_cb(uint8_t reason_code) } } -#ifndef ROAMING_SUPPORT +#ifndef CONFIG_WPA_11KV_SUPPORT static inline int esp_supplicant_common_init(struct wpa_funcs *wpa_cb) { wpa_cb->wpa_sta_rx_mgmt = NULL; @@ -242,7 +235,7 @@ int esp_supplicant_init(void) int ret = ESP_OK; struct wpa_funcs *wpa_cb; - wpa_cb = (struct wpa_funcs *)os_malloc(sizeof(struct wpa_funcs)); + wpa_cb = (struct wpa_funcs *)os_zalloc(sizeof(struct wpa_funcs)); if (!wpa_cb) { return ESP_ERR_NO_MEM; } @@ -267,6 +260,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); ret = esp_supplicant_common_init(wpa_cb); diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.c b/components/wpa_supplicant/src/rsn_supp/wpa.c index 62f2b65eb3..380cbc8529 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa.c +++ b/components/wpa_supplicant/src/rsn_supp/wpa.c @@ -23,6 +23,7 @@ #include "rsn_supp/wpa_ie.h" #include "esp_supplicant/esp_wpas_glue.h" #include "esp_supplicant/esp_wifi_driver.h" +#include "esp_supplicant/esp_common_i.h" #include "crypto/crypto.h" #include "crypto/sha1.h" @@ -580,6 +581,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); @@ -637,16 +640,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); } @@ -1106,6 +1123,22 @@ int ieee80211w_set_keys(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; } @@ -1273,6 +1306,9 @@ int ieee80211w_set_keys(struct wpa_sm *sm, goto failed; } + if (ie.transition_disable) + esp_wifi_sta_disable_wpa2_authmode_internal(); + if (sm->key_install && sm->key_info & WPA_KEY_INFO_INSTALL) { wpa_supplicant_install_ptk(sm, KEY_FLAG_RX); } @@ -2117,6 +2153,8 @@ bool wpa_sm_init(char * payload, WPA_SEND_FUNC snd_func, sm->wpa_deauthenticate = wpa_deauth; sm->wpa_neg_complete = wpa_neg_complete; sm->key_install = false; + sm->ap_rsnxe = NULL; + sm->assoc_rsnxe = NULL; spp_attrubute = esp_wifi_get_spp_attrubute_internal(WIFI_IF_STA); sm->spp_sup.capable = ((spp_attrubute & WPA_CAPABILITY_SPP_CAPABLE) ? SPP_AMSDU_CAP_ENABLE : SPP_AMSDU_CAP_DISABLE); @@ -2140,6 +2178,10 @@ void wpa_sm_deinit(void) { struct wpa_sm *sm = &gWpaSm; pmksa_cache_deinit(sm->pmksa); + os_free(sm->ap_rsnxe); + sm->ap_rsnxe = NULL; + os_free(sm->assoc_rsnxe); + sm->assoc_rsnxe = NULL; } @@ -2182,6 +2224,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 @@ -2237,6 +2281,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; + 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; @@ -2481,4 +2533,55 @@ void wpa_sta_clear_curr_pmksa(void) { pmksa_cache_clear_current(sm); } +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_sta_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; +} + #endif // ESP_SUPPLICANT diff --git a/components/wpa_supplicant/src/rsn_supp/wpa.h b/components/wpa_supplicant/src/rsn_supp/wpa.h index bd64af2bc7..66fcad53ad 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa.h +++ b/components/wpa_supplicant/src/rsn_supp/wpa.h @@ -136,4 +136,12 @@ unsigned cipher_type_map_public_to_supp(wifi_cipher_type_t cipher); void wpa_sta_clear_curr_pmksa(void); +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); + +struct wpa_sm * get_wpa_sm(void); + +void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm); + #endif /* WPA_H */ diff --git a/components/wpa_supplicant/src/rsn_supp/wpa_i.h b/components/wpa_supplicant/src/rsn_supp/wpa_i.h index 491acf6dd4..f62cc62819 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_i.h +++ b/components/wpa_supplicant/src/rsn_supp/wpa_i.h @@ -52,12 +52,15 @@ 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*/ ETSTimer cm_timer; 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; @@ -69,8 +72,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 b206d02e19..abcb049b1e 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; } @@ -300,6 +302,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 @@ -360,6 +390,15 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } #endif + if (pos[1] >= RSN_SELECTOR_LEN + 1 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_TRANSITION_DISABLE) { + ie->transition_disable = pos + 2 + RSN_SELECTOR_LEN; + ie->transition_disable_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, + "WPA: Transition Disable KDE in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } return 0; } @@ -400,6 +439,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..b799064635 100644 --- a/components/wpa_supplicant/src/rsn_supp/wpa_ie.h +++ b/components/wpa_supplicant/src/rsn_supp/wpa_ie.h @@ -37,12 +37,16 @@ struct wpa_eapol_ie_parse { const u8 *reassoc_deadline; const u8 *key_lifetime; #endif /* CONFIG_IEEE80211R */ + const u8 *transition_disable; + size_t transition_disable_len; + 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 e323ad3162..1b7ec77d30 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..4ecab0df92 100644 --- a/examples/wifi/getting_started/station/main/station_example_main.c +++ b/examples/wifi/getting_started/station/main/station_example_main.c @@ -114,7 +114,8 @@ void wifi_init_sta(void) /* Setting a password implies station will connect to all security modes including WEP/WPA. * 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, + .threshold.authmode = WIFI_AUTH_WPA3_PSK, + .sae_pwe_h2e = WPA3_SAE_PWE_BOTH, }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );