15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * Simultaneous authentication of equals 3780fb4a2SCy Schubert * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi> 45b9c547cSRui Paulo * 55b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 65b9c547cSRui Paulo * See README for more details. 75b9c547cSRui Paulo */ 85b9c547cSRui Paulo 95b9c547cSRui Paulo #include "includes.h" 105b9c547cSRui Paulo 115b9c547cSRui Paulo #include "common.h" 124bc52338SCy Schubert #include "utils/const_time.h" 135b9c547cSRui Paulo #include "crypto/crypto.h" 145b9c547cSRui Paulo #include "crypto/sha256.h" 15c1d255d3SCy Schubert #include "crypto/sha384.h" 16c1d255d3SCy Schubert #include "crypto/sha512.h" 175b9c547cSRui Paulo #include "crypto/random.h" 185b9c547cSRui Paulo #include "crypto/dh_groups.h" 195b9c547cSRui Paulo #include "ieee802_11_defs.h" 20206b73d0SCy Schubert #include "dragonfly.h" 215b9c547cSRui Paulo #include "sae.h" 225b9c547cSRui Paulo 235b9c547cSRui Paulo 245b9c547cSRui Paulo int sae_set_group(struct sae_data *sae, int group) 255b9c547cSRui Paulo { 265b9c547cSRui Paulo struct sae_temporary_data *tmp; 275b9c547cSRui Paulo 28206b73d0SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 29206b73d0SCy Schubert /* Allow all groups for testing purposes in non-production builds. */ 30206b73d0SCy Schubert #else /* CONFIG_TESTING_OPTIONS */ 31206b73d0SCy Schubert if (!dragonfly_suitable_group(group, 0)) { 324bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group); 334bc52338SCy Schubert return -1; 344bc52338SCy Schubert } 35206b73d0SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 364bc52338SCy Schubert 375b9c547cSRui Paulo sae_clear_data(sae); 385b9c547cSRui Paulo tmp = sae->tmp = os_zalloc(sizeof(*tmp)); 395b9c547cSRui Paulo if (tmp == NULL) 405b9c547cSRui Paulo return -1; 415b9c547cSRui Paulo 425b9c547cSRui Paulo /* First, check if this is an ECC group */ 435b9c547cSRui Paulo tmp->ec = crypto_ec_init(group); 445b9c547cSRui Paulo if (tmp->ec) { 4585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", 4685732ac8SCy Schubert group); 475b9c547cSRui Paulo sae->group = group; 485b9c547cSRui Paulo tmp->prime_len = crypto_ec_prime_len(tmp->ec); 495b9c547cSRui Paulo tmp->prime = crypto_ec_get_prime(tmp->ec); 50206b73d0SCy Schubert tmp->order_len = crypto_ec_order_len(tmp->ec); 515b9c547cSRui Paulo tmp->order = crypto_ec_get_order(tmp->ec); 525b9c547cSRui Paulo return 0; 535b9c547cSRui Paulo } 545b9c547cSRui Paulo 555b9c547cSRui Paulo /* Not an ECC group, check FFC */ 565b9c547cSRui Paulo tmp->dh = dh_groups_get(group); 575b9c547cSRui Paulo if (tmp->dh) { 5885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", 5985732ac8SCy Schubert group); 605b9c547cSRui Paulo sae->group = group; 615b9c547cSRui Paulo tmp->prime_len = tmp->dh->prime_len; 625b9c547cSRui Paulo if (tmp->prime_len > SAE_MAX_PRIME_LEN) { 635b9c547cSRui Paulo sae_clear_data(sae); 645b9c547cSRui Paulo return -1; 655b9c547cSRui Paulo } 665b9c547cSRui Paulo 675b9c547cSRui Paulo tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, 685b9c547cSRui Paulo tmp->prime_len); 695b9c547cSRui Paulo if (tmp->prime_buf == NULL) { 705b9c547cSRui Paulo sae_clear_data(sae); 715b9c547cSRui Paulo return -1; 725b9c547cSRui Paulo } 735b9c547cSRui Paulo tmp->prime = tmp->prime_buf; 745b9c547cSRui Paulo 75206b73d0SCy Schubert tmp->order_len = tmp->dh->order_len; 765b9c547cSRui Paulo tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, 775b9c547cSRui Paulo tmp->dh->order_len); 785b9c547cSRui Paulo if (tmp->order_buf == NULL) { 795b9c547cSRui Paulo sae_clear_data(sae); 805b9c547cSRui Paulo return -1; 815b9c547cSRui Paulo } 825b9c547cSRui Paulo tmp->order = tmp->order_buf; 835b9c547cSRui Paulo 845b9c547cSRui Paulo return 0; 855b9c547cSRui Paulo } 865b9c547cSRui Paulo 875b9c547cSRui Paulo /* Unsupported group */ 8885732ac8SCy Schubert wpa_printf(MSG_DEBUG, 8985732ac8SCy Schubert "SAE: Group %d not supported by the crypto library", group); 905b9c547cSRui Paulo return -1; 915b9c547cSRui Paulo } 925b9c547cSRui Paulo 935b9c547cSRui Paulo 945b9c547cSRui Paulo void sae_clear_temp_data(struct sae_data *sae) 955b9c547cSRui Paulo { 965b9c547cSRui Paulo struct sae_temporary_data *tmp; 975b9c547cSRui Paulo if (sae == NULL || sae->tmp == NULL) 985b9c547cSRui Paulo return; 995b9c547cSRui Paulo tmp = sae->tmp; 1005b9c547cSRui Paulo crypto_ec_deinit(tmp->ec); 1015b9c547cSRui Paulo crypto_bignum_deinit(tmp->prime_buf, 0); 1025b9c547cSRui Paulo crypto_bignum_deinit(tmp->order_buf, 0); 1035b9c547cSRui Paulo crypto_bignum_deinit(tmp->sae_rand, 1); 1045b9c547cSRui Paulo crypto_bignum_deinit(tmp->pwe_ffc, 1); 1055b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_scalar, 0); 1065b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); 1075b9c547cSRui Paulo crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); 1085b9c547cSRui Paulo crypto_ec_point_deinit(tmp->pwe_ecc, 1); 1095b9c547cSRui Paulo crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); 1105b9c547cSRui Paulo crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); 1115b9c547cSRui Paulo wpabuf_free(tmp->anti_clogging_token); 112c1d255d3SCy Schubert wpabuf_free(tmp->own_rejected_groups); 113c1d255d3SCy Schubert wpabuf_free(tmp->peer_rejected_groups); 11485732ac8SCy Schubert os_free(tmp->pw_id); 1155b9c547cSRui Paulo bin_clear_free(tmp, sizeof(*tmp)); 1165b9c547cSRui Paulo sae->tmp = NULL; 1175b9c547cSRui Paulo } 1185b9c547cSRui Paulo 1195b9c547cSRui Paulo 1205b9c547cSRui Paulo void sae_clear_data(struct sae_data *sae) 1215b9c547cSRui Paulo { 1225b9c547cSRui Paulo if (sae == NULL) 1235b9c547cSRui Paulo return; 1245b9c547cSRui Paulo sae_clear_temp_data(sae); 1255b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 126c1d255d3SCy Schubert crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0); 1275b9c547cSRui Paulo os_memset(sae, 0, sizeof(*sae)); 1285b9c547cSRui Paulo } 1295b9c547cSRui Paulo 1305b9c547cSRui Paulo 1315b9c547cSRui Paulo static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) 1325b9c547cSRui Paulo { 1335b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR 1345b9c547cSRui Paulo " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); 1355b9c547cSRui Paulo if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { 1365b9c547cSRui Paulo os_memcpy(key, addr1, ETH_ALEN); 1375b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); 1385b9c547cSRui Paulo } else { 1395b9c547cSRui Paulo os_memcpy(key, addr2, ETH_ALEN); 1405b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); 1415b9c547cSRui Paulo } 1425b9c547cSRui Paulo } 1435b9c547cSRui Paulo 1445b9c547cSRui Paulo 145325151a3SRui Paulo static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, 1464bc52338SCy Schubert const u8 *prime, const u8 *qr, const u8 *qnr, 1474bc52338SCy Schubert u8 *pwd_value) 148325151a3SRui Paulo { 149325151a3SRui Paulo struct crypto_bignum *y_sqr, *x_cand; 150325151a3SRui Paulo int res; 1515b9c547cSRui Paulo size_t bits; 152206b73d0SCy Schubert int cmp_prime; 153206b73d0SCy Schubert unsigned int in_range; 1545b9c547cSRui Paulo 1555b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 1565b9c547cSRui Paulo 1575b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 1585b9c547cSRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec); 159780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 160780fb4a2SCy Schubert prime, sae->tmp->prime_len, pwd_value, bits) < 0) 161780fb4a2SCy Schubert return -1; 1625b9c547cSRui Paulo if (bits % 8) 1634bc52338SCy Schubert buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8); 1645b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", 1655b9c547cSRui Paulo pwd_value, sae->tmp->prime_len); 1665b9c547cSRui Paulo 167206b73d0SCy Schubert cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len); 168206b73d0SCy Schubert /* Create a const_time mask for selection based on prf result 169206b73d0SCy Schubert * being smaller than prime. */ 170206b73d0SCy Schubert in_range = const_time_fill_msb((unsigned int) cmp_prime); 171206b73d0SCy Schubert /* The algorithm description would skip the next steps if 172c1d255d3SCy Schubert * cmp_prime >= 0 (return 0 here), but go through them regardless to 173206b73d0SCy Schubert * minimize externally observable differences in behavior. */ 1745b9c547cSRui Paulo 175325151a3SRui Paulo x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 176325151a3SRui Paulo if (!x_cand) 1775b9c547cSRui Paulo return -1; 178325151a3SRui Paulo y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); 179325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 1804bc52338SCy Schubert if (!y_sqr) 181325151a3SRui Paulo return -1; 1825b9c547cSRui Paulo 183206b73d0SCy Schubert res = dragonfly_is_quadratic_residue_blind(sae->tmp->ec, qr, qnr, 184206b73d0SCy Schubert y_sqr); 185325151a3SRui Paulo crypto_bignum_deinit(y_sqr, 1); 186206b73d0SCy Schubert if (res < 0) 187325151a3SRui Paulo return res; 188206b73d0SCy Schubert return const_time_select_int(in_range, res, 0); 189325151a3SRui Paulo } 1905b9c547cSRui Paulo 1915b9c547cSRui Paulo 1924bc52338SCy Schubert /* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided 1934bc52338SCy Schubert * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */ 1945b9c547cSRui Paulo static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, 1955b9c547cSRui Paulo struct crypto_bignum *pwe) 1965b9c547cSRui Paulo { 1975b9c547cSRui Paulo u8 pwd_value[SAE_MAX_PRIME_LEN]; 1985b9c547cSRui Paulo size_t bits = sae->tmp->prime_len * 8; 1995b9c547cSRui Paulo u8 exp[1]; 2004bc52338SCy Schubert struct crypto_bignum *a, *b = NULL; 2014bc52338SCy Schubert int res, is_val; 2024bc52338SCy Schubert u8 pwd_value_valid; 2035b9c547cSRui Paulo 2045b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 2055b9c547cSRui Paulo 2065b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 207780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 2085b9c547cSRui Paulo sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, 209780fb4a2SCy Schubert bits) < 0) 210780fb4a2SCy Schubert return -1; 2115b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, 2125b9c547cSRui Paulo sae->tmp->prime_len); 2135b9c547cSRui Paulo 2144bc52338SCy Schubert /* Check whether pwd-value < p */ 2154bc52338SCy Schubert res = const_time_memcmp(pwd_value, sae->tmp->dh->prime, 2164bc52338SCy Schubert sae->tmp->prime_len); 2174bc52338SCy Schubert /* pwd-value >= p is invalid, so res is < 0 for the valid cases and 2184bc52338SCy Schubert * the negative sign can be used to fill the mask for constant time 2194bc52338SCy Schubert * selection */ 2204bc52338SCy Schubert pwd_value_valid = const_time_fill_msb(res); 2214bc52338SCy Schubert 2224bc52338SCy Schubert /* If pwd-value >= p, force pwd-value to be < p and perform the 2234bc52338SCy Schubert * calculations anyway to hide timing difference. The derived PWE will 2244bc52338SCy Schubert * be ignored in that case. */ 2254bc52338SCy Schubert pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0); 2265b9c547cSRui Paulo 2275b9c547cSRui Paulo /* PWE = pwd-value^((p-1)/r) modulo p */ 2285b9c547cSRui Paulo 2294bc52338SCy Schubert res = -1; 2305b9c547cSRui Paulo a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 2314bc52338SCy Schubert if (!a) 2324bc52338SCy Schubert goto fail; 2335b9c547cSRui Paulo 2344bc52338SCy Schubert /* This is an optimization based on the used group that does not depend 2354bc52338SCy Schubert * on the password in any way, so it is fine to use separate branches 2364bc52338SCy Schubert * for this step without constant time operations. */ 2375b9c547cSRui Paulo if (sae->tmp->dh->safe_prime) { 2385b9c547cSRui Paulo /* 2395b9c547cSRui Paulo * r = (p-1)/2 for the group used here, so this becomes: 2405b9c547cSRui Paulo * PWE = pwd-value^2 modulo p 2415b9c547cSRui Paulo */ 2425b9c547cSRui Paulo exp[0] = 2; 2435b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 2445b9c547cSRui Paulo } else { 2455b9c547cSRui Paulo /* Calculate exponent: (p-1)/r */ 2465b9c547cSRui Paulo exp[0] = 1; 2475b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 2485b9c547cSRui Paulo if (b == NULL || 2495b9c547cSRui Paulo crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || 2504bc52338SCy Schubert crypto_bignum_div(b, sae->tmp->order, b) < 0) 2514bc52338SCy Schubert goto fail; 2525b9c547cSRui Paulo } 2535b9c547cSRui Paulo 2544bc52338SCy Schubert if (!b) 2554bc52338SCy Schubert goto fail; 2564bc52338SCy Schubert 2575b9c547cSRui Paulo res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); 2584bc52338SCy Schubert if (res < 0) 2594bc52338SCy Schubert goto fail; 2605b9c547cSRui Paulo 2614bc52338SCy Schubert /* There were no fatal errors in calculations, so determine the return 2624bc52338SCy Schubert * value using constant time operations. We get here for number of 2634bc52338SCy Schubert * invalid cases which are cleared here after having performed all the 2644bc52338SCy Schubert * computation. PWE is valid if pwd-value was less than prime and 2654bc52338SCy Schubert * PWE > 1. Start with pwd-value check first and then use constant time 2664bc52338SCy Schubert * operations to clear res to 0 if PWE is 0 or 1. 2674bc52338SCy Schubert */ 2684bc52338SCy Schubert res = const_time_select_u8(pwd_value_valid, 1, 0); 2694bc52338SCy Schubert is_val = crypto_bignum_is_zero(pwe); 2704bc52338SCy Schubert res = const_time_select_u8(const_time_is_zero(is_val), res, 0); 2714bc52338SCy Schubert is_val = crypto_bignum_is_one(pwe); 2724bc52338SCy Schubert res = const_time_select_u8(const_time_is_zero(is_val), res, 0); 2735b9c547cSRui Paulo 2744bc52338SCy Schubert fail: 2754bc52338SCy Schubert crypto_bignum_deinit(a, 1); 2764bc52338SCy Schubert crypto_bignum_deinit(b, 1); 2774bc52338SCy Schubert return res; 2785b9c547cSRui Paulo } 2795b9c547cSRui Paulo 2805b9c547cSRui Paulo 2815b9c547cSRui Paulo static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, 2825b9c547cSRui Paulo const u8 *addr2, const u8 *password, 283c1d255d3SCy Schubert size_t password_len) 2845b9c547cSRui Paulo { 285206b73d0SCy Schubert u8 counter, k; 2865b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 287c1d255d3SCy Schubert const u8 *addr[2]; 288c1d255d3SCy Schubert size_t len[2]; 2894b72b91aSCy Schubert u8 *stub_password, *tmp_password; 290325151a3SRui Paulo int pwd_seed_odd = 0; 291325151a3SRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 292325151a3SRui Paulo size_t prime_len; 2934bc52338SCy Schubert struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL; 2944bc52338SCy Schubert u8 x_bin[SAE_MAX_ECC_PRIME_LEN]; 2954bc52338SCy Schubert u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; 2964bc52338SCy Schubert u8 qr_bin[SAE_MAX_ECC_PRIME_LEN]; 2974bc52338SCy Schubert u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN]; 2984bc52338SCy Schubert int res = -1; 2994bc52338SCy Schubert u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* 3004bc52338SCy Schubert * mask */ 3015b9c547cSRui Paulo 3024bc52338SCy Schubert os_memset(x_bin, 0, sizeof(x_bin)); 3034bc52338SCy Schubert 3044b72b91aSCy Schubert stub_password = os_malloc(password_len); 3054bc52338SCy Schubert tmp_password = os_malloc(password_len); 3064b72b91aSCy Schubert if (!stub_password || !tmp_password || 3074b72b91aSCy Schubert random_get_bytes(stub_password, password_len) < 0) 3084bc52338SCy Schubert goto fail; 309325151a3SRui Paulo 310325151a3SRui Paulo prime_len = sae->tmp->prime_len; 311325151a3SRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 312325151a3SRui Paulo prime_len) < 0) 3134bc52338SCy Schubert goto fail; 314325151a3SRui Paulo 315325151a3SRui Paulo /* 316325151a3SRui Paulo * Create a random quadratic residue (qr) and quadratic non-residue 317325151a3SRui Paulo * (qnr) modulo p for blinding purposes during the loop. 318325151a3SRui Paulo */ 319206b73d0SCy Schubert if (dragonfly_get_random_qr_qnr(sae->tmp->prime, &qr, &qnr) < 0 || 3204bc52338SCy Schubert crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 || 3214bc52338SCy Schubert crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0) 3224bc52338SCy Schubert goto fail; 3235b9c547cSRui Paulo 3245b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 3255b9c547cSRui Paulo password, password_len); 3265b9c547cSRui Paulo 3275b9c547cSRui Paulo /* 3285b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 329c1d255d3SCy Schubert * base = password 3305b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 331325151a3SRui Paulo * base || counter) 3325b9c547cSRui Paulo */ 3335b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 3345b9c547cSRui Paulo 3354bc52338SCy Schubert addr[0] = tmp_password; 3365b9c547cSRui Paulo len[0] = password_len; 337c1d255d3SCy Schubert addr[1] = &counter; 338c1d255d3SCy Schubert len[1] = sizeof(counter); 3395b9c547cSRui Paulo 3405b9c547cSRui Paulo /* 3415b9c547cSRui Paulo * Continue for at least k iterations to protect against side-channel 3425b9c547cSRui Paulo * attacks that attempt to determine the number of iterations required 3435b9c547cSRui Paulo * in the loop. 3445b9c547cSRui Paulo */ 345206b73d0SCy Schubert k = dragonfly_min_pwe_loop_iter(sae->group); 346206b73d0SCy Schubert 3474bc52338SCy Schubert for (counter = 1; counter <= k || !found; counter++) { 3485b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 3495b9c547cSRui Paulo 3505b9c547cSRui Paulo if (counter > 200) { 3515b9c547cSRui Paulo /* This should not happen in practice */ 3525b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 3535b9c547cSRui Paulo break; 3545b9c547cSRui Paulo } 3555b9c547cSRui Paulo 3564bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter); 3574b72b91aSCy Schubert const_time_select_bin(found, stub_password, password, 3584bc52338SCy Schubert password_len, tmp_password); 359c1d255d3SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), 2, 36085732ac8SCy Schubert addr, len, pwd_seed) < 0) 3615b9c547cSRui Paulo break; 362325151a3SRui Paulo 3635b9c547cSRui Paulo res = sae_test_pwd_seed_ecc(sae, pwd_seed, 3644bc52338SCy Schubert prime, qr_bin, qnr_bin, x_cand_bin); 3654bc52338SCy Schubert const_time_select_bin(found, x_bin, x_cand_bin, prime_len, 3664bc52338SCy Schubert x_bin); 3674bc52338SCy Schubert pwd_seed_odd = const_time_select_u8( 3684bc52338SCy Schubert found, pwd_seed_odd, 3694bc52338SCy Schubert pwd_seed[SHA256_MAC_LEN - 1] & 0x01); 3704bc52338SCy Schubert os_memset(pwd_seed, 0, sizeof(pwd_seed)); 3715b9c547cSRui Paulo if (res < 0) 372325151a3SRui Paulo goto fail; 3734bc52338SCy Schubert /* Need to minimize differences in handling res == 0 and 1 here 3744bc52338SCy Schubert * to avoid differences in timing and instruction cache access, 3754bc52338SCy Schubert * so use const_time_select_*() to make local copies of the 3764bc52338SCy Schubert * values based on whether this loop iteration was the one that 3774bc52338SCy Schubert * found the pwd-seed/x. */ 378325151a3SRui Paulo 3794bc52338SCy Schubert /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them 3804bc52338SCy Schubert * (with res converted to 0/0xff) handles this in constant time. 381325151a3SRui Paulo */ 3824bc52338SCy Schubert found |= res * 0xff; 3834bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x", 3844bc52338SCy Schubert res, found); 3855b9c547cSRui Paulo } 3865b9c547cSRui Paulo 3874bc52338SCy Schubert if (!found) { 388325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); 389325151a3SRui Paulo res = -1; 390325151a3SRui Paulo goto fail; 391325151a3SRui Paulo } 3925b9c547cSRui Paulo 3934bc52338SCy Schubert x = crypto_bignum_init_set(x_bin, prime_len); 3944bc52338SCy Schubert if (!x) { 3954bc52338SCy Schubert res = -1; 3964bc52338SCy Schubert goto fail; 3974bc52338SCy Schubert } 3984bc52338SCy Schubert 399325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 400325151a3SRui Paulo sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); 401325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 402325151a3SRui Paulo res = -1; 403325151a3SRui Paulo else 404325151a3SRui Paulo res = crypto_ec_point_solve_y_coord(sae->tmp->ec, 405325151a3SRui Paulo sae->tmp->pwe_ecc, x, 406325151a3SRui Paulo pwd_seed_odd); 407325151a3SRui Paulo if (res < 0) { 408325151a3SRui Paulo /* 409325151a3SRui Paulo * This should not happen since we already checked that there 410325151a3SRui Paulo * is a result. 411325151a3SRui Paulo */ 412325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); 413325151a3SRui Paulo } 414325151a3SRui Paulo 415325151a3SRui Paulo fail: 416325151a3SRui Paulo crypto_bignum_deinit(qr, 0); 417325151a3SRui Paulo crypto_bignum_deinit(qnr, 0); 4184b72b91aSCy Schubert os_free(stub_password); 4194bc52338SCy Schubert bin_clear_free(tmp_password, password_len); 4204bc52338SCy Schubert crypto_bignum_deinit(x, 1); 4214bc52338SCy Schubert os_memset(x_bin, 0, sizeof(x_bin)); 4224bc52338SCy Schubert os_memset(x_cand_bin, 0, sizeof(x_cand_bin)); 423325151a3SRui Paulo 424325151a3SRui Paulo return res; 4255b9c547cSRui Paulo } 4265b9c547cSRui Paulo 4275b9c547cSRui Paulo 4285b9c547cSRui Paulo static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, 4295b9c547cSRui Paulo const u8 *addr2, const u8 *password, 430c1d255d3SCy Schubert size_t password_len) 4315b9c547cSRui Paulo { 4324bc52338SCy Schubert u8 counter, k, sel_counter = 0; 4335b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 434c1d255d3SCy Schubert const u8 *addr[2]; 435c1d255d3SCy Schubert size_t len[2]; 4364bc52338SCy Schubert u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* 4374bc52338SCy Schubert * mask */ 4384bc52338SCy Schubert u8 mask; 4394bc52338SCy Schubert struct crypto_bignum *pwe; 4404bc52338SCy Schubert size_t prime_len = sae->tmp->prime_len * 8; 4414bc52338SCy Schubert u8 *pwe_buf; 4425b9c547cSRui Paulo 4434bc52338SCy Schubert crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); 4444bc52338SCy Schubert sae->tmp->pwe_ffc = NULL; 4454bc52338SCy Schubert 4464bc52338SCy Schubert /* Allocate a buffer to maintain selected and candidate PWE for constant 4474bc52338SCy Schubert * time selection. */ 4484bc52338SCy Schubert pwe_buf = os_zalloc(prime_len * 2); 4494bc52338SCy Schubert pwe = crypto_bignum_init(); 4504bc52338SCy Schubert if (!pwe_buf || !pwe) 4514bc52338SCy Schubert goto fail; 4525b9c547cSRui Paulo 4535b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 4545b9c547cSRui Paulo password, password_len); 4555b9c547cSRui Paulo 4565b9c547cSRui Paulo /* 4575b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 4585b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 459c1d255d3SCy Schubert * password || counter) 4605b9c547cSRui Paulo */ 4615b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 4625b9c547cSRui Paulo 4635b9c547cSRui Paulo addr[0] = password; 4645b9c547cSRui Paulo len[0] = password_len; 465c1d255d3SCy Schubert addr[1] = &counter; 466c1d255d3SCy Schubert len[1] = sizeof(counter); 4675b9c547cSRui Paulo 468206b73d0SCy Schubert k = dragonfly_min_pwe_loop_iter(sae->group); 4694bc52338SCy Schubert 4704bc52338SCy Schubert for (counter = 1; counter <= k || !found; counter++) { 4715b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 4725b9c547cSRui Paulo int res; 4735b9c547cSRui Paulo 4745b9c547cSRui Paulo if (counter > 200) { 4755b9c547cSRui Paulo /* This should not happen in practice */ 4765b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 4775b9c547cSRui Paulo break; 4785b9c547cSRui Paulo } 4795b9c547cSRui Paulo 4804bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter); 481c1d255d3SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), 2, 48285732ac8SCy Schubert addr, len, pwd_seed) < 0) 4835b9c547cSRui Paulo break; 4844bc52338SCy Schubert res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe); 4854bc52338SCy Schubert /* res is -1 for fatal failure, 0 if a valid PWE was not found, 4864bc52338SCy Schubert * or 1 if a valid PWE was found. */ 4875b9c547cSRui Paulo if (res < 0) 4885b9c547cSRui Paulo break; 4894bc52338SCy Schubert /* Store the candidate PWE into the second half of pwe_buf and 4904bc52338SCy Schubert * the selected PWE in the beginning of pwe_buf using constant 4914bc52338SCy Schubert * time selection. */ 4924bc52338SCy Schubert if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len, 4934bc52338SCy Schubert prime_len) < 0) 4944bc52338SCy Schubert break; 4954bc52338SCy Schubert const_time_select_bin(found, pwe_buf, pwe_buf + prime_len, 4964bc52338SCy Schubert prime_len, pwe_buf); 4974bc52338SCy Schubert sel_counter = const_time_select_u8(found, sel_counter, counter); 4984bc52338SCy Schubert mask = const_time_eq_u8(res, 1); 4994bc52338SCy Schubert found = const_time_select_u8(found, found, mask); 5005b9c547cSRui Paulo } 5015b9c547cSRui Paulo 5024bc52338SCy Schubert if (!found) 5034bc52338SCy Schubert goto fail; 5044bc52338SCy Schubert 5054bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter); 5064bc52338SCy Schubert sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len); 5074bc52338SCy Schubert fail: 5084bc52338SCy Schubert crypto_bignum_deinit(pwe, 1); 5094bc52338SCy Schubert bin_clear_free(pwe_buf, prime_len * 2); 5104bc52338SCy Schubert return sae->tmp->pwe_ffc ? 0 : -1; 5115b9c547cSRui Paulo } 5125b9c547cSRui Paulo 5135b9c547cSRui Paulo 514c1d255d3SCy Schubert static int hkdf_extract(size_t hash_len, const u8 *salt, size_t salt_len, 515c1d255d3SCy Schubert size_t num_elem, const u8 *addr[], const size_t len[], 516c1d255d3SCy Schubert u8 *prk) 517c1d255d3SCy Schubert { 518c1d255d3SCy Schubert if (hash_len == 32) 519c1d255d3SCy Schubert return hmac_sha256_vector(salt, salt_len, num_elem, addr, len, 520c1d255d3SCy Schubert prk); 521c1d255d3SCy Schubert #ifdef CONFIG_SHA384 522c1d255d3SCy Schubert if (hash_len == 48) 523c1d255d3SCy Schubert return hmac_sha384_vector(salt, salt_len, num_elem, addr, len, 524c1d255d3SCy Schubert prk); 525c1d255d3SCy Schubert #endif /* CONFIG_SHA384 */ 526c1d255d3SCy Schubert #ifdef CONFIG_SHA512 527c1d255d3SCy Schubert if (hash_len == 64) 528c1d255d3SCy Schubert return hmac_sha512_vector(salt, salt_len, num_elem, addr, len, 529c1d255d3SCy Schubert prk); 530c1d255d3SCy Schubert #endif /* CONFIG_SHA512 */ 531c1d255d3SCy Schubert return -1; 532c1d255d3SCy Schubert } 533c1d255d3SCy Schubert 534c1d255d3SCy Schubert 535c1d255d3SCy Schubert static int hkdf_expand(size_t hash_len, const u8 *prk, size_t prk_len, 536c1d255d3SCy Schubert const char *info, u8 *okm, size_t okm_len) 537c1d255d3SCy Schubert { 538c1d255d3SCy Schubert size_t info_len = os_strlen(info); 539c1d255d3SCy Schubert 540c1d255d3SCy Schubert if (hash_len == 32) 541c1d255d3SCy Schubert return hmac_sha256_kdf(prk, prk_len, NULL, 542c1d255d3SCy Schubert (const u8 *) info, info_len, 543c1d255d3SCy Schubert okm, okm_len); 544c1d255d3SCy Schubert #ifdef CONFIG_SHA384 545c1d255d3SCy Schubert if (hash_len == 48) 546c1d255d3SCy Schubert return hmac_sha384_kdf(prk, prk_len, NULL, 547c1d255d3SCy Schubert (const u8 *) info, info_len, 548c1d255d3SCy Schubert okm, okm_len); 549c1d255d3SCy Schubert #endif /* CONFIG_SHA384 */ 550c1d255d3SCy Schubert #ifdef CONFIG_SHA512 551c1d255d3SCy Schubert if (hash_len == 64) 552c1d255d3SCy Schubert return hmac_sha512_kdf(prk, prk_len, NULL, 553c1d255d3SCy Schubert (const u8 *) info, info_len, 554c1d255d3SCy Schubert okm, okm_len); 555c1d255d3SCy Schubert #endif /* CONFIG_SHA512 */ 556c1d255d3SCy Schubert return -1; 557c1d255d3SCy Schubert } 558c1d255d3SCy Schubert 559c1d255d3SCy Schubert 560c1d255d3SCy Schubert static int sswu_curve_param(int group, int *z) 561c1d255d3SCy Schubert { 562c1d255d3SCy Schubert switch (group) { 563c1d255d3SCy Schubert case 19: 564c1d255d3SCy Schubert *z = -10; 565c1d255d3SCy Schubert return 0; 566c1d255d3SCy Schubert case 20: 567c1d255d3SCy Schubert *z = -12; 568c1d255d3SCy Schubert return 0; 569c1d255d3SCy Schubert case 21: 570c1d255d3SCy Schubert *z = -4; 571c1d255d3SCy Schubert return 0; 572c1d255d3SCy Schubert case 25: 573c1d255d3SCy Schubert case 29: 574c1d255d3SCy Schubert *z = -5; 575c1d255d3SCy Schubert return 0; 576c1d255d3SCy Schubert case 26: 577c1d255d3SCy Schubert *z = 31; 578c1d255d3SCy Schubert return 0; 579c1d255d3SCy Schubert case 28: 580c1d255d3SCy Schubert *z = -2; 581c1d255d3SCy Schubert return 0; 582c1d255d3SCy Schubert case 30: 583c1d255d3SCy Schubert *z = 7; 584c1d255d3SCy Schubert return 0; 585c1d255d3SCy Schubert } 586c1d255d3SCy Schubert 587c1d255d3SCy Schubert return -1; 588c1d255d3SCy Schubert } 589c1d255d3SCy Schubert 590c1d255d3SCy Schubert 591c1d255d3SCy Schubert static void debug_print_bignum(const char *title, const struct crypto_bignum *a, 592c1d255d3SCy Schubert size_t prime_len) 593c1d255d3SCy Schubert { 594c1d255d3SCy Schubert u8 *bin; 595c1d255d3SCy Schubert 596c1d255d3SCy Schubert bin = os_malloc(prime_len); 597c1d255d3SCy Schubert if (bin && crypto_bignum_to_bin(a, bin, prime_len, prime_len) >= 0) 598c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, title, bin, prime_len); 599c1d255d3SCy Schubert else 600c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "Could not print bignum (%s)", title); 601c1d255d3SCy Schubert bin_clear_free(bin, prime_len); 602c1d255d3SCy Schubert } 603c1d255d3SCy Schubert 604c1d255d3SCy Schubert 605c1d255d3SCy Schubert static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group, 606c1d255d3SCy Schubert const struct crypto_bignum *u) 607c1d255d3SCy Schubert { 608c1d255d3SCy Schubert int z_int; 609c1d255d3SCy Schubert const struct crypto_bignum *a, *b, *prime; 610c1d255d3SCy Schubert struct crypto_bignum *u2, *t1, *t2, *z, *t, *zero, *one, *two, *three, 611c1d255d3SCy Schubert *x1a, *x1b, *y = NULL; 612c1d255d3SCy Schubert struct crypto_bignum *x1 = NULL, *x2, *gx1, *gx2, *v = NULL; 613c1d255d3SCy Schubert unsigned int m_is_zero, is_qr, is_eq; 614c1d255d3SCy Schubert size_t prime_len; 615c1d255d3SCy Schubert u8 bin[SAE_MAX_ECC_PRIME_LEN]; 616c1d255d3SCy Schubert u8 bin1[SAE_MAX_ECC_PRIME_LEN]; 617c1d255d3SCy Schubert u8 bin2[SAE_MAX_ECC_PRIME_LEN]; 618c1d255d3SCy Schubert u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN]; 619c1d255d3SCy Schubert struct crypto_ec_point *p = NULL; 620c1d255d3SCy Schubert 621c1d255d3SCy Schubert if (sswu_curve_param(group, &z_int) < 0) 622c1d255d3SCy Schubert return NULL; 623c1d255d3SCy Schubert 624c1d255d3SCy Schubert prime = crypto_ec_get_prime(ec); 625c1d255d3SCy Schubert prime_len = crypto_ec_prime_len(ec); 626c1d255d3SCy Schubert a = crypto_ec_get_a(ec); 627c1d255d3SCy Schubert b = crypto_ec_get_b(ec); 628c1d255d3SCy Schubert 629c1d255d3SCy Schubert u2 = crypto_bignum_init(); 630c1d255d3SCy Schubert t1 = crypto_bignum_init(); 631c1d255d3SCy Schubert t2 = crypto_bignum_init(); 632c1d255d3SCy Schubert z = crypto_bignum_init_uint(abs(z_int)); 633c1d255d3SCy Schubert t = crypto_bignum_init(); 634c1d255d3SCy Schubert zero = crypto_bignum_init_uint(0); 635c1d255d3SCy Schubert one = crypto_bignum_init_uint(1); 636c1d255d3SCy Schubert two = crypto_bignum_init_uint(2); 637c1d255d3SCy Schubert three = crypto_bignum_init_uint(3); 638c1d255d3SCy Schubert x1a = crypto_bignum_init(); 639c1d255d3SCy Schubert x1b = crypto_bignum_init(); 640c1d255d3SCy Schubert x2 = crypto_bignum_init(); 641c1d255d3SCy Schubert gx1 = crypto_bignum_init(); 642c1d255d3SCy Schubert gx2 = crypto_bignum_init(); 643c1d255d3SCy Schubert if (!u2 || !t1 || !t2 || !z || !t || !zero || !one || !two || !three || 644c1d255d3SCy Schubert !x1a || !x1b || !x2 || !gx1 || !gx2) 645c1d255d3SCy Schubert goto fail; 646c1d255d3SCy Schubert 647c1d255d3SCy Schubert if (z_int < 0 && crypto_bignum_sub(prime, z, z) < 0) 648c1d255d3SCy Schubert goto fail; 649c1d255d3SCy Schubert 650c1d255d3SCy Schubert /* m = z^2 * u^4 + z * u^2 */ 651c1d255d3SCy Schubert /* --> tmp = z * u^2, m = tmp^2 + tmp */ 652c1d255d3SCy Schubert 653c1d255d3SCy Schubert /* u2 = u^2 654c1d255d3SCy Schubert * t1 = z * u2 655c1d255d3SCy Schubert * t2 = t1^2 656c1d255d3SCy Schubert * m = t1 = t1 + t2 */ 657c1d255d3SCy Schubert if (crypto_bignum_sqrmod(u, prime, u2) < 0 || 658c1d255d3SCy Schubert crypto_bignum_mulmod(z, u2, prime, t1) < 0 || 659c1d255d3SCy Schubert crypto_bignum_sqrmod(t1, prime, t2) < 0 || 660c1d255d3SCy Schubert crypto_bignum_addmod(t1, t2, prime, t1) < 0) 661c1d255d3SCy Schubert goto fail; 662c1d255d3SCy Schubert debug_print_bignum("SSWU: m", t1, prime_len); 663c1d255d3SCy Schubert 664c1d255d3SCy Schubert /* l = CEQ(m, 0) 665c1d255d3SCy Schubert * t = CSEL(l, 0, inverse(m); where inverse(x) is calculated as 666c1d255d3SCy Schubert * x^(p-2) modulo p which will handle m == 0 case correctly */ 667c1d255d3SCy Schubert /* TODO: Make sure crypto_bignum_is_zero() is constant time */ 668c1d255d3SCy Schubert m_is_zero = const_time_eq(crypto_bignum_is_zero(t1), 1); 669c1d255d3SCy Schubert /* t = m^(p-2) modulo p */ 670c1d255d3SCy Schubert if (crypto_bignum_sub(prime, two, t2) < 0 || 671c1d255d3SCy Schubert crypto_bignum_exptmod(t1, t2, prime, t) < 0) 672c1d255d3SCy Schubert goto fail; 673c1d255d3SCy Schubert debug_print_bignum("SSWU: t", t, prime_len); 674c1d255d3SCy Schubert 675c1d255d3SCy Schubert /* b / (z * a) */ 676c1d255d3SCy Schubert if (crypto_bignum_mulmod(z, a, prime, t1) < 0 || 677c1d255d3SCy Schubert crypto_bignum_inverse(t1, prime, t1) < 0 || 678c1d255d3SCy Schubert crypto_bignum_mulmod(b, t1, prime, x1a) < 0) 679c1d255d3SCy Schubert goto fail; 680c1d255d3SCy Schubert debug_print_bignum("SSWU: x1a = b / (z * a)", x1a, prime_len); 681c1d255d3SCy Schubert 682c1d255d3SCy Schubert /* (-b/a) * (1 + t) */ 683c1d255d3SCy Schubert if (crypto_bignum_sub(prime, b, t1) < 0 || 684c1d255d3SCy Schubert crypto_bignum_inverse(a, prime, t2) < 0 || 685c1d255d3SCy Schubert crypto_bignum_mulmod(t1, t2, prime, t1) < 0 || 686c1d255d3SCy Schubert crypto_bignum_addmod(one, t, prime, t2) < 0 || 687c1d255d3SCy Schubert crypto_bignum_mulmod(t1, t2, prime, x1b) < 0) 688c1d255d3SCy Schubert goto fail; 689c1d255d3SCy Schubert debug_print_bignum("SSWU: x1b = (-b/a) * (1 + t)", x1b, prime_len); 690c1d255d3SCy Schubert 691c1d255d3SCy Schubert /* x1 = CSEL(CEQ(m, 0), x1a, x1b) */ 692c1d255d3SCy Schubert if (crypto_bignum_to_bin(x1a, bin1, sizeof(bin1), prime_len) < 0 || 693c1d255d3SCy Schubert crypto_bignum_to_bin(x1b, bin2, sizeof(bin2), prime_len) < 0) 694c1d255d3SCy Schubert goto fail; 695c1d255d3SCy Schubert const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin); 696c1d255d3SCy Schubert x1 = crypto_bignum_init_set(bin, prime_len); 697c1d255d3SCy Schubert if (!x1) 698c1d255d3SCy Schubert goto fail; 699c1d255d3SCy Schubert debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len); 700c1d255d3SCy Schubert 701c1d255d3SCy Schubert /* gx1 = x1^3 + a * x1 + b */ 702c1d255d3SCy Schubert if (crypto_bignum_exptmod(x1, three, prime, t1) < 0 || 703c1d255d3SCy Schubert crypto_bignum_mulmod(a, x1, prime, t2) < 0 || 704c1d255d3SCy Schubert crypto_bignum_addmod(t1, t2, prime, t1) < 0 || 705c1d255d3SCy Schubert crypto_bignum_addmod(t1, b, prime, gx1) < 0) 706c1d255d3SCy Schubert goto fail; 707c1d255d3SCy Schubert debug_print_bignum("SSWU: gx1 = x1^3 + a * x1 + b", gx1, prime_len); 708c1d255d3SCy Schubert 709c1d255d3SCy Schubert /* x2 = z * u^2 * x1 */ 710c1d255d3SCy Schubert if (crypto_bignum_mulmod(z, u2, prime, t1) < 0 || 711c1d255d3SCy Schubert crypto_bignum_mulmod(t1, x1, prime, x2) < 0) 712c1d255d3SCy Schubert goto fail; 713c1d255d3SCy Schubert debug_print_bignum("SSWU: x2 = z * u^2 * x1", x2, prime_len); 714c1d255d3SCy Schubert 715c1d255d3SCy Schubert /* gx2 = x2^3 + a * x2 + b */ 716c1d255d3SCy Schubert if (crypto_bignum_exptmod(x2, three, prime, t1) < 0 || 717c1d255d3SCy Schubert crypto_bignum_mulmod(a, x2, prime, t2) < 0 || 718c1d255d3SCy Schubert crypto_bignum_addmod(t1, t2, prime, t1) < 0 || 719c1d255d3SCy Schubert crypto_bignum_addmod(t1, b, prime, gx2) < 0) 720c1d255d3SCy Schubert goto fail; 721c1d255d3SCy Schubert debug_print_bignum("SSWU: gx2 = x2^3 + a * x2 + b", gx2, prime_len); 722c1d255d3SCy Schubert 723c1d255d3SCy Schubert /* l = gx1 is a quadratic residue modulo p 724c1d255d3SCy Schubert * --> gx1^((p-1)/2) modulo p is zero or one */ 725c1d255d3SCy Schubert if (crypto_bignum_sub(prime, one, t1) < 0 || 726c1d255d3SCy Schubert crypto_bignum_rshift(t1, 1, t1) < 0 || 727c1d255d3SCy Schubert crypto_bignum_exptmod(gx1, t1, prime, t1) < 0) 728c1d255d3SCy Schubert goto fail; 729c1d255d3SCy Schubert debug_print_bignum("SSWU: gx1^((p-1)/2) modulo p", t1, prime_len); 730c1d255d3SCy Schubert is_qr = const_time_eq(crypto_bignum_is_zero(t1) | 731c1d255d3SCy Schubert crypto_bignum_is_one(t1), 1); 732c1d255d3SCy Schubert 733c1d255d3SCy Schubert /* v = CSEL(l, gx1, gx2) */ 734c1d255d3SCy Schubert if (crypto_bignum_to_bin(gx1, bin1, sizeof(bin1), prime_len) < 0 || 735c1d255d3SCy Schubert crypto_bignum_to_bin(gx2, bin2, sizeof(bin2), prime_len) < 0) 736c1d255d3SCy Schubert goto fail; 737c1d255d3SCy Schubert const_time_select_bin(is_qr, bin1, bin2, prime_len, bin); 738c1d255d3SCy Schubert v = crypto_bignum_init_set(bin, prime_len); 739c1d255d3SCy Schubert if (!v) 740c1d255d3SCy Schubert goto fail; 741c1d255d3SCy Schubert debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len); 742c1d255d3SCy Schubert 743c1d255d3SCy Schubert /* x = CSEL(l, x1, x2) */ 744c1d255d3SCy Schubert if (crypto_bignum_to_bin(x1, bin1, sizeof(bin1), prime_len) < 0 || 745c1d255d3SCy Schubert crypto_bignum_to_bin(x2, bin2, sizeof(bin2), prime_len) < 0) 746c1d255d3SCy Schubert goto fail; 747c1d255d3SCy Schubert const_time_select_bin(is_qr, bin1, bin2, prime_len, x_y); 748c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SSWU: x = CSEL(l, x1, x2)", x_y, prime_len); 749c1d255d3SCy Schubert 750c1d255d3SCy Schubert /* y = sqrt(v) 751c1d255d3SCy Schubert * For prime p such that p = 3 mod 4 --> v^((p+1)/4) */ 752c1d255d3SCy Schubert if (crypto_bignum_to_bin(prime, bin1, sizeof(bin1), prime_len) < 0) 753c1d255d3SCy Schubert goto fail; 754c1d255d3SCy Schubert if ((bin1[prime_len - 1] & 0x03) != 3) { 755c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SSWU: prime does not have p = 3 mod 4"); 756c1d255d3SCy Schubert goto fail; 757c1d255d3SCy Schubert } 758c1d255d3SCy Schubert y = crypto_bignum_init(); 759c1d255d3SCy Schubert if (!y || 760c1d255d3SCy Schubert crypto_bignum_add(prime, one, t1) < 0 || 761c1d255d3SCy Schubert crypto_bignum_rshift(t1, 2, t1) < 0 || 762c1d255d3SCy Schubert crypto_bignum_exptmod(v, t1, prime, y) < 0) 763c1d255d3SCy Schubert goto fail; 764c1d255d3SCy Schubert debug_print_bignum("SSWU: y = sqrt(v)", y, prime_len); 765c1d255d3SCy Schubert 766c1d255d3SCy Schubert /* l = CEQ(LSB(u), LSB(y)) */ 767c1d255d3SCy Schubert if (crypto_bignum_to_bin(u, bin1, sizeof(bin1), prime_len) < 0 || 768c1d255d3SCy Schubert crypto_bignum_to_bin(y, bin2, sizeof(bin2), prime_len) < 0) 769c1d255d3SCy Schubert goto fail; 770c1d255d3SCy Schubert is_eq = const_time_eq(bin1[prime_len - 1] & 0x01, 771c1d255d3SCy Schubert bin2[prime_len - 1] & 0x01); 772c1d255d3SCy Schubert 773c1d255d3SCy Schubert /* P = CSEL(l, (x,y), (x, p-y)) */ 774c1d255d3SCy Schubert if (crypto_bignum_sub(prime, y, t1) < 0) 775c1d255d3SCy Schubert goto fail; 776c1d255d3SCy Schubert debug_print_bignum("SSWU: p - y", t1, prime_len); 777c1d255d3SCy Schubert if (crypto_bignum_to_bin(y, bin1, sizeof(bin1), prime_len) < 0 || 778c1d255d3SCy Schubert crypto_bignum_to_bin(t1, bin2, sizeof(bin2), prime_len) < 0) 779c1d255d3SCy Schubert goto fail; 780c1d255d3SCy Schubert const_time_select_bin(is_eq, bin1, bin2, prime_len, &x_y[prime_len]); 781c1d255d3SCy Schubert 782c1d255d3SCy Schubert /* output P */ 783c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SSWU: P.x", x_y, prime_len); 784c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SSWU: P.y", &x_y[prime_len], prime_len); 785c1d255d3SCy Schubert p = crypto_ec_point_from_bin(ec, x_y); 786c1d255d3SCy Schubert 787c1d255d3SCy Schubert fail: 788c1d255d3SCy Schubert crypto_bignum_deinit(u2, 1); 789c1d255d3SCy Schubert crypto_bignum_deinit(t1, 1); 790c1d255d3SCy Schubert crypto_bignum_deinit(t2, 1); 791c1d255d3SCy Schubert crypto_bignum_deinit(z, 0); 792c1d255d3SCy Schubert crypto_bignum_deinit(t, 1); 793c1d255d3SCy Schubert crypto_bignum_deinit(x1a, 1); 794c1d255d3SCy Schubert crypto_bignum_deinit(x1b, 1); 795c1d255d3SCy Schubert crypto_bignum_deinit(x1, 1); 796c1d255d3SCy Schubert crypto_bignum_deinit(x2, 1); 797c1d255d3SCy Schubert crypto_bignum_deinit(gx1, 1); 798c1d255d3SCy Schubert crypto_bignum_deinit(gx2, 1); 799c1d255d3SCy Schubert crypto_bignum_deinit(y, 1); 800c1d255d3SCy Schubert crypto_bignum_deinit(v, 1); 801c1d255d3SCy Schubert crypto_bignum_deinit(zero, 0); 802c1d255d3SCy Schubert crypto_bignum_deinit(one, 0); 803c1d255d3SCy Schubert crypto_bignum_deinit(two, 0); 804c1d255d3SCy Schubert crypto_bignum_deinit(three, 0); 805c1d255d3SCy Schubert forced_memzero(bin, sizeof(bin)); 806c1d255d3SCy Schubert forced_memzero(bin1, sizeof(bin1)); 807c1d255d3SCy Schubert forced_memzero(bin2, sizeof(bin2)); 808c1d255d3SCy Schubert forced_memzero(x_y, sizeof(x_y)); 809c1d255d3SCy Schubert return p; 810c1d255d3SCy Schubert } 811c1d255d3SCy Schubert 812c1d255d3SCy Schubert 813c1d255d3SCy Schubert static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len, 814c1d255d3SCy Schubert const u8 *password, size_t password_len, 815c1d255d3SCy Schubert const char *identifier, u8 *pwd_seed) 816c1d255d3SCy Schubert { 817c1d255d3SCy Schubert const u8 *addr[2]; 818c1d255d3SCy Schubert size_t len[2]; 819c1d255d3SCy Schubert size_t num_elem; 820c1d255d3SCy Schubert 821c1d255d3SCy Schubert /* pwd-seed = HKDF-Extract(ssid, password [ || identifier ]) */ 822c1d255d3SCy Schubert addr[0] = password; 823c1d255d3SCy Schubert len[0] = password_len; 824c1d255d3SCy Schubert num_elem = 1; 825c1d255d3SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "SAE: SSID", ssid, ssid_len); 826c1d255d3SCy Schubert wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 827c1d255d3SCy Schubert password, password_len); 828c1d255d3SCy Schubert if (identifier) { 829c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", 830c1d255d3SCy Schubert identifier); 831c1d255d3SCy Schubert addr[num_elem] = (const u8 *) identifier; 832c1d255d3SCy Schubert len[num_elem] = os_strlen(identifier); 833c1d255d3SCy Schubert num_elem++; 834c1d255d3SCy Schubert } 835c1d255d3SCy Schubert if (hkdf_extract(hash_len, ssid, ssid_len, num_elem, addr, len, 836c1d255d3SCy Schubert pwd_seed) < 0) 837c1d255d3SCy Schubert return -1; 838c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, hash_len); 839c1d255d3SCy Schubert return 0; 840c1d255d3SCy Schubert } 841c1d255d3SCy Schubert 842c1d255d3SCy Schubert 843c1d255d3SCy Schubert size_t sae_ecc_prime_len_2_hash_len(size_t prime_len) 844c1d255d3SCy Schubert { 845c1d255d3SCy Schubert if (prime_len <= 256 / 8) 846c1d255d3SCy Schubert return 32; 847c1d255d3SCy Schubert if (prime_len <= 384 / 8) 848c1d255d3SCy Schubert return 48; 849c1d255d3SCy Schubert return 64; 850c1d255d3SCy Schubert } 851c1d255d3SCy Schubert 852c1d255d3SCy Schubert 853c1d255d3SCy Schubert static struct crypto_ec_point * 854c1d255d3SCy Schubert sae_derive_pt_ecc(struct crypto_ec *ec, int group, 855c1d255d3SCy Schubert const u8 *ssid, size_t ssid_len, 856c1d255d3SCy Schubert const u8 *password, size_t password_len, 857c1d255d3SCy Schubert const char *identifier) 858c1d255d3SCy Schubert { 859c1d255d3SCy Schubert u8 pwd_seed[64]; 860c1d255d3SCy Schubert u8 pwd_value[SAE_MAX_ECC_PRIME_LEN * 2]; 861c1d255d3SCy Schubert size_t pwd_value_len, hash_len, prime_len; 862c1d255d3SCy Schubert const struct crypto_bignum *prime; 863c1d255d3SCy Schubert struct crypto_bignum *bn = NULL; 864c1d255d3SCy Schubert struct crypto_ec_point *p1 = NULL, *p2 = NULL, *pt = NULL; 865c1d255d3SCy Schubert 866c1d255d3SCy Schubert prime = crypto_ec_get_prime(ec); 867c1d255d3SCy Schubert prime_len = crypto_ec_prime_len(ec); 868c1d255d3SCy Schubert if (prime_len > SAE_MAX_ECC_PRIME_LEN) 869c1d255d3SCy Schubert goto fail; 870c1d255d3SCy Schubert hash_len = sae_ecc_prime_len_2_hash_len(prime_len); 871c1d255d3SCy Schubert 872c1d255d3SCy Schubert /* len = olen(p) + ceil(olen(p)/2) */ 873c1d255d3SCy Schubert pwd_value_len = prime_len + (prime_len + 1) / 2; 874c1d255d3SCy Schubert 875c1d255d3SCy Schubert if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, 876c1d255d3SCy Schubert identifier, pwd_seed) < 0) 877c1d255d3SCy Schubert goto fail; 878c1d255d3SCy Schubert 879c1d255d3SCy Schubert /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u1 P1", len) 880c1d255d3SCy Schubert */ 881c1d255d3SCy Schubert if (hkdf_expand(hash_len, pwd_seed, hash_len, 882c1d255d3SCy Schubert "SAE Hash to Element u1 P1", pwd_value, pwd_value_len) < 883c1d255d3SCy Schubert 0) 884c1d255d3SCy Schubert goto fail; 885c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u1 P1)", 886c1d255d3SCy Schubert pwd_value, pwd_value_len); 887c1d255d3SCy Schubert 888c1d255d3SCy Schubert /* u1 = pwd-value modulo p */ 889c1d255d3SCy Schubert bn = crypto_bignum_init_set(pwd_value, pwd_value_len); 890c1d255d3SCy Schubert if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 || 891c1d255d3SCy Schubert crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), 892c1d255d3SCy Schubert prime_len) < 0) 893c1d255d3SCy Schubert goto fail; 894c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: u1", pwd_value, prime_len); 895c1d255d3SCy Schubert 896c1d255d3SCy Schubert /* P1 = SSWU(u1) */ 897c1d255d3SCy Schubert p1 = sswu(ec, group, bn); 898c1d255d3SCy Schubert if (!p1) 899c1d255d3SCy Schubert goto fail; 900c1d255d3SCy Schubert 901c1d255d3SCy Schubert /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u2 P2", len) 902c1d255d3SCy Schubert */ 903c1d255d3SCy Schubert if (hkdf_expand(hash_len, pwd_seed, hash_len, 904c1d255d3SCy Schubert "SAE Hash to Element u2 P2", pwd_value, 905c1d255d3SCy Schubert pwd_value_len) < 0) 906c1d255d3SCy Schubert goto fail; 907c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u2 P2)", 908c1d255d3SCy Schubert pwd_value, pwd_value_len); 909c1d255d3SCy Schubert 910c1d255d3SCy Schubert /* u2 = pwd-value modulo p */ 911c1d255d3SCy Schubert crypto_bignum_deinit(bn, 1); 912c1d255d3SCy Schubert bn = crypto_bignum_init_set(pwd_value, pwd_value_len); 913c1d255d3SCy Schubert if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 || 914c1d255d3SCy Schubert crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), 915c1d255d3SCy Schubert prime_len) < 0) 916c1d255d3SCy Schubert goto fail; 917c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: u2", pwd_value, prime_len); 918c1d255d3SCy Schubert 919c1d255d3SCy Schubert /* P2 = SSWU(u2) */ 920c1d255d3SCy Schubert p2 = sswu(ec, group, bn); 921c1d255d3SCy Schubert if (!p2) 922c1d255d3SCy Schubert goto fail; 923c1d255d3SCy Schubert 924c1d255d3SCy Schubert /* PT = elem-op(P1, P2) */ 925c1d255d3SCy Schubert pt = crypto_ec_point_init(ec); 926c1d255d3SCy Schubert if (!pt) 927c1d255d3SCy Schubert goto fail; 928c1d255d3SCy Schubert if (crypto_ec_point_add(ec, p1, p2, pt) < 0) { 929c1d255d3SCy Schubert crypto_ec_point_deinit(pt, 1); 930c1d255d3SCy Schubert pt = NULL; 931c1d255d3SCy Schubert } 932c1d255d3SCy Schubert 933c1d255d3SCy Schubert fail: 934c1d255d3SCy Schubert forced_memzero(pwd_seed, sizeof(pwd_seed)); 935c1d255d3SCy Schubert forced_memzero(pwd_value, sizeof(pwd_value)); 936c1d255d3SCy Schubert crypto_bignum_deinit(bn, 1); 937c1d255d3SCy Schubert crypto_ec_point_deinit(p1, 1); 938c1d255d3SCy Schubert crypto_ec_point_deinit(p2, 1); 939c1d255d3SCy Schubert return pt; 940c1d255d3SCy Schubert } 941c1d255d3SCy Schubert 942c1d255d3SCy Schubert 943c1d255d3SCy Schubert size_t sae_ffc_prime_len_2_hash_len(size_t prime_len) 944c1d255d3SCy Schubert { 945c1d255d3SCy Schubert if (prime_len <= 2048 / 8) 946c1d255d3SCy Schubert return 32; 947c1d255d3SCy Schubert if (prime_len <= 3072 / 8) 948c1d255d3SCy Schubert return 48; 949c1d255d3SCy Schubert return 64; 950c1d255d3SCy Schubert } 951c1d255d3SCy Schubert 952c1d255d3SCy Schubert 953c1d255d3SCy Schubert static struct crypto_bignum * 954c1d255d3SCy Schubert sae_derive_pt_ffc(const struct dh_group *dh, int group, 955c1d255d3SCy Schubert const u8 *ssid, size_t ssid_len, 956c1d255d3SCy Schubert const u8 *password, size_t password_len, 957c1d255d3SCy Schubert const char *identifier) 958c1d255d3SCy Schubert { 959c1d255d3SCy Schubert size_t hash_len, prime_len, pwd_value_len; 960c1d255d3SCy Schubert struct crypto_bignum *prime, *order; 961c1d255d3SCy Schubert struct crypto_bignum *one = NULL, *two = NULL, *bn = NULL, *tmp = NULL, 962c1d255d3SCy Schubert *pt = NULL; 963c1d255d3SCy Schubert u8 pwd_seed[64]; 964c1d255d3SCy Schubert u8 pwd_value[SAE_MAX_PRIME_LEN + SAE_MAX_PRIME_LEN / 2]; 965c1d255d3SCy Schubert 966c1d255d3SCy Schubert prime = crypto_bignum_init_set(dh->prime, dh->prime_len); 967c1d255d3SCy Schubert order = crypto_bignum_init_set(dh->order, dh->order_len); 968c1d255d3SCy Schubert if (!prime || !order) 969c1d255d3SCy Schubert goto fail; 970c1d255d3SCy Schubert prime_len = dh->prime_len; 971c1d255d3SCy Schubert if (prime_len > SAE_MAX_PRIME_LEN) 972c1d255d3SCy Schubert goto fail; 973c1d255d3SCy Schubert hash_len = sae_ffc_prime_len_2_hash_len(prime_len); 974c1d255d3SCy Schubert 975c1d255d3SCy Schubert /* len = olen(p) + ceil(olen(p)/2) */ 976c1d255d3SCy Schubert pwd_value_len = prime_len + (prime_len + 1) / 2; 977c1d255d3SCy Schubert if (pwd_value_len > sizeof(pwd_value)) 978c1d255d3SCy Schubert goto fail; 979c1d255d3SCy Schubert 980c1d255d3SCy Schubert if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, 981c1d255d3SCy Schubert identifier, pwd_seed) < 0) 982c1d255d3SCy Schubert goto fail; 983c1d255d3SCy Schubert 984c1d255d3SCy Schubert /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element", len) */ 985c1d255d3SCy Schubert if (hkdf_expand(hash_len, pwd_seed, hash_len, 986c1d255d3SCy Schubert "SAE Hash to Element", pwd_value, pwd_value_len) < 0) 987c1d255d3SCy Schubert goto fail; 988c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", 989c1d255d3SCy Schubert pwd_value, pwd_value_len); 990c1d255d3SCy Schubert 991c1d255d3SCy Schubert /* pwd-value = (pwd-value modulo (p-2)) + 2 */ 992c1d255d3SCy Schubert bn = crypto_bignum_init_set(pwd_value, pwd_value_len); 993c1d255d3SCy Schubert one = crypto_bignum_init_uint(1); 994c1d255d3SCy Schubert two = crypto_bignum_init_uint(2); 995c1d255d3SCy Schubert tmp = crypto_bignum_init(); 996c1d255d3SCy Schubert if (!bn || !one || !two || !tmp || 997c1d255d3SCy Schubert crypto_bignum_sub(prime, two, tmp) < 0 || 998c1d255d3SCy Schubert crypto_bignum_mod(bn, tmp, bn) < 0 || 999c1d255d3SCy Schubert crypto_bignum_add(bn, two, bn) < 0 || 1000c1d255d3SCy Schubert crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), 1001c1d255d3SCy Schubert prime_len) < 0) 1002c1d255d3SCy Schubert goto fail; 1003c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value(reduced)", 1004c1d255d3SCy Schubert pwd_value, prime_len); 1005c1d255d3SCy Schubert 1006c1d255d3SCy Schubert /* PT = pwd-value^((p-1)/q) modulo p */ 1007c1d255d3SCy Schubert pt = crypto_bignum_init(); 1008c1d255d3SCy Schubert if (!pt || 1009c1d255d3SCy Schubert crypto_bignum_sub(prime, one, tmp) < 0 || 1010c1d255d3SCy Schubert crypto_bignum_div(tmp, order, tmp) < 0 || 1011c1d255d3SCy Schubert crypto_bignum_exptmod(bn, tmp, prime, pt) < 0) { 1012c1d255d3SCy Schubert crypto_bignum_deinit(pt, 1); 1013c1d255d3SCy Schubert pt = NULL; 1014c1d255d3SCy Schubert goto fail; 1015c1d255d3SCy Schubert } 1016c1d255d3SCy Schubert debug_print_bignum("SAE: PT", pt, prime_len); 1017c1d255d3SCy Schubert 1018c1d255d3SCy Schubert fail: 1019c1d255d3SCy Schubert forced_memzero(pwd_seed, sizeof(pwd_seed)); 1020c1d255d3SCy Schubert forced_memzero(pwd_value, sizeof(pwd_value)); 1021c1d255d3SCy Schubert crypto_bignum_deinit(bn, 1); 1022c1d255d3SCy Schubert crypto_bignum_deinit(tmp, 1); 1023c1d255d3SCy Schubert crypto_bignum_deinit(one, 0); 1024c1d255d3SCy Schubert crypto_bignum_deinit(two, 0); 1025c1d255d3SCy Schubert crypto_bignum_deinit(prime, 0); 1026c1d255d3SCy Schubert crypto_bignum_deinit(order, 0); 1027c1d255d3SCy Schubert return pt; 1028c1d255d3SCy Schubert } 1029c1d255d3SCy Schubert 1030c1d255d3SCy Schubert 1031c1d255d3SCy Schubert static struct sae_pt * 1032c1d255d3SCy Schubert sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, 1033c1d255d3SCy Schubert const u8 *password, size_t password_len, 1034c1d255d3SCy Schubert const char *identifier) 1035c1d255d3SCy Schubert { 1036c1d255d3SCy Schubert struct sae_pt *pt; 1037c1d255d3SCy Schubert 1038c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group); 1039c1d255d3SCy Schubert 1040c1d255d3SCy Schubert if (ssid_len > 32) 1041c1d255d3SCy Schubert return NULL; 1042c1d255d3SCy Schubert 1043c1d255d3SCy Schubert pt = os_zalloc(sizeof(*pt)); 1044c1d255d3SCy Schubert if (!pt) 1045c1d255d3SCy Schubert return NULL; 1046c1d255d3SCy Schubert 1047c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1048c1d255d3SCy Schubert os_memcpy(pt->ssid, ssid, ssid_len); 1049c1d255d3SCy Schubert pt->ssid_len = ssid_len; 1050c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 1051c1d255d3SCy Schubert pt->group = group; 1052c1d255d3SCy Schubert pt->ec = crypto_ec_init(group); 1053c1d255d3SCy Schubert if (pt->ec) { 1054c1d255d3SCy Schubert pt->ecc_pt = sae_derive_pt_ecc(pt->ec, group, ssid, ssid_len, 1055c1d255d3SCy Schubert password, password_len, 1056c1d255d3SCy Schubert identifier); 1057c1d255d3SCy Schubert if (!pt->ecc_pt) { 1058c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); 1059c1d255d3SCy Schubert goto fail; 1060c1d255d3SCy Schubert } 1061c1d255d3SCy Schubert 1062c1d255d3SCy Schubert return pt; 1063c1d255d3SCy Schubert } 1064c1d255d3SCy Schubert 1065c1d255d3SCy Schubert pt->dh = dh_groups_get(group); 1066c1d255d3SCy Schubert if (!pt->dh) { 1067c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Unsupported group %d", group); 1068c1d255d3SCy Schubert goto fail; 1069c1d255d3SCy Schubert } 1070c1d255d3SCy Schubert 1071c1d255d3SCy Schubert pt->ffc_pt = sae_derive_pt_ffc(pt->dh, group, ssid, ssid_len, 1072c1d255d3SCy Schubert password, password_len, identifier); 1073c1d255d3SCy Schubert if (!pt->ffc_pt) { 1074c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); 1075c1d255d3SCy Schubert goto fail; 1076c1d255d3SCy Schubert } 1077c1d255d3SCy Schubert 1078c1d255d3SCy Schubert return pt; 1079c1d255d3SCy Schubert fail: 1080c1d255d3SCy Schubert sae_deinit_pt(pt); 1081c1d255d3SCy Schubert return NULL; 1082c1d255d3SCy Schubert } 1083c1d255d3SCy Schubert 1084c1d255d3SCy Schubert 1085c1d255d3SCy Schubert struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len, 1086c1d255d3SCy Schubert const u8 *password, size_t password_len, 1087c1d255d3SCy Schubert const char *identifier) 1088c1d255d3SCy Schubert { 1089c1d255d3SCy Schubert struct sae_pt *pt = NULL, *last = NULL, *tmp; 1090c1d255d3SCy Schubert int default_groups[] = { 19, 0 }; 1091c1d255d3SCy Schubert int i; 1092c1d255d3SCy Schubert 1093c1d255d3SCy Schubert if (!groups) 1094c1d255d3SCy Schubert groups = default_groups; 1095c1d255d3SCy Schubert for (i = 0; groups[i] > 0; i++) { 1096c1d255d3SCy Schubert tmp = sae_derive_pt_group(groups[i], ssid, ssid_len, password, 1097c1d255d3SCy Schubert password_len, identifier); 1098c1d255d3SCy Schubert if (!tmp) 1099c1d255d3SCy Schubert continue; 1100c1d255d3SCy Schubert 1101c1d255d3SCy Schubert if (last) 1102c1d255d3SCy Schubert last->next = tmp; 1103c1d255d3SCy Schubert else 1104c1d255d3SCy Schubert pt = tmp; 1105c1d255d3SCy Schubert last = tmp; 1106c1d255d3SCy Schubert } 1107c1d255d3SCy Schubert 1108c1d255d3SCy Schubert return pt; 1109c1d255d3SCy Schubert } 1110c1d255d3SCy Schubert 1111c1d255d3SCy Schubert 1112c1d255d3SCy Schubert static void sae_max_min_addr(const u8 *addr[], size_t len[], 1113c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2) 1114c1d255d3SCy Schubert { 1115c1d255d3SCy Schubert len[0] = ETH_ALEN; 1116c1d255d3SCy Schubert len[1] = ETH_ALEN; 1117c1d255d3SCy Schubert if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { 1118c1d255d3SCy Schubert addr[0] = addr1; 1119c1d255d3SCy Schubert addr[1] = addr2; 1120c1d255d3SCy Schubert } else { 1121c1d255d3SCy Schubert addr[0] = addr2; 1122c1d255d3SCy Schubert addr[1] = addr1; 1123c1d255d3SCy Schubert } 1124c1d255d3SCy Schubert } 1125c1d255d3SCy Schubert 1126c1d255d3SCy Schubert 1127c1d255d3SCy Schubert struct crypto_ec_point * 1128c1d255d3SCy Schubert sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt, 1129c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2) 1130c1d255d3SCy Schubert { 1131c1d255d3SCy Schubert u8 bin[SAE_MAX_ECC_PRIME_LEN * 2]; 1132c1d255d3SCy Schubert size_t prime_len; 1133c1d255d3SCy Schubert const u8 *addr[2]; 1134c1d255d3SCy Schubert size_t len[2]; 1135c1d255d3SCy Schubert u8 salt[64], hash[64]; 1136c1d255d3SCy Schubert size_t hash_len; 1137c1d255d3SCy Schubert const struct crypto_bignum *order; 1138c1d255d3SCy Schubert struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL; 1139c1d255d3SCy Schubert struct crypto_ec_point *pwe = NULL; 1140c1d255d3SCy Schubert 1141c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT"); 1142c1d255d3SCy Schubert prime_len = crypto_ec_prime_len(pt->ec); 1143c1d255d3SCy Schubert if (crypto_ec_point_to_bin(pt->ec, pt->ecc_pt, 1144c1d255d3SCy Schubert bin, bin + prime_len) < 0) 1145c1d255d3SCy Schubert return NULL; 1146c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PT.x", bin, prime_len); 1147c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PT.y", bin + prime_len, prime_len); 1148c1d255d3SCy Schubert 1149c1d255d3SCy Schubert sae_max_min_addr(addr, len, addr1, addr2); 1150c1d255d3SCy Schubert 1151c1d255d3SCy Schubert /* val = H(0^n, 1152c1d255d3SCy Schubert * MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */ 1153c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))"); 1154c1d255d3SCy Schubert hash_len = sae_ecc_prime_len_2_hash_len(prime_len); 1155c1d255d3SCy Schubert os_memset(salt, 0, hash_len); 1156c1d255d3SCy Schubert if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0) 1157c1d255d3SCy Schubert goto fail; 1158c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len); 1159c1d255d3SCy Schubert 1160c1d255d3SCy Schubert /* val = val modulo (q - 1) + 1 */ 1161c1d255d3SCy Schubert order = crypto_ec_get_order(pt->ec); 1162c1d255d3SCy Schubert tmp = crypto_bignum_init(); 1163c1d255d3SCy Schubert val = crypto_bignum_init_set(hash, hash_len); 1164c1d255d3SCy Schubert one = crypto_bignum_init_uint(1); 1165c1d255d3SCy Schubert if (!tmp || !val || !one || 1166c1d255d3SCy Schubert crypto_bignum_sub(order, one, tmp) < 0 || 1167c1d255d3SCy Schubert crypto_bignum_mod(val, tmp, val) < 0 || 1168c1d255d3SCy Schubert crypto_bignum_add(val, one, val) < 0) 1169c1d255d3SCy Schubert goto fail; 1170c1d255d3SCy Schubert debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len); 1171c1d255d3SCy Schubert 1172c1d255d3SCy Schubert /* PWE = scalar-op(val, PT) */ 1173c1d255d3SCy Schubert pwe = crypto_ec_point_init(pt->ec); 1174c1d255d3SCy Schubert if (!pwe || 1175c1d255d3SCy Schubert crypto_ec_point_mul(pt->ec, pt->ecc_pt, val, pwe) < 0 || 1176c1d255d3SCy Schubert crypto_ec_point_to_bin(pt->ec, pwe, bin, bin + prime_len) < 0) { 1177c1d255d3SCy Schubert crypto_ec_point_deinit(pwe, 1); 1178c1d255d3SCy Schubert pwe = NULL; 1179c1d255d3SCy Schubert goto fail; 1180c1d255d3SCy Schubert } 1181c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.x", bin, prime_len); 1182c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.y", bin + prime_len, prime_len); 1183c1d255d3SCy Schubert 1184c1d255d3SCy Schubert fail: 1185c1d255d3SCy Schubert crypto_bignum_deinit(tmp, 1); 1186c1d255d3SCy Schubert crypto_bignum_deinit(val, 1); 1187c1d255d3SCy Schubert crypto_bignum_deinit(one, 0); 1188c1d255d3SCy Schubert return pwe; 1189c1d255d3SCy Schubert } 1190c1d255d3SCy Schubert 1191c1d255d3SCy Schubert 1192c1d255d3SCy Schubert struct crypto_bignum * 1193c1d255d3SCy Schubert sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt, 1194c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2) 1195c1d255d3SCy Schubert { 1196c1d255d3SCy Schubert size_t prime_len; 1197c1d255d3SCy Schubert const u8 *addr[2]; 1198c1d255d3SCy Schubert size_t len[2]; 1199c1d255d3SCy Schubert u8 salt[64], hash[64]; 1200c1d255d3SCy Schubert size_t hash_len; 1201c1d255d3SCy Schubert struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL; 1202c1d255d3SCy Schubert struct crypto_bignum *pwe = NULL, *order = NULL, *prime = NULL; 1203c1d255d3SCy Schubert 1204c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT"); 1205c1d255d3SCy Schubert prime = crypto_bignum_init_set(pt->dh->prime, pt->dh->prime_len); 1206c1d255d3SCy Schubert order = crypto_bignum_init_set(pt->dh->order, pt->dh->order_len); 1207c1d255d3SCy Schubert if (!prime || !order) 1208c1d255d3SCy Schubert goto fail; 1209c1d255d3SCy Schubert prime_len = pt->dh->prime_len; 1210c1d255d3SCy Schubert 1211c1d255d3SCy Schubert sae_max_min_addr(addr, len, addr1, addr2); 1212c1d255d3SCy Schubert 1213c1d255d3SCy Schubert /* val = H(0^n, 1214c1d255d3SCy Schubert * MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */ 1215c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))"); 1216c1d255d3SCy Schubert hash_len = sae_ffc_prime_len_2_hash_len(prime_len); 1217c1d255d3SCy Schubert os_memset(salt, 0, hash_len); 1218c1d255d3SCy Schubert if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0) 1219c1d255d3SCy Schubert goto fail; 1220c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len); 1221c1d255d3SCy Schubert 1222c1d255d3SCy Schubert /* val = val modulo (q - 1) + 1 */ 1223c1d255d3SCy Schubert tmp = crypto_bignum_init(); 1224c1d255d3SCy Schubert val = crypto_bignum_init_set(hash, hash_len); 1225c1d255d3SCy Schubert one = crypto_bignum_init_uint(1); 1226c1d255d3SCy Schubert if (!tmp || !val || !one || 1227c1d255d3SCy Schubert crypto_bignum_sub(order, one, tmp) < 0 || 1228c1d255d3SCy Schubert crypto_bignum_mod(val, tmp, val) < 0 || 1229c1d255d3SCy Schubert crypto_bignum_add(val, one, val) < 0) 1230c1d255d3SCy Schubert goto fail; 1231c1d255d3SCy Schubert debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len); 1232c1d255d3SCy Schubert 1233c1d255d3SCy Schubert /* PWE = scalar-op(val, PT) */ 1234c1d255d3SCy Schubert pwe = crypto_bignum_init(); 1235c1d255d3SCy Schubert if (!pwe || crypto_bignum_exptmod(pt->ffc_pt, val, prime, pwe) < 0) { 1236c1d255d3SCy Schubert crypto_bignum_deinit(pwe, 1); 1237c1d255d3SCy Schubert pwe = NULL; 1238c1d255d3SCy Schubert goto fail; 1239c1d255d3SCy Schubert } 1240c1d255d3SCy Schubert debug_print_bignum("SAE: PWE", pwe, prime_len); 1241c1d255d3SCy Schubert 1242c1d255d3SCy Schubert fail: 1243c1d255d3SCy Schubert crypto_bignum_deinit(tmp, 1); 1244c1d255d3SCy Schubert crypto_bignum_deinit(val, 1); 1245c1d255d3SCy Schubert crypto_bignum_deinit(one, 0); 1246c1d255d3SCy Schubert crypto_bignum_deinit(prime, 0); 1247c1d255d3SCy Schubert crypto_bignum_deinit(order, 0); 1248c1d255d3SCy Schubert return pwe; 1249c1d255d3SCy Schubert } 1250c1d255d3SCy Schubert 1251c1d255d3SCy Schubert 1252c1d255d3SCy Schubert void sae_deinit_pt(struct sae_pt *pt) 1253c1d255d3SCy Schubert { 1254c1d255d3SCy Schubert struct sae_pt *prev; 1255c1d255d3SCy Schubert 1256c1d255d3SCy Schubert while (pt) { 1257c1d255d3SCy Schubert crypto_ec_point_deinit(pt->ecc_pt, 1); 1258c1d255d3SCy Schubert crypto_bignum_deinit(pt->ffc_pt, 1); 1259c1d255d3SCy Schubert crypto_ec_deinit(pt->ec); 1260c1d255d3SCy Schubert prev = pt; 1261c1d255d3SCy Schubert pt = pt->next; 1262c1d255d3SCy Schubert os_free(prev); 1263c1d255d3SCy Schubert } 1264c1d255d3SCy Schubert } 1265c1d255d3SCy Schubert 1266c1d255d3SCy Schubert 12675b9c547cSRui Paulo static int sae_derive_commit_element_ecc(struct sae_data *sae, 12685b9c547cSRui Paulo struct crypto_bignum *mask) 12695b9c547cSRui Paulo { 12705b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 12715b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) { 12725b9c547cSRui Paulo sae->tmp->own_commit_element_ecc = 12735b9c547cSRui Paulo crypto_ec_point_init(sae->tmp->ec); 12745b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) 12755b9c547cSRui Paulo return -1; 12765b9c547cSRui Paulo } 12775b9c547cSRui Paulo 12785b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, 12795b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0 || 12805b9c547cSRui Paulo crypto_ec_point_invert(sae->tmp->ec, 12815b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0) { 12825b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 12835b9c547cSRui Paulo return -1; 12845b9c547cSRui Paulo } 12855b9c547cSRui Paulo 12865b9c547cSRui Paulo return 0; 12875b9c547cSRui Paulo } 12885b9c547cSRui Paulo 12895b9c547cSRui Paulo 12905b9c547cSRui Paulo static int sae_derive_commit_element_ffc(struct sae_data *sae, 12915b9c547cSRui Paulo struct crypto_bignum *mask) 12925b9c547cSRui Paulo { 12935b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 12945b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) { 12955b9c547cSRui Paulo sae->tmp->own_commit_element_ffc = crypto_bignum_init(); 12965b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) 12975b9c547cSRui Paulo return -1; 12985b9c547cSRui Paulo } 12995b9c547cSRui Paulo 13005b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, 13015b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0 || 13025b9c547cSRui Paulo crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, 13035b9c547cSRui Paulo sae->tmp->prime, 13045b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0) { 13055b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 13065b9c547cSRui Paulo return -1; 13075b9c547cSRui Paulo } 13085b9c547cSRui Paulo 13095b9c547cSRui Paulo return 0; 13105b9c547cSRui Paulo } 13115b9c547cSRui Paulo 13125b9c547cSRui Paulo 13135b9c547cSRui Paulo static int sae_derive_commit(struct sae_data *sae) 13145b9c547cSRui Paulo { 13155b9c547cSRui Paulo struct crypto_bignum *mask; 1316206b73d0SCy Schubert int ret; 1317325151a3SRui Paulo 1318206b73d0SCy Schubert mask = crypto_bignum_init(); 1319206b73d0SCy Schubert if (!sae->tmp->sae_rand) 1320206b73d0SCy Schubert sae->tmp->sae_rand = crypto_bignum_init(); 13215b9c547cSRui Paulo if (!sae->tmp->own_commit_scalar) 1322206b73d0SCy Schubert sae->tmp->own_commit_scalar = crypto_bignum_init(); 1323206b73d0SCy Schubert ret = !mask || !sae->tmp->sae_rand || !sae->tmp->own_commit_scalar || 1324206b73d0SCy Schubert dragonfly_generate_scalar(sae->tmp->order, sae->tmp->sae_rand, 1325206b73d0SCy Schubert mask, 1326206b73d0SCy Schubert sae->tmp->own_commit_scalar) < 0 || 1327206b73d0SCy Schubert (sae->tmp->ec && 1328206b73d0SCy Schubert sae_derive_commit_element_ecc(sae, mask) < 0) || 1329206b73d0SCy Schubert (sae->tmp->dh && 1330206b73d0SCy Schubert sae_derive_commit_element_ffc(sae, mask) < 0); 13315b9c547cSRui Paulo crypto_bignum_deinit(mask, 1); 1332206b73d0SCy Schubert return ret ? -1 : 0; 13335b9c547cSRui Paulo } 13345b9c547cSRui Paulo 13355b9c547cSRui Paulo 13365b9c547cSRui Paulo int sae_prepare_commit(const u8 *addr1, const u8 *addr2, 13375b9c547cSRui Paulo const u8 *password, size_t password_len, 1338c1d255d3SCy Schubert struct sae_data *sae) 13395b9c547cSRui Paulo { 1340325151a3SRui Paulo if (sae->tmp == NULL || 1341325151a3SRui Paulo (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, 1342c1d255d3SCy Schubert password_len) < 0) || 1343325151a3SRui Paulo (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, 1344c1d255d3SCy Schubert password_len) < 0)) 13455b9c547cSRui Paulo return -1; 1346c1d255d3SCy Schubert 1347c1d255d3SCy Schubert sae->h2e = 0; 1348c1d255d3SCy Schubert sae->pk = 0; 1349c1d255d3SCy Schubert return sae_derive_commit(sae); 1350c1d255d3SCy Schubert } 1351c1d255d3SCy Schubert 1352c1d255d3SCy Schubert 1353c1d255d3SCy Schubert int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt, 1354c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2, 1355c1d255d3SCy Schubert int *rejected_groups, const struct sae_pk *pk) 1356c1d255d3SCy Schubert { 1357c1d255d3SCy Schubert if (!sae->tmp) 1358c1d255d3SCy Schubert return -1; 1359c1d255d3SCy Schubert 1360c1d255d3SCy Schubert while (pt) { 1361c1d255d3SCy Schubert if (pt->group == sae->group) 1362c1d255d3SCy Schubert break; 1363c1d255d3SCy Schubert pt = pt->next; 1364c1d255d3SCy Schubert } 1365c1d255d3SCy Schubert if (!pt) { 1366c1d255d3SCy Schubert wpa_printf(MSG_INFO, "SAE: Could not find PT for group %u", 1367c1d255d3SCy Schubert sae->group); 1368c1d255d3SCy Schubert return -1; 1369c1d255d3SCy Schubert } 1370c1d255d3SCy Schubert 1371c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1372c1d255d3SCy Schubert os_memcpy(sae->tmp->ssid, pt->ssid, pt->ssid_len); 1373c1d255d3SCy Schubert sae->tmp->ssid_len = pt->ssid_len; 1374c1d255d3SCy Schubert sae->tmp->ap_pk = pk; 1375c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 1376c1d255d3SCy Schubert sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0; 1377c1d255d3SCy Schubert wpabuf_free(sae->tmp->own_rejected_groups); 1378c1d255d3SCy Schubert sae->tmp->own_rejected_groups = NULL; 1379c1d255d3SCy Schubert if (rejected_groups) { 1380c1d255d3SCy Schubert int count, i; 1381c1d255d3SCy Schubert struct wpabuf *groups; 1382c1d255d3SCy Schubert 1383c1d255d3SCy Schubert count = int_array_len(rejected_groups); 1384c1d255d3SCy Schubert groups = wpabuf_alloc(count * 2); 1385c1d255d3SCy Schubert if (!groups) 1386c1d255d3SCy Schubert return -1; 1387c1d255d3SCy Schubert for (i = 0; i < count; i++) 1388c1d255d3SCy Schubert wpabuf_put_le16(groups, rejected_groups[i]); 1389c1d255d3SCy Schubert sae->tmp->own_rejected_groups = groups; 1390c1d255d3SCy Schubert } 1391c1d255d3SCy Schubert 1392c1d255d3SCy Schubert if (pt->ec) { 1393c1d255d3SCy Schubert crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1); 1394c1d255d3SCy Schubert sae->tmp->pwe_ecc = sae_derive_pwe_from_pt_ecc(pt, addr1, 1395c1d255d3SCy Schubert addr2); 1396c1d255d3SCy Schubert if (!sae->tmp->pwe_ecc) 1397c1d255d3SCy Schubert return -1; 1398c1d255d3SCy Schubert } 1399c1d255d3SCy Schubert 1400c1d255d3SCy Schubert if (pt->dh) { 1401c1d255d3SCy Schubert crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); 1402c1d255d3SCy Schubert sae->tmp->pwe_ffc = sae_derive_pwe_from_pt_ffc(pt, addr1, 1403c1d255d3SCy Schubert addr2); 1404c1d255d3SCy Schubert if (!sae->tmp->pwe_ffc) 1405c1d255d3SCy Schubert return -1; 1406c1d255d3SCy Schubert } 1407c1d255d3SCy Schubert 1408c1d255d3SCy Schubert sae->h2e = 1; 1409c1d255d3SCy Schubert return sae_derive_commit(sae); 14105b9c547cSRui Paulo } 14115b9c547cSRui Paulo 14125b9c547cSRui Paulo 14135b9c547cSRui Paulo static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) 14145b9c547cSRui Paulo { 14155b9c547cSRui Paulo struct crypto_ec_point *K; 14165b9c547cSRui Paulo int ret = -1; 14175b9c547cSRui Paulo 14185b9c547cSRui Paulo K = crypto_ec_point_init(sae->tmp->ec); 14195b9c547cSRui Paulo if (K == NULL) 14205b9c547cSRui Paulo goto fail; 14215b9c547cSRui Paulo 14225b9c547cSRui Paulo /* 14235b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 14245b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 14255b9c547cSRui Paulo * If K is identity element (point-at-infinity), reject 14265b9c547cSRui Paulo * k = F(K) (= x coordinate) 14275b9c547cSRui Paulo */ 14285b9c547cSRui Paulo 14295b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, 14305b9c547cSRui Paulo sae->peer_commit_scalar, K) < 0 || 14315b9c547cSRui Paulo crypto_ec_point_add(sae->tmp->ec, K, 14325b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, K) < 0 || 14335b9c547cSRui Paulo crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || 14345b9c547cSRui Paulo crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || 14355b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { 14365b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 14375b9c547cSRui Paulo goto fail; 14385b9c547cSRui Paulo } 14395b9c547cSRui Paulo 14405b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 14415b9c547cSRui Paulo 14425b9c547cSRui Paulo ret = 0; 14435b9c547cSRui Paulo fail: 14445b9c547cSRui Paulo crypto_ec_point_deinit(K, 1); 14455b9c547cSRui Paulo return ret; 14465b9c547cSRui Paulo } 14475b9c547cSRui Paulo 14485b9c547cSRui Paulo 14495b9c547cSRui Paulo static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) 14505b9c547cSRui Paulo { 14515b9c547cSRui Paulo struct crypto_bignum *K; 14525b9c547cSRui Paulo int ret = -1; 14535b9c547cSRui Paulo 14545b9c547cSRui Paulo K = crypto_bignum_init(); 14555b9c547cSRui Paulo if (K == NULL) 14565b9c547cSRui Paulo goto fail; 14575b9c547cSRui Paulo 14585b9c547cSRui Paulo /* 14595b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 14605b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 14615b9c547cSRui Paulo * If K is identity element (one), reject. 14625b9c547cSRui Paulo * k = F(K) (= x coordinate) 14635b9c547cSRui Paulo */ 14645b9c547cSRui Paulo 14655b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, 14665b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 14675b9c547cSRui Paulo crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, 14685b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 14695b9c547cSRui Paulo crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 14705b9c547cSRui Paulo || 14715b9c547cSRui Paulo crypto_bignum_is_one(K) || 14725b9c547cSRui Paulo crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < 14735b9c547cSRui Paulo 0) { 14745b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 14755b9c547cSRui Paulo goto fail; 14765b9c547cSRui Paulo } 14775b9c547cSRui Paulo 14785b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 14795b9c547cSRui Paulo 14805b9c547cSRui Paulo ret = 0; 14815b9c547cSRui Paulo fail: 14825b9c547cSRui Paulo crypto_bignum_deinit(K, 1); 14835b9c547cSRui Paulo return ret; 14845b9c547cSRui Paulo } 14855b9c547cSRui Paulo 14865b9c547cSRui Paulo 1487c1d255d3SCy Schubert static int sae_kdf_hash(size_t hash_len, const u8 *k, const char *label, 1488c1d255d3SCy Schubert const u8 *context, size_t context_len, 1489c1d255d3SCy Schubert u8 *out, size_t out_len) 1490c1d255d3SCy Schubert { 1491c1d255d3SCy Schubert if (hash_len == 32) 1492c1d255d3SCy Schubert return sha256_prf(k, hash_len, label, 1493c1d255d3SCy Schubert context, context_len, out, out_len); 1494c1d255d3SCy Schubert #ifdef CONFIG_SHA384 1495c1d255d3SCy Schubert if (hash_len == 48) 1496c1d255d3SCy Schubert return sha384_prf(k, hash_len, label, 1497c1d255d3SCy Schubert context, context_len, out, out_len); 1498c1d255d3SCy Schubert #endif /* CONFIG_SHA384 */ 1499c1d255d3SCy Schubert #ifdef CONFIG_SHA512 1500c1d255d3SCy Schubert if (hash_len == 64) 1501c1d255d3SCy Schubert return sha512_prf(k, hash_len, label, 1502c1d255d3SCy Schubert context, context_len, out, out_len); 1503c1d255d3SCy Schubert #endif /* CONFIG_SHA512 */ 1504c1d255d3SCy Schubert return -1; 1505c1d255d3SCy Schubert } 1506c1d255d3SCy Schubert 1507c1d255d3SCy Schubert 15085b9c547cSRui Paulo static int sae_derive_keys(struct sae_data *sae, const u8 *k) 15095b9c547cSRui Paulo { 1510c1d255d3SCy Schubert u8 zero[SAE_MAX_HASH_LEN], val[SAE_MAX_PRIME_LEN]; 1511c1d255d3SCy Schubert const u8 *salt; 1512c1d255d3SCy Schubert struct wpabuf *rejected_groups = NULL; 1513c1d255d3SCy Schubert u8 keyseed[SAE_MAX_HASH_LEN]; 1514c1d255d3SCy Schubert u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN]; 15155b9c547cSRui Paulo struct crypto_bignum *tmp; 15165b9c547cSRui Paulo int ret = -1; 1517c1d255d3SCy Schubert size_t hash_len, salt_len, prime_len = sae->tmp->prime_len; 1518c1d255d3SCy Schubert const u8 *addr[1]; 1519c1d255d3SCy Schubert size_t len[1]; 15205b9c547cSRui Paulo 15215b9c547cSRui Paulo tmp = crypto_bignum_init(); 15225b9c547cSRui Paulo if (tmp == NULL) 15235b9c547cSRui Paulo goto fail; 15245b9c547cSRui Paulo 1525c1d255d3SCy Schubert /* keyseed = H(salt, k) 1526c1d255d3SCy Schubert * KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK", 15275b9c547cSRui Paulo * (commit-scalar + peer-commit-scalar) modulo r) 15285b9c547cSRui Paulo * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) 1529c1d255d3SCy Schubert * 1530c1d255d3SCy Schubert * When SAE-PK is used, 1531c1d255d3SCy Schubert * KCK || PMK || KEK = KDF-Hash-Length(keyseed, "SAE-PK keys", context) 15325b9c547cSRui Paulo */ 1533c1d255d3SCy Schubert if (!sae->h2e) 1534c1d255d3SCy Schubert hash_len = SHA256_MAC_LEN; 1535c1d255d3SCy Schubert else if (sae->tmp->dh) 1536c1d255d3SCy Schubert hash_len = sae_ffc_prime_len_2_hash_len(prime_len); 1537c1d255d3SCy Schubert else 1538c1d255d3SCy Schubert hash_len = sae_ecc_prime_len_2_hash_len(prime_len); 1539c1d255d3SCy Schubert if (sae->h2e && (sae->tmp->own_rejected_groups || 1540c1d255d3SCy Schubert sae->tmp->peer_rejected_groups)) { 1541c1d255d3SCy Schubert struct wpabuf *own, *peer; 15425b9c547cSRui Paulo 1543c1d255d3SCy Schubert own = sae->tmp->own_rejected_groups; 1544c1d255d3SCy Schubert peer = sae->tmp->peer_rejected_groups; 1545c1d255d3SCy Schubert salt_len = 0; 1546c1d255d3SCy Schubert if (own) 1547c1d255d3SCy Schubert salt_len += wpabuf_len(own); 1548c1d255d3SCy Schubert if (peer) 1549c1d255d3SCy Schubert salt_len += wpabuf_len(peer); 1550c1d255d3SCy Schubert rejected_groups = wpabuf_alloc(salt_len); 1551c1d255d3SCy Schubert if (!rejected_groups) 1552c1d255d3SCy Schubert goto fail; 1553c1d255d3SCy Schubert if (sae->tmp->own_addr_higher) { 1554c1d255d3SCy Schubert if (own) 1555c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, own); 1556c1d255d3SCy Schubert if (peer) 1557c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, peer); 1558c1d255d3SCy Schubert } else { 1559c1d255d3SCy Schubert if (peer) 1560c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, peer); 1561c1d255d3SCy Schubert if (own) 1562c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, own); 1563c1d255d3SCy Schubert } 1564c1d255d3SCy Schubert salt = wpabuf_head(rejected_groups); 1565c1d255d3SCy Schubert salt_len = wpabuf_len(rejected_groups); 1566c1d255d3SCy Schubert } else { 1567c1d255d3SCy Schubert os_memset(zero, 0, hash_len); 1568c1d255d3SCy Schubert salt = zero; 1569c1d255d3SCy Schubert salt_len = hash_len; 1570c1d255d3SCy Schubert } 1571c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: salt for keyseed derivation", 1572c1d255d3SCy Schubert salt, salt_len); 1573c1d255d3SCy Schubert addr[0] = k; 1574c1d255d3SCy Schubert len[0] = prime_len; 1575c1d255d3SCy Schubert if (hkdf_extract(hash_len, salt, salt_len, 1, addr, len, keyseed) < 0) 1576c1d255d3SCy Schubert goto fail; 1577c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, hash_len); 15785b9c547cSRui Paulo 1579c1d255d3SCy Schubert if (crypto_bignum_add(sae->tmp->own_commit_scalar, 1580c1d255d3SCy Schubert sae->peer_commit_scalar, tmp) < 0 || 1581c1d255d3SCy Schubert crypto_bignum_mod(tmp, sae->tmp->order, tmp) < 0) 1582c1d255d3SCy Schubert goto fail; 1583206b73d0SCy Schubert /* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit 1584206b73d0SCy Schubert * string that is needed for KCK, PMK, and PMKID derivation, but it 1585206b73d0SCy Schubert * seems to make most sense to encode the 1586206b73d0SCy Schubert * (commit-scalar + peer-commit-scalar) mod r part as a bit string by 1587206b73d0SCy Schubert * zero padding it from left to the length of the order (in full 1588206b73d0SCy Schubert * octets). */ 1589206b73d0SCy Schubert crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len); 15905b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); 1591c1d255d3SCy Schubert 1592c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1593c1d255d3SCy Schubert if (sae->pk) { 1594c1d255d3SCy Schubert if (sae_kdf_hash(hash_len, keyseed, "SAE-PK keys", 1595c1d255d3SCy Schubert val, sae->tmp->order_len, 1596c1d255d3SCy Schubert keys, 2 * hash_len + SAE_PMK_LEN) < 0) 1597780fb4a2SCy Schubert goto fail; 1598c1d255d3SCy Schubert } else { 1599c1d255d3SCy Schubert if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK", 1600c1d255d3SCy Schubert val, sae->tmp->order_len, 1601c1d255d3SCy Schubert keys, hash_len + SAE_PMK_LEN) < 0) 1602c1d255d3SCy Schubert goto fail; 1603c1d255d3SCy Schubert } 1604c1d255d3SCy Schubert #else /* CONFIG_SAE_PK */ 1605c1d255d3SCy Schubert if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK", 1606c1d255d3SCy Schubert val, sae->tmp->order_len, 1607c1d255d3SCy Schubert keys, hash_len + SAE_PMK_LEN) < 0) 1608c1d255d3SCy Schubert goto fail; 1609c1d255d3SCy Schubert #endif /* !CONFIG_SAE_PK */ 1610c1d255d3SCy Schubert 1611c1d255d3SCy Schubert forced_memzero(keyseed, sizeof(keyseed)); 1612c1d255d3SCy Schubert os_memcpy(sae->tmp->kck, keys, hash_len); 1613c1d255d3SCy Schubert sae->tmp->kck_len = hash_len; 1614c1d255d3SCy Schubert os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN); 1615780fb4a2SCy Schubert os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); 1616c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1617c1d255d3SCy Schubert if (sae->pk) { 1618c1d255d3SCy Schubert os_memcpy(sae->tmp->kek, keys + hash_len + SAE_PMK_LEN, 1619c1d255d3SCy Schubert hash_len); 1620c1d255d3SCy Schubert sae->tmp->kek_len = hash_len; 1621c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: KEK for SAE-PK", 1622c1d255d3SCy Schubert sae->tmp->kek, sae->tmp->kek_len); 1623c1d255d3SCy Schubert } 1624c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 1625c1d255d3SCy Schubert forced_memzero(keys, sizeof(keys)); 1626c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", 1627c1d255d3SCy Schubert sae->tmp->kck, sae->tmp->kck_len); 16285b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); 16295b9c547cSRui Paulo 16305b9c547cSRui Paulo ret = 0; 16315b9c547cSRui Paulo fail: 1632c1d255d3SCy Schubert wpabuf_free(rejected_groups); 16335b9c547cSRui Paulo crypto_bignum_deinit(tmp, 0); 16345b9c547cSRui Paulo return ret; 16355b9c547cSRui Paulo } 16365b9c547cSRui Paulo 16375b9c547cSRui Paulo 16385b9c547cSRui Paulo int sae_process_commit(struct sae_data *sae) 16395b9c547cSRui Paulo { 16405b9c547cSRui Paulo u8 k[SAE_MAX_PRIME_LEN]; 16415b9c547cSRui Paulo if (sae->tmp == NULL || 16425b9c547cSRui Paulo (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || 16435b9c547cSRui Paulo (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || 16445b9c547cSRui Paulo sae_derive_keys(sae, k) < 0) 16455b9c547cSRui Paulo return -1; 16465b9c547cSRui Paulo return 0; 16475b9c547cSRui Paulo } 16485b9c547cSRui Paulo 16495b9c547cSRui Paulo 1650c1d255d3SCy Schubert int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, 165185732ac8SCy Schubert const struct wpabuf *token, const char *identifier) 16525b9c547cSRui Paulo { 16535b9c547cSRui Paulo u8 *pos; 16545b9c547cSRui Paulo 16555b9c547cSRui Paulo if (sae->tmp == NULL) 1656c1d255d3SCy Schubert return -1; 16575b9c547cSRui Paulo 16585b9c547cSRui Paulo wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ 1659c1d255d3SCy Schubert if (!sae->h2e && token) { 16605b9c547cSRui Paulo wpabuf_put_buf(buf, token); 16615b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", 16625b9c547cSRui Paulo wpabuf_head(token), wpabuf_len(token)); 16635b9c547cSRui Paulo } 16645b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 1665c1d255d3SCy Schubert if (crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, 1666c1d255d3SCy Schubert sae->tmp->prime_len, sae->tmp->prime_len) < 0) 1667c1d255d3SCy Schubert return -1; 16685b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", 16695b9c547cSRui Paulo pos, sae->tmp->prime_len); 16705b9c547cSRui Paulo if (sae->tmp->ec) { 16715b9c547cSRui Paulo pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); 1672c1d255d3SCy Schubert if (crypto_ec_point_to_bin(sae->tmp->ec, 16735b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 1674c1d255d3SCy Schubert pos, pos + sae->tmp->prime_len) < 0) 1675c1d255d3SCy Schubert return -1; 16765b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", 16775b9c547cSRui Paulo pos, sae->tmp->prime_len); 16785b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", 16795b9c547cSRui Paulo pos + sae->tmp->prime_len, sae->tmp->prime_len); 16805b9c547cSRui Paulo } else { 16815b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 1682c1d255d3SCy Schubert if (crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, 1683c1d255d3SCy Schubert sae->tmp->prime_len, 1684c1d255d3SCy Schubert sae->tmp->prime_len) < 0) 1685c1d255d3SCy Schubert return -1; 16865b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", 16875b9c547cSRui Paulo pos, sae->tmp->prime_len); 16885b9c547cSRui Paulo } 168985732ac8SCy Schubert 169085732ac8SCy Schubert if (identifier) { 169185732ac8SCy Schubert /* Password Identifier element */ 169285732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 169385732ac8SCy Schubert wpabuf_put_u8(buf, 1 + os_strlen(identifier)); 169485732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); 169585732ac8SCy Schubert wpabuf_put_str(buf, identifier); 169685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", 169785732ac8SCy Schubert identifier); 169885732ac8SCy Schubert } 1699c1d255d3SCy Schubert 1700c1d255d3SCy Schubert if (sae->h2e && sae->tmp->own_rejected_groups) { 1701c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups", 1702c1d255d3SCy Schubert sae->tmp->own_rejected_groups); 1703c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 1704c1d255d3SCy Schubert wpabuf_put_u8(buf, 1705c1d255d3SCy Schubert 1 + wpabuf_len(sae->tmp->own_rejected_groups)); 1706c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_REJECTED_GROUPS); 1707c1d255d3SCy Schubert wpabuf_put_buf(buf, sae->tmp->own_rejected_groups); 1708c1d255d3SCy Schubert } 1709c1d255d3SCy Schubert 1710c1d255d3SCy Schubert if (sae->h2e && token) { 1711c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 1712c1d255d3SCy Schubert wpabuf_put_u8(buf, 1 + wpabuf_len(token)); 1713c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN); 1714c1d255d3SCy Schubert wpabuf_put_buf(buf, token); 1715c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, 1716c1d255d3SCy Schubert "SAE: Anti-clogging token (in container)", 1717c1d255d3SCy Schubert token); 1718c1d255d3SCy Schubert } 1719c1d255d3SCy Schubert 1720c1d255d3SCy Schubert return 0; 17215b9c547cSRui Paulo } 17225b9c547cSRui Paulo 17235b9c547cSRui Paulo 17245b9c547cSRui Paulo u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) 17255b9c547cSRui Paulo { 17265b9c547cSRui Paulo if (allowed_groups) { 17275b9c547cSRui Paulo int i; 17285b9c547cSRui Paulo for (i = 0; allowed_groups[i] > 0; i++) { 17295b9c547cSRui Paulo if (allowed_groups[i] == group) 17305b9c547cSRui Paulo break; 17315b9c547cSRui Paulo } 17325b9c547cSRui Paulo if (allowed_groups[i] != group) { 17335b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " 17345b9c547cSRui Paulo "enabled in the current configuration", 17355b9c547cSRui Paulo group); 17365b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 17375b9c547cSRui Paulo } 17385b9c547cSRui Paulo } 17395b9c547cSRui Paulo 17405b9c547cSRui Paulo if (sae->state == SAE_COMMITTED && group != sae->group) { 17415b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); 17425b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 17435b9c547cSRui Paulo } 17445b9c547cSRui Paulo 17455b9c547cSRui Paulo if (group != sae->group && sae_set_group(sae, group) < 0) { 17465b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", 17475b9c547cSRui Paulo group); 17485b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 17495b9c547cSRui Paulo } 17505b9c547cSRui Paulo 17515b9c547cSRui Paulo if (sae->tmp == NULL) { 17525b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); 17535b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 17545b9c547cSRui Paulo } 17555b9c547cSRui Paulo 17565b9c547cSRui Paulo if (sae->tmp->dh && !allowed_groups) { 17575b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " 17585b9c547cSRui Paulo "explicit configuration enabling it", group); 17595b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 17605b9c547cSRui Paulo } 17615b9c547cSRui Paulo 17625b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 17635b9c547cSRui Paulo } 17645b9c547cSRui Paulo 17655b9c547cSRui Paulo 176685732ac8SCy Schubert static int sae_is_password_id_elem(const u8 *pos, const u8 *end) 176785732ac8SCy Schubert { 176885732ac8SCy Schubert return end - pos >= 3 && 176985732ac8SCy Schubert pos[0] == WLAN_EID_EXTENSION && 177085732ac8SCy Schubert pos[1] >= 1 && 177185732ac8SCy Schubert end - pos - 2 >= pos[1] && 177285732ac8SCy Schubert pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; 177385732ac8SCy Schubert } 177485732ac8SCy Schubert 177585732ac8SCy Schubert 1776c1d255d3SCy Schubert static int sae_is_rejected_groups_elem(const u8 *pos, const u8 *end) 1777c1d255d3SCy Schubert { 1778c1d255d3SCy Schubert return end - pos >= 3 && 1779c1d255d3SCy Schubert pos[0] == WLAN_EID_EXTENSION && 1780c1d255d3SCy Schubert pos[1] >= 2 && 1781c1d255d3SCy Schubert end - pos - 2 >= pos[1] && 1782c1d255d3SCy Schubert pos[2] == WLAN_EID_EXT_REJECTED_GROUPS; 1783c1d255d3SCy Schubert } 1784c1d255d3SCy Schubert 1785c1d255d3SCy Schubert 1786c1d255d3SCy Schubert static int sae_is_token_container_elem(const u8 *pos, const u8 *end) 1787c1d255d3SCy Schubert { 1788c1d255d3SCy Schubert return end - pos >= 3 && 1789c1d255d3SCy Schubert pos[0] == WLAN_EID_EXTENSION && 1790c1d255d3SCy Schubert pos[1] >= 1 && 1791c1d255d3SCy Schubert end - pos - 2 >= pos[1] && 1792c1d255d3SCy Schubert pos[2] == WLAN_EID_EXT_ANTI_CLOGGING_TOKEN; 1793c1d255d3SCy Schubert } 1794c1d255d3SCy Schubert 1795c1d255d3SCy Schubert 17965b9c547cSRui Paulo static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, 17975b9c547cSRui Paulo const u8 *end, const u8 **token, 1798c1d255d3SCy Schubert size_t *token_len, int h2e) 17995b9c547cSRui Paulo { 180085732ac8SCy Schubert size_t scalar_elem_len, tlen; 180185732ac8SCy Schubert 180285732ac8SCy Schubert if (token) 180385732ac8SCy Schubert *token = NULL; 180485732ac8SCy Schubert if (token_len) 180585732ac8SCy Schubert *token_len = 0; 180685732ac8SCy Schubert 1807c1d255d3SCy Schubert if (h2e) 1808c1d255d3SCy Schubert return; /* No Anti-Clogging Token field outside container IE */ 1809c1d255d3SCy Schubert 181085732ac8SCy Schubert scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; 181185732ac8SCy Schubert if (scalar_elem_len >= (size_t) (end - *pos)) 181285732ac8SCy Schubert return; /* No extra data beyond peer scalar and element */ 181385732ac8SCy Schubert 181485732ac8SCy Schubert tlen = end - (*pos + scalar_elem_len); 181585732ac8SCy Schubert 181685732ac8SCy Schubert if (tlen < SHA256_MAC_LEN) { 181785732ac8SCy Schubert wpa_printf(MSG_DEBUG, 181885732ac8SCy Schubert "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", 181985732ac8SCy Schubert (unsigned int) tlen); 182085732ac8SCy Schubert return; 182185732ac8SCy Schubert } 182285732ac8SCy Schubert 18235b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); 18245b9c547cSRui Paulo if (token) 18255b9c547cSRui Paulo *token = *pos; 18265b9c547cSRui Paulo if (token_len) 18275b9c547cSRui Paulo *token_len = tlen; 18285b9c547cSRui Paulo *pos += tlen; 18295b9c547cSRui Paulo } 18305b9c547cSRui Paulo 18315b9c547cSRui Paulo 1832c1d255d3SCy Schubert static void sae_parse_token_container(struct sae_data *sae, 1833c1d255d3SCy Schubert const u8 *pos, const u8 *end, 1834c1d255d3SCy Schubert const u8 **token, size_t *token_len) 1835c1d255d3SCy Schubert { 1836c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 1837c1d255d3SCy Schubert pos, end - pos); 1838c1d255d3SCy Schubert if (!sae_is_token_container_elem(pos, end)) 1839c1d255d3SCy Schubert return; 1840c1d255d3SCy Schubert *token = pos + 3; 1841c1d255d3SCy Schubert *token_len = pos[1] - 1; 1842c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token (in container)", 1843c1d255d3SCy Schubert *token, *token_len); 1844c1d255d3SCy Schubert } 1845c1d255d3SCy Schubert 1846c1d255d3SCy Schubert 18475b9c547cSRui Paulo static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, 18485b9c547cSRui Paulo const u8 *end) 18495b9c547cSRui Paulo { 18505b9c547cSRui Paulo struct crypto_bignum *peer_scalar; 18515b9c547cSRui Paulo 1852780fb4a2SCy Schubert if (sae->tmp->prime_len > end - *pos) { 18535b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); 18545b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 18555b9c547cSRui Paulo } 18565b9c547cSRui Paulo 18575b9c547cSRui Paulo peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); 18585b9c547cSRui Paulo if (peer_scalar == NULL) 18595b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 18605b9c547cSRui Paulo 18615b9c547cSRui Paulo /* 18625b9c547cSRui Paulo * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for 18635b9c547cSRui Paulo * the peer and it is in Authenticated state, the new Commit Message 18645b9c547cSRui Paulo * shall be dropped if the peer-scalar is identical to the one used in 18655b9c547cSRui Paulo * the existing protocol instance. 18665b9c547cSRui Paulo */ 1867c1d255d3SCy Schubert if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar_accepted && 1868c1d255d3SCy Schubert crypto_bignum_cmp(sae->peer_commit_scalar_accepted, 1869c1d255d3SCy Schubert peer_scalar) == 0) { 18705b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " 18715b9c547cSRui Paulo "peer-commit-scalar"); 18725b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 18735b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 18745b9c547cSRui Paulo } 18755b9c547cSRui Paulo 1876325151a3SRui Paulo /* 1 < scalar < r */ 18775b9c547cSRui Paulo if (crypto_bignum_is_zero(peer_scalar) || 1878325151a3SRui Paulo crypto_bignum_is_one(peer_scalar) || 18795b9c547cSRui Paulo crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { 18805b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); 18815b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 18825b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 18835b9c547cSRui Paulo } 18845b9c547cSRui Paulo 18855b9c547cSRui Paulo 18865b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 18875b9c547cSRui Paulo sae->peer_commit_scalar = peer_scalar; 18885b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", 18895b9c547cSRui Paulo *pos, sae->tmp->prime_len); 18905b9c547cSRui Paulo *pos += sae->tmp->prime_len; 18915b9c547cSRui Paulo 18925b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 18935b9c547cSRui Paulo } 18945b9c547cSRui Paulo 18955b9c547cSRui Paulo 189685732ac8SCy Schubert static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, 18975b9c547cSRui Paulo const u8 *end) 18985b9c547cSRui Paulo { 18995b9c547cSRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 19005b9c547cSRui Paulo 190185732ac8SCy Schubert if (2 * sae->tmp->prime_len > end - *pos) { 19025b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 19035b9c547cSRui Paulo "commit-element"); 19045b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19055b9c547cSRui Paulo } 19065b9c547cSRui Paulo 19075b9c547cSRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 19085b9c547cSRui Paulo sae->tmp->prime_len) < 0) 19095b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19105b9c547cSRui Paulo 19115b9c547cSRui Paulo /* element x and y coordinates < p */ 191285732ac8SCy Schubert if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || 191385732ac8SCy Schubert os_memcmp(*pos + sae->tmp->prime_len, prime, 19145b9c547cSRui Paulo sae->tmp->prime_len) >= 0) { 19155b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " 19165b9c547cSRui Paulo "element"); 19175b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19185b9c547cSRui Paulo } 19195b9c547cSRui Paulo 19205b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", 192185732ac8SCy Schubert *pos, sae->tmp->prime_len); 19225b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", 192385732ac8SCy Schubert *pos + sae->tmp->prime_len, sae->tmp->prime_len); 19245b9c547cSRui Paulo 19255b9c547cSRui Paulo crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); 19265b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc = 192785732ac8SCy Schubert crypto_ec_point_from_bin(sae->tmp->ec, *pos); 19285b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ecc == NULL) 19295b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19305b9c547cSRui Paulo 19315b9c547cSRui Paulo if (!crypto_ec_point_is_on_curve(sae->tmp->ec, 19325b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc)) { 19335b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); 19345b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19355b9c547cSRui Paulo } 19365b9c547cSRui Paulo 193785732ac8SCy Schubert *pos += 2 * sae->tmp->prime_len; 193885732ac8SCy Schubert 19395b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 19405b9c547cSRui Paulo } 19415b9c547cSRui Paulo 19425b9c547cSRui Paulo 194385732ac8SCy Schubert static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, 19445b9c547cSRui Paulo const u8 *end) 19455b9c547cSRui Paulo { 1946325151a3SRui Paulo struct crypto_bignum *res, *one; 1947325151a3SRui Paulo const u8 one_bin[1] = { 0x01 }; 19485b9c547cSRui Paulo 194985732ac8SCy Schubert if (sae->tmp->prime_len > end - *pos) { 19505b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 19515b9c547cSRui Paulo "commit-element"); 19525b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19535b9c547cSRui Paulo } 195485732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, 19555b9c547cSRui Paulo sae->tmp->prime_len); 19565b9c547cSRui Paulo 19575b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); 19585b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc = 195985732ac8SCy Schubert crypto_bignum_init_set(*pos, sae->tmp->prime_len); 19605b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ffc == NULL) 19615b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 1962325151a3SRui Paulo /* 1 < element < p - 1 */ 1963325151a3SRui Paulo res = crypto_bignum_init(); 1964325151a3SRui Paulo one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); 1965325151a3SRui Paulo if (!res || !one || 1966325151a3SRui Paulo crypto_bignum_sub(sae->tmp->prime, one, res) || 1967325151a3SRui Paulo crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || 19685b9c547cSRui Paulo crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || 1969325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { 1970325151a3SRui Paulo crypto_bignum_deinit(res, 0); 1971325151a3SRui Paulo crypto_bignum_deinit(one, 0); 19725b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); 19735b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19745b9c547cSRui Paulo } 1975325151a3SRui Paulo crypto_bignum_deinit(one, 0); 19765b9c547cSRui Paulo 19775b9c547cSRui Paulo /* scalar-op(r, ELEMENT) = 1 modulo p */ 1978325151a3SRui Paulo if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, 19795b9c547cSRui Paulo sae->tmp->order, sae->tmp->prime, res) < 0 || 19805b9c547cSRui Paulo !crypto_bignum_is_one(res)) { 19815b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); 19825b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 19835b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19845b9c547cSRui Paulo } 19855b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 19865b9c547cSRui Paulo 198785732ac8SCy Schubert *pos += sae->tmp->prime_len; 198885732ac8SCy Schubert 19895b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 19905b9c547cSRui Paulo } 19915b9c547cSRui Paulo 19925b9c547cSRui Paulo 199385732ac8SCy Schubert static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, 19945b9c547cSRui Paulo const u8 *end) 19955b9c547cSRui Paulo { 19965b9c547cSRui Paulo if (sae->tmp->dh) 19975b9c547cSRui Paulo return sae_parse_commit_element_ffc(sae, pos, end); 19985b9c547cSRui Paulo return sae_parse_commit_element_ecc(sae, pos, end); 19995b9c547cSRui Paulo } 20005b9c547cSRui Paulo 20015b9c547cSRui Paulo 200285732ac8SCy Schubert static int sae_parse_password_identifier(struct sae_data *sae, 2003c1d255d3SCy Schubert const u8 **pos, const u8 *end) 200485732ac8SCy Schubert { 2005c1d255d3SCy Schubert const u8 *epos; 2006c1d255d3SCy Schubert u8 len; 2007c1d255d3SCy Schubert 200885732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 2009c1d255d3SCy Schubert *pos, end - *pos); 2010c1d255d3SCy Schubert if (!sae_is_password_id_elem(*pos, end)) { 201185732ac8SCy Schubert if (sae->tmp->pw_id) { 201285732ac8SCy Schubert wpa_printf(MSG_DEBUG, 201385732ac8SCy Schubert "SAE: No Password Identifier included, but expected one (%s)", 201485732ac8SCy Schubert sae->tmp->pw_id); 201585732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 201685732ac8SCy Schubert } 201785732ac8SCy Schubert os_free(sae->tmp->pw_id); 201885732ac8SCy Schubert sae->tmp->pw_id = NULL; 201985732ac8SCy Schubert return WLAN_STATUS_SUCCESS; /* No Password Identifier */ 202085732ac8SCy Schubert } 202185732ac8SCy Schubert 2022c1d255d3SCy Schubert epos = *pos; 2023c1d255d3SCy Schubert epos++; /* skip IE type */ 2024c1d255d3SCy Schubert len = *epos++; /* IE length */ 2025c1d255d3SCy Schubert if (len > end - epos || len < 1) 2026c1d255d3SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2027c1d255d3SCy Schubert epos++; /* skip ext ID */ 2028c1d255d3SCy Schubert len--; 2029c1d255d3SCy Schubert 203085732ac8SCy Schubert if (sae->tmp->pw_id && 2031c1d255d3SCy Schubert (len != os_strlen(sae->tmp->pw_id) || 2032c1d255d3SCy Schubert os_memcmp(sae->tmp->pw_id, epos, len) != 0)) { 203385732ac8SCy Schubert wpa_printf(MSG_DEBUG, 203485732ac8SCy Schubert "SAE: The included Password Identifier does not match the expected one (%s)", 203585732ac8SCy Schubert sae->tmp->pw_id); 203685732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 203785732ac8SCy Schubert } 203885732ac8SCy Schubert 203985732ac8SCy Schubert os_free(sae->tmp->pw_id); 2040c1d255d3SCy Schubert sae->tmp->pw_id = os_malloc(len + 1); 204185732ac8SCy Schubert if (!sae->tmp->pw_id) 204285732ac8SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2043c1d255d3SCy Schubert os_memcpy(sae->tmp->pw_id, epos, len); 2044c1d255d3SCy Schubert sae->tmp->pw_id[len] = '\0'; 204585732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier", 2046c1d255d3SCy Schubert sae->tmp->pw_id, len); 2047c1d255d3SCy Schubert *pos = epos + len; 2048c1d255d3SCy Schubert return WLAN_STATUS_SUCCESS; 2049c1d255d3SCy Schubert } 2050c1d255d3SCy Schubert 2051c1d255d3SCy Schubert 2052c1d255d3SCy Schubert static int sae_parse_rejected_groups(struct sae_data *sae, 2053c1d255d3SCy Schubert const u8 **pos, const u8 *end) 2054c1d255d3SCy Schubert { 2055c1d255d3SCy Schubert const u8 *epos; 2056c1d255d3SCy Schubert u8 len; 2057c1d255d3SCy Schubert 2058c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 2059c1d255d3SCy Schubert *pos, end - *pos); 2060c1d255d3SCy Schubert if (!sae_is_rejected_groups_elem(*pos, end)) 2061c1d255d3SCy Schubert return WLAN_STATUS_SUCCESS; 2062c1d255d3SCy Schubert 2063c1d255d3SCy Schubert epos = *pos; 2064c1d255d3SCy Schubert epos++; /* skip IE type */ 2065c1d255d3SCy Schubert len = *epos++; /* IE length */ 2066c1d255d3SCy Schubert if (len > end - epos || len < 1) 2067c1d255d3SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2068c1d255d3SCy Schubert epos++; /* skip ext ID */ 2069c1d255d3SCy Schubert len--; 2070c1d255d3SCy Schubert 2071c1d255d3SCy Schubert wpabuf_free(sae->tmp->peer_rejected_groups); 2072c1d255d3SCy Schubert sae->tmp->peer_rejected_groups = wpabuf_alloc(len); 2073c1d255d3SCy Schubert if (!sae->tmp->peer_rejected_groups) 2074c1d255d3SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2075c1d255d3SCy Schubert wpabuf_put_data(sae->tmp->peer_rejected_groups, epos, len); 2076c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "SAE: Received Rejected Groups list", 2077c1d255d3SCy Schubert sae->tmp->peer_rejected_groups); 2078c1d255d3SCy Schubert *pos = epos + len; 207985732ac8SCy Schubert return WLAN_STATUS_SUCCESS; 208085732ac8SCy Schubert } 208185732ac8SCy Schubert 208285732ac8SCy Schubert 20835b9c547cSRui Paulo u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, 2084c1d255d3SCy Schubert const u8 **token, size_t *token_len, int *allowed_groups, 2085c1d255d3SCy Schubert int h2e) 20865b9c547cSRui Paulo { 20875b9c547cSRui Paulo const u8 *pos = data, *end = data + len; 20885b9c547cSRui Paulo u16 res; 20895b9c547cSRui Paulo 20905b9c547cSRui Paulo /* Check Finite Cyclic Group */ 2091780fb4a2SCy Schubert if (end - pos < 2) 20925b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 20935b9c547cSRui Paulo res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); 20945b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 20955b9c547cSRui Paulo return res; 20965b9c547cSRui Paulo pos += 2; 20975b9c547cSRui Paulo 20985b9c547cSRui Paulo /* Optional Anti-Clogging Token */ 2099c1d255d3SCy Schubert sae_parse_commit_token(sae, &pos, end, token, token_len, h2e); 21005b9c547cSRui Paulo 21015b9c547cSRui Paulo /* commit-scalar */ 21025b9c547cSRui Paulo res = sae_parse_commit_scalar(sae, &pos, end); 21035b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 21045b9c547cSRui Paulo return res; 21055b9c547cSRui Paulo 21065b9c547cSRui Paulo /* commit-element */ 210785732ac8SCy Schubert res = sae_parse_commit_element(sae, &pos, end); 210885732ac8SCy Schubert if (res != WLAN_STATUS_SUCCESS) 210985732ac8SCy Schubert return res; 211085732ac8SCy Schubert 211185732ac8SCy Schubert /* Optional Password Identifier element */ 2112c1d255d3SCy Schubert res = sae_parse_password_identifier(sae, &pos, end); 2113325151a3SRui Paulo if (res != WLAN_STATUS_SUCCESS) 2114325151a3SRui Paulo return res; 2115325151a3SRui Paulo 2116c1d255d3SCy Schubert /* Conditional Rejected Groups element */ 2117c1d255d3SCy Schubert if (h2e) { 2118c1d255d3SCy Schubert res = sae_parse_rejected_groups(sae, &pos, end); 2119c1d255d3SCy Schubert if (res != WLAN_STATUS_SUCCESS) 2120c1d255d3SCy Schubert return res; 2121c1d255d3SCy Schubert } 2122c1d255d3SCy Schubert 2123c1d255d3SCy Schubert /* Optional Anti-Clogging Token Container element */ 2124c1d255d3SCy Schubert if (h2e) 2125c1d255d3SCy Schubert sae_parse_token_container(sae, pos, end, token, token_len); 2126c1d255d3SCy Schubert 2127325151a3SRui Paulo /* 2128325151a3SRui Paulo * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as 2129325151a3SRui Paulo * the values we sent which would be evidence of a reflection attack. 2130325151a3SRui Paulo */ 2131325151a3SRui Paulo if (!sae->tmp->own_commit_scalar || 2132325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_scalar, 2133325151a3SRui Paulo sae->peer_commit_scalar) != 0 || 2134325151a3SRui Paulo (sae->tmp->dh && 2135325151a3SRui Paulo (!sae->tmp->own_commit_element_ffc || 2136325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, 2137325151a3SRui Paulo sae->tmp->peer_commit_element_ffc) != 0)) || 2138325151a3SRui Paulo (sae->tmp->ec && 2139325151a3SRui Paulo (!sae->tmp->own_commit_element_ecc || 2140325151a3SRui Paulo crypto_ec_point_cmp(sae->tmp->ec, 2141325151a3SRui Paulo sae->tmp->own_commit_element_ecc, 2142325151a3SRui Paulo sae->tmp->peer_commit_element_ecc) != 0))) 2143325151a3SRui Paulo return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ 2144325151a3SRui Paulo 2145325151a3SRui Paulo /* 2146325151a3SRui Paulo * This is a reflection attack - return special value to trigger caller 2147325151a3SRui Paulo * to silently discard the frame instead of replying with a specific 2148325151a3SRui Paulo * status code. 2149325151a3SRui Paulo */ 2150325151a3SRui Paulo return SAE_SILENTLY_DISCARD; 21515b9c547cSRui Paulo } 21525b9c547cSRui Paulo 21535b9c547cSRui Paulo 2154c1d255d3SCy Schubert static int sae_cn_confirm(struct sae_data *sae, const u8 *sc, 21555b9c547cSRui Paulo const struct crypto_bignum *scalar1, 21565b9c547cSRui Paulo const u8 *element1, size_t element1_len, 21575b9c547cSRui Paulo const struct crypto_bignum *scalar2, 21585b9c547cSRui Paulo const u8 *element2, size_t element2_len, 21595b9c547cSRui Paulo u8 *confirm) 21605b9c547cSRui Paulo { 21615b9c547cSRui Paulo const u8 *addr[5]; 21625b9c547cSRui Paulo size_t len[5]; 21635b9c547cSRui Paulo u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; 21645b9c547cSRui Paulo 21655b9c547cSRui Paulo /* Confirm 21665b9c547cSRui Paulo * CN(key, X, Y, Z, ...) = 21675b9c547cSRui Paulo * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) 21685b9c547cSRui Paulo * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, 21695b9c547cSRui Paulo * peer-commit-scalar, PEER-COMMIT-ELEMENT) 21705b9c547cSRui Paulo * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, 21715b9c547cSRui Paulo * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) 21725b9c547cSRui Paulo */ 2173c1d255d3SCy Schubert if (crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), 2174c1d255d3SCy Schubert sae->tmp->prime_len) < 0 || 2175c1d255d3SCy Schubert crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), 2176c1d255d3SCy Schubert sae->tmp->prime_len) < 0) 2177c1d255d3SCy Schubert return -1; 21785b9c547cSRui Paulo addr[0] = sc; 21795b9c547cSRui Paulo len[0] = 2; 21805b9c547cSRui Paulo addr[1] = scalar_b1; 21815b9c547cSRui Paulo len[1] = sae->tmp->prime_len; 21825b9c547cSRui Paulo addr[2] = element1; 21835b9c547cSRui Paulo len[2] = element1_len; 21845b9c547cSRui Paulo addr[3] = scalar_b2; 21855b9c547cSRui Paulo len[3] = sae->tmp->prime_len; 21865b9c547cSRui Paulo addr[4] = element2; 21875b9c547cSRui Paulo len[4] = element2_len; 2188c1d255d3SCy Schubert return hkdf_extract(sae->tmp->kck_len, sae->tmp->kck, sae->tmp->kck_len, 2189c1d255d3SCy Schubert 5, addr, len, confirm); 21905b9c547cSRui Paulo } 21915b9c547cSRui Paulo 21925b9c547cSRui Paulo 2193c1d255d3SCy Schubert static int sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, 21945b9c547cSRui Paulo const struct crypto_bignum *scalar1, 21955b9c547cSRui Paulo const struct crypto_ec_point *element1, 21965b9c547cSRui Paulo const struct crypto_bignum *scalar2, 21975b9c547cSRui Paulo const struct crypto_ec_point *element2, 21985b9c547cSRui Paulo u8 *confirm) 21995b9c547cSRui Paulo { 22005b9c547cSRui Paulo u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; 22015b9c547cSRui Paulo u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; 22025b9c547cSRui Paulo 2203c1d255d3SCy Schubert if (crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, 2204c1d255d3SCy Schubert element_b1 + sae->tmp->prime_len) < 0 || 22055b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, 2206c1d255d3SCy Schubert element_b2 + sae->tmp->prime_len) < 0 || 2207c1d255d3SCy Schubert sae_cn_confirm(sae, sc, scalar1, element_b1, 2208c1d255d3SCy Schubert 2 * sae->tmp->prime_len, 2209c1d255d3SCy Schubert scalar2, element_b2, 2 * sae->tmp->prime_len, 2210c1d255d3SCy Schubert confirm) < 0) 2211c1d255d3SCy Schubert return -1; 2212c1d255d3SCy Schubert return 0; 22135b9c547cSRui Paulo } 22145b9c547cSRui Paulo 22155b9c547cSRui Paulo 2216c1d255d3SCy Schubert static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, 22175b9c547cSRui Paulo const struct crypto_bignum *scalar1, 22185b9c547cSRui Paulo const struct crypto_bignum *element1, 22195b9c547cSRui Paulo const struct crypto_bignum *scalar2, 22205b9c547cSRui Paulo const struct crypto_bignum *element2, 22215b9c547cSRui Paulo u8 *confirm) 22225b9c547cSRui Paulo { 22235b9c547cSRui Paulo u8 element_b1[SAE_MAX_PRIME_LEN]; 22245b9c547cSRui Paulo u8 element_b2[SAE_MAX_PRIME_LEN]; 22255b9c547cSRui Paulo 2226c1d255d3SCy Schubert if (crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), 2227c1d255d3SCy Schubert sae->tmp->prime_len) < 0 || 22285b9c547cSRui Paulo crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), 2229c1d255d3SCy Schubert sae->tmp->prime_len) < 0 || 22305b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, 2231c1d255d3SCy Schubert scalar2, element_b2, sae->tmp->prime_len, 2232c1d255d3SCy Schubert confirm) < 0) 2233c1d255d3SCy Schubert return -1; 2234c1d255d3SCy Schubert return 0; 22355b9c547cSRui Paulo } 22365b9c547cSRui Paulo 22375b9c547cSRui Paulo 2238c1d255d3SCy Schubert int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) 22395b9c547cSRui Paulo { 22405b9c547cSRui Paulo const u8 *sc; 2241c1d255d3SCy Schubert size_t hash_len; 2242c1d255d3SCy Schubert int res; 22435b9c547cSRui Paulo 22445b9c547cSRui Paulo if (sae->tmp == NULL) 2245c1d255d3SCy Schubert return -1; 2246c1d255d3SCy Schubert 2247c1d255d3SCy Schubert hash_len = sae->tmp->kck_len; 22485b9c547cSRui Paulo 22495b9c547cSRui Paulo /* Send-Confirm */ 225085732ac8SCy Schubert if (sae->send_confirm < 0xffff) 22515b9c547cSRui Paulo sae->send_confirm++; 2252c1d255d3SCy Schubert sc = wpabuf_put(buf, 0); 2253c1d255d3SCy Schubert wpabuf_put_le16(buf, sae->send_confirm); 22545b9c547cSRui Paulo 22555b9c547cSRui Paulo if (sae->tmp->ec) 2256c1d255d3SCy Schubert res = sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, 22575b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 22585b9c547cSRui Paulo sae->peer_commit_scalar, 22595b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 2260c1d255d3SCy Schubert wpabuf_put(buf, hash_len)); 22615b9c547cSRui Paulo else 2262c1d255d3SCy Schubert res = sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, 22635b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 22645b9c547cSRui Paulo sae->peer_commit_scalar, 22655b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 2266c1d255d3SCy Schubert wpabuf_put(buf, hash_len)); 2267c1d255d3SCy Schubert if (res) 2268c1d255d3SCy Schubert return res; 2269c1d255d3SCy Schubert 2270c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 2271c1d255d3SCy Schubert if (sae_write_confirm_pk(sae, buf) < 0) 2272c1d255d3SCy Schubert return -1; 2273c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 2274c1d255d3SCy Schubert 2275c1d255d3SCy Schubert return 0; 22765b9c547cSRui Paulo } 22775b9c547cSRui Paulo 22785b9c547cSRui Paulo 22795b9c547cSRui Paulo int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) 22805b9c547cSRui Paulo { 2281c1d255d3SCy Schubert u8 verifier[SAE_MAX_HASH_LEN]; 2282c1d255d3SCy Schubert size_t hash_len; 22835b9c547cSRui Paulo 2284c1d255d3SCy Schubert if (!sae->tmp) 2285c1d255d3SCy Schubert return -1; 2286c1d255d3SCy Schubert 2287c1d255d3SCy Schubert hash_len = sae->tmp->kck_len; 2288c1d255d3SCy Schubert if (len < 2 + hash_len) { 22895b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); 22905b9c547cSRui Paulo return -1; 22915b9c547cSRui Paulo } 22925b9c547cSRui Paulo 22935b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); 22945b9c547cSRui Paulo 2295c1d255d3SCy Schubert if (!sae->peer_commit_scalar || !sae->tmp->own_commit_scalar) { 22965b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); 22975b9c547cSRui Paulo return -1; 22985b9c547cSRui Paulo } 22995b9c547cSRui Paulo 23004bc52338SCy Schubert if (sae->tmp->ec) { 23014bc52338SCy Schubert if (!sae->tmp->peer_commit_element_ecc || 2302c1d255d3SCy Schubert !sae->tmp->own_commit_element_ecc || 23035b9c547cSRui Paulo sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, 23045b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 23055b9c547cSRui Paulo sae->tmp->own_commit_scalar, 23065b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 2307c1d255d3SCy Schubert verifier) < 0) 2308c1d255d3SCy Schubert return -1; 23094bc52338SCy Schubert } else { 23104bc52338SCy Schubert if (!sae->tmp->peer_commit_element_ffc || 2311c1d255d3SCy Schubert !sae->tmp->own_commit_element_ffc || 23125b9c547cSRui Paulo sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, 23135b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 23145b9c547cSRui Paulo sae->tmp->own_commit_scalar, 23155b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 2316c1d255d3SCy Schubert verifier) < 0) 23175b9c547cSRui Paulo return -1; 23185b9c547cSRui Paulo } 23195b9c547cSRui Paulo 2320c1d255d3SCy Schubert if (os_memcmp_const(verifier, data + 2, hash_len) != 0) { 2321c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); 2322c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", 2323c1d255d3SCy Schubert data + 2, hash_len); 2324c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", 2325c1d255d3SCy Schubert verifier, hash_len); 2326c1d255d3SCy Schubert return -1; 2327c1d255d3SCy Schubert } 2328c1d255d3SCy Schubert 2329c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 2330c1d255d3SCy Schubert if (sae_check_confirm_pk(sae, data + 2 + hash_len, 2331c1d255d3SCy Schubert len - 2 - hash_len) < 0) 2332c1d255d3SCy Schubert return -1; 2333c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 2334c1d255d3SCy Schubert 23355b9c547cSRui Paulo return 0; 23365b9c547cSRui Paulo } 233785732ac8SCy Schubert 233885732ac8SCy Schubert 233985732ac8SCy Schubert const char * sae_state_txt(enum sae_state state) 234085732ac8SCy Schubert { 234185732ac8SCy Schubert switch (state) { 234285732ac8SCy Schubert case SAE_NOTHING: 234385732ac8SCy Schubert return "Nothing"; 234485732ac8SCy Schubert case SAE_COMMITTED: 234585732ac8SCy Schubert return "Committed"; 234685732ac8SCy Schubert case SAE_CONFIRMED: 234785732ac8SCy Schubert return "Confirmed"; 234885732ac8SCy Schubert case SAE_ACCEPTED: 234985732ac8SCy Schubert return "Accepted"; 235085732ac8SCy Schubert } 235185732ac8SCy Schubert return "?"; 235285732ac8SCy Schubert } 2353