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" 12*a90b9d01SCy Schubert #include "common/defs.h" 13*a90b9d01SCy Schubert #include "common/wpa_common.h" 144bc52338SCy Schubert #include "utils/const_time.h" 155b9c547cSRui Paulo #include "crypto/crypto.h" 165b9c547cSRui Paulo #include "crypto/sha256.h" 17c1d255d3SCy Schubert #include "crypto/sha384.h" 18c1d255d3SCy Schubert #include "crypto/sha512.h" 195b9c547cSRui Paulo #include "crypto/random.h" 205b9c547cSRui Paulo #include "crypto/dh_groups.h" 215b9c547cSRui Paulo #include "ieee802_11_defs.h" 22206b73d0SCy Schubert #include "dragonfly.h" 235b9c547cSRui Paulo #include "sae.h" 245b9c547cSRui Paulo 255b9c547cSRui Paulo 265b9c547cSRui Paulo int sae_set_group(struct sae_data *sae, int group) 275b9c547cSRui Paulo { 285b9c547cSRui Paulo struct sae_temporary_data *tmp; 295b9c547cSRui Paulo 30206b73d0SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 31206b73d0SCy Schubert /* Allow all groups for testing purposes in non-production builds. */ 32206b73d0SCy Schubert #else /* CONFIG_TESTING_OPTIONS */ 33206b73d0SCy Schubert if (!dragonfly_suitable_group(group, 0)) { 344bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group); 354bc52338SCy Schubert return -1; 364bc52338SCy Schubert } 37206b73d0SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 384bc52338SCy Schubert 395b9c547cSRui Paulo sae_clear_data(sae); 405b9c547cSRui Paulo tmp = sae->tmp = os_zalloc(sizeof(*tmp)); 415b9c547cSRui Paulo if (tmp == NULL) 425b9c547cSRui Paulo return -1; 435b9c547cSRui Paulo 445b9c547cSRui Paulo /* First, check if this is an ECC group */ 455b9c547cSRui Paulo tmp->ec = crypto_ec_init(group); 465b9c547cSRui Paulo if (tmp->ec) { 4785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", 4885732ac8SCy Schubert group); 495b9c547cSRui Paulo sae->group = group; 505b9c547cSRui Paulo tmp->prime_len = crypto_ec_prime_len(tmp->ec); 515b9c547cSRui Paulo tmp->prime = crypto_ec_get_prime(tmp->ec); 52206b73d0SCy Schubert tmp->order_len = crypto_ec_order_len(tmp->ec); 535b9c547cSRui Paulo tmp->order = crypto_ec_get_order(tmp->ec); 545b9c547cSRui Paulo return 0; 555b9c547cSRui Paulo } 565b9c547cSRui Paulo 575b9c547cSRui Paulo /* Not an ECC group, check FFC */ 585b9c547cSRui Paulo tmp->dh = dh_groups_get(group); 595b9c547cSRui Paulo if (tmp->dh) { 6085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", 6185732ac8SCy Schubert group); 625b9c547cSRui Paulo sae->group = group; 635b9c547cSRui Paulo tmp->prime_len = tmp->dh->prime_len; 645b9c547cSRui Paulo if (tmp->prime_len > SAE_MAX_PRIME_LEN) { 655b9c547cSRui Paulo sae_clear_data(sae); 665b9c547cSRui Paulo return -1; 675b9c547cSRui Paulo } 685b9c547cSRui Paulo 695b9c547cSRui Paulo tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, 705b9c547cSRui Paulo tmp->prime_len); 715b9c547cSRui Paulo if (tmp->prime_buf == NULL) { 725b9c547cSRui Paulo sae_clear_data(sae); 735b9c547cSRui Paulo return -1; 745b9c547cSRui Paulo } 755b9c547cSRui Paulo tmp->prime = tmp->prime_buf; 765b9c547cSRui Paulo 77206b73d0SCy Schubert tmp->order_len = tmp->dh->order_len; 785b9c547cSRui Paulo tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, 795b9c547cSRui Paulo tmp->dh->order_len); 805b9c547cSRui Paulo if (tmp->order_buf == NULL) { 815b9c547cSRui Paulo sae_clear_data(sae); 825b9c547cSRui Paulo return -1; 835b9c547cSRui Paulo } 845b9c547cSRui Paulo tmp->order = tmp->order_buf; 855b9c547cSRui Paulo 865b9c547cSRui Paulo return 0; 875b9c547cSRui Paulo } 885b9c547cSRui Paulo 895b9c547cSRui Paulo /* Unsupported group */ 9085732ac8SCy Schubert wpa_printf(MSG_DEBUG, 9185732ac8SCy Schubert "SAE: Group %d not supported by the crypto library", group); 925b9c547cSRui Paulo return -1; 935b9c547cSRui Paulo } 945b9c547cSRui Paulo 955b9c547cSRui Paulo 965b9c547cSRui Paulo void sae_clear_temp_data(struct sae_data *sae) 975b9c547cSRui Paulo { 985b9c547cSRui Paulo struct sae_temporary_data *tmp; 995b9c547cSRui Paulo if (sae == NULL || sae->tmp == NULL) 1005b9c547cSRui Paulo return; 1015b9c547cSRui Paulo tmp = sae->tmp; 1025b9c547cSRui Paulo crypto_ec_deinit(tmp->ec); 1035b9c547cSRui Paulo crypto_bignum_deinit(tmp->prime_buf, 0); 1045b9c547cSRui Paulo crypto_bignum_deinit(tmp->order_buf, 0); 1055b9c547cSRui Paulo crypto_bignum_deinit(tmp->sae_rand, 1); 1065b9c547cSRui Paulo crypto_bignum_deinit(tmp->pwe_ffc, 1); 1075b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_scalar, 0); 1085b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); 1095b9c547cSRui Paulo crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); 1105b9c547cSRui Paulo crypto_ec_point_deinit(tmp->pwe_ecc, 1); 1115b9c547cSRui Paulo crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); 1125b9c547cSRui Paulo crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); 1135b9c547cSRui Paulo wpabuf_free(tmp->anti_clogging_token); 114c1d255d3SCy Schubert wpabuf_free(tmp->own_rejected_groups); 115c1d255d3SCy Schubert wpabuf_free(tmp->peer_rejected_groups); 11685732ac8SCy Schubert os_free(tmp->pw_id); 1175b9c547cSRui Paulo bin_clear_free(tmp, sizeof(*tmp)); 1185b9c547cSRui Paulo sae->tmp = NULL; 1195b9c547cSRui Paulo } 1205b9c547cSRui Paulo 1215b9c547cSRui Paulo 1225b9c547cSRui Paulo void sae_clear_data(struct sae_data *sae) 1235b9c547cSRui Paulo { 1245b9c547cSRui Paulo if (sae == NULL) 1255b9c547cSRui Paulo return; 1265b9c547cSRui Paulo sae_clear_temp_data(sae); 1275b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 128c1d255d3SCy Schubert crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0); 1295b9c547cSRui Paulo os_memset(sae, 0, sizeof(*sae)); 1305b9c547cSRui Paulo } 1315b9c547cSRui Paulo 1325b9c547cSRui Paulo 1335b9c547cSRui Paulo static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) 1345b9c547cSRui Paulo { 1355b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR 1365b9c547cSRui Paulo " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); 1375b9c547cSRui Paulo if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { 1385b9c547cSRui Paulo os_memcpy(key, addr1, ETH_ALEN); 1395b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); 1405b9c547cSRui Paulo } else { 1415b9c547cSRui Paulo os_memcpy(key, addr2, ETH_ALEN); 1425b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); 1435b9c547cSRui Paulo } 1445b9c547cSRui Paulo } 1455b9c547cSRui Paulo 1465b9c547cSRui Paulo 147325151a3SRui Paulo static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, 1484bc52338SCy Schubert const u8 *prime, const u8 *qr, const u8 *qnr, 1494bc52338SCy Schubert u8 *pwd_value) 150325151a3SRui Paulo { 151325151a3SRui Paulo struct crypto_bignum *y_sqr, *x_cand; 152325151a3SRui Paulo int res; 1535b9c547cSRui Paulo size_t bits; 154206b73d0SCy Schubert int cmp_prime; 155206b73d0SCy Schubert unsigned int in_range; 1565b9c547cSRui Paulo 1575b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 1585b9c547cSRui Paulo 1595b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 1605b9c547cSRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec); 161780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 162780fb4a2SCy Schubert prime, sae->tmp->prime_len, pwd_value, bits) < 0) 163780fb4a2SCy Schubert return -1; 1645b9c547cSRui Paulo if (bits % 8) 1654bc52338SCy Schubert buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8); 1665b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", 1675b9c547cSRui Paulo pwd_value, sae->tmp->prime_len); 1685b9c547cSRui Paulo 169206b73d0SCy Schubert cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len); 170206b73d0SCy Schubert /* Create a const_time mask for selection based on prf result 171206b73d0SCy Schubert * being smaller than prime. */ 172206b73d0SCy Schubert in_range = const_time_fill_msb((unsigned int) cmp_prime); 173206b73d0SCy Schubert /* The algorithm description would skip the next steps if 174c1d255d3SCy Schubert * cmp_prime >= 0 (return 0 here), but go through them regardless to 175206b73d0SCy Schubert * minimize externally observable differences in behavior. */ 1765b9c547cSRui Paulo 177325151a3SRui Paulo x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 178325151a3SRui Paulo if (!x_cand) 1795b9c547cSRui Paulo return -1; 180325151a3SRui Paulo y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); 181325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 1824bc52338SCy Schubert if (!y_sqr) 183325151a3SRui Paulo return -1; 1845b9c547cSRui Paulo 185206b73d0SCy Schubert res = dragonfly_is_quadratic_residue_blind(sae->tmp->ec, qr, qnr, 186206b73d0SCy Schubert y_sqr); 187325151a3SRui Paulo crypto_bignum_deinit(y_sqr, 1); 188206b73d0SCy Schubert if (res < 0) 189325151a3SRui Paulo return res; 190206b73d0SCy Schubert return const_time_select_int(in_range, res, 0); 191325151a3SRui Paulo } 1925b9c547cSRui Paulo 1935b9c547cSRui Paulo 1944bc52338SCy Schubert /* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided 1954bc52338SCy Schubert * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */ 1965b9c547cSRui Paulo static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, 1975b9c547cSRui Paulo struct crypto_bignum *pwe) 1985b9c547cSRui Paulo { 1995b9c547cSRui Paulo u8 pwd_value[SAE_MAX_PRIME_LEN]; 2005b9c547cSRui Paulo size_t bits = sae->tmp->prime_len * 8; 2015b9c547cSRui Paulo u8 exp[1]; 2024bc52338SCy Schubert struct crypto_bignum *a, *b = NULL; 2034bc52338SCy Schubert int res, is_val; 2044bc52338SCy Schubert u8 pwd_value_valid; 2055b9c547cSRui Paulo 2065b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 2075b9c547cSRui Paulo 2085b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 209780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 2105b9c547cSRui Paulo sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, 211780fb4a2SCy Schubert bits) < 0) 212780fb4a2SCy Schubert return -1; 2135b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, 2145b9c547cSRui Paulo sae->tmp->prime_len); 2155b9c547cSRui Paulo 2164bc52338SCy Schubert /* Check whether pwd-value < p */ 2174bc52338SCy Schubert res = const_time_memcmp(pwd_value, sae->tmp->dh->prime, 2184bc52338SCy Schubert sae->tmp->prime_len); 2194bc52338SCy Schubert /* pwd-value >= p is invalid, so res is < 0 for the valid cases and 2204bc52338SCy Schubert * the negative sign can be used to fill the mask for constant time 2214bc52338SCy Schubert * selection */ 2224bc52338SCy Schubert pwd_value_valid = const_time_fill_msb(res); 2234bc52338SCy Schubert 2244bc52338SCy Schubert /* If pwd-value >= p, force pwd-value to be < p and perform the 2254bc52338SCy Schubert * calculations anyway to hide timing difference. The derived PWE will 2264bc52338SCy Schubert * be ignored in that case. */ 2274bc52338SCy Schubert pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0); 2285b9c547cSRui Paulo 2295b9c547cSRui Paulo /* PWE = pwd-value^((p-1)/r) modulo p */ 2305b9c547cSRui Paulo 2314bc52338SCy Schubert res = -1; 2325b9c547cSRui Paulo a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 2334bc52338SCy Schubert if (!a) 2344bc52338SCy Schubert goto fail; 2355b9c547cSRui Paulo 2364bc52338SCy Schubert /* This is an optimization based on the used group that does not depend 2374bc52338SCy Schubert * on the password in any way, so it is fine to use separate branches 2384bc52338SCy Schubert * for this step without constant time operations. */ 2395b9c547cSRui Paulo if (sae->tmp->dh->safe_prime) { 2405b9c547cSRui Paulo /* 2415b9c547cSRui Paulo * r = (p-1)/2 for the group used here, so this becomes: 2425b9c547cSRui Paulo * PWE = pwd-value^2 modulo p 2435b9c547cSRui Paulo */ 2445b9c547cSRui Paulo exp[0] = 2; 2455b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 2465b9c547cSRui Paulo } else { 2475b9c547cSRui Paulo /* Calculate exponent: (p-1)/r */ 2485b9c547cSRui Paulo exp[0] = 1; 2495b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 2505b9c547cSRui Paulo if (b == NULL || 2515b9c547cSRui Paulo crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || 2524bc52338SCy Schubert crypto_bignum_div(b, sae->tmp->order, b) < 0) 2534bc52338SCy Schubert goto fail; 2545b9c547cSRui Paulo } 2555b9c547cSRui Paulo 2564bc52338SCy Schubert if (!b) 2574bc52338SCy Schubert goto fail; 2584bc52338SCy Schubert 2595b9c547cSRui Paulo res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); 2604bc52338SCy Schubert if (res < 0) 2614bc52338SCy Schubert goto fail; 2625b9c547cSRui Paulo 2634bc52338SCy Schubert /* There were no fatal errors in calculations, so determine the return 2644bc52338SCy Schubert * value using constant time operations. We get here for number of 2654bc52338SCy Schubert * invalid cases which are cleared here after having performed all the 2664bc52338SCy Schubert * computation. PWE is valid if pwd-value was less than prime and 2674bc52338SCy Schubert * PWE > 1. Start with pwd-value check first and then use constant time 2684bc52338SCy Schubert * operations to clear res to 0 if PWE is 0 or 1. 2694bc52338SCy Schubert */ 2704bc52338SCy Schubert res = const_time_select_u8(pwd_value_valid, 1, 0); 2714bc52338SCy Schubert is_val = crypto_bignum_is_zero(pwe); 2724bc52338SCy Schubert res = const_time_select_u8(const_time_is_zero(is_val), res, 0); 2734bc52338SCy Schubert is_val = crypto_bignum_is_one(pwe); 2744bc52338SCy Schubert res = const_time_select_u8(const_time_is_zero(is_val), res, 0); 2755b9c547cSRui Paulo 2764bc52338SCy Schubert fail: 2774bc52338SCy Schubert crypto_bignum_deinit(a, 1); 2784bc52338SCy Schubert crypto_bignum_deinit(b, 1); 2794bc52338SCy Schubert return res; 2805b9c547cSRui Paulo } 2815b9c547cSRui Paulo 2825b9c547cSRui Paulo 2835b9c547cSRui Paulo static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, 2845b9c547cSRui Paulo const u8 *addr2, const u8 *password, 285c1d255d3SCy Schubert size_t password_len) 2865b9c547cSRui Paulo { 287206b73d0SCy Schubert u8 counter, k; 2885b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 289c1d255d3SCy Schubert const u8 *addr[2]; 290c1d255d3SCy Schubert size_t len[2]; 2914b72b91aSCy Schubert u8 *stub_password, *tmp_password; 292325151a3SRui Paulo int pwd_seed_odd = 0; 293325151a3SRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 294325151a3SRui Paulo size_t prime_len; 295ec080394SCy Schubert struct crypto_bignum *x = NULL, *y = NULL, *qr = NULL, *qnr = NULL; 2964bc52338SCy Schubert u8 x_bin[SAE_MAX_ECC_PRIME_LEN]; 2974bc52338SCy Schubert u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; 2984bc52338SCy Schubert u8 qr_bin[SAE_MAX_ECC_PRIME_LEN]; 2994bc52338SCy Schubert u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN]; 300ec080394SCy Schubert u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN]; 3014bc52338SCy Schubert int res = -1; 3024bc52338SCy Schubert u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* 3034bc52338SCy Schubert * mask */ 304ec080394SCy Schubert unsigned int is_eq; 3055b9c547cSRui Paulo 3064bc52338SCy Schubert os_memset(x_bin, 0, sizeof(x_bin)); 3074bc52338SCy Schubert 3084b72b91aSCy Schubert stub_password = os_malloc(password_len); 3094bc52338SCy Schubert tmp_password = os_malloc(password_len); 3104b72b91aSCy Schubert if (!stub_password || !tmp_password || 3114b72b91aSCy Schubert random_get_bytes(stub_password, password_len) < 0) 3124bc52338SCy Schubert goto fail; 313325151a3SRui Paulo 314325151a3SRui Paulo prime_len = sae->tmp->prime_len; 315325151a3SRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 316325151a3SRui Paulo prime_len) < 0) 3174bc52338SCy Schubert goto fail; 318325151a3SRui Paulo 319325151a3SRui Paulo /* 320325151a3SRui Paulo * Create a random quadratic residue (qr) and quadratic non-residue 321325151a3SRui Paulo * (qnr) modulo p for blinding purposes during the loop. 322325151a3SRui Paulo */ 323206b73d0SCy Schubert if (dragonfly_get_random_qr_qnr(sae->tmp->prime, &qr, &qnr) < 0 || 3244bc52338SCy Schubert crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 || 3254bc52338SCy Schubert crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0) 3264bc52338SCy Schubert goto fail; 3275b9c547cSRui Paulo 3285b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 3295b9c547cSRui Paulo password, password_len); 3305b9c547cSRui Paulo 3315b9c547cSRui Paulo /* 3325b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 333c1d255d3SCy Schubert * base = password 3345b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 335325151a3SRui Paulo * base || counter) 3365b9c547cSRui Paulo */ 3375b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 3385b9c547cSRui Paulo 3394bc52338SCy Schubert addr[0] = tmp_password; 3405b9c547cSRui Paulo len[0] = password_len; 341c1d255d3SCy Schubert addr[1] = &counter; 342c1d255d3SCy Schubert len[1] = sizeof(counter); 3435b9c547cSRui Paulo 3445b9c547cSRui Paulo /* 3455b9c547cSRui Paulo * Continue for at least k iterations to protect against side-channel 3465b9c547cSRui Paulo * attacks that attempt to determine the number of iterations required 3475b9c547cSRui Paulo * in the loop. 3485b9c547cSRui Paulo */ 349206b73d0SCy Schubert k = dragonfly_min_pwe_loop_iter(sae->group); 350206b73d0SCy Schubert 3514bc52338SCy Schubert for (counter = 1; counter <= k || !found; counter++) { 3525b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 3535b9c547cSRui Paulo 3545b9c547cSRui Paulo if (counter > 200) { 3555b9c547cSRui Paulo /* This should not happen in practice */ 3565b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 3575b9c547cSRui Paulo break; 3585b9c547cSRui Paulo } 3595b9c547cSRui Paulo 3604bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter); 3614b72b91aSCy Schubert const_time_select_bin(found, stub_password, password, 3624bc52338SCy Schubert password_len, tmp_password); 363c1d255d3SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), 2, 36485732ac8SCy Schubert addr, len, pwd_seed) < 0) 3655b9c547cSRui Paulo break; 366325151a3SRui Paulo 3675b9c547cSRui Paulo res = sae_test_pwd_seed_ecc(sae, pwd_seed, 3684bc52338SCy Schubert prime, qr_bin, qnr_bin, x_cand_bin); 3694bc52338SCy Schubert const_time_select_bin(found, x_bin, x_cand_bin, prime_len, 3704bc52338SCy Schubert x_bin); 3714bc52338SCy Schubert pwd_seed_odd = const_time_select_u8( 3724bc52338SCy Schubert found, pwd_seed_odd, 3734bc52338SCy Schubert pwd_seed[SHA256_MAC_LEN - 1] & 0x01); 3744bc52338SCy Schubert os_memset(pwd_seed, 0, sizeof(pwd_seed)); 3755b9c547cSRui Paulo if (res < 0) 376325151a3SRui Paulo goto fail; 3774bc52338SCy Schubert /* Need to minimize differences in handling res == 0 and 1 here 3784bc52338SCy Schubert * to avoid differences in timing and instruction cache access, 3794bc52338SCy Schubert * so use const_time_select_*() to make local copies of the 3804bc52338SCy Schubert * values based on whether this loop iteration was the one that 3814bc52338SCy Schubert * found the pwd-seed/x. */ 382325151a3SRui Paulo 3834bc52338SCy Schubert /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them 3844bc52338SCy Schubert * (with res converted to 0/0xff) handles this in constant time. 385325151a3SRui Paulo */ 3864bc52338SCy Schubert found |= res * 0xff; 3874bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x", 3884bc52338SCy Schubert res, found); 3895b9c547cSRui Paulo } 3905b9c547cSRui Paulo 3914bc52338SCy Schubert if (!found) { 392325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); 393325151a3SRui Paulo res = -1; 394325151a3SRui Paulo goto fail; 395325151a3SRui Paulo } 3965b9c547cSRui Paulo 3974bc52338SCy Schubert x = crypto_bignum_init_set(x_bin, prime_len); 3984bc52338SCy Schubert if (!x) { 3994bc52338SCy Schubert res = -1; 4004bc52338SCy Schubert goto fail; 4014bc52338SCy Schubert } 4024bc52338SCy Schubert 403ec080394SCy Schubert /* y = sqrt(x^3 + ax + b) mod p 404ec080394SCy Schubert * if LSB(save) == LSB(y): PWE = (x, y) 405ec080394SCy Schubert * else: PWE = (x, p - y) 406ec080394SCy Schubert * 407ec080394SCy Schubert * Calculate y and the two possible values for PWE and after that, 408ec080394SCy Schubert * use constant time selection to copy the correct alternative. 40964e33c5cSCy Schubert */ 410ec080394SCy Schubert y = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x); 411ec080394SCy Schubert if (!y || 412ec080394SCy Schubert dragonfly_sqrt(sae->tmp->ec, y, y) < 0 || 413ec080394SCy Schubert crypto_bignum_to_bin(y, x_y, SAE_MAX_ECC_PRIME_LEN, 414ec080394SCy Schubert prime_len) < 0 || 415ec080394SCy Schubert crypto_bignum_sub(sae->tmp->prime, y, y) < 0 || 416ec080394SCy Schubert crypto_bignum_to_bin(y, x_y + SAE_MAX_ECC_PRIME_LEN, 417ec080394SCy Schubert SAE_MAX_ECC_PRIME_LEN, prime_len) < 0) { 41864e33c5cSCy Schubert wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); 419ec080394SCy Schubert goto fail; 420ec080394SCy Schubert } 421ec080394SCy Schubert 422ec080394SCy Schubert is_eq = const_time_eq(pwd_seed_odd, x_y[prime_len - 1] & 0x01); 423ec080394SCy Schubert const_time_select_bin(is_eq, x_y, x_y + SAE_MAX_ECC_PRIME_LEN, 424ec080394SCy Schubert prime_len, x_y + prime_len); 425ec080394SCy Schubert os_memcpy(x_y, x_bin, prime_len); 426ec080394SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PWE", x_y, 2 * prime_len); 427ec080394SCy Schubert crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1); 428ec080394SCy Schubert sae->tmp->pwe_ecc = crypto_ec_point_from_bin(sae->tmp->ec, x_y); 429ec080394SCy Schubert if (!sae->tmp->pwe_ecc) { 430ec080394SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); 431ec080394SCy Schubert res = -1; 432325151a3SRui Paulo } 433325151a3SRui Paulo 434325151a3SRui Paulo fail: 435ec080394SCy Schubert forced_memzero(x_y, sizeof(x_y)); 436325151a3SRui Paulo crypto_bignum_deinit(qr, 0); 437325151a3SRui Paulo crypto_bignum_deinit(qnr, 0); 438ec080394SCy Schubert crypto_bignum_deinit(y, 1); 4394b72b91aSCy Schubert os_free(stub_password); 4404bc52338SCy Schubert bin_clear_free(tmp_password, password_len); 4414bc52338SCy Schubert crypto_bignum_deinit(x, 1); 4424bc52338SCy Schubert os_memset(x_bin, 0, sizeof(x_bin)); 4434bc52338SCy Schubert os_memset(x_cand_bin, 0, sizeof(x_cand_bin)); 444325151a3SRui Paulo 445325151a3SRui Paulo return res; 4465b9c547cSRui Paulo } 4475b9c547cSRui Paulo 4485b9c547cSRui Paulo 4495b9c547cSRui Paulo static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, 4505b9c547cSRui Paulo const u8 *addr2, const u8 *password, 451c1d255d3SCy Schubert size_t password_len) 4525b9c547cSRui Paulo { 4534bc52338SCy Schubert u8 counter, k, sel_counter = 0; 4545b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 455c1d255d3SCy Schubert const u8 *addr[2]; 456c1d255d3SCy Schubert size_t len[2]; 4574bc52338SCy Schubert u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* 4584bc52338SCy Schubert * mask */ 4594bc52338SCy Schubert u8 mask; 4604bc52338SCy Schubert struct crypto_bignum *pwe; 461*a90b9d01SCy Schubert size_t prime_len = sae->tmp->prime_len; 4624bc52338SCy Schubert u8 *pwe_buf; 4635b9c547cSRui Paulo 4644bc52338SCy Schubert crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); 4654bc52338SCy Schubert sae->tmp->pwe_ffc = NULL; 4664bc52338SCy Schubert 4674bc52338SCy Schubert /* Allocate a buffer to maintain selected and candidate PWE for constant 4684bc52338SCy Schubert * time selection. */ 4694bc52338SCy Schubert pwe_buf = os_zalloc(prime_len * 2); 4704bc52338SCy Schubert pwe = crypto_bignum_init(); 4714bc52338SCy Schubert if (!pwe_buf || !pwe) 4724bc52338SCy Schubert goto fail; 4735b9c547cSRui Paulo 4745b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 4755b9c547cSRui Paulo password, password_len); 4765b9c547cSRui Paulo 4775b9c547cSRui Paulo /* 4785b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 4795b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 480c1d255d3SCy Schubert * password || counter) 4815b9c547cSRui Paulo */ 4825b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 4835b9c547cSRui Paulo 4845b9c547cSRui Paulo addr[0] = password; 4855b9c547cSRui Paulo len[0] = password_len; 486c1d255d3SCy Schubert addr[1] = &counter; 487c1d255d3SCy Schubert len[1] = sizeof(counter); 4885b9c547cSRui Paulo 489206b73d0SCy Schubert k = dragonfly_min_pwe_loop_iter(sae->group); 4904bc52338SCy Schubert 4914bc52338SCy Schubert for (counter = 1; counter <= k || !found; counter++) { 4925b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 4935b9c547cSRui Paulo int res; 4945b9c547cSRui Paulo 4955b9c547cSRui Paulo if (counter > 200) { 4965b9c547cSRui Paulo /* This should not happen in practice */ 4975b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 4985b9c547cSRui Paulo break; 4995b9c547cSRui Paulo } 5005b9c547cSRui Paulo 5014bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter); 502c1d255d3SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), 2, 50385732ac8SCy Schubert addr, len, pwd_seed) < 0) 5045b9c547cSRui Paulo break; 5054bc52338SCy Schubert res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe); 5064bc52338SCy Schubert /* res is -1 for fatal failure, 0 if a valid PWE was not found, 5074bc52338SCy Schubert * or 1 if a valid PWE was found. */ 5085b9c547cSRui Paulo if (res < 0) 5095b9c547cSRui Paulo break; 5104bc52338SCy Schubert /* Store the candidate PWE into the second half of pwe_buf and 5114bc52338SCy Schubert * the selected PWE in the beginning of pwe_buf using constant 5124bc52338SCy Schubert * time selection. */ 5134bc52338SCy Schubert if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len, 5144bc52338SCy Schubert prime_len) < 0) 5154bc52338SCy Schubert break; 5164bc52338SCy Schubert const_time_select_bin(found, pwe_buf, pwe_buf + prime_len, 5174bc52338SCy Schubert prime_len, pwe_buf); 5184bc52338SCy Schubert sel_counter = const_time_select_u8(found, sel_counter, counter); 5194bc52338SCy Schubert mask = const_time_eq_u8(res, 1); 5204bc52338SCy Schubert found = const_time_select_u8(found, found, mask); 5215b9c547cSRui Paulo } 5225b9c547cSRui Paulo 5234bc52338SCy Schubert if (!found) 5244bc52338SCy Schubert goto fail; 5254bc52338SCy Schubert 5264bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter); 5274bc52338SCy Schubert sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len); 5284bc52338SCy Schubert fail: 5294bc52338SCy Schubert crypto_bignum_deinit(pwe, 1); 5304bc52338SCy Schubert bin_clear_free(pwe_buf, prime_len * 2); 5314bc52338SCy Schubert return sae->tmp->pwe_ffc ? 0 : -1; 5325b9c547cSRui Paulo } 5335b9c547cSRui Paulo 5345b9c547cSRui Paulo 535c1d255d3SCy Schubert static int hkdf_extract(size_t hash_len, const u8 *salt, size_t salt_len, 536c1d255d3SCy Schubert size_t num_elem, const u8 *addr[], const size_t len[], 537c1d255d3SCy Schubert u8 *prk) 538c1d255d3SCy Schubert { 539c1d255d3SCy Schubert if (hash_len == 32) 540c1d255d3SCy Schubert return hmac_sha256_vector(salt, salt_len, num_elem, addr, len, 541c1d255d3SCy Schubert prk); 542c1d255d3SCy Schubert #ifdef CONFIG_SHA384 543c1d255d3SCy Schubert if (hash_len == 48) 544c1d255d3SCy Schubert return hmac_sha384_vector(salt, salt_len, num_elem, addr, len, 545c1d255d3SCy Schubert prk); 546c1d255d3SCy Schubert #endif /* CONFIG_SHA384 */ 547c1d255d3SCy Schubert #ifdef CONFIG_SHA512 548c1d255d3SCy Schubert if (hash_len == 64) 549c1d255d3SCy Schubert return hmac_sha512_vector(salt, salt_len, num_elem, addr, len, 550c1d255d3SCy Schubert prk); 551c1d255d3SCy Schubert #endif /* CONFIG_SHA512 */ 552c1d255d3SCy Schubert return -1; 553c1d255d3SCy Schubert } 554c1d255d3SCy Schubert 555c1d255d3SCy Schubert 556c1d255d3SCy Schubert static int hkdf_expand(size_t hash_len, const u8 *prk, size_t prk_len, 557c1d255d3SCy Schubert const char *info, u8 *okm, size_t okm_len) 558c1d255d3SCy Schubert { 559c1d255d3SCy Schubert size_t info_len = os_strlen(info); 560c1d255d3SCy Schubert 561c1d255d3SCy Schubert if (hash_len == 32) 562c1d255d3SCy Schubert return hmac_sha256_kdf(prk, prk_len, NULL, 563c1d255d3SCy Schubert (const u8 *) info, info_len, 564c1d255d3SCy Schubert okm, okm_len); 565c1d255d3SCy Schubert #ifdef CONFIG_SHA384 566c1d255d3SCy Schubert if (hash_len == 48) 567c1d255d3SCy Schubert return hmac_sha384_kdf(prk, prk_len, NULL, 568c1d255d3SCy Schubert (const u8 *) info, info_len, 569c1d255d3SCy Schubert okm, okm_len); 570c1d255d3SCy Schubert #endif /* CONFIG_SHA384 */ 571c1d255d3SCy Schubert #ifdef CONFIG_SHA512 572c1d255d3SCy Schubert if (hash_len == 64) 573c1d255d3SCy Schubert return hmac_sha512_kdf(prk, prk_len, NULL, 574c1d255d3SCy Schubert (const u8 *) info, info_len, 575c1d255d3SCy Schubert okm, okm_len); 576c1d255d3SCy Schubert #endif /* CONFIG_SHA512 */ 577c1d255d3SCy Schubert return -1; 578c1d255d3SCy Schubert } 579c1d255d3SCy Schubert 580c1d255d3SCy Schubert 581c1d255d3SCy Schubert static int sswu_curve_param(int group, int *z) 582c1d255d3SCy Schubert { 583c1d255d3SCy Schubert switch (group) { 584c1d255d3SCy Schubert case 19: 585c1d255d3SCy Schubert *z = -10; 586c1d255d3SCy Schubert return 0; 587c1d255d3SCy Schubert case 20: 588c1d255d3SCy Schubert *z = -12; 589c1d255d3SCy Schubert return 0; 590c1d255d3SCy Schubert case 21: 591c1d255d3SCy Schubert *z = -4; 592c1d255d3SCy Schubert return 0; 593c1d255d3SCy Schubert case 25: 594c1d255d3SCy Schubert case 29: 595c1d255d3SCy Schubert *z = -5; 596c1d255d3SCy Schubert return 0; 597c1d255d3SCy Schubert case 26: 598c1d255d3SCy Schubert *z = 31; 599c1d255d3SCy Schubert return 0; 600c1d255d3SCy Schubert case 28: 601c1d255d3SCy Schubert *z = -2; 602c1d255d3SCy Schubert return 0; 603c1d255d3SCy Schubert case 30: 604c1d255d3SCy Schubert *z = 7; 605c1d255d3SCy Schubert return 0; 606*a90b9d01SCy Schubert default: 607c1d255d3SCy Schubert return -1; 608c1d255d3SCy Schubert } 609*a90b9d01SCy Schubert } 610c1d255d3SCy Schubert 611c1d255d3SCy Schubert 612c1d255d3SCy Schubert static void debug_print_bignum(const char *title, const struct crypto_bignum *a, 613c1d255d3SCy Schubert size_t prime_len) 614c1d255d3SCy Schubert { 615c1d255d3SCy Schubert u8 *bin; 616c1d255d3SCy Schubert 617c1d255d3SCy Schubert bin = os_malloc(prime_len); 618c1d255d3SCy Schubert if (bin && crypto_bignum_to_bin(a, bin, prime_len, prime_len) >= 0) 619c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, title, bin, prime_len); 620c1d255d3SCy Schubert else 621c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "Could not print bignum (%s)", title); 622c1d255d3SCy Schubert bin_clear_free(bin, prime_len); 623c1d255d3SCy Schubert } 624c1d255d3SCy Schubert 625c1d255d3SCy Schubert 626c1d255d3SCy Schubert static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group, 627c1d255d3SCy Schubert const struct crypto_bignum *u) 628c1d255d3SCy Schubert { 629c1d255d3SCy Schubert int z_int; 630c1d255d3SCy Schubert const struct crypto_bignum *a, *b, *prime; 631c1d255d3SCy Schubert struct crypto_bignum *u2, *t1, *t2, *z, *t, *zero, *one, *two, *three, 632c1d255d3SCy Schubert *x1a, *x1b, *y = NULL; 633c1d255d3SCy Schubert struct crypto_bignum *x1 = NULL, *x2, *gx1, *gx2, *v = NULL; 634c1d255d3SCy Schubert unsigned int m_is_zero, is_qr, is_eq; 635c1d255d3SCy Schubert size_t prime_len; 636c1d255d3SCy Schubert u8 bin[SAE_MAX_ECC_PRIME_LEN]; 637c1d255d3SCy Schubert u8 bin1[SAE_MAX_ECC_PRIME_LEN]; 638c1d255d3SCy Schubert u8 bin2[SAE_MAX_ECC_PRIME_LEN]; 639c1d255d3SCy Schubert u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN]; 640c1d255d3SCy Schubert struct crypto_ec_point *p = NULL; 641c1d255d3SCy Schubert 642c1d255d3SCy Schubert if (sswu_curve_param(group, &z_int) < 0) 643c1d255d3SCy Schubert return NULL; 644c1d255d3SCy Schubert 645c1d255d3SCy Schubert prime = crypto_ec_get_prime(ec); 646c1d255d3SCy Schubert prime_len = crypto_ec_prime_len(ec); 647c1d255d3SCy Schubert a = crypto_ec_get_a(ec); 648c1d255d3SCy Schubert b = crypto_ec_get_b(ec); 649c1d255d3SCy Schubert 650c1d255d3SCy Schubert u2 = crypto_bignum_init(); 651c1d255d3SCy Schubert t1 = crypto_bignum_init(); 652c1d255d3SCy Schubert t2 = crypto_bignum_init(); 653c1d255d3SCy Schubert z = crypto_bignum_init_uint(abs(z_int)); 654c1d255d3SCy Schubert t = crypto_bignum_init(); 655c1d255d3SCy Schubert zero = crypto_bignum_init_uint(0); 656c1d255d3SCy Schubert one = crypto_bignum_init_uint(1); 657c1d255d3SCy Schubert two = crypto_bignum_init_uint(2); 658c1d255d3SCy Schubert three = crypto_bignum_init_uint(3); 659c1d255d3SCy Schubert x1a = crypto_bignum_init(); 660c1d255d3SCy Schubert x1b = crypto_bignum_init(); 661c1d255d3SCy Schubert x2 = crypto_bignum_init(); 662c1d255d3SCy Schubert gx1 = crypto_bignum_init(); 663c1d255d3SCy Schubert gx2 = crypto_bignum_init(); 664c1d255d3SCy Schubert if (!u2 || !t1 || !t2 || !z || !t || !zero || !one || !two || !three || 665c1d255d3SCy Schubert !x1a || !x1b || !x2 || !gx1 || !gx2) 666c1d255d3SCy Schubert goto fail; 667c1d255d3SCy Schubert 668c1d255d3SCy Schubert if (z_int < 0 && crypto_bignum_sub(prime, z, z) < 0) 669c1d255d3SCy Schubert goto fail; 670c1d255d3SCy Schubert 671c1d255d3SCy Schubert /* m = z^2 * u^4 + z * u^2 */ 672c1d255d3SCy Schubert /* --> tmp = z * u^2, m = tmp^2 + tmp */ 673c1d255d3SCy Schubert 674c1d255d3SCy Schubert /* u2 = u^2 675c1d255d3SCy Schubert * t1 = z * u2 676c1d255d3SCy Schubert * t2 = t1^2 677c1d255d3SCy Schubert * m = t1 = t1 + t2 */ 678c1d255d3SCy Schubert if (crypto_bignum_sqrmod(u, prime, u2) < 0 || 679c1d255d3SCy Schubert crypto_bignum_mulmod(z, u2, prime, t1) < 0 || 680c1d255d3SCy Schubert crypto_bignum_sqrmod(t1, prime, t2) < 0 || 681c1d255d3SCy Schubert crypto_bignum_addmod(t1, t2, prime, t1) < 0) 682c1d255d3SCy Schubert goto fail; 683c1d255d3SCy Schubert debug_print_bignum("SSWU: m", t1, prime_len); 684c1d255d3SCy Schubert 685c1d255d3SCy Schubert /* l = CEQ(m, 0) 686c1d255d3SCy Schubert * t = CSEL(l, 0, inverse(m); where inverse(x) is calculated as 687c1d255d3SCy Schubert * x^(p-2) modulo p which will handle m == 0 case correctly */ 688c1d255d3SCy Schubert /* TODO: Make sure crypto_bignum_is_zero() is constant time */ 689c1d255d3SCy Schubert m_is_zero = const_time_eq(crypto_bignum_is_zero(t1), 1); 690c1d255d3SCy Schubert /* t = m^(p-2) modulo p */ 691c1d255d3SCy Schubert if (crypto_bignum_sub(prime, two, t2) < 0 || 692c1d255d3SCy Schubert crypto_bignum_exptmod(t1, t2, prime, t) < 0) 693c1d255d3SCy Schubert goto fail; 694c1d255d3SCy Schubert debug_print_bignum("SSWU: t", t, prime_len); 695c1d255d3SCy Schubert 696c1d255d3SCy Schubert /* b / (z * a) */ 697c1d255d3SCy Schubert if (crypto_bignum_mulmod(z, a, prime, t1) < 0 || 698c1d255d3SCy Schubert crypto_bignum_inverse(t1, prime, t1) < 0 || 699c1d255d3SCy Schubert crypto_bignum_mulmod(b, t1, prime, x1a) < 0) 700c1d255d3SCy Schubert goto fail; 701c1d255d3SCy Schubert debug_print_bignum("SSWU: x1a = b / (z * a)", x1a, prime_len); 702c1d255d3SCy Schubert 703c1d255d3SCy Schubert /* (-b/a) * (1 + t) */ 704c1d255d3SCy Schubert if (crypto_bignum_sub(prime, b, t1) < 0 || 705c1d255d3SCy Schubert crypto_bignum_inverse(a, prime, t2) < 0 || 706c1d255d3SCy Schubert crypto_bignum_mulmod(t1, t2, prime, t1) < 0 || 707c1d255d3SCy Schubert crypto_bignum_addmod(one, t, prime, t2) < 0 || 708c1d255d3SCy Schubert crypto_bignum_mulmod(t1, t2, prime, x1b) < 0) 709c1d255d3SCy Schubert goto fail; 710c1d255d3SCy Schubert debug_print_bignum("SSWU: x1b = (-b/a) * (1 + t)", x1b, prime_len); 711c1d255d3SCy Schubert 712c1d255d3SCy Schubert /* x1 = CSEL(CEQ(m, 0), x1a, x1b) */ 713c1d255d3SCy Schubert if (crypto_bignum_to_bin(x1a, bin1, sizeof(bin1), prime_len) < 0 || 714c1d255d3SCy Schubert crypto_bignum_to_bin(x1b, bin2, sizeof(bin2), prime_len) < 0) 715c1d255d3SCy Schubert goto fail; 716c1d255d3SCy Schubert const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin); 717c1d255d3SCy Schubert x1 = crypto_bignum_init_set(bin, prime_len); 718c1d255d3SCy Schubert if (!x1) 719c1d255d3SCy Schubert goto fail; 720c1d255d3SCy Schubert debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len); 721c1d255d3SCy Schubert 722c1d255d3SCy Schubert /* gx1 = x1^3 + a * x1 + b */ 723c1d255d3SCy Schubert if (crypto_bignum_exptmod(x1, three, prime, t1) < 0 || 724c1d255d3SCy Schubert crypto_bignum_mulmod(a, x1, prime, t2) < 0 || 725c1d255d3SCy Schubert crypto_bignum_addmod(t1, t2, prime, t1) < 0 || 726c1d255d3SCy Schubert crypto_bignum_addmod(t1, b, prime, gx1) < 0) 727c1d255d3SCy Schubert goto fail; 728c1d255d3SCy Schubert debug_print_bignum("SSWU: gx1 = x1^3 + a * x1 + b", gx1, prime_len); 729c1d255d3SCy Schubert 730c1d255d3SCy Schubert /* x2 = z * u^2 * x1 */ 731c1d255d3SCy Schubert if (crypto_bignum_mulmod(z, u2, prime, t1) < 0 || 732c1d255d3SCy Schubert crypto_bignum_mulmod(t1, x1, prime, x2) < 0) 733c1d255d3SCy Schubert goto fail; 734c1d255d3SCy Schubert debug_print_bignum("SSWU: x2 = z * u^2 * x1", x2, prime_len); 735c1d255d3SCy Schubert 736c1d255d3SCy Schubert /* gx2 = x2^3 + a * x2 + b */ 737c1d255d3SCy Schubert if (crypto_bignum_exptmod(x2, three, prime, t1) < 0 || 738c1d255d3SCy Schubert crypto_bignum_mulmod(a, x2, prime, t2) < 0 || 739c1d255d3SCy Schubert crypto_bignum_addmod(t1, t2, prime, t1) < 0 || 740c1d255d3SCy Schubert crypto_bignum_addmod(t1, b, prime, gx2) < 0) 741c1d255d3SCy Schubert goto fail; 742c1d255d3SCy Schubert debug_print_bignum("SSWU: gx2 = x2^3 + a * x2 + b", gx2, prime_len); 743c1d255d3SCy Schubert 744c1d255d3SCy Schubert /* l = gx1 is a quadratic residue modulo p 745c1d255d3SCy Schubert * --> gx1^((p-1)/2) modulo p is zero or one */ 746c1d255d3SCy Schubert if (crypto_bignum_sub(prime, one, t1) < 0 || 747c1d255d3SCy Schubert crypto_bignum_rshift(t1, 1, t1) < 0 || 748c1d255d3SCy Schubert crypto_bignum_exptmod(gx1, t1, prime, t1) < 0) 749c1d255d3SCy Schubert goto fail; 750c1d255d3SCy Schubert debug_print_bignum("SSWU: gx1^((p-1)/2) modulo p", t1, prime_len); 751c1d255d3SCy Schubert is_qr = const_time_eq(crypto_bignum_is_zero(t1) | 752c1d255d3SCy Schubert crypto_bignum_is_one(t1), 1); 753c1d255d3SCy Schubert 754c1d255d3SCy Schubert /* v = CSEL(l, gx1, gx2) */ 755c1d255d3SCy Schubert if (crypto_bignum_to_bin(gx1, bin1, sizeof(bin1), prime_len) < 0 || 756c1d255d3SCy Schubert crypto_bignum_to_bin(gx2, bin2, sizeof(bin2), prime_len) < 0) 757c1d255d3SCy Schubert goto fail; 758c1d255d3SCy Schubert const_time_select_bin(is_qr, bin1, bin2, prime_len, bin); 759c1d255d3SCy Schubert v = crypto_bignum_init_set(bin, prime_len); 760c1d255d3SCy Schubert if (!v) 761c1d255d3SCy Schubert goto fail; 762c1d255d3SCy Schubert debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len); 763c1d255d3SCy Schubert 764c1d255d3SCy Schubert /* x = CSEL(l, x1, x2) */ 765c1d255d3SCy Schubert if (crypto_bignum_to_bin(x1, bin1, sizeof(bin1), prime_len) < 0 || 766c1d255d3SCy Schubert crypto_bignum_to_bin(x2, bin2, sizeof(bin2), prime_len) < 0) 767c1d255d3SCy Schubert goto fail; 768c1d255d3SCy Schubert const_time_select_bin(is_qr, bin1, bin2, prime_len, x_y); 769c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SSWU: x = CSEL(l, x1, x2)", x_y, prime_len); 770c1d255d3SCy Schubert 771ec080394SCy Schubert /* y = sqrt(v) */ 772c1d255d3SCy Schubert y = crypto_bignum_init(); 773ec080394SCy Schubert if (!y || dragonfly_sqrt(ec, v, y) < 0) 774c1d255d3SCy Schubert goto fail; 775c1d255d3SCy Schubert debug_print_bignum("SSWU: y = sqrt(v)", y, prime_len); 776c1d255d3SCy Schubert 777c1d255d3SCy Schubert /* l = CEQ(LSB(u), LSB(y)) */ 778c1d255d3SCy Schubert if (crypto_bignum_to_bin(u, bin1, sizeof(bin1), prime_len) < 0 || 779c1d255d3SCy Schubert crypto_bignum_to_bin(y, bin2, sizeof(bin2), prime_len) < 0) 780c1d255d3SCy Schubert goto fail; 781c1d255d3SCy Schubert is_eq = const_time_eq(bin1[prime_len - 1] & 0x01, 782c1d255d3SCy Schubert bin2[prime_len - 1] & 0x01); 783c1d255d3SCy Schubert 784c1d255d3SCy Schubert /* P = CSEL(l, (x,y), (x, p-y)) */ 785c1d255d3SCy Schubert if (crypto_bignum_sub(prime, y, t1) < 0) 786c1d255d3SCy Schubert goto fail; 787c1d255d3SCy Schubert debug_print_bignum("SSWU: p - y", t1, prime_len); 788c1d255d3SCy Schubert if (crypto_bignum_to_bin(y, bin1, sizeof(bin1), prime_len) < 0 || 789c1d255d3SCy Schubert crypto_bignum_to_bin(t1, bin2, sizeof(bin2), prime_len) < 0) 790c1d255d3SCy Schubert goto fail; 791c1d255d3SCy Schubert const_time_select_bin(is_eq, bin1, bin2, prime_len, &x_y[prime_len]); 792c1d255d3SCy Schubert 793c1d255d3SCy Schubert /* output P */ 794c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SSWU: P.x", x_y, prime_len); 795c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SSWU: P.y", &x_y[prime_len], prime_len); 796c1d255d3SCy Schubert p = crypto_ec_point_from_bin(ec, x_y); 797c1d255d3SCy Schubert 798c1d255d3SCy Schubert fail: 799c1d255d3SCy Schubert crypto_bignum_deinit(u2, 1); 800c1d255d3SCy Schubert crypto_bignum_deinit(t1, 1); 801c1d255d3SCy Schubert crypto_bignum_deinit(t2, 1); 802c1d255d3SCy Schubert crypto_bignum_deinit(z, 0); 803c1d255d3SCy Schubert crypto_bignum_deinit(t, 1); 804c1d255d3SCy Schubert crypto_bignum_deinit(x1a, 1); 805c1d255d3SCy Schubert crypto_bignum_deinit(x1b, 1); 806c1d255d3SCy Schubert crypto_bignum_deinit(x1, 1); 807c1d255d3SCy Schubert crypto_bignum_deinit(x2, 1); 808c1d255d3SCy Schubert crypto_bignum_deinit(gx1, 1); 809c1d255d3SCy Schubert crypto_bignum_deinit(gx2, 1); 810c1d255d3SCy Schubert crypto_bignum_deinit(y, 1); 811c1d255d3SCy Schubert crypto_bignum_deinit(v, 1); 812c1d255d3SCy Schubert crypto_bignum_deinit(zero, 0); 813c1d255d3SCy Schubert crypto_bignum_deinit(one, 0); 814c1d255d3SCy Schubert crypto_bignum_deinit(two, 0); 815c1d255d3SCy Schubert crypto_bignum_deinit(three, 0); 816c1d255d3SCy Schubert forced_memzero(bin, sizeof(bin)); 817c1d255d3SCy Schubert forced_memzero(bin1, sizeof(bin1)); 818c1d255d3SCy Schubert forced_memzero(bin2, sizeof(bin2)); 819c1d255d3SCy Schubert forced_memzero(x_y, sizeof(x_y)); 820c1d255d3SCy Schubert return p; 821c1d255d3SCy Schubert } 822c1d255d3SCy Schubert 823c1d255d3SCy Schubert 824c1d255d3SCy Schubert static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len, 825c1d255d3SCy Schubert const u8 *password, size_t password_len, 826c1d255d3SCy Schubert const char *identifier, u8 *pwd_seed) 827c1d255d3SCy Schubert { 828c1d255d3SCy Schubert const u8 *addr[2]; 829c1d255d3SCy Schubert size_t len[2]; 830c1d255d3SCy Schubert size_t num_elem; 831c1d255d3SCy Schubert 832c1d255d3SCy Schubert /* pwd-seed = HKDF-Extract(ssid, password [ || identifier ]) */ 833c1d255d3SCy Schubert addr[0] = password; 834c1d255d3SCy Schubert len[0] = password_len; 835c1d255d3SCy Schubert num_elem = 1; 836c1d255d3SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "SAE: SSID", ssid, ssid_len); 837c1d255d3SCy Schubert wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 838c1d255d3SCy Schubert password, password_len); 839c1d255d3SCy Schubert if (identifier) { 840c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", 841c1d255d3SCy Schubert identifier); 842c1d255d3SCy Schubert addr[num_elem] = (const u8 *) identifier; 843c1d255d3SCy Schubert len[num_elem] = os_strlen(identifier); 844c1d255d3SCy Schubert num_elem++; 845c1d255d3SCy Schubert } 846c1d255d3SCy Schubert if (hkdf_extract(hash_len, ssid, ssid_len, num_elem, addr, len, 847c1d255d3SCy Schubert pwd_seed) < 0) 848c1d255d3SCy Schubert return -1; 849c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, hash_len); 850c1d255d3SCy Schubert return 0; 851c1d255d3SCy Schubert } 852c1d255d3SCy Schubert 853c1d255d3SCy Schubert 854c1d255d3SCy Schubert size_t sae_ecc_prime_len_2_hash_len(size_t prime_len) 855c1d255d3SCy Schubert { 856c1d255d3SCy Schubert if (prime_len <= 256 / 8) 857c1d255d3SCy Schubert return 32; 858c1d255d3SCy Schubert if (prime_len <= 384 / 8) 859c1d255d3SCy Schubert return 48; 860c1d255d3SCy Schubert return 64; 861c1d255d3SCy Schubert } 862c1d255d3SCy Schubert 863c1d255d3SCy Schubert 864c1d255d3SCy Schubert static struct crypto_ec_point * 865c1d255d3SCy Schubert sae_derive_pt_ecc(struct crypto_ec *ec, int group, 866c1d255d3SCy Schubert const u8 *ssid, size_t ssid_len, 867c1d255d3SCy Schubert const u8 *password, size_t password_len, 868c1d255d3SCy Schubert const char *identifier) 869c1d255d3SCy Schubert { 870c1d255d3SCy Schubert u8 pwd_seed[64]; 871c1d255d3SCy Schubert u8 pwd_value[SAE_MAX_ECC_PRIME_LEN * 2]; 872c1d255d3SCy Schubert size_t pwd_value_len, hash_len, prime_len; 873c1d255d3SCy Schubert const struct crypto_bignum *prime; 874c1d255d3SCy Schubert struct crypto_bignum *bn = NULL; 875c1d255d3SCy Schubert struct crypto_ec_point *p1 = NULL, *p2 = NULL, *pt = NULL; 876c1d255d3SCy Schubert 877c1d255d3SCy Schubert prime = crypto_ec_get_prime(ec); 878c1d255d3SCy Schubert prime_len = crypto_ec_prime_len(ec); 879c1d255d3SCy Schubert if (prime_len > SAE_MAX_ECC_PRIME_LEN) 880c1d255d3SCy Schubert goto fail; 881c1d255d3SCy Schubert hash_len = sae_ecc_prime_len_2_hash_len(prime_len); 882c1d255d3SCy Schubert 883c1d255d3SCy Schubert /* len = olen(p) + ceil(olen(p)/2) */ 884c1d255d3SCy Schubert pwd_value_len = prime_len + (prime_len + 1) / 2; 885c1d255d3SCy Schubert 886c1d255d3SCy Schubert if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, 887c1d255d3SCy Schubert identifier, pwd_seed) < 0) 888c1d255d3SCy Schubert goto fail; 889c1d255d3SCy Schubert 890c1d255d3SCy Schubert /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u1 P1", len) 891c1d255d3SCy Schubert */ 892c1d255d3SCy Schubert if (hkdf_expand(hash_len, pwd_seed, hash_len, 893c1d255d3SCy Schubert "SAE Hash to Element u1 P1", pwd_value, pwd_value_len) < 894c1d255d3SCy Schubert 0) 895c1d255d3SCy Schubert goto fail; 896c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u1 P1)", 897c1d255d3SCy Schubert pwd_value, pwd_value_len); 898c1d255d3SCy Schubert 899c1d255d3SCy Schubert /* u1 = pwd-value modulo p */ 900c1d255d3SCy Schubert bn = crypto_bignum_init_set(pwd_value, pwd_value_len); 901c1d255d3SCy Schubert if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 || 902c1d255d3SCy Schubert crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), 903c1d255d3SCy Schubert prime_len) < 0) 904c1d255d3SCy Schubert goto fail; 905c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: u1", pwd_value, prime_len); 906c1d255d3SCy Schubert 907c1d255d3SCy Schubert /* P1 = SSWU(u1) */ 908c1d255d3SCy Schubert p1 = sswu(ec, group, bn); 909c1d255d3SCy Schubert if (!p1) 910c1d255d3SCy Schubert goto fail; 911c1d255d3SCy Schubert 912c1d255d3SCy Schubert /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u2 P2", len) 913c1d255d3SCy Schubert */ 914c1d255d3SCy Schubert if (hkdf_expand(hash_len, pwd_seed, hash_len, 915c1d255d3SCy Schubert "SAE Hash to Element u2 P2", pwd_value, 916c1d255d3SCy Schubert pwd_value_len) < 0) 917c1d255d3SCy Schubert goto fail; 918c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u2 P2)", 919c1d255d3SCy Schubert pwd_value, pwd_value_len); 920c1d255d3SCy Schubert 921c1d255d3SCy Schubert /* u2 = pwd-value modulo p */ 922c1d255d3SCy Schubert crypto_bignum_deinit(bn, 1); 923c1d255d3SCy Schubert bn = crypto_bignum_init_set(pwd_value, pwd_value_len); 924c1d255d3SCy Schubert if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 || 925c1d255d3SCy Schubert crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), 926c1d255d3SCy Schubert prime_len) < 0) 927c1d255d3SCy Schubert goto fail; 928c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: u2", pwd_value, prime_len); 929c1d255d3SCy Schubert 930c1d255d3SCy Schubert /* P2 = SSWU(u2) */ 931c1d255d3SCy Schubert p2 = sswu(ec, group, bn); 932c1d255d3SCy Schubert if (!p2) 933c1d255d3SCy Schubert goto fail; 934c1d255d3SCy Schubert 935c1d255d3SCy Schubert /* PT = elem-op(P1, P2) */ 936c1d255d3SCy Schubert pt = crypto_ec_point_init(ec); 937c1d255d3SCy Schubert if (!pt) 938c1d255d3SCy Schubert goto fail; 939c1d255d3SCy Schubert if (crypto_ec_point_add(ec, p1, p2, pt) < 0) { 940c1d255d3SCy Schubert crypto_ec_point_deinit(pt, 1); 941c1d255d3SCy Schubert pt = NULL; 942c1d255d3SCy Schubert } 943c1d255d3SCy Schubert 944c1d255d3SCy Schubert fail: 945c1d255d3SCy Schubert forced_memzero(pwd_seed, sizeof(pwd_seed)); 946c1d255d3SCy Schubert forced_memzero(pwd_value, sizeof(pwd_value)); 947c1d255d3SCy Schubert crypto_bignum_deinit(bn, 1); 948c1d255d3SCy Schubert crypto_ec_point_deinit(p1, 1); 949c1d255d3SCy Schubert crypto_ec_point_deinit(p2, 1); 950c1d255d3SCy Schubert return pt; 951c1d255d3SCy Schubert } 952c1d255d3SCy Schubert 953c1d255d3SCy Schubert 954c1d255d3SCy Schubert size_t sae_ffc_prime_len_2_hash_len(size_t prime_len) 955c1d255d3SCy Schubert { 956c1d255d3SCy Schubert if (prime_len <= 2048 / 8) 957c1d255d3SCy Schubert return 32; 958c1d255d3SCy Schubert if (prime_len <= 3072 / 8) 959c1d255d3SCy Schubert return 48; 960c1d255d3SCy Schubert return 64; 961c1d255d3SCy Schubert } 962c1d255d3SCy Schubert 963c1d255d3SCy Schubert 964c1d255d3SCy Schubert static struct crypto_bignum * 965c1d255d3SCy Schubert sae_derive_pt_ffc(const struct dh_group *dh, int group, 966c1d255d3SCy Schubert const u8 *ssid, size_t ssid_len, 967c1d255d3SCy Schubert const u8 *password, size_t password_len, 968c1d255d3SCy Schubert const char *identifier) 969c1d255d3SCy Schubert { 970c1d255d3SCy Schubert size_t hash_len, prime_len, pwd_value_len; 971c1d255d3SCy Schubert struct crypto_bignum *prime, *order; 972c1d255d3SCy Schubert struct crypto_bignum *one = NULL, *two = NULL, *bn = NULL, *tmp = NULL, 973c1d255d3SCy Schubert *pt = NULL; 974c1d255d3SCy Schubert u8 pwd_seed[64]; 975c1d255d3SCy Schubert u8 pwd_value[SAE_MAX_PRIME_LEN + SAE_MAX_PRIME_LEN / 2]; 976c1d255d3SCy Schubert 977c1d255d3SCy Schubert prime = crypto_bignum_init_set(dh->prime, dh->prime_len); 978c1d255d3SCy Schubert order = crypto_bignum_init_set(dh->order, dh->order_len); 979c1d255d3SCy Schubert if (!prime || !order) 980c1d255d3SCy Schubert goto fail; 981c1d255d3SCy Schubert prime_len = dh->prime_len; 982c1d255d3SCy Schubert if (prime_len > SAE_MAX_PRIME_LEN) 983c1d255d3SCy Schubert goto fail; 984c1d255d3SCy Schubert hash_len = sae_ffc_prime_len_2_hash_len(prime_len); 985c1d255d3SCy Schubert 986c1d255d3SCy Schubert /* len = olen(p) + ceil(olen(p)/2) */ 987c1d255d3SCy Schubert pwd_value_len = prime_len + (prime_len + 1) / 2; 988c1d255d3SCy Schubert if (pwd_value_len > sizeof(pwd_value)) 989c1d255d3SCy Schubert goto fail; 990c1d255d3SCy Schubert 991c1d255d3SCy Schubert if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, 992c1d255d3SCy Schubert identifier, pwd_seed) < 0) 993c1d255d3SCy Schubert goto fail; 994c1d255d3SCy Schubert 995c1d255d3SCy Schubert /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element", len) */ 996c1d255d3SCy Schubert if (hkdf_expand(hash_len, pwd_seed, hash_len, 997c1d255d3SCy Schubert "SAE Hash to Element", pwd_value, pwd_value_len) < 0) 998c1d255d3SCy Schubert goto fail; 999c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", 1000c1d255d3SCy Schubert pwd_value, pwd_value_len); 1001c1d255d3SCy Schubert 1002c1d255d3SCy Schubert /* pwd-value = (pwd-value modulo (p-2)) + 2 */ 1003c1d255d3SCy Schubert bn = crypto_bignum_init_set(pwd_value, pwd_value_len); 1004c1d255d3SCy Schubert one = crypto_bignum_init_uint(1); 1005c1d255d3SCy Schubert two = crypto_bignum_init_uint(2); 1006c1d255d3SCy Schubert tmp = crypto_bignum_init(); 1007c1d255d3SCy Schubert if (!bn || !one || !two || !tmp || 1008c1d255d3SCy Schubert crypto_bignum_sub(prime, two, tmp) < 0 || 1009c1d255d3SCy Schubert crypto_bignum_mod(bn, tmp, bn) < 0 || 1010c1d255d3SCy Schubert crypto_bignum_add(bn, two, bn) < 0 || 1011c1d255d3SCy Schubert crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), 1012c1d255d3SCy Schubert prime_len) < 0) 1013c1d255d3SCy Schubert goto fail; 1014c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value(reduced)", 1015c1d255d3SCy Schubert pwd_value, prime_len); 1016c1d255d3SCy Schubert 1017c1d255d3SCy Schubert /* PT = pwd-value^((p-1)/q) modulo p */ 1018c1d255d3SCy Schubert pt = crypto_bignum_init(); 1019c1d255d3SCy Schubert if (!pt || 1020c1d255d3SCy Schubert crypto_bignum_sub(prime, one, tmp) < 0 || 1021c1d255d3SCy Schubert crypto_bignum_div(tmp, order, tmp) < 0 || 1022c1d255d3SCy Schubert crypto_bignum_exptmod(bn, tmp, prime, pt) < 0) { 1023c1d255d3SCy Schubert crypto_bignum_deinit(pt, 1); 1024c1d255d3SCy Schubert pt = NULL; 1025c1d255d3SCy Schubert goto fail; 1026c1d255d3SCy Schubert } 1027c1d255d3SCy Schubert debug_print_bignum("SAE: PT", pt, prime_len); 1028c1d255d3SCy Schubert 1029c1d255d3SCy Schubert fail: 1030c1d255d3SCy Schubert forced_memzero(pwd_seed, sizeof(pwd_seed)); 1031c1d255d3SCy Schubert forced_memzero(pwd_value, sizeof(pwd_value)); 1032c1d255d3SCy Schubert crypto_bignum_deinit(bn, 1); 1033c1d255d3SCy Schubert crypto_bignum_deinit(tmp, 1); 1034c1d255d3SCy Schubert crypto_bignum_deinit(one, 0); 1035c1d255d3SCy Schubert crypto_bignum_deinit(two, 0); 1036c1d255d3SCy Schubert crypto_bignum_deinit(prime, 0); 1037c1d255d3SCy Schubert crypto_bignum_deinit(order, 0); 1038c1d255d3SCy Schubert return pt; 1039c1d255d3SCy Schubert } 1040c1d255d3SCy Schubert 1041c1d255d3SCy Schubert 1042c1d255d3SCy Schubert static struct sae_pt * 1043c1d255d3SCy Schubert sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, 1044c1d255d3SCy Schubert const u8 *password, size_t password_len, 1045c1d255d3SCy Schubert const char *identifier) 1046c1d255d3SCy Schubert { 1047c1d255d3SCy Schubert struct sae_pt *pt; 1048c1d255d3SCy Schubert 1049c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group); 1050c1d255d3SCy Schubert 1051c1d255d3SCy Schubert if (ssid_len > 32) 1052c1d255d3SCy Schubert return NULL; 1053c1d255d3SCy Schubert 1054c1d255d3SCy Schubert pt = os_zalloc(sizeof(*pt)); 1055c1d255d3SCy Schubert if (!pt) 1056c1d255d3SCy Schubert return NULL; 1057c1d255d3SCy Schubert 1058c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1059c1d255d3SCy Schubert os_memcpy(pt->ssid, ssid, ssid_len); 1060c1d255d3SCy Schubert pt->ssid_len = ssid_len; 1061c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 1062c1d255d3SCy Schubert pt->group = group; 1063c1d255d3SCy Schubert pt->ec = crypto_ec_init(group); 1064c1d255d3SCy Schubert if (pt->ec) { 1065c1d255d3SCy Schubert pt->ecc_pt = sae_derive_pt_ecc(pt->ec, group, ssid, ssid_len, 1066c1d255d3SCy Schubert password, password_len, 1067c1d255d3SCy Schubert identifier); 1068c1d255d3SCy Schubert if (!pt->ecc_pt) { 1069c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); 1070c1d255d3SCy Schubert goto fail; 1071c1d255d3SCy Schubert } 1072c1d255d3SCy Schubert 1073c1d255d3SCy Schubert return pt; 1074c1d255d3SCy Schubert } 1075c1d255d3SCy Schubert 1076c1d255d3SCy Schubert pt->dh = dh_groups_get(group); 1077c1d255d3SCy Schubert if (!pt->dh) { 1078c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Unsupported group %d", group); 1079c1d255d3SCy Schubert goto fail; 1080c1d255d3SCy Schubert } 1081c1d255d3SCy Schubert 1082c1d255d3SCy Schubert pt->ffc_pt = sae_derive_pt_ffc(pt->dh, group, ssid, ssid_len, 1083c1d255d3SCy Schubert password, password_len, identifier); 1084c1d255d3SCy Schubert if (!pt->ffc_pt) { 1085c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); 1086c1d255d3SCy Schubert goto fail; 1087c1d255d3SCy Schubert } 1088c1d255d3SCy Schubert 1089c1d255d3SCy Schubert return pt; 1090c1d255d3SCy Schubert fail: 1091c1d255d3SCy Schubert sae_deinit_pt(pt); 1092c1d255d3SCy Schubert return NULL; 1093c1d255d3SCy Schubert } 1094c1d255d3SCy Schubert 1095c1d255d3SCy Schubert 1096c1d255d3SCy Schubert struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len, 1097c1d255d3SCy Schubert const u8 *password, size_t password_len, 1098c1d255d3SCy Schubert const char *identifier) 1099c1d255d3SCy Schubert { 1100c1d255d3SCy Schubert struct sae_pt *pt = NULL, *last = NULL, *tmp; 1101c1d255d3SCy Schubert int default_groups[] = { 19, 0 }; 1102c1d255d3SCy Schubert int i; 1103c1d255d3SCy Schubert 1104c1d255d3SCy Schubert if (!groups) 1105c1d255d3SCy Schubert groups = default_groups; 1106c1d255d3SCy Schubert for (i = 0; groups[i] > 0; i++) { 1107c1d255d3SCy Schubert tmp = sae_derive_pt_group(groups[i], ssid, ssid_len, password, 1108c1d255d3SCy Schubert password_len, identifier); 1109c1d255d3SCy Schubert if (!tmp) 1110c1d255d3SCy Schubert continue; 1111c1d255d3SCy Schubert 1112c1d255d3SCy Schubert if (last) 1113c1d255d3SCy Schubert last->next = tmp; 1114c1d255d3SCy Schubert else 1115c1d255d3SCy Schubert pt = tmp; 1116c1d255d3SCy Schubert last = tmp; 1117c1d255d3SCy Schubert } 1118c1d255d3SCy Schubert 1119c1d255d3SCy Schubert return pt; 1120c1d255d3SCy Schubert } 1121c1d255d3SCy Schubert 1122c1d255d3SCy Schubert 1123c1d255d3SCy Schubert static void sae_max_min_addr(const u8 *addr[], size_t len[], 1124c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2) 1125c1d255d3SCy Schubert { 1126c1d255d3SCy Schubert len[0] = ETH_ALEN; 1127c1d255d3SCy Schubert len[1] = ETH_ALEN; 1128c1d255d3SCy Schubert if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { 1129c1d255d3SCy Schubert addr[0] = addr1; 1130c1d255d3SCy Schubert addr[1] = addr2; 1131c1d255d3SCy Schubert } else { 1132c1d255d3SCy Schubert addr[0] = addr2; 1133c1d255d3SCy Schubert addr[1] = addr1; 1134c1d255d3SCy Schubert } 1135c1d255d3SCy Schubert } 1136c1d255d3SCy Schubert 1137c1d255d3SCy Schubert 1138c1d255d3SCy Schubert struct crypto_ec_point * 1139c1d255d3SCy Schubert sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt, 1140c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2) 1141c1d255d3SCy Schubert { 1142c1d255d3SCy Schubert u8 bin[SAE_MAX_ECC_PRIME_LEN * 2]; 1143c1d255d3SCy Schubert size_t prime_len; 1144c1d255d3SCy Schubert const u8 *addr[2]; 1145c1d255d3SCy Schubert size_t len[2]; 1146c1d255d3SCy Schubert u8 salt[64], hash[64]; 1147c1d255d3SCy Schubert size_t hash_len; 1148c1d255d3SCy Schubert const struct crypto_bignum *order; 1149c1d255d3SCy Schubert struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL; 1150c1d255d3SCy Schubert struct crypto_ec_point *pwe = NULL; 1151c1d255d3SCy Schubert 1152c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT"); 1153c1d255d3SCy Schubert prime_len = crypto_ec_prime_len(pt->ec); 1154c1d255d3SCy Schubert if (crypto_ec_point_to_bin(pt->ec, pt->ecc_pt, 1155c1d255d3SCy Schubert bin, bin + prime_len) < 0) 1156c1d255d3SCy Schubert return NULL; 1157c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PT.x", bin, prime_len); 1158c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PT.y", bin + prime_len, prime_len); 1159c1d255d3SCy Schubert 1160c1d255d3SCy Schubert sae_max_min_addr(addr, len, addr1, addr2); 1161c1d255d3SCy Schubert 1162c1d255d3SCy Schubert /* val = H(0^n, 1163c1d255d3SCy Schubert * MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */ 1164c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))"); 1165c1d255d3SCy Schubert hash_len = sae_ecc_prime_len_2_hash_len(prime_len); 1166c1d255d3SCy Schubert os_memset(salt, 0, hash_len); 1167c1d255d3SCy Schubert if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0) 1168c1d255d3SCy Schubert goto fail; 1169c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len); 1170c1d255d3SCy Schubert 1171c1d255d3SCy Schubert /* val = val modulo (q - 1) + 1 */ 1172c1d255d3SCy Schubert order = crypto_ec_get_order(pt->ec); 1173c1d255d3SCy Schubert tmp = crypto_bignum_init(); 1174c1d255d3SCy Schubert val = crypto_bignum_init_set(hash, hash_len); 1175c1d255d3SCy Schubert one = crypto_bignum_init_uint(1); 1176c1d255d3SCy Schubert if (!tmp || !val || !one || 1177c1d255d3SCy Schubert crypto_bignum_sub(order, one, tmp) < 0 || 1178c1d255d3SCy Schubert crypto_bignum_mod(val, tmp, val) < 0 || 1179c1d255d3SCy Schubert crypto_bignum_add(val, one, val) < 0) 1180c1d255d3SCy Schubert goto fail; 1181c1d255d3SCy Schubert debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len); 1182c1d255d3SCy Schubert 1183c1d255d3SCy Schubert /* PWE = scalar-op(val, PT) */ 1184c1d255d3SCy Schubert pwe = crypto_ec_point_init(pt->ec); 1185c1d255d3SCy Schubert if (!pwe || 1186c1d255d3SCy Schubert crypto_ec_point_mul(pt->ec, pt->ecc_pt, val, pwe) < 0 || 1187c1d255d3SCy Schubert crypto_ec_point_to_bin(pt->ec, pwe, bin, bin + prime_len) < 0) { 1188c1d255d3SCy Schubert crypto_ec_point_deinit(pwe, 1); 1189c1d255d3SCy Schubert pwe = NULL; 1190c1d255d3SCy Schubert goto fail; 1191c1d255d3SCy Schubert } 1192c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.x", bin, prime_len); 1193c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.y", bin + prime_len, prime_len); 1194c1d255d3SCy Schubert 1195c1d255d3SCy Schubert fail: 1196c1d255d3SCy Schubert crypto_bignum_deinit(tmp, 1); 1197c1d255d3SCy Schubert crypto_bignum_deinit(val, 1); 1198c1d255d3SCy Schubert crypto_bignum_deinit(one, 0); 1199c1d255d3SCy Schubert return pwe; 1200c1d255d3SCy Schubert } 1201c1d255d3SCy Schubert 1202c1d255d3SCy Schubert 1203c1d255d3SCy Schubert struct crypto_bignum * 1204c1d255d3SCy Schubert sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt, 1205c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2) 1206c1d255d3SCy Schubert { 1207c1d255d3SCy Schubert size_t prime_len; 1208c1d255d3SCy Schubert const u8 *addr[2]; 1209c1d255d3SCy Schubert size_t len[2]; 1210c1d255d3SCy Schubert u8 salt[64], hash[64]; 1211c1d255d3SCy Schubert size_t hash_len; 1212c1d255d3SCy Schubert struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL; 1213c1d255d3SCy Schubert struct crypto_bignum *pwe = NULL, *order = NULL, *prime = NULL; 1214c1d255d3SCy Schubert 1215c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT"); 1216c1d255d3SCy Schubert prime = crypto_bignum_init_set(pt->dh->prime, pt->dh->prime_len); 1217c1d255d3SCy Schubert order = crypto_bignum_init_set(pt->dh->order, pt->dh->order_len); 1218c1d255d3SCy Schubert if (!prime || !order) 1219c1d255d3SCy Schubert goto fail; 1220c1d255d3SCy Schubert prime_len = pt->dh->prime_len; 1221c1d255d3SCy Schubert 1222c1d255d3SCy Schubert sae_max_min_addr(addr, len, addr1, addr2); 1223c1d255d3SCy Schubert 1224c1d255d3SCy Schubert /* val = H(0^n, 1225c1d255d3SCy Schubert * MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */ 1226c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))"); 1227c1d255d3SCy Schubert hash_len = sae_ffc_prime_len_2_hash_len(prime_len); 1228c1d255d3SCy Schubert os_memset(salt, 0, hash_len); 1229c1d255d3SCy Schubert if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0) 1230c1d255d3SCy Schubert goto fail; 1231c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len); 1232c1d255d3SCy Schubert 1233c1d255d3SCy Schubert /* val = val modulo (q - 1) + 1 */ 1234c1d255d3SCy Schubert tmp = crypto_bignum_init(); 1235c1d255d3SCy Schubert val = crypto_bignum_init_set(hash, hash_len); 1236c1d255d3SCy Schubert one = crypto_bignum_init_uint(1); 1237c1d255d3SCy Schubert if (!tmp || !val || !one || 1238c1d255d3SCy Schubert crypto_bignum_sub(order, one, tmp) < 0 || 1239c1d255d3SCy Schubert crypto_bignum_mod(val, tmp, val) < 0 || 1240c1d255d3SCy Schubert crypto_bignum_add(val, one, val) < 0) 1241c1d255d3SCy Schubert goto fail; 1242c1d255d3SCy Schubert debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len); 1243c1d255d3SCy Schubert 1244c1d255d3SCy Schubert /* PWE = scalar-op(val, PT) */ 1245c1d255d3SCy Schubert pwe = crypto_bignum_init(); 1246c1d255d3SCy Schubert if (!pwe || crypto_bignum_exptmod(pt->ffc_pt, val, prime, pwe) < 0) { 1247c1d255d3SCy Schubert crypto_bignum_deinit(pwe, 1); 1248c1d255d3SCy Schubert pwe = NULL; 1249c1d255d3SCy Schubert goto fail; 1250c1d255d3SCy Schubert } 1251c1d255d3SCy Schubert debug_print_bignum("SAE: PWE", pwe, prime_len); 1252c1d255d3SCy Schubert 1253c1d255d3SCy Schubert fail: 1254c1d255d3SCy Schubert crypto_bignum_deinit(tmp, 1); 1255c1d255d3SCy Schubert crypto_bignum_deinit(val, 1); 1256c1d255d3SCy Schubert crypto_bignum_deinit(one, 0); 1257c1d255d3SCy Schubert crypto_bignum_deinit(prime, 0); 1258c1d255d3SCy Schubert crypto_bignum_deinit(order, 0); 1259c1d255d3SCy Schubert return pwe; 1260c1d255d3SCy Schubert } 1261c1d255d3SCy Schubert 1262c1d255d3SCy Schubert 1263c1d255d3SCy Schubert void sae_deinit_pt(struct sae_pt *pt) 1264c1d255d3SCy Schubert { 1265c1d255d3SCy Schubert struct sae_pt *prev; 1266c1d255d3SCy Schubert 1267c1d255d3SCy Schubert while (pt) { 1268c1d255d3SCy Schubert crypto_ec_point_deinit(pt->ecc_pt, 1); 1269c1d255d3SCy Schubert crypto_bignum_deinit(pt->ffc_pt, 1); 1270c1d255d3SCy Schubert crypto_ec_deinit(pt->ec); 1271c1d255d3SCy Schubert prev = pt; 1272c1d255d3SCy Schubert pt = pt->next; 1273c1d255d3SCy Schubert os_free(prev); 1274c1d255d3SCy Schubert } 1275c1d255d3SCy Schubert } 1276c1d255d3SCy Schubert 1277c1d255d3SCy Schubert 12785b9c547cSRui Paulo static int sae_derive_commit_element_ecc(struct sae_data *sae, 12795b9c547cSRui Paulo struct crypto_bignum *mask) 12805b9c547cSRui Paulo { 12815b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 12825b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) { 12835b9c547cSRui Paulo sae->tmp->own_commit_element_ecc = 12845b9c547cSRui Paulo crypto_ec_point_init(sae->tmp->ec); 12855b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) 12865b9c547cSRui Paulo return -1; 12875b9c547cSRui Paulo } 12885b9c547cSRui Paulo 12895b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, 12905b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0 || 12915b9c547cSRui Paulo crypto_ec_point_invert(sae->tmp->ec, 12925b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0) { 12935b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 12945b9c547cSRui Paulo return -1; 12955b9c547cSRui Paulo } 12965b9c547cSRui Paulo 12975b9c547cSRui Paulo return 0; 12985b9c547cSRui Paulo } 12995b9c547cSRui Paulo 13005b9c547cSRui Paulo 13015b9c547cSRui Paulo static int sae_derive_commit_element_ffc(struct sae_data *sae, 13025b9c547cSRui Paulo struct crypto_bignum *mask) 13035b9c547cSRui Paulo { 13045b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 13055b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) { 13065b9c547cSRui Paulo sae->tmp->own_commit_element_ffc = crypto_bignum_init(); 13075b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) 13085b9c547cSRui Paulo return -1; 13095b9c547cSRui Paulo } 13105b9c547cSRui Paulo 13115b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, 13125b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0 || 13135b9c547cSRui Paulo crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, 13145b9c547cSRui Paulo sae->tmp->prime, 13155b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0) { 13165b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 13175b9c547cSRui Paulo return -1; 13185b9c547cSRui Paulo } 13195b9c547cSRui Paulo 13205b9c547cSRui Paulo return 0; 13215b9c547cSRui Paulo } 13225b9c547cSRui Paulo 13235b9c547cSRui Paulo 13245b9c547cSRui Paulo static int sae_derive_commit(struct sae_data *sae) 13255b9c547cSRui Paulo { 13265b9c547cSRui Paulo struct crypto_bignum *mask; 1327206b73d0SCy Schubert int ret; 1328325151a3SRui Paulo 1329206b73d0SCy Schubert mask = crypto_bignum_init(); 1330206b73d0SCy Schubert if (!sae->tmp->sae_rand) 1331206b73d0SCy Schubert sae->tmp->sae_rand = crypto_bignum_init(); 13325b9c547cSRui Paulo if (!sae->tmp->own_commit_scalar) 1333206b73d0SCy Schubert sae->tmp->own_commit_scalar = crypto_bignum_init(); 1334206b73d0SCy Schubert ret = !mask || !sae->tmp->sae_rand || !sae->tmp->own_commit_scalar || 1335206b73d0SCy Schubert dragonfly_generate_scalar(sae->tmp->order, sae->tmp->sae_rand, 1336206b73d0SCy Schubert mask, 1337206b73d0SCy Schubert sae->tmp->own_commit_scalar) < 0 || 1338206b73d0SCy Schubert (sae->tmp->ec && 1339206b73d0SCy Schubert sae_derive_commit_element_ecc(sae, mask) < 0) || 1340206b73d0SCy Schubert (sae->tmp->dh && 1341206b73d0SCy Schubert sae_derive_commit_element_ffc(sae, mask) < 0); 13425b9c547cSRui Paulo crypto_bignum_deinit(mask, 1); 1343206b73d0SCy Schubert return ret ? -1 : 0; 13445b9c547cSRui Paulo } 13455b9c547cSRui Paulo 13465b9c547cSRui Paulo 13475b9c547cSRui Paulo int sae_prepare_commit(const u8 *addr1, const u8 *addr2, 13485b9c547cSRui Paulo const u8 *password, size_t password_len, 1349c1d255d3SCy Schubert struct sae_data *sae) 13505b9c547cSRui Paulo { 1351325151a3SRui Paulo if (sae->tmp == NULL || 1352325151a3SRui Paulo (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, 1353c1d255d3SCy Schubert password_len) < 0) || 1354325151a3SRui Paulo (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, 1355c1d255d3SCy Schubert password_len) < 0)) 13565b9c547cSRui Paulo return -1; 1357c1d255d3SCy Schubert 1358c1d255d3SCy Schubert sae->h2e = 0; 1359c1d255d3SCy Schubert sae->pk = 0; 1360c1d255d3SCy Schubert return sae_derive_commit(sae); 1361c1d255d3SCy Schubert } 1362c1d255d3SCy Schubert 1363c1d255d3SCy Schubert 1364c1d255d3SCy Schubert int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt, 1365c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2, 1366c1d255d3SCy Schubert int *rejected_groups, const struct sae_pk *pk) 1367c1d255d3SCy Schubert { 1368c1d255d3SCy Schubert if (!sae->tmp) 1369c1d255d3SCy Schubert return -1; 1370c1d255d3SCy Schubert 1371c1d255d3SCy Schubert while (pt) { 1372c1d255d3SCy Schubert if (pt->group == sae->group) 1373c1d255d3SCy Schubert break; 1374c1d255d3SCy Schubert pt = pt->next; 1375c1d255d3SCy Schubert } 1376c1d255d3SCy Schubert if (!pt) { 1377c1d255d3SCy Schubert wpa_printf(MSG_INFO, "SAE: Could not find PT for group %u", 1378c1d255d3SCy Schubert sae->group); 1379c1d255d3SCy Schubert return -1; 1380c1d255d3SCy Schubert } 1381c1d255d3SCy Schubert 1382c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1383c1d255d3SCy Schubert os_memcpy(sae->tmp->ssid, pt->ssid, pt->ssid_len); 1384c1d255d3SCy Schubert sae->tmp->ssid_len = pt->ssid_len; 1385c1d255d3SCy Schubert sae->tmp->ap_pk = pk; 1386c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 1387c1d255d3SCy Schubert sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0; 1388c1d255d3SCy Schubert wpabuf_free(sae->tmp->own_rejected_groups); 1389c1d255d3SCy Schubert sae->tmp->own_rejected_groups = NULL; 1390c1d255d3SCy Schubert if (rejected_groups) { 1391c1d255d3SCy Schubert int count, i; 1392c1d255d3SCy Schubert struct wpabuf *groups; 1393c1d255d3SCy Schubert 1394c1d255d3SCy Schubert count = int_array_len(rejected_groups); 1395c1d255d3SCy Schubert groups = wpabuf_alloc(count * 2); 1396c1d255d3SCy Schubert if (!groups) 1397c1d255d3SCy Schubert return -1; 1398c1d255d3SCy Schubert for (i = 0; i < count; i++) 1399c1d255d3SCy Schubert wpabuf_put_le16(groups, rejected_groups[i]); 1400c1d255d3SCy Schubert sae->tmp->own_rejected_groups = groups; 1401c1d255d3SCy Schubert } 1402c1d255d3SCy Schubert 1403c1d255d3SCy Schubert if (pt->ec) { 1404c1d255d3SCy Schubert crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1); 1405c1d255d3SCy Schubert sae->tmp->pwe_ecc = sae_derive_pwe_from_pt_ecc(pt, addr1, 1406c1d255d3SCy Schubert addr2); 1407c1d255d3SCy Schubert if (!sae->tmp->pwe_ecc) 1408c1d255d3SCy Schubert return -1; 1409c1d255d3SCy Schubert } 1410c1d255d3SCy Schubert 1411c1d255d3SCy Schubert if (pt->dh) { 1412c1d255d3SCy Schubert crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); 1413c1d255d3SCy Schubert sae->tmp->pwe_ffc = sae_derive_pwe_from_pt_ffc(pt, addr1, 1414c1d255d3SCy Schubert addr2); 1415c1d255d3SCy Schubert if (!sae->tmp->pwe_ffc) 1416c1d255d3SCy Schubert return -1; 1417c1d255d3SCy Schubert } 1418c1d255d3SCy Schubert 1419c1d255d3SCy Schubert sae->h2e = 1; 1420c1d255d3SCy Schubert return sae_derive_commit(sae); 14215b9c547cSRui Paulo } 14225b9c547cSRui Paulo 14235b9c547cSRui Paulo 14245b9c547cSRui Paulo static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) 14255b9c547cSRui Paulo { 14265b9c547cSRui Paulo struct crypto_ec_point *K; 14275b9c547cSRui Paulo int ret = -1; 14285b9c547cSRui Paulo 14295b9c547cSRui Paulo K = crypto_ec_point_init(sae->tmp->ec); 14305b9c547cSRui Paulo if (K == NULL) 14315b9c547cSRui Paulo goto fail; 14325b9c547cSRui Paulo 14335b9c547cSRui Paulo /* 14345b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 14355b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 14365b9c547cSRui Paulo * If K is identity element (point-at-infinity), reject 14375b9c547cSRui Paulo * k = F(K) (= x coordinate) 14385b9c547cSRui Paulo */ 14395b9c547cSRui Paulo 14405b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, 14415b9c547cSRui Paulo sae->peer_commit_scalar, K) < 0 || 14425b9c547cSRui Paulo crypto_ec_point_add(sae->tmp->ec, K, 14435b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, K) < 0 || 14445b9c547cSRui Paulo crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || 14455b9c547cSRui Paulo crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || 14465b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { 14475b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 14485b9c547cSRui Paulo goto fail; 14495b9c547cSRui Paulo } 14505b9c547cSRui Paulo 14515b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 14525b9c547cSRui Paulo 14535b9c547cSRui Paulo ret = 0; 14545b9c547cSRui Paulo fail: 14555b9c547cSRui Paulo crypto_ec_point_deinit(K, 1); 14565b9c547cSRui Paulo return ret; 14575b9c547cSRui Paulo } 14585b9c547cSRui Paulo 14595b9c547cSRui Paulo 14605b9c547cSRui Paulo static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) 14615b9c547cSRui Paulo { 14625b9c547cSRui Paulo struct crypto_bignum *K; 14635b9c547cSRui Paulo int ret = -1; 14645b9c547cSRui Paulo 14655b9c547cSRui Paulo K = crypto_bignum_init(); 14665b9c547cSRui Paulo if (K == NULL) 14675b9c547cSRui Paulo goto fail; 14685b9c547cSRui Paulo 14695b9c547cSRui Paulo /* 14705b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 14715b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 14725b9c547cSRui Paulo * If K is identity element (one), reject. 14735b9c547cSRui Paulo * k = F(K) (= x coordinate) 14745b9c547cSRui Paulo */ 14755b9c547cSRui Paulo 14765b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, 14775b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 14785b9c547cSRui Paulo crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, 14795b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 14805b9c547cSRui Paulo crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 14815b9c547cSRui Paulo || 14825b9c547cSRui Paulo crypto_bignum_is_one(K) || 14835b9c547cSRui Paulo crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < 14845b9c547cSRui Paulo 0) { 14855b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 14865b9c547cSRui Paulo goto fail; 14875b9c547cSRui Paulo } 14885b9c547cSRui Paulo 14895b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 14905b9c547cSRui Paulo 14915b9c547cSRui Paulo ret = 0; 14925b9c547cSRui Paulo fail: 14935b9c547cSRui Paulo crypto_bignum_deinit(K, 1); 14945b9c547cSRui Paulo return ret; 14955b9c547cSRui Paulo } 14965b9c547cSRui Paulo 14975b9c547cSRui Paulo 1498c1d255d3SCy Schubert static int sae_kdf_hash(size_t hash_len, const u8 *k, const char *label, 1499c1d255d3SCy Schubert const u8 *context, size_t context_len, 1500c1d255d3SCy Schubert u8 *out, size_t out_len) 1501c1d255d3SCy Schubert { 1502c1d255d3SCy Schubert if (hash_len == 32) 1503c1d255d3SCy Schubert return sha256_prf(k, hash_len, label, 1504c1d255d3SCy Schubert context, context_len, out, out_len); 1505c1d255d3SCy Schubert #ifdef CONFIG_SHA384 1506c1d255d3SCy Schubert if (hash_len == 48) 1507c1d255d3SCy Schubert return sha384_prf(k, hash_len, label, 1508c1d255d3SCy Schubert context, context_len, out, out_len); 1509c1d255d3SCy Schubert #endif /* CONFIG_SHA384 */ 1510c1d255d3SCy Schubert #ifdef CONFIG_SHA512 1511c1d255d3SCy Schubert if (hash_len == 64) 1512c1d255d3SCy Schubert return sha512_prf(k, hash_len, label, 1513c1d255d3SCy Schubert context, context_len, out, out_len); 1514c1d255d3SCy Schubert #endif /* CONFIG_SHA512 */ 1515c1d255d3SCy Schubert return -1; 1516c1d255d3SCy Schubert } 1517c1d255d3SCy Schubert 1518c1d255d3SCy Schubert 15195b9c547cSRui Paulo static int sae_derive_keys(struct sae_data *sae, const u8 *k) 15205b9c547cSRui Paulo { 1521c1d255d3SCy Schubert u8 zero[SAE_MAX_HASH_LEN], val[SAE_MAX_PRIME_LEN]; 1522c1d255d3SCy Schubert const u8 *salt; 1523c1d255d3SCy Schubert struct wpabuf *rejected_groups = NULL; 1524c1d255d3SCy Schubert u8 keyseed[SAE_MAX_HASH_LEN]; 1525*a90b9d01SCy Schubert u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN_MAX]; 15265b9c547cSRui Paulo struct crypto_bignum *tmp; 15275b9c547cSRui Paulo int ret = -1; 1528c1d255d3SCy Schubert size_t hash_len, salt_len, prime_len = sae->tmp->prime_len; 1529*a90b9d01SCy Schubert size_t pmk_len; 1530c1d255d3SCy Schubert const u8 *addr[1]; 1531c1d255d3SCy Schubert size_t len[1]; 15325b9c547cSRui Paulo 15335b9c547cSRui Paulo tmp = crypto_bignum_init(); 15345b9c547cSRui Paulo if (tmp == NULL) 15355b9c547cSRui Paulo goto fail; 15365b9c547cSRui Paulo 1537c1d255d3SCy Schubert /* keyseed = H(salt, k) 1538c1d255d3SCy Schubert * KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK", 15395b9c547cSRui Paulo * (commit-scalar + peer-commit-scalar) modulo r) 15405b9c547cSRui Paulo * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) 1541c1d255d3SCy Schubert * 1542c1d255d3SCy Schubert * When SAE-PK is used, 1543c1d255d3SCy Schubert * KCK || PMK || KEK = KDF-Hash-Length(keyseed, "SAE-PK keys", context) 15445b9c547cSRui Paulo */ 1545c1d255d3SCy Schubert if (!sae->h2e) 1546c1d255d3SCy Schubert hash_len = SHA256_MAC_LEN; 1547c1d255d3SCy Schubert else if (sae->tmp->dh) 1548c1d255d3SCy Schubert hash_len = sae_ffc_prime_len_2_hash_len(prime_len); 1549c1d255d3SCy Schubert else 1550c1d255d3SCy Schubert hash_len = sae_ecc_prime_len_2_hash_len(prime_len); 1551*a90b9d01SCy Schubert if (wpa_key_mgmt_sae_ext_key(sae->akmp)) 1552*a90b9d01SCy Schubert pmk_len = hash_len; 1553*a90b9d01SCy Schubert else 1554*a90b9d01SCy Schubert pmk_len = SAE_PMK_LEN; 1555*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive keys - H2E=%d AKMP=0x%x = %08x (%s)", 1556*a90b9d01SCy Schubert sae->h2e, sae->akmp, 1557*a90b9d01SCy Schubert wpa_akm_to_suite(sae->akmp), 1558*a90b9d01SCy Schubert wpa_key_mgmt_txt(sae->akmp, WPA_PROTO_RSN)); 1559c1d255d3SCy Schubert if (sae->h2e && (sae->tmp->own_rejected_groups || 1560c1d255d3SCy Schubert sae->tmp->peer_rejected_groups)) { 1561c1d255d3SCy Schubert struct wpabuf *own, *peer; 15625b9c547cSRui Paulo 1563c1d255d3SCy Schubert own = sae->tmp->own_rejected_groups; 1564c1d255d3SCy Schubert peer = sae->tmp->peer_rejected_groups; 1565c1d255d3SCy Schubert salt_len = 0; 1566c1d255d3SCy Schubert if (own) 1567c1d255d3SCy Schubert salt_len += wpabuf_len(own); 1568c1d255d3SCy Schubert if (peer) 1569c1d255d3SCy Schubert salt_len += wpabuf_len(peer); 1570c1d255d3SCy Schubert rejected_groups = wpabuf_alloc(salt_len); 1571c1d255d3SCy Schubert if (!rejected_groups) 1572c1d255d3SCy Schubert goto fail; 1573c1d255d3SCy Schubert if (sae->tmp->own_addr_higher) { 1574c1d255d3SCy Schubert if (own) 1575c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, own); 1576c1d255d3SCy Schubert if (peer) 1577c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, peer); 1578c1d255d3SCy Schubert } else { 1579c1d255d3SCy Schubert if (peer) 1580c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, peer); 1581c1d255d3SCy Schubert if (own) 1582c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, own); 1583c1d255d3SCy Schubert } 1584c1d255d3SCy Schubert salt = wpabuf_head(rejected_groups); 1585c1d255d3SCy Schubert salt_len = wpabuf_len(rejected_groups); 1586c1d255d3SCy Schubert } else { 1587c1d255d3SCy Schubert os_memset(zero, 0, hash_len); 1588c1d255d3SCy Schubert salt = zero; 1589c1d255d3SCy Schubert salt_len = hash_len; 1590c1d255d3SCy Schubert } 1591c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: salt for keyseed derivation", 1592c1d255d3SCy Schubert salt, salt_len); 1593c1d255d3SCy Schubert addr[0] = k; 1594c1d255d3SCy Schubert len[0] = prime_len; 1595c1d255d3SCy Schubert if (hkdf_extract(hash_len, salt, salt_len, 1, addr, len, keyseed) < 0) 1596c1d255d3SCy Schubert goto fail; 1597c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, hash_len); 15985b9c547cSRui Paulo 1599c1d255d3SCy Schubert if (crypto_bignum_add(sae->tmp->own_commit_scalar, 1600c1d255d3SCy Schubert sae->peer_commit_scalar, tmp) < 0 || 1601c1d255d3SCy Schubert crypto_bignum_mod(tmp, sae->tmp->order, tmp) < 0) 1602c1d255d3SCy Schubert goto fail; 1603206b73d0SCy Schubert /* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit 1604206b73d0SCy Schubert * string that is needed for KCK, PMK, and PMKID derivation, but it 1605206b73d0SCy Schubert * seems to make most sense to encode the 1606206b73d0SCy Schubert * (commit-scalar + peer-commit-scalar) mod r part as a bit string by 1607206b73d0SCy Schubert * zero padding it from left to the length of the order (in full 1608206b73d0SCy Schubert * octets). */ 1609*a90b9d01SCy Schubert if (crypto_bignum_to_bin(tmp, val, sizeof(val), 1610*a90b9d01SCy Schubert sae->tmp->order_len) < 0) 1611*a90b9d01SCy Schubert goto fail; 16125b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); 1613c1d255d3SCy Schubert 1614c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1615c1d255d3SCy Schubert if (sae->pk) { 1616c1d255d3SCy Schubert if (sae_kdf_hash(hash_len, keyseed, "SAE-PK keys", 1617c1d255d3SCy Schubert val, sae->tmp->order_len, 1618*a90b9d01SCy Schubert keys, 2 * hash_len + pmk_len) < 0) 1619780fb4a2SCy Schubert goto fail; 1620c1d255d3SCy Schubert } else { 1621c1d255d3SCy Schubert if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK", 1622c1d255d3SCy Schubert val, sae->tmp->order_len, 1623*a90b9d01SCy Schubert keys, hash_len + pmk_len) < 0) 1624c1d255d3SCy Schubert goto fail; 1625c1d255d3SCy Schubert } 1626c1d255d3SCy Schubert #else /* CONFIG_SAE_PK */ 1627c1d255d3SCy Schubert if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK", 1628c1d255d3SCy Schubert val, sae->tmp->order_len, 1629*a90b9d01SCy Schubert keys, hash_len + pmk_len) < 0) 1630c1d255d3SCy Schubert goto fail; 1631c1d255d3SCy Schubert #endif /* !CONFIG_SAE_PK */ 1632c1d255d3SCy Schubert 1633c1d255d3SCy Schubert forced_memzero(keyseed, sizeof(keyseed)); 1634c1d255d3SCy Schubert os_memcpy(sae->tmp->kck, keys, hash_len); 1635c1d255d3SCy Schubert sae->tmp->kck_len = hash_len; 1636*a90b9d01SCy Schubert os_memcpy(sae->pmk, keys + hash_len, pmk_len); 1637*a90b9d01SCy Schubert sae->pmk_len = pmk_len; 1638780fb4a2SCy Schubert os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); 1639c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 1640c1d255d3SCy Schubert if (sae->pk) { 1641c1d255d3SCy Schubert os_memcpy(sae->tmp->kek, keys + hash_len + SAE_PMK_LEN, 1642c1d255d3SCy Schubert hash_len); 1643c1d255d3SCy Schubert sae->tmp->kek_len = hash_len; 1644c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: KEK for SAE-PK", 1645c1d255d3SCy Schubert sae->tmp->kek, sae->tmp->kek_len); 1646c1d255d3SCy Schubert } 1647c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 1648c1d255d3SCy Schubert forced_memzero(keys, sizeof(keys)); 1649c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", 1650c1d255d3SCy Schubert sae->tmp->kck, sae->tmp->kck_len); 1651*a90b9d01SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, sae->pmk_len); 16525b9c547cSRui Paulo 16535b9c547cSRui Paulo ret = 0; 16545b9c547cSRui Paulo fail: 1655c1d255d3SCy Schubert wpabuf_free(rejected_groups); 16565b9c547cSRui Paulo crypto_bignum_deinit(tmp, 0); 16575b9c547cSRui Paulo return ret; 16585b9c547cSRui Paulo } 16595b9c547cSRui Paulo 16605b9c547cSRui Paulo 16615b9c547cSRui Paulo int sae_process_commit(struct sae_data *sae) 16625b9c547cSRui Paulo { 16635b9c547cSRui Paulo u8 k[SAE_MAX_PRIME_LEN]; 16645b9c547cSRui Paulo if (sae->tmp == NULL || 16655b9c547cSRui Paulo (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || 16665b9c547cSRui Paulo (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || 16675b9c547cSRui Paulo sae_derive_keys(sae, k) < 0) 16685b9c547cSRui Paulo return -1; 16695b9c547cSRui Paulo return 0; 16705b9c547cSRui Paulo } 16715b9c547cSRui Paulo 16725b9c547cSRui Paulo 1673c1d255d3SCy Schubert int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, 167485732ac8SCy Schubert const struct wpabuf *token, const char *identifier) 16755b9c547cSRui Paulo { 16765b9c547cSRui Paulo u8 *pos; 16775b9c547cSRui Paulo 16785b9c547cSRui Paulo if (sae->tmp == NULL) 1679c1d255d3SCy Schubert return -1; 16805b9c547cSRui Paulo 16815b9c547cSRui Paulo wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ 1682c1d255d3SCy Schubert if (!sae->h2e && token) { 16835b9c547cSRui Paulo wpabuf_put_buf(buf, token); 16845b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", 16855b9c547cSRui Paulo wpabuf_head(token), wpabuf_len(token)); 16865b9c547cSRui Paulo } 16875b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 1688c1d255d3SCy Schubert if (crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, 1689c1d255d3SCy Schubert sae->tmp->prime_len, sae->tmp->prime_len) < 0) 1690c1d255d3SCy Schubert return -1; 16915b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", 16925b9c547cSRui Paulo pos, sae->tmp->prime_len); 16935b9c547cSRui Paulo if (sae->tmp->ec) { 16945b9c547cSRui Paulo pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); 1695c1d255d3SCy Schubert if (crypto_ec_point_to_bin(sae->tmp->ec, 16965b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 1697c1d255d3SCy Schubert pos, pos + sae->tmp->prime_len) < 0) 1698c1d255d3SCy Schubert return -1; 16995b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", 17005b9c547cSRui Paulo pos, sae->tmp->prime_len); 17015b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", 17025b9c547cSRui Paulo pos + sae->tmp->prime_len, sae->tmp->prime_len); 17035b9c547cSRui Paulo } else { 17045b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 1705c1d255d3SCy Schubert if (crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, 1706c1d255d3SCy Schubert sae->tmp->prime_len, 1707c1d255d3SCy Schubert sae->tmp->prime_len) < 0) 1708c1d255d3SCy Schubert return -1; 17095b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", 17105b9c547cSRui Paulo pos, sae->tmp->prime_len); 17115b9c547cSRui Paulo } 171285732ac8SCy Schubert 171385732ac8SCy Schubert if (identifier) { 171485732ac8SCy Schubert /* Password Identifier element */ 171585732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 171685732ac8SCy Schubert wpabuf_put_u8(buf, 1 + os_strlen(identifier)); 171785732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); 171885732ac8SCy Schubert wpabuf_put_str(buf, identifier); 171985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", 172085732ac8SCy Schubert identifier); 172185732ac8SCy Schubert } 1722c1d255d3SCy Schubert 1723c1d255d3SCy Schubert if (sae->h2e && sae->tmp->own_rejected_groups) { 1724c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups", 1725c1d255d3SCy Schubert sae->tmp->own_rejected_groups); 1726c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 1727c1d255d3SCy Schubert wpabuf_put_u8(buf, 1728c1d255d3SCy Schubert 1 + wpabuf_len(sae->tmp->own_rejected_groups)); 1729c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_REJECTED_GROUPS); 1730c1d255d3SCy Schubert wpabuf_put_buf(buf, sae->tmp->own_rejected_groups); 1731c1d255d3SCy Schubert } 1732c1d255d3SCy Schubert 1733c1d255d3SCy Schubert if (sae->h2e && token) { 1734c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 1735c1d255d3SCy Schubert wpabuf_put_u8(buf, 1 + wpabuf_len(token)); 1736c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN); 1737c1d255d3SCy Schubert wpabuf_put_buf(buf, token); 1738c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, 1739c1d255d3SCy Schubert "SAE: Anti-clogging token (in container)", 1740c1d255d3SCy Schubert token); 1741c1d255d3SCy Schubert } 1742c1d255d3SCy Schubert 1743*a90b9d01SCy Schubert if (wpa_key_mgmt_sae_ext_key(sae->akmp)) { 1744*a90b9d01SCy Schubert u32 suite = wpa_akm_to_suite(sae->akmp); 1745*a90b9d01SCy Schubert 1746*a90b9d01SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 1747*a90b9d01SCy Schubert wpabuf_put_u8(buf, 1 + RSN_SELECTOR_LEN); 1748*a90b9d01SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_AKM_SUITE_SELECTOR); 1749*a90b9d01SCy Schubert RSN_SELECTOR_PUT(wpabuf_put(buf, RSN_SELECTOR_LEN), suite); 1750*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "SAE: AKM Suite Selector: %08x", suite); 1751*a90b9d01SCy Schubert sae->own_akm_suite_selector = suite; 1752*a90b9d01SCy Schubert } 1753*a90b9d01SCy Schubert 1754c1d255d3SCy Schubert return 0; 17555b9c547cSRui Paulo } 17565b9c547cSRui Paulo 17575b9c547cSRui Paulo 17585b9c547cSRui Paulo u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) 17595b9c547cSRui Paulo { 17605b9c547cSRui Paulo if (allowed_groups) { 17615b9c547cSRui Paulo int i; 17625b9c547cSRui Paulo for (i = 0; allowed_groups[i] > 0; i++) { 17635b9c547cSRui Paulo if (allowed_groups[i] == group) 17645b9c547cSRui Paulo break; 17655b9c547cSRui Paulo } 17665b9c547cSRui Paulo if (allowed_groups[i] != group) { 17675b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " 17685b9c547cSRui Paulo "enabled in the current configuration", 17695b9c547cSRui Paulo group); 17705b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 17715b9c547cSRui Paulo } 17725b9c547cSRui Paulo } 17735b9c547cSRui Paulo 17745b9c547cSRui Paulo if (sae->state == SAE_COMMITTED && group != sae->group) { 17755b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); 17765b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 17775b9c547cSRui Paulo } 17785b9c547cSRui Paulo 17795b9c547cSRui Paulo if (group != sae->group && sae_set_group(sae, group) < 0) { 17805b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", 17815b9c547cSRui Paulo group); 17825b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 17835b9c547cSRui Paulo } 17845b9c547cSRui Paulo 17855b9c547cSRui Paulo if (sae->tmp == NULL) { 17865b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); 17875b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 17885b9c547cSRui Paulo } 17895b9c547cSRui Paulo 17905b9c547cSRui Paulo if (sae->tmp->dh && !allowed_groups) { 17915b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " 17925b9c547cSRui Paulo "explicit configuration enabling it", group); 17935b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 17945b9c547cSRui Paulo } 17955b9c547cSRui Paulo 17965b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 17975b9c547cSRui Paulo } 17985b9c547cSRui Paulo 17995b9c547cSRui Paulo 180085732ac8SCy Schubert static int sae_is_password_id_elem(const u8 *pos, const u8 *end) 180185732ac8SCy Schubert { 180285732ac8SCy Schubert return end - pos >= 3 && 180385732ac8SCy Schubert pos[0] == WLAN_EID_EXTENSION && 180485732ac8SCy Schubert pos[1] >= 1 && 180585732ac8SCy Schubert end - pos - 2 >= pos[1] && 180685732ac8SCy Schubert pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; 180785732ac8SCy Schubert } 180885732ac8SCy Schubert 180985732ac8SCy Schubert 1810c1d255d3SCy Schubert static int sae_is_rejected_groups_elem(const u8 *pos, const u8 *end) 1811c1d255d3SCy Schubert { 1812c1d255d3SCy Schubert return end - pos >= 3 && 1813c1d255d3SCy Schubert pos[0] == WLAN_EID_EXTENSION && 1814c1d255d3SCy Schubert pos[1] >= 2 && 1815c1d255d3SCy Schubert end - pos - 2 >= pos[1] && 1816c1d255d3SCy Schubert pos[2] == WLAN_EID_EXT_REJECTED_GROUPS; 1817c1d255d3SCy Schubert } 1818c1d255d3SCy Schubert 1819c1d255d3SCy Schubert 1820c1d255d3SCy Schubert static int sae_is_token_container_elem(const u8 *pos, const u8 *end) 1821c1d255d3SCy Schubert { 1822c1d255d3SCy Schubert return end - pos >= 3 && 1823c1d255d3SCy Schubert pos[0] == WLAN_EID_EXTENSION && 1824c1d255d3SCy Schubert pos[1] >= 1 && 1825c1d255d3SCy Schubert end - pos - 2 >= pos[1] && 1826c1d255d3SCy Schubert pos[2] == WLAN_EID_EXT_ANTI_CLOGGING_TOKEN; 1827c1d255d3SCy Schubert } 1828c1d255d3SCy Schubert 1829c1d255d3SCy Schubert 1830*a90b9d01SCy Schubert static int sae_is_akm_suite_selector_elem(const u8 *pos, const u8 *end) 1831*a90b9d01SCy Schubert { 1832*a90b9d01SCy Schubert return end - pos >= 2 + 1 + RSN_SELECTOR_LEN && 1833*a90b9d01SCy Schubert pos[0] == WLAN_EID_EXTENSION && 1834*a90b9d01SCy Schubert pos[1] >= 1 + RSN_SELECTOR_LEN && 1835*a90b9d01SCy Schubert end - pos - 2 >= pos[1] && 1836*a90b9d01SCy Schubert pos[2] == WLAN_EID_EXT_AKM_SUITE_SELECTOR; 1837*a90b9d01SCy Schubert } 1838*a90b9d01SCy Schubert 1839*a90b9d01SCy Schubert 18405b9c547cSRui Paulo static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, 18415b9c547cSRui Paulo const u8 *end, const u8 **token, 1842c1d255d3SCy Schubert size_t *token_len, int h2e) 18435b9c547cSRui Paulo { 184485732ac8SCy Schubert size_t scalar_elem_len, tlen; 184585732ac8SCy Schubert 184685732ac8SCy Schubert if (token) 184785732ac8SCy Schubert *token = NULL; 184885732ac8SCy Schubert if (token_len) 184985732ac8SCy Schubert *token_len = 0; 185085732ac8SCy Schubert 1851c1d255d3SCy Schubert if (h2e) 1852c1d255d3SCy Schubert return; /* No Anti-Clogging Token field outside container IE */ 1853c1d255d3SCy Schubert 185485732ac8SCy Schubert scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; 185585732ac8SCy Schubert if (scalar_elem_len >= (size_t) (end - *pos)) 185685732ac8SCy Schubert return; /* No extra data beyond peer scalar and element */ 185785732ac8SCy Schubert 185885732ac8SCy Schubert tlen = end - (*pos + scalar_elem_len); 185985732ac8SCy Schubert 186085732ac8SCy Schubert if (tlen < SHA256_MAC_LEN) { 186185732ac8SCy Schubert wpa_printf(MSG_DEBUG, 186285732ac8SCy Schubert "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", 186385732ac8SCy Schubert (unsigned int) tlen); 186485732ac8SCy Schubert return; 186585732ac8SCy Schubert } 186685732ac8SCy Schubert 18675b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); 18685b9c547cSRui Paulo if (token) 18695b9c547cSRui Paulo *token = *pos; 18705b9c547cSRui Paulo if (token_len) 18715b9c547cSRui Paulo *token_len = tlen; 18725b9c547cSRui Paulo *pos += tlen; 18735b9c547cSRui Paulo } 18745b9c547cSRui Paulo 18755b9c547cSRui Paulo 1876c1d255d3SCy Schubert static void sae_parse_token_container(struct sae_data *sae, 1877c1d255d3SCy Schubert const u8 *pos, const u8 *end, 1878c1d255d3SCy Schubert const u8 **token, size_t *token_len) 1879c1d255d3SCy Schubert { 1880c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 1881c1d255d3SCy Schubert pos, end - pos); 1882c1d255d3SCy Schubert if (!sae_is_token_container_elem(pos, end)) 1883c1d255d3SCy Schubert return; 1884c1d255d3SCy Schubert *token = pos + 3; 1885c1d255d3SCy Schubert *token_len = pos[1] - 1; 1886c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token (in container)", 1887c1d255d3SCy Schubert *token, *token_len); 1888c1d255d3SCy Schubert } 1889c1d255d3SCy Schubert 1890c1d255d3SCy Schubert 18915b9c547cSRui Paulo static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, 18925b9c547cSRui Paulo const u8 *end) 18935b9c547cSRui Paulo { 18945b9c547cSRui Paulo struct crypto_bignum *peer_scalar; 18955b9c547cSRui Paulo 1896780fb4a2SCy Schubert if (sae->tmp->prime_len > end - *pos) { 18975b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); 18985b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 18995b9c547cSRui Paulo } 19005b9c547cSRui Paulo 19015b9c547cSRui Paulo peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); 19025b9c547cSRui Paulo if (peer_scalar == NULL) 19035b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19045b9c547cSRui Paulo 19055b9c547cSRui Paulo /* 19065b9c547cSRui Paulo * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for 19075b9c547cSRui Paulo * the peer and it is in Authenticated state, the new Commit Message 19085b9c547cSRui Paulo * shall be dropped if the peer-scalar is identical to the one used in 19095b9c547cSRui Paulo * the existing protocol instance. 19105b9c547cSRui Paulo */ 1911c1d255d3SCy Schubert if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar_accepted && 1912c1d255d3SCy Schubert crypto_bignum_cmp(sae->peer_commit_scalar_accepted, 1913c1d255d3SCy Schubert peer_scalar) == 0) { 19145b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " 19155b9c547cSRui Paulo "peer-commit-scalar"); 19165b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 19175b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19185b9c547cSRui Paulo } 19195b9c547cSRui Paulo 1920325151a3SRui Paulo /* 1 < scalar < r */ 19215b9c547cSRui Paulo if (crypto_bignum_is_zero(peer_scalar) || 1922325151a3SRui Paulo crypto_bignum_is_one(peer_scalar) || 19235b9c547cSRui Paulo crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { 19245b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); 19255b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 19265b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19275b9c547cSRui Paulo } 19285b9c547cSRui Paulo 19295b9c547cSRui Paulo 19305b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 19315b9c547cSRui Paulo sae->peer_commit_scalar = peer_scalar; 19325b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", 19335b9c547cSRui Paulo *pos, sae->tmp->prime_len); 19345b9c547cSRui Paulo *pos += sae->tmp->prime_len; 19355b9c547cSRui Paulo 19365b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 19375b9c547cSRui Paulo } 19385b9c547cSRui Paulo 19395b9c547cSRui Paulo 194085732ac8SCy Schubert static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, 19415b9c547cSRui Paulo const u8 *end) 19425b9c547cSRui Paulo { 19435b9c547cSRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 19445b9c547cSRui Paulo 194585732ac8SCy Schubert if (2 * sae->tmp->prime_len > end - *pos) { 19465b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 19475b9c547cSRui Paulo "commit-element"); 19485b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19495b9c547cSRui Paulo } 19505b9c547cSRui Paulo 19515b9c547cSRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 19525b9c547cSRui Paulo sae->tmp->prime_len) < 0) 19535b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19545b9c547cSRui Paulo 19555b9c547cSRui Paulo /* element x and y coordinates < p */ 195685732ac8SCy Schubert if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || 195785732ac8SCy Schubert os_memcmp(*pos + sae->tmp->prime_len, prime, 19585b9c547cSRui Paulo sae->tmp->prime_len) >= 0) { 19595b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " 19605b9c547cSRui Paulo "element"); 19615b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19625b9c547cSRui Paulo } 19635b9c547cSRui Paulo 19645b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", 196585732ac8SCy Schubert *pos, sae->tmp->prime_len); 19665b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", 196785732ac8SCy Schubert *pos + sae->tmp->prime_len, sae->tmp->prime_len); 19685b9c547cSRui Paulo 19695b9c547cSRui Paulo crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); 19705b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc = 197185732ac8SCy Schubert crypto_ec_point_from_bin(sae->tmp->ec, *pos); 1972*a90b9d01SCy Schubert if (!sae->tmp->peer_commit_element_ecc) { 1973*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Peer element is not a valid point"); 19745b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 1975*a90b9d01SCy Schubert } 19765b9c547cSRui Paulo 19775b9c547cSRui Paulo if (!crypto_ec_point_is_on_curve(sae->tmp->ec, 19785b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc)) { 19795b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); 19805b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19815b9c547cSRui Paulo } 19825b9c547cSRui Paulo 198385732ac8SCy Schubert *pos += 2 * sae->tmp->prime_len; 198485732ac8SCy Schubert 19855b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 19865b9c547cSRui Paulo } 19875b9c547cSRui Paulo 19885b9c547cSRui Paulo 198985732ac8SCy Schubert static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, 19905b9c547cSRui Paulo const u8 *end) 19915b9c547cSRui Paulo { 1992325151a3SRui Paulo struct crypto_bignum *res, *one; 1993325151a3SRui Paulo const u8 one_bin[1] = { 0x01 }; 19945b9c547cSRui Paulo 199585732ac8SCy Schubert if (sae->tmp->prime_len > end - *pos) { 19965b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 19975b9c547cSRui Paulo "commit-element"); 19985b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 19995b9c547cSRui Paulo } 200085732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, 20015b9c547cSRui Paulo sae->tmp->prime_len); 20025b9c547cSRui Paulo 20035b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); 20045b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc = 200585732ac8SCy Schubert crypto_bignum_init_set(*pos, sae->tmp->prime_len); 20065b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ffc == NULL) 20075b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 2008325151a3SRui Paulo /* 1 < element < p - 1 */ 2009325151a3SRui Paulo res = crypto_bignum_init(); 2010325151a3SRui Paulo one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); 2011325151a3SRui Paulo if (!res || !one || 2012325151a3SRui Paulo crypto_bignum_sub(sae->tmp->prime, one, res) || 2013325151a3SRui Paulo crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || 20145b9c547cSRui Paulo crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || 2015325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { 2016325151a3SRui Paulo crypto_bignum_deinit(res, 0); 2017325151a3SRui Paulo crypto_bignum_deinit(one, 0); 20185b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); 20195b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 20205b9c547cSRui Paulo } 2021325151a3SRui Paulo crypto_bignum_deinit(one, 0); 20225b9c547cSRui Paulo 20235b9c547cSRui Paulo /* scalar-op(r, ELEMENT) = 1 modulo p */ 2024325151a3SRui Paulo if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, 20255b9c547cSRui Paulo sae->tmp->order, sae->tmp->prime, res) < 0 || 20265b9c547cSRui Paulo !crypto_bignum_is_one(res)) { 20275b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); 20285b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 20295b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 20305b9c547cSRui Paulo } 20315b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 20325b9c547cSRui Paulo 203385732ac8SCy Schubert *pos += sae->tmp->prime_len; 203485732ac8SCy Schubert 20355b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 20365b9c547cSRui Paulo } 20375b9c547cSRui Paulo 20385b9c547cSRui Paulo 203985732ac8SCy Schubert static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, 20405b9c547cSRui Paulo const u8 *end) 20415b9c547cSRui Paulo { 20425b9c547cSRui Paulo if (sae->tmp->dh) 20435b9c547cSRui Paulo return sae_parse_commit_element_ffc(sae, pos, end); 20445b9c547cSRui Paulo return sae_parse_commit_element_ecc(sae, pos, end); 20455b9c547cSRui Paulo } 20465b9c547cSRui Paulo 20475b9c547cSRui Paulo 204885732ac8SCy Schubert static int sae_parse_password_identifier(struct sae_data *sae, 2049c1d255d3SCy Schubert const u8 **pos, const u8 *end) 205085732ac8SCy Schubert { 2051c1d255d3SCy Schubert const u8 *epos; 2052c1d255d3SCy Schubert u8 len; 2053c1d255d3SCy Schubert 205485732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 2055c1d255d3SCy Schubert *pos, end - *pos); 2056c1d255d3SCy Schubert if (!sae_is_password_id_elem(*pos, end)) { 205785732ac8SCy Schubert if (sae->tmp->pw_id) { 205885732ac8SCy Schubert wpa_printf(MSG_DEBUG, 205985732ac8SCy Schubert "SAE: No Password Identifier included, but expected one (%s)", 206085732ac8SCy Schubert sae->tmp->pw_id); 206185732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 206285732ac8SCy Schubert } 206385732ac8SCy Schubert os_free(sae->tmp->pw_id); 206485732ac8SCy Schubert sae->tmp->pw_id = NULL; 206585732ac8SCy Schubert return WLAN_STATUS_SUCCESS; /* No Password Identifier */ 206685732ac8SCy Schubert } 206785732ac8SCy Schubert 2068c1d255d3SCy Schubert epos = *pos; 2069c1d255d3SCy Schubert epos++; /* skip IE type */ 2070c1d255d3SCy Schubert len = *epos++; /* IE length */ 2071c1d255d3SCy Schubert if (len > end - epos || len < 1) 2072c1d255d3SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2073c1d255d3SCy Schubert epos++; /* skip ext ID */ 2074c1d255d3SCy Schubert len--; 2075c1d255d3SCy Schubert 207685732ac8SCy Schubert if (sae->tmp->pw_id && 2077c1d255d3SCy Schubert (len != os_strlen(sae->tmp->pw_id) || 2078c1d255d3SCy Schubert os_memcmp(sae->tmp->pw_id, epos, len) != 0)) { 207985732ac8SCy Schubert wpa_printf(MSG_DEBUG, 208085732ac8SCy Schubert "SAE: The included Password Identifier does not match the expected one (%s)", 208185732ac8SCy Schubert sae->tmp->pw_id); 208285732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 208385732ac8SCy Schubert } 208485732ac8SCy Schubert 208585732ac8SCy Schubert os_free(sae->tmp->pw_id); 2086c1d255d3SCy Schubert sae->tmp->pw_id = os_malloc(len + 1); 208785732ac8SCy Schubert if (!sae->tmp->pw_id) 208885732ac8SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2089c1d255d3SCy Schubert os_memcpy(sae->tmp->pw_id, epos, len); 2090c1d255d3SCy Schubert sae->tmp->pw_id[len] = '\0'; 209185732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier", 2092c1d255d3SCy Schubert sae->tmp->pw_id, len); 2093c1d255d3SCy Schubert *pos = epos + len; 2094c1d255d3SCy Schubert return WLAN_STATUS_SUCCESS; 2095c1d255d3SCy Schubert } 2096c1d255d3SCy Schubert 2097c1d255d3SCy Schubert 2098c1d255d3SCy Schubert static int sae_parse_rejected_groups(struct sae_data *sae, 2099c1d255d3SCy Schubert const u8 **pos, const u8 *end) 2100c1d255d3SCy Schubert { 2101c1d255d3SCy Schubert const u8 *epos; 2102c1d255d3SCy Schubert u8 len; 2103c1d255d3SCy Schubert 2104c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 2105c1d255d3SCy Schubert *pos, end - *pos); 2106*a90b9d01SCy Schubert if (!sae_is_rejected_groups_elem(*pos, end)) { 2107*a90b9d01SCy Schubert wpabuf_free(sae->tmp->peer_rejected_groups); 2108*a90b9d01SCy Schubert sae->tmp->peer_rejected_groups = NULL; 2109c1d255d3SCy Schubert return WLAN_STATUS_SUCCESS; 2110*a90b9d01SCy Schubert } 2111c1d255d3SCy Schubert 2112c1d255d3SCy Schubert epos = *pos; 2113c1d255d3SCy Schubert epos++; /* skip IE type */ 2114c1d255d3SCy Schubert len = *epos++; /* IE length */ 2115c1d255d3SCy Schubert if (len > end - epos || len < 1) 2116c1d255d3SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2117c1d255d3SCy Schubert epos++; /* skip ext ID */ 2118c1d255d3SCy Schubert len--; 2119*a90b9d01SCy Schubert if (len & 1) { 2120*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 2121*a90b9d01SCy Schubert "SAE: Invalid length of the Rejected Groups element payload: %u", 2122*a90b9d01SCy Schubert len); 2123*a90b9d01SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2124*a90b9d01SCy Schubert } 2125c1d255d3SCy Schubert 2126c1d255d3SCy Schubert wpabuf_free(sae->tmp->peer_rejected_groups); 2127c1d255d3SCy Schubert sae->tmp->peer_rejected_groups = wpabuf_alloc(len); 2128c1d255d3SCy Schubert if (!sae->tmp->peer_rejected_groups) 2129c1d255d3SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2130c1d255d3SCy Schubert wpabuf_put_data(sae->tmp->peer_rejected_groups, epos, len); 2131c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "SAE: Received Rejected Groups list", 2132c1d255d3SCy Schubert sae->tmp->peer_rejected_groups); 2133c1d255d3SCy Schubert *pos = epos + len; 213485732ac8SCy Schubert return WLAN_STATUS_SUCCESS; 213585732ac8SCy Schubert } 213685732ac8SCy Schubert 213785732ac8SCy Schubert 2138*a90b9d01SCy Schubert static int sae_parse_akm_suite_selector(struct sae_data *sae, 2139*a90b9d01SCy Schubert const u8 **pos, const u8 *end) 2140*a90b9d01SCy Schubert { 2141*a90b9d01SCy Schubert const u8 *epos; 2142*a90b9d01SCy Schubert u8 len; 2143*a90b9d01SCy Schubert 2144*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 2145*a90b9d01SCy Schubert *pos, end - *pos); 2146*a90b9d01SCy Schubert if (!sae_is_akm_suite_selector_elem(*pos, end)) 2147*a90b9d01SCy Schubert return WLAN_STATUS_SUCCESS; 2148*a90b9d01SCy Schubert 2149*a90b9d01SCy Schubert epos = *pos; 2150*a90b9d01SCy Schubert epos++; /* skip IE type */ 2151*a90b9d01SCy Schubert len = *epos++; /* IE length */ 2152*a90b9d01SCy Schubert if (len > end - epos || len < 1) 2153*a90b9d01SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2154*a90b9d01SCy Schubert epos++; /* skip ext ID */ 2155*a90b9d01SCy Schubert len--; 2156*a90b9d01SCy Schubert 2157*a90b9d01SCy Schubert if (len < RSN_SELECTOR_LEN) 2158*a90b9d01SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2159*a90b9d01SCy Schubert sae->peer_akm_suite_selector = RSN_SELECTOR_GET(epos); 2160*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Received AKM Suite Selector: %08x", 2161*a90b9d01SCy Schubert sae->peer_akm_suite_selector); 2162*a90b9d01SCy Schubert *pos = epos + len; 2163*a90b9d01SCy Schubert return WLAN_STATUS_SUCCESS; 2164*a90b9d01SCy Schubert } 2165*a90b9d01SCy Schubert 2166*a90b9d01SCy Schubert 21675b9c547cSRui Paulo u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, 2168c1d255d3SCy Schubert const u8 **token, size_t *token_len, int *allowed_groups, 2169*a90b9d01SCy Schubert int h2e, int *ie_offset) 21705b9c547cSRui Paulo { 21715b9c547cSRui Paulo const u8 *pos = data, *end = data + len; 21725b9c547cSRui Paulo u16 res; 21735b9c547cSRui Paulo 21745b9c547cSRui Paulo /* Check Finite Cyclic Group */ 2175780fb4a2SCy Schubert if (end - pos < 2) 21765b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 21775b9c547cSRui Paulo res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); 21785b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 21795b9c547cSRui Paulo return res; 21805b9c547cSRui Paulo pos += 2; 21815b9c547cSRui Paulo 21825b9c547cSRui Paulo /* Optional Anti-Clogging Token */ 2183c1d255d3SCy Schubert sae_parse_commit_token(sae, &pos, end, token, token_len, h2e); 21845b9c547cSRui Paulo 21855b9c547cSRui Paulo /* commit-scalar */ 21865b9c547cSRui Paulo res = sae_parse_commit_scalar(sae, &pos, end); 21875b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 21885b9c547cSRui Paulo return res; 21895b9c547cSRui Paulo 21905b9c547cSRui Paulo /* commit-element */ 219185732ac8SCy Schubert res = sae_parse_commit_element(sae, &pos, end); 219285732ac8SCy Schubert if (res != WLAN_STATUS_SUCCESS) 219385732ac8SCy Schubert return res; 219485732ac8SCy Schubert 2195*a90b9d01SCy Schubert if (ie_offset) 2196*a90b9d01SCy Schubert *ie_offset = pos - data; 2197*a90b9d01SCy Schubert 219885732ac8SCy Schubert /* Optional Password Identifier element */ 2199c1d255d3SCy Schubert res = sae_parse_password_identifier(sae, &pos, end); 2200325151a3SRui Paulo if (res != WLAN_STATUS_SUCCESS) 2201325151a3SRui Paulo return res; 2202325151a3SRui Paulo 2203c1d255d3SCy Schubert /* Conditional Rejected Groups element */ 2204c1d255d3SCy Schubert if (h2e) { 2205c1d255d3SCy Schubert res = sae_parse_rejected_groups(sae, &pos, end); 2206c1d255d3SCy Schubert if (res != WLAN_STATUS_SUCCESS) 2207c1d255d3SCy Schubert return res; 2208*a90b9d01SCy Schubert } else { 2209*a90b9d01SCy Schubert wpabuf_free(sae->tmp->peer_rejected_groups); 2210*a90b9d01SCy Schubert sae->tmp->peer_rejected_groups = NULL; 2211c1d255d3SCy Schubert } 2212c1d255d3SCy Schubert 2213c1d255d3SCy Schubert /* Optional Anti-Clogging Token Container element */ 2214c1d255d3SCy Schubert if (h2e) 2215c1d255d3SCy Schubert sae_parse_token_container(sae, pos, end, token, token_len); 2216c1d255d3SCy Schubert 2217*a90b9d01SCy Schubert /* Conditional AKM Suite Selector element */ 2218*a90b9d01SCy Schubert if (h2e) { 2219*a90b9d01SCy Schubert res = sae_parse_akm_suite_selector(sae, &pos, end); 2220*a90b9d01SCy Schubert if (res != WLAN_STATUS_SUCCESS) 2221*a90b9d01SCy Schubert return res; 2222*a90b9d01SCy Schubert } 2223*a90b9d01SCy Schubert 2224*a90b9d01SCy Schubert if (sae->own_akm_suite_selector && 2225*a90b9d01SCy Schubert sae->own_akm_suite_selector != sae->peer_akm_suite_selector) { 2226*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, 2227*a90b9d01SCy Schubert "SAE: AKM suite selector mismatch: own=%08x peer=%08x", 2228*a90b9d01SCy Schubert sae->own_akm_suite_selector, 2229*a90b9d01SCy Schubert sae->peer_akm_suite_selector); 2230*a90b9d01SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 2231*a90b9d01SCy Schubert } 2232*a90b9d01SCy Schubert 2233*a90b9d01SCy Schubert if (!sae->akmp) { 2234*a90b9d01SCy Schubert if (sae->peer_akm_suite_selector == 2235*a90b9d01SCy Schubert RSN_AUTH_KEY_MGMT_SAE_EXT_KEY) 2236*a90b9d01SCy Schubert sae->akmp = WPA_KEY_MGMT_SAE_EXT_KEY; 2237*a90b9d01SCy Schubert else if (sae->peer_akm_suite_selector == 2238*a90b9d01SCy Schubert RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY) 2239*a90b9d01SCy Schubert sae->akmp = WPA_KEY_MGMT_FT_SAE_EXT_KEY; 2240*a90b9d01SCy Schubert } 2241*a90b9d01SCy Schubert 2242325151a3SRui Paulo /* 2243325151a3SRui Paulo * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as 2244325151a3SRui Paulo * the values we sent which would be evidence of a reflection attack. 2245325151a3SRui Paulo */ 2246325151a3SRui Paulo if (!sae->tmp->own_commit_scalar || 2247325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_scalar, 2248325151a3SRui Paulo sae->peer_commit_scalar) != 0 || 2249325151a3SRui Paulo (sae->tmp->dh && 2250325151a3SRui Paulo (!sae->tmp->own_commit_element_ffc || 2251325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, 2252325151a3SRui Paulo sae->tmp->peer_commit_element_ffc) != 0)) || 2253325151a3SRui Paulo (sae->tmp->ec && 2254325151a3SRui Paulo (!sae->tmp->own_commit_element_ecc || 2255325151a3SRui Paulo crypto_ec_point_cmp(sae->tmp->ec, 2256325151a3SRui Paulo sae->tmp->own_commit_element_ecc, 2257325151a3SRui Paulo sae->tmp->peer_commit_element_ecc) != 0))) 2258325151a3SRui Paulo return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ 2259325151a3SRui Paulo 2260325151a3SRui Paulo /* 2261325151a3SRui Paulo * This is a reflection attack - return special value to trigger caller 2262325151a3SRui Paulo * to silently discard the frame instead of replying with a specific 2263325151a3SRui Paulo * status code. 2264325151a3SRui Paulo */ 2265325151a3SRui Paulo return SAE_SILENTLY_DISCARD; 22665b9c547cSRui Paulo } 22675b9c547cSRui Paulo 22685b9c547cSRui Paulo 2269c1d255d3SCy Schubert static int sae_cn_confirm(struct sae_data *sae, const u8 *sc, 22705b9c547cSRui Paulo const struct crypto_bignum *scalar1, 22715b9c547cSRui Paulo const u8 *element1, size_t element1_len, 22725b9c547cSRui Paulo const struct crypto_bignum *scalar2, 22735b9c547cSRui Paulo const u8 *element2, size_t element2_len, 22745b9c547cSRui Paulo u8 *confirm) 22755b9c547cSRui Paulo { 22765b9c547cSRui Paulo const u8 *addr[5]; 22775b9c547cSRui Paulo size_t len[5]; 22785b9c547cSRui Paulo u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; 22795b9c547cSRui Paulo 22805b9c547cSRui Paulo /* Confirm 22815b9c547cSRui Paulo * CN(key, X, Y, Z, ...) = 22825b9c547cSRui Paulo * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) 22835b9c547cSRui Paulo * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, 22845b9c547cSRui Paulo * peer-commit-scalar, PEER-COMMIT-ELEMENT) 22855b9c547cSRui Paulo * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, 22865b9c547cSRui Paulo * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) 22875b9c547cSRui Paulo */ 2288c1d255d3SCy Schubert if (crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), 2289c1d255d3SCy Schubert sae->tmp->prime_len) < 0 || 2290c1d255d3SCy Schubert crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), 2291c1d255d3SCy Schubert sae->tmp->prime_len) < 0) 2292c1d255d3SCy Schubert return -1; 22935b9c547cSRui Paulo addr[0] = sc; 22945b9c547cSRui Paulo len[0] = 2; 22955b9c547cSRui Paulo addr[1] = scalar_b1; 22965b9c547cSRui Paulo len[1] = sae->tmp->prime_len; 22975b9c547cSRui Paulo addr[2] = element1; 22985b9c547cSRui Paulo len[2] = element1_len; 22995b9c547cSRui Paulo addr[3] = scalar_b2; 23005b9c547cSRui Paulo len[3] = sae->tmp->prime_len; 23015b9c547cSRui Paulo addr[4] = element2; 23025b9c547cSRui Paulo len[4] = element2_len; 2303c1d255d3SCy Schubert return hkdf_extract(sae->tmp->kck_len, sae->tmp->kck, sae->tmp->kck_len, 2304c1d255d3SCy Schubert 5, addr, len, confirm); 23055b9c547cSRui Paulo } 23065b9c547cSRui Paulo 23075b9c547cSRui Paulo 2308c1d255d3SCy Schubert static int sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, 23095b9c547cSRui Paulo const struct crypto_bignum *scalar1, 23105b9c547cSRui Paulo const struct crypto_ec_point *element1, 23115b9c547cSRui Paulo const struct crypto_bignum *scalar2, 23125b9c547cSRui Paulo const struct crypto_ec_point *element2, 23135b9c547cSRui Paulo u8 *confirm) 23145b9c547cSRui Paulo { 23155b9c547cSRui Paulo u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; 23165b9c547cSRui Paulo u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; 23175b9c547cSRui Paulo 2318c1d255d3SCy Schubert if (crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, 2319c1d255d3SCy Schubert element_b1 + sae->tmp->prime_len) < 0 || 23205b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, 2321c1d255d3SCy Schubert element_b2 + sae->tmp->prime_len) < 0 || 2322c1d255d3SCy Schubert sae_cn_confirm(sae, sc, scalar1, element_b1, 2323c1d255d3SCy Schubert 2 * sae->tmp->prime_len, 2324c1d255d3SCy Schubert scalar2, element_b2, 2 * sae->tmp->prime_len, 2325c1d255d3SCy Schubert confirm) < 0) 2326c1d255d3SCy Schubert return -1; 2327c1d255d3SCy Schubert return 0; 23285b9c547cSRui Paulo } 23295b9c547cSRui Paulo 23305b9c547cSRui Paulo 2331c1d255d3SCy Schubert static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, 23325b9c547cSRui Paulo const struct crypto_bignum *scalar1, 23335b9c547cSRui Paulo const struct crypto_bignum *element1, 23345b9c547cSRui Paulo const struct crypto_bignum *scalar2, 23355b9c547cSRui Paulo const struct crypto_bignum *element2, 23365b9c547cSRui Paulo u8 *confirm) 23375b9c547cSRui Paulo { 23385b9c547cSRui Paulo u8 element_b1[SAE_MAX_PRIME_LEN]; 23395b9c547cSRui Paulo u8 element_b2[SAE_MAX_PRIME_LEN]; 23405b9c547cSRui Paulo 2341c1d255d3SCy Schubert if (crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), 2342c1d255d3SCy Schubert sae->tmp->prime_len) < 0 || 23435b9c547cSRui Paulo crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), 2344c1d255d3SCy Schubert sae->tmp->prime_len) < 0 || 23455b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, 2346c1d255d3SCy Schubert scalar2, element_b2, sae->tmp->prime_len, 2347c1d255d3SCy Schubert confirm) < 0) 2348c1d255d3SCy Schubert return -1; 2349c1d255d3SCy Schubert return 0; 23505b9c547cSRui Paulo } 23515b9c547cSRui Paulo 23525b9c547cSRui Paulo 2353c1d255d3SCy Schubert int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) 23545b9c547cSRui Paulo { 23555b9c547cSRui Paulo const u8 *sc; 2356c1d255d3SCy Schubert size_t hash_len; 2357c1d255d3SCy Schubert int res; 23585b9c547cSRui Paulo 23595b9c547cSRui Paulo if (sae->tmp == NULL) 2360c1d255d3SCy Schubert return -1; 2361c1d255d3SCy Schubert 2362c1d255d3SCy Schubert hash_len = sae->tmp->kck_len; 23635b9c547cSRui Paulo 23645b9c547cSRui Paulo /* Send-Confirm */ 236585732ac8SCy Schubert if (sae->send_confirm < 0xffff) 23665b9c547cSRui Paulo sae->send_confirm++; 2367c1d255d3SCy Schubert sc = wpabuf_put(buf, 0); 2368c1d255d3SCy Schubert wpabuf_put_le16(buf, sae->send_confirm); 23695b9c547cSRui Paulo 23705b9c547cSRui Paulo if (sae->tmp->ec) 2371c1d255d3SCy Schubert res = sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, 23725b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 23735b9c547cSRui Paulo sae->peer_commit_scalar, 23745b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 2375c1d255d3SCy Schubert wpabuf_put(buf, hash_len)); 23765b9c547cSRui Paulo else 2377c1d255d3SCy Schubert res = sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, 23785b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 23795b9c547cSRui Paulo sae->peer_commit_scalar, 23805b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 2381c1d255d3SCy Schubert wpabuf_put(buf, hash_len)); 2382c1d255d3SCy Schubert if (res) 2383c1d255d3SCy Schubert return res; 2384c1d255d3SCy Schubert 2385c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 2386c1d255d3SCy Schubert if (sae_write_confirm_pk(sae, buf) < 0) 2387c1d255d3SCy Schubert return -1; 2388c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 2389c1d255d3SCy Schubert 2390c1d255d3SCy Schubert return 0; 23915b9c547cSRui Paulo } 23925b9c547cSRui Paulo 23935b9c547cSRui Paulo 2394*a90b9d01SCy Schubert int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len, 2395*a90b9d01SCy Schubert int *ie_offset) 23965b9c547cSRui Paulo { 2397c1d255d3SCy Schubert u8 verifier[SAE_MAX_HASH_LEN]; 2398c1d255d3SCy Schubert size_t hash_len; 23995b9c547cSRui Paulo 2400c1d255d3SCy Schubert if (!sae->tmp) 2401c1d255d3SCy Schubert return -1; 2402c1d255d3SCy Schubert 2403c1d255d3SCy Schubert hash_len = sae->tmp->kck_len; 2404c1d255d3SCy Schubert if (len < 2 + hash_len) { 24055b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); 24065b9c547cSRui Paulo return -1; 24075b9c547cSRui Paulo } 24085b9c547cSRui Paulo 24095b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); 24105b9c547cSRui Paulo 2411c1d255d3SCy Schubert if (!sae->peer_commit_scalar || !sae->tmp->own_commit_scalar) { 24125b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); 24135b9c547cSRui Paulo return -1; 24145b9c547cSRui Paulo } 24155b9c547cSRui Paulo 24164bc52338SCy Schubert if (sae->tmp->ec) { 24174bc52338SCy Schubert if (!sae->tmp->peer_commit_element_ecc || 2418c1d255d3SCy Schubert !sae->tmp->own_commit_element_ecc || 24195b9c547cSRui Paulo sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, 24205b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 24215b9c547cSRui Paulo sae->tmp->own_commit_scalar, 24225b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 2423c1d255d3SCy Schubert verifier) < 0) 2424c1d255d3SCy Schubert return -1; 24254bc52338SCy Schubert } else { 24264bc52338SCy Schubert if (!sae->tmp->peer_commit_element_ffc || 2427c1d255d3SCy Schubert !sae->tmp->own_commit_element_ffc || 24285b9c547cSRui Paulo sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, 24295b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 24305b9c547cSRui Paulo sae->tmp->own_commit_scalar, 24315b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 2432c1d255d3SCy Schubert verifier) < 0) 24335b9c547cSRui Paulo return -1; 24345b9c547cSRui Paulo } 24355b9c547cSRui Paulo 2436c1d255d3SCy Schubert if (os_memcmp_const(verifier, data + 2, hash_len) != 0) { 2437c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); 2438c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", 2439c1d255d3SCy Schubert data + 2, hash_len); 2440c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", 2441c1d255d3SCy Schubert verifier, hash_len); 2442c1d255d3SCy Schubert return -1; 2443c1d255d3SCy Schubert } 2444c1d255d3SCy Schubert 2445c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK 2446c1d255d3SCy Schubert if (sae_check_confirm_pk(sae, data + 2 + hash_len, 2447c1d255d3SCy Schubert len - 2 - hash_len) < 0) 2448c1d255d3SCy Schubert return -1; 2449c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */ 2450c1d255d3SCy Schubert 2451*a90b9d01SCy Schubert /* 2 bytes are for send-confirm, then the hash, followed by IEs */ 2452*a90b9d01SCy Schubert if (ie_offset) 2453*a90b9d01SCy Schubert *ie_offset = 2 + hash_len; 2454*a90b9d01SCy Schubert 24555b9c547cSRui Paulo return 0; 24565b9c547cSRui Paulo } 245785732ac8SCy Schubert 245885732ac8SCy Schubert 245985732ac8SCy Schubert const char * sae_state_txt(enum sae_state state) 246085732ac8SCy Schubert { 246185732ac8SCy Schubert switch (state) { 246285732ac8SCy Schubert case SAE_NOTHING: 246385732ac8SCy Schubert return "Nothing"; 246485732ac8SCy Schubert case SAE_COMMITTED: 246585732ac8SCy Schubert return "Committed"; 246685732ac8SCy Schubert case SAE_CONFIRMED: 246785732ac8SCy Schubert return "Confirmed"; 246885732ac8SCy Schubert case SAE_ACCEPTED: 246985732ac8SCy Schubert return "Accepted"; 247085732ac8SCy Schubert } 247185732ac8SCy Schubert return "?"; 247285732ac8SCy Schubert } 2473