15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * Simultaneous authentication of equals 3780fb4a2SCy Schubert * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi> 45b9c547cSRui Paulo * 55b9c547cSRui Paulo * This software may be distributed under the terms of the BSD license. 65b9c547cSRui Paulo * See README for more details. 75b9c547cSRui Paulo */ 85b9c547cSRui Paulo 95b9c547cSRui Paulo #include "includes.h" 105b9c547cSRui Paulo 115b9c547cSRui Paulo #include "common.h" 124bc52338SCy Schubert #include "utils/const_time.h" 135b9c547cSRui Paulo #include "crypto/crypto.h" 145b9c547cSRui Paulo #include "crypto/sha256.h" 155b9c547cSRui Paulo #include "crypto/random.h" 165b9c547cSRui Paulo #include "crypto/dh_groups.h" 175b9c547cSRui Paulo #include "ieee802_11_defs.h" 185b9c547cSRui Paulo #include "sae.h" 195b9c547cSRui Paulo 205b9c547cSRui Paulo 214bc52338SCy Schubert static int sae_suitable_group(int group) 224bc52338SCy Schubert { 234bc52338SCy Schubert #ifdef CONFIG_TESTING_OPTIONS 244bc52338SCy Schubert /* Allow all groups for testing purposes in non-production builds. */ 254bc52338SCy Schubert return 1; 264bc52338SCy Schubert #else /* CONFIG_TESTING_OPTIONS */ 274bc52338SCy Schubert /* Enforce REVmd rules on which SAE groups are suitable for production 284bc52338SCy Schubert * purposes: FFC groups whose prime is >= 3072 bits and ECC groups 294bc52338SCy Schubert * defined over a prime field whose prime is >= 256 bits. Furthermore, 304bc52338SCy Schubert * ECC groups defined over a characteristic 2 finite field and ECC 314bc52338SCy Schubert * groups with a co-factor greater than 1 are not suitable. */ 324bc52338SCy Schubert return group == 19 || group == 20 || group == 21 || 334bc52338SCy Schubert group == 28 || group == 29 || group == 30 || 344bc52338SCy Schubert group == 15 || group == 16 || group == 17 || group == 18; 354bc52338SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */ 364bc52338SCy Schubert } 374bc52338SCy Schubert 384bc52338SCy Schubert 395b9c547cSRui Paulo int sae_set_group(struct sae_data *sae, int group) 405b9c547cSRui Paulo { 415b9c547cSRui Paulo struct sae_temporary_data *tmp; 425b9c547cSRui Paulo 434bc52338SCy Schubert if (!sae_suitable_group(group)) { 444bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group); 454bc52338SCy Schubert return -1; 464bc52338SCy Schubert } 474bc52338SCy Schubert 485b9c547cSRui Paulo sae_clear_data(sae); 495b9c547cSRui Paulo tmp = sae->tmp = os_zalloc(sizeof(*tmp)); 505b9c547cSRui Paulo if (tmp == NULL) 515b9c547cSRui Paulo return -1; 525b9c547cSRui Paulo 535b9c547cSRui Paulo /* First, check if this is an ECC group */ 545b9c547cSRui Paulo tmp->ec = crypto_ec_init(group); 555b9c547cSRui Paulo if (tmp->ec) { 5685732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", 5785732ac8SCy Schubert group); 585b9c547cSRui Paulo sae->group = group; 595b9c547cSRui Paulo tmp->prime_len = crypto_ec_prime_len(tmp->ec); 605b9c547cSRui Paulo tmp->prime = crypto_ec_get_prime(tmp->ec); 615b9c547cSRui Paulo tmp->order = crypto_ec_get_order(tmp->ec); 625b9c547cSRui Paulo return 0; 635b9c547cSRui Paulo } 645b9c547cSRui Paulo 655b9c547cSRui Paulo /* Not an ECC group, check FFC */ 665b9c547cSRui Paulo tmp->dh = dh_groups_get(group); 675b9c547cSRui Paulo if (tmp->dh) { 6885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", 6985732ac8SCy Schubert group); 705b9c547cSRui Paulo sae->group = group; 715b9c547cSRui Paulo tmp->prime_len = tmp->dh->prime_len; 725b9c547cSRui Paulo if (tmp->prime_len > SAE_MAX_PRIME_LEN) { 735b9c547cSRui Paulo sae_clear_data(sae); 745b9c547cSRui Paulo return -1; 755b9c547cSRui Paulo } 765b9c547cSRui Paulo 775b9c547cSRui Paulo tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, 785b9c547cSRui Paulo tmp->prime_len); 795b9c547cSRui Paulo if (tmp->prime_buf == NULL) { 805b9c547cSRui Paulo sae_clear_data(sae); 815b9c547cSRui Paulo return -1; 825b9c547cSRui Paulo } 835b9c547cSRui Paulo tmp->prime = tmp->prime_buf; 845b9c547cSRui Paulo 855b9c547cSRui Paulo tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, 865b9c547cSRui Paulo tmp->dh->order_len); 875b9c547cSRui Paulo if (tmp->order_buf == NULL) { 885b9c547cSRui Paulo sae_clear_data(sae); 895b9c547cSRui Paulo return -1; 905b9c547cSRui Paulo } 915b9c547cSRui Paulo tmp->order = tmp->order_buf; 925b9c547cSRui Paulo 935b9c547cSRui Paulo return 0; 945b9c547cSRui Paulo } 955b9c547cSRui Paulo 965b9c547cSRui Paulo /* Unsupported group */ 9785732ac8SCy Schubert wpa_printf(MSG_DEBUG, 9885732ac8SCy Schubert "SAE: Group %d not supported by the crypto library", group); 995b9c547cSRui Paulo return -1; 1005b9c547cSRui Paulo } 1015b9c547cSRui Paulo 1025b9c547cSRui Paulo 1035b9c547cSRui Paulo void sae_clear_temp_data(struct sae_data *sae) 1045b9c547cSRui Paulo { 1055b9c547cSRui Paulo struct sae_temporary_data *tmp; 1065b9c547cSRui Paulo if (sae == NULL || sae->tmp == NULL) 1075b9c547cSRui Paulo return; 1085b9c547cSRui Paulo tmp = sae->tmp; 1095b9c547cSRui Paulo crypto_ec_deinit(tmp->ec); 1105b9c547cSRui Paulo crypto_bignum_deinit(tmp->prime_buf, 0); 1115b9c547cSRui Paulo crypto_bignum_deinit(tmp->order_buf, 0); 1125b9c547cSRui Paulo crypto_bignum_deinit(tmp->sae_rand, 1); 1135b9c547cSRui Paulo crypto_bignum_deinit(tmp->pwe_ffc, 1); 1145b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_scalar, 0); 1155b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); 1165b9c547cSRui Paulo crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); 1175b9c547cSRui Paulo crypto_ec_point_deinit(tmp->pwe_ecc, 1); 1185b9c547cSRui Paulo crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); 1195b9c547cSRui Paulo crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); 1205b9c547cSRui Paulo wpabuf_free(tmp->anti_clogging_token); 12185732ac8SCy Schubert os_free(tmp->pw_id); 1225b9c547cSRui Paulo bin_clear_free(tmp, sizeof(*tmp)); 1235b9c547cSRui Paulo sae->tmp = NULL; 1245b9c547cSRui Paulo } 1255b9c547cSRui Paulo 1265b9c547cSRui Paulo 1275b9c547cSRui Paulo void sae_clear_data(struct sae_data *sae) 1285b9c547cSRui Paulo { 1295b9c547cSRui Paulo if (sae == NULL) 1305b9c547cSRui Paulo return; 1315b9c547cSRui Paulo sae_clear_temp_data(sae); 1325b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 1335b9c547cSRui Paulo os_memset(sae, 0, sizeof(*sae)); 1345b9c547cSRui Paulo } 1355b9c547cSRui Paulo 1365b9c547cSRui Paulo 1375b9c547cSRui Paulo static void buf_shift_right(u8 *buf, size_t len, size_t bits) 1385b9c547cSRui Paulo { 1395b9c547cSRui Paulo size_t i; 1405b9c547cSRui Paulo for (i = len - 1; i > 0; i--) 1415b9c547cSRui Paulo buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); 1425b9c547cSRui Paulo buf[0] >>= bits; 1435b9c547cSRui Paulo } 1445b9c547cSRui Paulo 1455b9c547cSRui Paulo 1465b9c547cSRui Paulo static struct crypto_bignum * sae_get_rand(struct sae_data *sae) 1475b9c547cSRui Paulo { 1485b9c547cSRui Paulo u8 val[SAE_MAX_PRIME_LEN]; 1495b9c547cSRui Paulo int iter = 0; 1505b9c547cSRui Paulo struct crypto_bignum *bn = NULL; 1515b9c547cSRui Paulo int order_len_bits = crypto_bignum_bits(sae->tmp->order); 1525b9c547cSRui Paulo size_t order_len = (order_len_bits + 7) / 8; 1535b9c547cSRui Paulo 1545b9c547cSRui Paulo if (order_len > sizeof(val)) 1555b9c547cSRui Paulo return NULL; 1565b9c547cSRui Paulo 1575b9c547cSRui Paulo for (;;) { 158325151a3SRui Paulo if (iter++ > 100 || random_get_bytes(val, order_len) < 0) 1595b9c547cSRui Paulo return NULL; 1605b9c547cSRui Paulo if (order_len_bits % 8) 1615b9c547cSRui Paulo buf_shift_right(val, order_len, 8 - order_len_bits % 8); 1625b9c547cSRui Paulo bn = crypto_bignum_init_set(val, order_len); 1635b9c547cSRui Paulo if (bn == NULL) 1645b9c547cSRui Paulo return NULL; 1655b9c547cSRui Paulo if (crypto_bignum_is_zero(bn) || 1665b9c547cSRui Paulo crypto_bignum_is_one(bn) || 1675b9c547cSRui Paulo crypto_bignum_cmp(bn, sae->tmp->order) >= 0) { 1685b9c547cSRui Paulo crypto_bignum_deinit(bn, 0); 1695b9c547cSRui Paulo continue; 1705b9c547cSRui Paulo } 1715b9c547cSRui Paulo break; 1725b9c547cSRui Paulo } 1735b9c547cSRui Paulo 1745b9c547cSRui Paulo os_memset(val, 0, order_len); 1755b9c547cSRui Paulo return bn; 1765b9c547cSRui Paulo } 1775b9c547cSRui Paulo 1785b9c547cSRui Paulo 1795b9c547cSRui Paulo static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) 1805b9c547cSRui Paulo { 1815b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->sae_rand, 1); 1825b9c547cSRui Paulo sae->tmp->sae_rand = sae_get_rand(sae); 1835b9c547cSRui Paulo if (sae->tmp->sae_rand == NULL) 1845b9c547cSRui Paulo return NULL; 1855b9c547cSRui Paulo return sae_get_rand(sae); 1865b9c547cSRui Paulo } 1875b9c547cSRui Paulo 1885b9c547cSRui Paulo 1895b9c547cSRui Paulo static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) 1905b9c547cSRui Paulo { 1915b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR 1925b9c547cSRui Paulo " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); 1935b9c547cSRui Paulo if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { 1945b9c547cSRui Paulo os_memcpy(key, addr1, ETH_ALEN); 1955b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); 1965b9c547cSRui Paulo } else { 1975b9c547cSRui Paulo os_memcpy(key, addr2, ETH_ALEN); 1985b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); 1995b9c547cSRui Paulo } 2005b9c547cSRui Paulo } 2015b9c547cSRui Paulo 2025b9c547cSRui Paulo 203325151a3SRui Paulo static struct crypto_bignum * 204325151a3SRui Paulo get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, 205325151a3SRui Paulo int *r_odd) 2065b9c547cSRui Paulo { 207325151a3SRui Paulo for (;;) { 208325151a3SRui Paulo struct crypto_bignum *r; 209325151a3SRui Paulo u8 tmp[SAE_MAX_ECC_PRIME_LEN]; 210325151a3SRui Paulo 211325151a3SRui Paulo if (random_get_bytes(tmp, prime_len) < 0) 212325151a3SRui Paulo break; 213325151a3SRui Paulo if (prime_bits % 8) 214325151a3SRui Paulo buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); 215325151a3SRui Paulo if (os_memcmp(tmp, prime, prime_len) >= 0) 216325151a3SRui Paulo continue; 217325151a3SRui Paulo r = crypto_bignum_init_set(tmp, prime_len); 218325151a3SRui Paulo if (!r) 219325151a3SRui Paulo break; 220325151a3SRui Paulo if (crypto_bignum_is_zero(r)) { 221325151a3SRui Paulo crypto_bignum_deinit(r, 0); 222325151a3SRui Paulo continue; 223325151a3SRui Paulo } 224325151a3SRui Paulo 225325151a3SRui Paulo *r_odd = tmp[prime_len - 1] & 0x01; 226325151a3SRui Paulo return r; 227325151a3SRui Paulo } 228325151a3SRui Paulo 229325151a3SRui Paulo return NULL; 230325151a3SRui Paulo } 231325151a3SRui Paulo 232325151a3SRui Paulo 233325151a3SRui Paulo static int is_quadratic_residue_blind(struct sae_data *sae, 234325151a3SRui Paulo const u8 *prime, size_t bits, 2354bc52338SCy Schubert const u8 *qr, const u8 *qnr, 236325151a3SRui Paulo const struct crypto_bignum *y_sqr) 237325151a3SRui Paulo { 2384bc52338SCy Schubert struct crypto_bignum *r, *num, *qr_or_qnr = NULL; 239325151a3SRui Paulo int r_odd, check, res = -1; 2404bc52338SCy Schubert u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN]; 2414bc52338SCy Schubert size_t prime_len = sae->tmp->prime_len; 2424bc52338SCy Schubert unsigned int mask; 243325151a3SRui Paulo 244325151a3SRui Paulo /* 245325151a3SRui Paulo * Use the blinding technique to mask y_sqr while determining 246325151a3SRui Paulo * whether it is a quadratic residue modulo p to avoid leaking 247325151a3SRui Paulo * timing information while determining the Legendre symbol. 248325151a3SRui Paulo * 249325151a3SRui Paulo * v = y_sqr 250325151a3SRui Paulo * r = a random number between 1 and p-1, inclusive 251325151a3SRui Paulo * num = (v * r * r) modulo p 252325151a3SRui Paulo */ 2534bc52338SCy Schubert r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd); 254325151a3SRui Paulo if (!r) 255325151a3SRui Paulo return -1; 256325151a3SRui Paulo 257325151a3SRui Paulo num = crypto_bignum_init(); 258325151a3SRui Paulo if (!num || 259325151a3SRui Paulo crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 || 260325151a3SRui Paulo crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) 261325151a3SRui Paulo goto fail; 262325151a3SRui Paulo 263325151a3SRui Paulo /* 2644bc52338SCy Schubert * Need to minimize differences in handling different cases, so try to 2654bc52338SCy Schubert * avoid branches and timing differences. 2664bc52338SCy Schubert * 2674bc52338SCy Schubert * If r_odd: 268325151a3SRui Paulo * num = (num * qr) module p 269325151a3SRui Paulo * LGR(num, p) = 1 ==> quadratic residue 2704bc52338SCy Schubert * else: 271325151a3SRui Paulo * num = (num * qnr) module p 272325151a3SRui Paulo * LGR(num, p) = -1 ==> quadratic residue 273325151a3SRui Paulo */ 2744bc52338SCy Schubert mask = const_time_is_zero(r_odd); 2754bc52338SCy Schubert const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin); 2764bc52338SCy Schubert qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len); 2774bc52338SCy Schubert if (!qr_or_qnr || 2784bc52338SCy Schubert crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0) 279325151a3SRui Paulo goto fail; 2804bc52338SCy Schubert /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */ 2814bc52338SCy Schubert check = const_time_select_int(mask, -1, 1); 282325151a3SRui Paulo 283325151a3SRui Paulo res = crypto_bignum_legendre(num, sae->tmp->prime); 284325151a3SRui Paulo if (res == -2) { 285325151a3SRui Paulo res = -1; 286325151a3SRui Paulo goto fail; 287325151a3SRui Paulo } 2884bc52338SCy Schubert /* branchless version of res = res == check 2894bc52338SCy Schubert * (res is -1, 0, or 1; check is -1 or 1) */ 2904bc52338SCy Schubert mask = const_time_eq(res, check); 2914bc52338SCy Schubert res = const_time_select_int(mask, 1, 0); 292325151a3SRui Paulo fail: 293325151a3SRui Paulo crypto_bignum_deinit(num, 1); 294325151a3SRui Paulo crypto_bignum_deinit(r, 1); 2954bc52338SCy Schubert crypto_bignum_deinit(qr_or_qnr, 1); 296325151a3SRui Paulo return res; 297325151a3SRui Paulo } 298325151a3SRui Paulo 299325151a3SRui Paulo 300325151a3SRui Paulo static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, 3014bc52338SCy Schubert const u8 *prime, const u8 *qr, const u8 *qnr, 3024bc52338SCy Schubert u8 *pwd_value) 303325151a3SRui Paulo { 304325151a3SRui Paulo struct crypto_bignum *y_sqr, *x_cand; 305325151a3SRui Paulo int res; 3065b9c547cSRui Paulo size_t bits; 3075b9c547cSRui Paulo 3085b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 3095b9c547cSRui Paulo 3105b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 3115b9c547cSRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec); 312780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 313780fb4a2SCy Schubert prime, sae->tmp->prime_len, pwd_value, bits) < 0) 314780fb4a2SCy Schubert return -1; 3155b9c547cSRui Paulo if (bits % 8) 3164bc52338SCy Schubert buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8); 3175b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", 3185b9c547cSRui Paulo pwd_value, sae->tmp->prime_len); 3195b9c547cSRui Paulo 3205b9c547cSRui Paulo if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) 3215b9c547cSRui Paulo return 0; 3225b9c547cSRui Paulo 323325151a3SRui Paulo x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 324325151a3SRui Paulo if (!x_cand) 3255b9c547cSRui Paulo return -1; 326325151a3SRui Paulo y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); 327325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 3284bc52338SCy Schubert if (!y_sqr) 329325151a3SRui Paulo return -1; 3305b9c547cSRui Paulo 331325151a3SRui Paulo res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); 332325151a3SRui Paulo crypto_bignum_deinit(y_sqr, 1); 333325151a3SRui Paulo return res; 334325151a3SRui Paulo } 3355b9c547cSRui Paulo 3365b9c547cSRui Paulo 3374bc52338SCy Schubert /* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided 3384bc52338SCy Schubert * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */ 3395b9c547cSRui Paulo static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, 3405b9c547cSRui Paulo struct crypto_bignum *pwe) 3415b9c547cSRui Paulo { 3425b9c547cSRui Paulo u8 pwd_value[SAE_MAX_PRIME_LEN]; 3435b9c547cSRui Paulo size_t bits = sae->tmp->prime_len * 8; 3445b9c547cSRui Paulo u8 exp[1]; 3454bc52338SCy Schubert struct crypto_bignum *a, *b = NULL; 3464bc52338SCy Schubert int res, is_val; 3474bc52338SCy Schubert u8 pwd_value_valid; 3485b9c547cSRui Paulo 3495b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 3505b9c547cSRui Paulo 3515b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 352780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 3535b9c547cSRui Paulo sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, 354780fb4a2SCy Schubert bits) < 0) 355780fb4a2SCy Schubert return -1; 3565b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, 3575b9c547cSRui Paulo sae->tmp->prime_len); 3585b9c547cSRui Paulo 3594bc52338SCy Schubert /* Check whether pwd-value < p */ 3604bc52338SCy Schubert res = const_time_memcmp(pwd_value, sae->tmp->dh->prime, 3614bc52338SCy Schubert sae->tmp->prime_len); 3624bc52338SCy Schubert /* pwd-value >= p is invalid, so res is < 0 for the valid cases and 3634bc52338SCy Schubert * the negative sign can be used to fill the mask for constant time 3644bc52338SCy Schubert * selection */ 3654bc52338SCy Schubert pwd_value_valid = const_time_fill_msb(res); 3664bc52338SCy Schubert 3674bc52338SCy Schubert /* If pwd-value >= p, force pwd-value to be < p and perform the 3684bc52338SCy Schubert * calculations anyway to hide timing difference. The derived PWE will 3694bc52338SCy Schubert * be ignored in that case. */ 3704bc52338SCy Schubert pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0); 3715b9c547cSRui Paulo 3725b9c547cSRui Paulo /* PWE = pwd-value^((p-1)/r) modulo p */ 3735b9c547cSRui Paulo 3744bc52338SCy Schubert res = -1; 3755b9c547cSRui Paulo a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 3764bc52338SCy Schubert if (!a) 3774bc52338SCy Schubert goto fail; 3785b9c547cSRui Paulo 3794bc52338SCy Schubert /* This is an optimization based on the used group that does not depend 3804bc52338SCy Schubert * on the password in any way, so it is fine to use separate branches 3814bc52338SCy Schubert * for this step without constant time operations. */ 3825b9c547cSRui Paulo if (sae->tmp->dh->safe_prime) { 3835b9c547cSRui Paulo /* 3845b9c547cSRui Paulo * r = (p-1)/2 for the group used here, so this becomes: 3855b9c547cSRui Paulo * PWE = pwd-value^2 modulo p 3865b9c547cSRui Paulo */ 3875b9c547cSRui Paulo exp[0] = 2; 3885b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 3895b9c547cSRui Paulo } else { 3905b9c547cSRui Paulo /* Calculate exponent: (p-1)/r */ 3915b9c547cSRui Paulo exp[0] = 1; 3925b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 3935b9c547cSRui Paulo if (b == NULL || 3945b9c547cSRui Paulo crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || 3954bc52338SCy Schubert crypto_bignum_div(b, sae->tmp->order, b) < 0) 3964bc52338SCy Schubert goto fail; 3975b9c547cSRui Paulo } 3985b9c547cSRui Paulo 3994bc52338SCy Schubert if (!b) 4004bc52338SCy Schubert goto fail; 4014bc52338SCy Schubert 4025b9c547cSRui Paulo res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); 4034bc52338SCy Schubert if (res < 0) 4044bc52338SCy Schubert goto fail; 4055b9c547cSRui Paulo 4064bc52338SCy Schubert /* There were no fatal errors in calculations, so determine the return 4074bc52338SCy Schubert * value using constant time operations. We get here for number of 4084bc52338SCy Schubert * invalid cases which are cleared here after having performed all the 4094bc52338SCy Schubert * computation. PWE is valid if pwd-value was less than prime and 4104bc52338SCy Schubert * PWE > 1. Start with pwd-value check first and then use constant time 4114bc52338SCy Schubert * operations to clear res to 0 if PWE is 0 or 1. 4124bc52338SCy Schubert */ 4134bc52338SCy Schubert res = const_time_select_u8(pwd_value_valid, 1, 0); 4144bc52338SCy Schubert is_val = crypto_bignum_is_zero(pwe); 4154bc52338SCy Schubert res = const_time_select_u8(const_time_is_zero(is_val), res, 0); 4164bc52338SCy Schubert is_val = crypto_bignum_is_one(pwe); 4174bc52338SCy Schubert res = const_time_select_u8(const_time_is_zero(is_val), res, 0); 4185b9c547cSRui Paulo 4194bc52338SCy Schubert fail: 4204bc52338SCy Schubert crypto_bignum_deinit(a, 1); 4214bc52338SCy Schubert crypto_bignum_deinit(b, 1); 4224bc52338SCy Schubert return res; 4235b9c547cSRui Paulo } 4245b9c547cSRui Paulo 4255b9c547cSRui Paulo 426325151a3SRui Paulo static int get_random_qr_qnr(const u8 *prime, size_t prime_len, 427325151a3SRui Paulo const struct crypto_bignum *prime_bn, 428325151a3SRui Paulo size_t prime_bits, struct crypto_bignum **qr, 429325151a3SRui Paulo struct crypto_bignum **qnr) 430325151a3SRui Paulo { 431325151a3SRui Paulo *qr = NULL; 432325151a3SRui Paulo *qnr = NULL; 433325151a3SRui Paulo 434325151a3SRui Paulo while (!(*qr) || !(*qnr)) { 435325151a3SRui Paulo u8 tmp[SAE_MAX_ECC_PRIME_LEN]; 436325151a3SRui Paulo struct crypto_bignum *q; 437325151a3SRui Paulo int res; 438325151a3SRui Paulo 439325151a3SRui Paulo if (random_get_bytes(tmp, prime_len) < 0) 440325151a3SRui Paulo break; 441325151a3SRui Paulo if (prime_bits % 8) 442325151a3SRui Paulo buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); 443325151a3SRui Paulo if (os_memcmp(tmp, prime, prime_len) >= 0) 444325151a3SRui Paulo continue; 445325151a3SRui Paulo q = crypto_bignum_init_set(tmp, prime_len); 446325151a3SRui Paulo if (!q) 447325151a3SRui Paulo break; 448325151a3SRui Paulo res = crypto_bignum_legendre(q, prime_bn); 449325151a3SRui Paulo 450325151a3SRui Paulo if (res == 1 && !(*qr)) 451325151a3SRui Paulo *qr = q; 452325151a3SRui Paulo else if (res == -1 && !(*qnr)) 453325151a3SRui Paulo *qnr = q; 454325151a3SRui Paulo else 455325151a3SRui Paulo crypto_bignum_deinit(q, 0); 456325151a3SRui Paulo } 457325151a3SRui Paulo 458325151a3SRui Paulo return (*qr && *qnr) ? 0 : -1; 459325151a3SRui Paulo } 460325151a3SRui Paulo 461325151a3SRui Paulo 4625b9c547cSRui Paulo static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, 4635b9c547cSRui Paulo const u8 *addr2, const u8 *password, 46485732ac8SCy Schubert size_t password_len, const char *identifier) 4655b9c547cSRui Paulo { 466325151a3SRui Paulo u8 counter, k = 40; 4675b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 46885732ac8SCy Schubert const u8 *addr[3]; 46985732ac8SCy Schubert size_t len[3]; 47085732ac8SCy Schubert size_t num_elem; 4714bc52338SCy Schubert u8 *dummy_password, *tmp_password; 472325151a3SRui Paulo int pwd_seed_odd = 0; 473325151a3SRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 474325151a3SRui Paulo size_t prime_len; 4754bc52338SCy Schubert struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL; 4764bc52338SCy Schubert u8 x_bin[SAE_MAX_ECC_PRIME_LEN]; 4774bc52338SCy Schubert u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; 4784bc52338SCy Schubert u8 qr_bin[SAE_MAX_ECC_PRIME_LEN]; 4794bc52338SCy Schubert u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN]; 480325151a3SRui Paulo size_t bits; 4814bc52338SCy Schubert int res = -1; 4824bc52338SCy Schubert u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* 4834bc52338SCy Schubert * mask */ 4845b9c547cSRui Paulo 4854bc52338SCy Schubert os_memset(x_bin, 0, sizeof(x_bin)); 4864bc52338SCy Schubert 4874bc52338SCy Schubert dummy_password = os_malloc(password_len); 4884bc52338SCy Schubert tmp_password = os_malloc(password_len); 4894bc52338SCy Schubert if (!dummy_password || !tmp_password || 4904bc52338SCy Schubert random_get_bytes(dummy_password, password_len) < 0) 4914bc52338SCy Schubert goto fail; 492325151a3SRui Paulo 493325151a3SRui Paulo prime_len = sae->tmp->prime_len; 494325151a3SRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 495325151a3SRui Paulo prime_len) < 0) 4964bc52338SCy Schubert goto fail; 497325151a3SRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec); 498325151a3SRui Paulo 499325151a3SRui Paulo /* 500325151a3SRui Paulo * Create a random quadratic residue (qr) and quadratic non-residue 501325151a3SRui Paulo * (qnr) modulo p for blinding purposes during the loop. 502325151a3SRui Paulo */ 503325151a3SRui Paulo if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, 5044bc52338SCy Schubert &qr, &qnr) < 0 || 5054bc52338SCy Schubert crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 || 5064bc52338SCy Schubert crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0) 5074bc52338SCy Schubert goto fail; 5085b9c547cSRui Paulo 5095b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 5105b9c547cSRui Paulo password, password_len); 51185732ac8SCy Schubert if (identifier) 51285732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", 51385732ac8SCy Schubert identifier); 5145b9c547cSRui Paulo 5155b9c547cSRui Paulo /* 5165b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 51785732ac8SCy Schubert * base = password [|| identifier] 5185b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 519325151a3SRui Paulo * base || counter) 5205b9c547cSRui Paulo */ 5215b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 5225b9c547cSRui Paulo 5234bc52338SCy Schubert addr[0] = tmp_password; 5245b9c547cSRui Paulo len[0] = password_len; 52585732ac8SCy Schubert num_elem = 1; 52685732ac8SCy Schubert if (identifier) { 52785732ac8SCy Schubert addr[num_elem] = (const u8 *) identifier; 52885732ac8SCy Schubert len[num_elem] = os_strlen(identifier); 52985732ac8SCy Schubert num_elem++; 53085732ac8SCy Schubert } 53185732ac8SCy Schubert addr[num_elem] = &counter; 53285732ac8SCy Schubert len[num_elem] = sizeof(counter); 53385732ac8SCy Schubert num_elem++; 5345b9c547cSRui Paulo 5355b9c547cSRui Paulo /* 5365b9c547cSRui Paulo * Continue for at least k iterations to protect against side-channel 5375b9c547cSRui Paulo * attacks that attempt to determine the number of iterations required 5385b9c547cSRui Paulo * in the loop. 5395b9c547cSRui Paulo */ 5404bc52338SCy Schubert for (counter = 1; counter <= k || !found; counter++) { 5415b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 5425b9c547cSRui Paulo 5435b9c547cSRui Paulo if (counter > 200) { 5445b9c547cSRui Paulo /* This should not happen in practice */ 5455b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 5465b9c547cSRui Paulo break; 5475b9c547cSRui Paulo } 5485b9c547cSRui Paulo 5494bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter); 5504bc52338SCy Schubert const_time_select_bin(found, dummy_password, password, 5514bc52338SCy Schubert password_len, tmp_password); 55285732ac8SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, 55385732ac8SCy Schubert addr, len, pwd_seed) < 0) 5545b9c547cSRui Paulo break; 555325151a3SRui Paulo 5565b9c547cSRui Paulo res = sae_test_pwd_seed_ecc(sae, pwd_seed, 5574bc52338SCy Schubert prime, qr_bin, qnr_bin, x_cand_bin); 5584bc52338SCy Schubert const_time_select_bin(found, x_bin, x_cand_bin, prime_len, 5594bc52338SCy Schubert x_bin); 5604bc52338SCy Schubert pwd_seed_odd = const_time_select_u8( 5614bc52338SCy Schubert found, pwd_seed_odd, 5624bc52338SCy Schubert pwd_seed[SHA256_MAC_LEN - 1] & 0x01); 5634bc52338SCy Schubert os_memset(pwd_seed, 0, sizeof(pwd_seed)); 5645b9c547cSRui Paulo if (res < 0) 565325151a3SRui Paulo goto fail; 5664bc52338SCy Schubert /* Need to minimize differences in handling res == 0 and 1 here 5674bc52338SCy Schubert * to avoid differences in timing and instruction cache access, 5684bc52338SCy Schubert * so use const_time_select_*() to make local copies of the 5694bc52338SCy Schubert * values based on whether this loop iteration was the one that 5704bc52338SCy Schubert * found the pwd-seed/x. */ 571325151a3SRui Paulo 5724bc52338SCy Schubert /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them 5734bc52338SCy Schubert * (with res converted to 0/0xff) handles this in constant time. 574325151a3SRui Paulo */ 5754bc52338SCy Schubert found |= res * 0xff; 5764bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x", 5774bc52338SCy Schubert res, found); 5785b9c547cSRui Paulo } 5795b9c547cSRui Paulo 5804bc52338SCy Schubert if (!found) { 581325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); 582325151a3SRui Paulo res = -1; 583325151a3SRui Paulo goto fail; 584325151a3SRui Paulo } 5855b9c547cSRui Paulo 5864bc52338SCy Schubert x = crypto_bignum_init_set(x_bin, prime_len); 5874bc52338SCy Schubert if (!x) { 5884bc52338SCy Schubert res = -1; 5894bc52338SCy Schubert goto fail; 5904bc52338SCy Schubert } 5914bc52338SCy Schubert 592325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 593325151a3SRui Paulo sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); 594325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 595325151a3SRui Paulo res = -1; 596325151a3SRui Paulo else 597325151a3SRui Paulo res = crypto_ec_point_solve_y_coord(sae->tmp->ec, 598325151a3SRui Paulo sae->tmp->pwe_ecc, x, 599325151a3SRui Paulo pwd_seed_odd); 600325151a3SRui Paulo if (res < 0) { 601325151a3SRui Paulo /* 602325151a3SRui Paulo * This should not happen since we already checked that there 603325151a3SRui Paulo * is a result. 604325151a3SRui Paulo */ 605325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); 606325151a3SRui Paulo } 607325151a3SRui Paulo 608325151a3SRui Paulo fail: 609325151a3SRui Paulo crypto_bignum_deinit(qr, 0); 610325151a3SRui Paulo crypto_bignum_deinit(qnr, 0); 6114bc52338SCy Schubert os_free(dummy_password); 6124bc52338SCy Schubert bin_clear_free(tmp_password, password_len); 6134bc52338SCy Schubert crypto_bignum_deinit(x, 1); 6144bc52338SCy Schubert os_memset(x_bin, 0, sizeof(x_bin)); 6154bc52338SCy Schubert os_memset(x_cand_bin, 0, sizeof(x_cand_bin)); 616325151a3SRui Paulo 617325151a3SRui Paulo return res; 6185b9c547cSRui Paulo } 6195b9c547cSRui Paulo 6205b9c547cSRui Paulo 6214bc52338SCy Schubert static int sae_modp_group_require_masking(int group) 6224bc52338SCy Schubert { 6234bc52338SCy Schubert /* Groups for which pwd-value is likely to be >= p frequently */ 6244bc52338SCy Schubert return group == 22 || group == 23 || group == 24; 6254bc52338SCy Schubert } 6264bc52338SCy Schubert 6274bc52338SCy Schubert 6285b9c547cSRui Paulo static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, 6295b9c547cSRui Paulo const u8 *addr2, const u8 *password, 63085732ac8SCy Schubert size_t password_len, const char *identifier) 6315b9c547cSRui Paulo { 6324bc52338SCy Schubert u8 counter, k, sel_counter = 0; 6335b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 63485732ac8SCy Schubert const u8 *addr[3]; 63585732ac8SCy Schubert size_t len[3]; 63685732ac8SCy Schubert size_t num_elem; 6374bc52338SCy Schubert u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* 6384bc52338SCy Schubert * mask */ 6394bc52338SCy Schubert u8 mask; 6404bc52338SCy Schubert struct crypto_bignum *pwe; 6414bc52338SCy Schubert size_t prime_len = sae->tmp->prime_len * 8; 6424bc52338SCy Schubert u8 *pwe_buf; 6435b9c547cSRui Paulo 6444bc52338SCy Schubert crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); 6454bc52338SCy Schubert sae->tmp->pwe_ffc = NULL; 6464bc52338SCy Schubert 6474bc52338SCy Schubert /* Allocate a buffer to maintain selected and candidate PWE for constant 6484bc52338SCy Schubert * time selection. */ 6494bc52338SCy Schubert pwe_buf = os_zalloc(prime_len * 2); 6504bc52338SCy Schubert pwe = crypto_bignum_init(); 6514bc52338SCy Schubert if (!pwe_buf || !pwe) 6524bc52338SCy Schubert goto fail; 6535b9c547cSRui Paulo 6545b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 6555b9c547cSRui Paulo password, password_len); 6565b9c547cSRui Paulo 6575b9c547cSRui Paulo /* 6585b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 6595b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 66085732ac8SCy Schubert * password [|| identifier] || counter) 6615b9c547cSRui Paulo */ 6625b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 6635b9c547cSRui Paulo 6645b9c547cSRui Paulo addr[0] = password; 6655b9c547cSRui Paulo len[0] = password_len; 66685732ac8SCy Schubert num_elem = 1; 66785732ac8SCy Schubert if (identifier) { 66885732ac8SCy Schubert addr[num_elem] = (const u8 *) identifier; 66985732ac8SCy Schubert len[num_elem] = os_strlen(identifier); 67085732ac8SCy Schubert num_elem++; 67185732ac8SCy Schubert } 67285732ac8SCy Schubert addr[num_elem] = &counter; 67385732ac8SCy Schubert len[num_elem] = sizeof(counter); 67485732ac8SCy Schubert num_elem++; 6755b9c547cSRui Paulo 6764bc52338SCy Schubert k = sae_modp_group_require_masking(sae->group) ? 40 : 1; 6774bc52338SCy Schubert 6784bc52338SCy Schubert for (counter = 1; counter <= k || !found; counter++) { 6795b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 6805b9c547cSRui Paulo int res; 6815b9c547cSRui Paulo 6825b9c547cSRui Paulo if (counter > 200) { 6835b9c547cSRui Paulo /* This should not happen in practice */ 6845b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 6855b9c547cSRui Paulo break; 6865b9c547cSRui Paulo } 6875b9c547cSRui Paulo 6884bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter); 68985732ac8SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, 69085732ac8SCy Schubert addr, len, pwd_seed) < 0) 6915b9c547cSRui Paulo break; 6924bc52338SCy Schubert res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe); 6934bc52338SCy Schubert /* res is -1 for fatal failure, 0 if a valid PWE was not found, 6944bc52338SCy Schubert * or 1 if a valid PWE was found. */ 6955b9c547cSRui Paulo if (res < 0) 6965b9c547cSRui Paulo break; 6974bc52338SCy Schubert /* Store the candidate PWE into the second half of pwe_buf and 6984bc52338SCy Schubert * the selected PWE in the beginning of pwe_buf using constant 6994bc52338SCy Schubert * time selection. */ 7004bc52338SCy Schubert if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len, 7014bc52338SCy Schubert prime_len) < 0) 7024bc52338SCy Schubert break; 7034bc52338SCy Schubert const_time_select_bin(found, pwe_buf, pwe_buf + prime_len, 7044bc52338SCy Schubert prime_len, pwe_buf); 7054bc52338SCy Schubert sel_counter = const_time_select_u8(found, sel_counter, counter); 7064bc52338SCy Schubert mask = const_time_eq_u8(res, 1); 7074bc52338SCy Schubert found = const_time_select_u8(found, found, mask); 7085b9c547cSRui Paulo } 7095b9c547cSRui Paulo 7104bc52338SCy Schubert if (!found) 7114bc52338SCy Schubert goto fail; 7124bc52338SCy Schubert 7134bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter); 7144bc52338SCy Schubert sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len); 7154bc52338SCy Schubert fail: 7164bc52338SCy Schubert crypto_bignum_deinit(pwe, 1); 7174bc52338SCy Schubert bin_clear_free(pwe_buf, prime_len * 2); 7184bc52338SCy Schubert return sae->tmp->pwe_ffc ? 0 : -1; 7195b9c547cSRui Paulo } 7205b9c547cSRui Paulo 7215b9c547cSRui Paulo 7225b9c547cSRui Paulo static int sae_derive_commit_element_ecc(struct sae_data *sae, 7235b9c547cSRui Paulo struct crypto_bignum *mask) 7245b9c547cSRui Paulo { 7255b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 7265b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) { 7275b9c547cSRui Paulo sae->tmp->own_commit_element_ecc = 7285b9c547cSRui Paulo crypto_ec_point_init(sae->tmp->ec); 7295b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) 7305b9c547cSRui Paulo return -1; 7315b9c547cSRui Paulo } 7325b9c547cSRui Paulo 7335b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, 7345b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0 || 7355b9c547cSRui Paulo crypto_ec_point_invert(sae->tmp->ec, 7365b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0) { 7375b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 7385b9c547cSRui Paulo return -1; 7395b9c547cSRui Paulo } 7405b9c547cSRui Paulo 7415b9c547cSRui Paulo return 0; 7425b9c547cSRui Paulo } 7435b9c547cSRui Paulo 7445b9c547cSRui Paulo 7455b9c547cSRui Paulo static int sae_derive_commit_element_ffc(struct sae_data *sae, 7465b9c547cSRui Paulo struct crypto_bignum *mask) 7475b9c547cSRui Paulo { 7485b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 7495b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) { 7505b9c547cSRui Paulo sae->tmp->own_commit_element_ffc = crypto_bignum_init(); 7515b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) 7525b9c547cSRui Paulo return -1; 7535b9c547cSRui Paulo } 7545b9c547cSRui Paulo 7555b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, 7565b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0 || 7575b9c547cSRui Paulo crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, 7585b9c547cSRui Paulo sae->tmp->prime, 7595b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0) { 7605b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 7615b9c547cSRui Paulo return -1; 7625b9c547cSRui Paulo } 7635b9c547cSRui Paulo 7645b9c547cSRui Paulo return 0; 7655b9c547cSRui Paulo } 7665b9c547cSRui Paulo 7675b9c547cSRui Paulo 7685b9c547cSRui Paulo static int sae_derive_commit(struct sae_data *sae) 7695b9c547cSRui Paulo { 7705b9c547cSRui Paulo struct crypto_bignum *mask; 7715b9c547cSRui Paulo int ret = -1; 772325151a3SRui Paulo unsigned int counter = 0; 773325151a3SRui Paulo 774325151a3SRui Paulo do { 775325151a3SRui Paulo counter++; 776325151a3SRui Paulo if (counter > 100) { 777325151a3SRui Paulo /* 778325151a3SRui Paulo * This cannot really happen in practice if the random 779325151a3SRui Paulo * number generator is working. Anyway, to avoid even a 780325151a3SRui Paulo * theoretical infinite loop, break out after 100 781325151a3SRui Paulo * attemps. 782325151a3SRui Paulo */ 783325151a3SRui Paulo return -1; 784325151a3SRui Paulo } 7855b9c547cSRui Paulo 7865b9c547cSRui Paulo mask = sae_get_rand_and_mask(sae); 7875b9c547cSRui Paulo if (mask == NULL) { 7885b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); 7895b9c547cSRui Paulo return -1; 7905b9c547cSRui Paulo } 7915b9c547cSRui Paulo 7925b9c547cSRui Paulo /* commit-scalar = (rand + mask) modulo r */ 7935b9c547cSRui Paulo if (!sae->tmp->own_commit_scalar) { 7945b9c547cSRui Paulo sae->tmp->own_commit_scalar = crypto_bignum_init(); 7955b9c547cSRui Paulo if (!sae->tmp->own_commit_scalar) 7965b9c547cSRui Paulo goto fail; 7975b9c547cSRui Paulo } 7985b9c547cSRui Paulo crypto_bignum_add(sae->tmp->sae_rand, mask, 7995b9c547cSRui Paulo sae->tmp->own_commit_scalar); 8005b9c547cSRui Paulo crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, 8015b9c547cSRui Paulo sae->tmp->own_commit_scalar); 802325151a3SRui Paulo } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) || 803325151a3SRui Paulo crypto_bignum_is_one(sae->tmp->own_commit_scalar)); 8045b9c547cSRui Paulo 805325151a3SRui Paulo if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) || 806325151a3SRui Paulo (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)) 8075b9c547cSRui Paulo goto fail; 8085b9c547cSRui Paulo 8095b9c547cSRui Paulo ret = 0; 8105b9c547cSRui Paulo fail: 8115b9c547cSRui Paulo crypto_bignum_deinit(mask, 1); 8125b9c547cSRui Paulo return ret; 8135b9c547cSRui Paulo } 8145b9c547cSRui Paulo 8155b9c547cSRui Paulo 8165b9c547cSRui Paulo int sae_prepare_commit(const u8 *addr1, const u8 *addr2, 8175b9c547cSRui Paulo const u8 *password, size_t password_len, 81885732ac8SCy Schubert const char *identifier, struct sae_data *sae) 8195b9c547cSRui Paulo { 820325151a3SRui Paulo if (sae->tmp == NULL || 821325151a3SRui Paulo (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, 82285732ac8SCy Schubert password_len, 82385732ac8SCy Schubert identifier) < 0) || 824325151a3SRui Paulo (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, 82585732ac8SCy Schubert password_len, 82685732ac8SCy Schubert identifier) < 0) || 827325151a3SRui Paulo sae_derive_commit(sae) < 0) 8285b9c547cSRui Paulo return -1; 8295b9c547cSRui Paulo return 0; 8305b9c547cSRui Paulo } 8315b9c547cSRui Paulo 8325b9c547cSRui Paulo 8335b9c547cSRui Paulo static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) 8345b9c547cSRui Paulo { 8355b9c547cSRui Paulo struct crypto_ec_point *K; 8365b9c547cSRui Paulo int ret = -1; 8375b9c547cSRui Paulo 8385b9c547cSRui Paulo K = crypto_ec_point_init(sae->tmp->ec); 8395b9c547cSRui Paulo if (K == NULL) 8405b9c547cSRui Paulo goto fail; 8415b9c547cSRui Paulo 8425b9c547cSRui Paulo /* 8435b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 8445b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 8455b9c547cSRui Paulo * If K is identity element (point-at-infinity), reject 8465b9c547cSRui Paulo * k = F(K) (= x coordinate) 8475b9c547cSRui Paulo */ 8485b9c547cSRui Paulo 8495b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, 8505b9c547cSRui Paulo sae->peer_commit_scalar, K) < 0 || 8515b9c547cSRui Paulo crypto_ec_point_add(sae->tmp->ec, K, 8525b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, K) < 0 || 8535b9c547cSRui Paulo crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || 8545b9c547cSRui Paulo crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || 8555b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { 8565b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 8575b9c547cSRui Paulo goto fail; 8585b9c547cSRui Paulo } 8595b9c547cSRui Paulo 8605b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 8615b9c547cSRui Paulo 8625b9c547cSRui Paulo ret = 0; 8635b9c547cSRui Paulo fail: 8645b9c547cSRui Paulo crypto_ec_point_deinit(K, 1); 8655b9c547cSRui Paulo return ret; 8665b9c547cSRui Paulo } 8675b9c547cSRui Paulo 8685b9c547cSRui Paulo 8695b9c547cSRui Paulo static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) 8705b9c547cSRui Paulo { 8715b9c547cSRui Paulo struct crypto_bignum *K; 8725b9c547cSRui Paulo int ret = -1; 8735b9c547cSRui Paulo 8745b9c547cSRui Paulo K = crypto_bignum_init(); 8755b9c547cSRui Paulo if (K == NULL) 8765b9c547cSRui Paulo goto fail; 8775b9c547cSRui Paulo 8785b9c547cSRui Paulo /* 8795b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 8805b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 8815b9c547cSRui Paulo * If K is identity element (one), reject. 8825b9c547cSRui Paulo * k = F(K) (= x coordinate) 8835b9c547cSRui Paulo */ 8845b9c547cSRui Paulo 8855b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, 8865b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 8875b9c547cSRui Paulo crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, 8885b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 8895b9c547cSRui Paulo crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 8905b9c547cSRui Paulo || 8915b9c547cSRui Paulo crypto_bignum_is_one(K) || 8925b9c547cSRui Paulo crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < 8935b9c547cSRui Paulo 0) { 8945b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 8955b9c547cSRui Paulo goto fail; 8965b9c547cSRui Paulo } 8975b9c547cSRui Paulo 8985b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 8995b9c547cSRui Paulo 9005b9c547cSRui Paulo ret = 0; 9015b9c547cSRui Paulo fail: 9025b9c547cSRui Paulo crypto_bignum_deinit(K, 1); 9035b9c547cSRui Paulo return ret; 9045b9c547cSRui Paulo } 9055b9c547cSRui Paulo 9065b9c547cSRui Paulo 9075b9c547cSRui Paulo static int sae_derive_keys(struct sae_data *sae, const u8 *k) 9085b9c547cSRui Paulo { 9095b9c547cSRui Paulo u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; 9105b9c547cSRui Paulo u8 keyseed[SHA256_MAC_LEN]; 9115b9c547cSRui Paulo u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; 9125b9c547cSRui Paulo struct crypto_bignum *tmp; 9135b9c547cSRui Paulo int ret = -1; 9145b9c547cSRui Paulo 9155b9c547cSRui Paulo tmp = crypto_bignum_init(); 9165b9c547cSRui Paulo if (tmp == NULL) 9175b9c547cSRui Paulo goto fail; 9185b9c547cSRui Paulo 9195b9c547cSRui Paulo /* keyseed = H(<0>32, k) 9205b9c547cSRui Paulo * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK", 9215b9c547cSRui Paulo * (commit-scalar + peer-commit-scalar) modulo r) 9225b9c547cSRui Paulo * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) 9235b9c547cSRui Paulo */ 9245b9c547cSRui Paulo 9255b9c547cSRui Paulo os_memset(null_key, 0, sizeof(null_key)); 9265b9c547cSRui Paulo hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, 9275b9c547cSRui Paulo keyseed); 9285b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); 9295b9c547cSRui Paulo 9305b9c547cSRui Paulo crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, 9315b9c547cSRui Paulo tmp); 9325b9c547cSRui Paulo crypto_bignum_mod(tmp, sae->tmp->order, tmp); 9335b9c547cSRui Paulo crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); 9345b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); 935780fb4a2SCy Schubert if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", 936780fb4a2SCy Schubert val, sae->tmp->prime_len, keys, sizeof(keys)) < 0) 937780fb4a2SCy Schubert goto fail; 9385b9c547cSRui Paulo os_memset(keyseed, 0, sizeof(keyseed)); 9395b9c547cSRui Paulo os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); 9405b9c547cSRui Paulo os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); 941780fb4a2SCy Schubert os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); 9425b9c547cSRui Paulo os_memset(keys, 0, sizeof(keys)); 9435b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); 9445b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); 9455b9c547cSRui Paulo 9465b9c547cSRui Paulo ret = 0; 9475b9c547cSRui Paulo fail: 9485b9c547cSRui Paulo crypto_bignum_deinit(tmp, 0); 9495b9c547cSRui Paulo return ret; 9505b9c547cSRui Paulo } 9515b9c547cSRui Paulo 9525b9c547cSRui Paulo 9535b9c547cSRui Paulo int sae_process_commit(struct sae_data *sae) 9545b9c547cSRui Paulo { 9555b9c547cSRui Paulo u8 k[SAE_MAX_PRIME_LEN]; 9565b9c547cSRui Paulo if (sae->tmp == NULL || 9575b9c547cSRui Paulo (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || 9585b9c547cSRui Paulo (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || 9595b9c547cSRui Paulo sae_derive_keys(sae, k) < 0) 9605b9c547cSRui Paulo return -1; 9615b9c547cSRui Paulo return 0; 9625b9c547cSRui Paulo } 9635b9c547cSRui Paulo 9645b9c547cSRui Paulo 9655b9c547cSRui Paulo void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, 96685732ac8SCy Schubert const struct wpabuf *token, const char *identifier) 9675b9c547cSRui Paulo { 9685b9c547cSRui Paulo u8 *pos; 9695b9c547cSRui Paulo 9705b9c547cSRui Paulo if (sae->tmp == NULL) 9715b9c547cSRui Paulo return; 9725b9c547cSRui Paulo 9735b9c547cSRui Paulo wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ 9745b9c547cSRui Paulo if (token) { 9755b9c547cSRui Paulo wpabuf_put_buf(buf, token); 9765b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", 9775b9c547cSRui Paulo wpabuf_head(token), wpabuf_len(token)); 9785b9c547cSRui Paulo } 9795b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 9805b9c547cSRui Paulo crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, 9815b9c547cSRui Paulo sae->tmp->prime_len, sae->tmp->prime_len); 9825b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", 9835b9c547cSRui Paulo pos, sae->tmp->prime_len); 9845b9c547cSRui Paulo if (sae->tmp->ec) { 9855b9c547cSRui Paulo pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); 9865b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, 9875b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 9885b9c547cSRui Paulo pos, pos + sae->tmp->prime_len); 9895b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", 9905b9c547cSRui Paulo pos, sae->tmp->prime_len); 9915b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", 9925b9c547cSRui Paulo pos + sae->tmp->prime_len, sae->tmp->prime_len); 9935b9c547cSRui Paulo } else { 9945b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 9955b9c547cSRui Paulo crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, 9965b9c547cSRui Paulo sae->tmp->prime_len, sae->tmp->prime_len); 9975b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", 9985b9c547cSRui Paulo pos, sae->tmp->prime_len); 9995b9c547cSRui Paulo } 100085732ac8SCy Schubert 100185732ac8SCy Schubert if (identifier) { 100285732ac8SCy Schubert /* Password Identifier element */ 100385732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 100485732ac8SCy Schubert wpabuf_put_u8(buf, 1 + os_strlen(identifier)); 100585732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); 100685732ac8SCy Schubert wpabuf_put_str(buf, identifier); 100785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", 100885732ac8SCy Schubert identifier); 100985732ac8SCy Schubert } 10105b9c547cSRui Paulo } 10115b9c547cSRui Paulo 10125b9c547cSRui Paulo 10135b9c547cSRui Paulo u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) 10145b9c547cSRui Paulo { 10155b9c547cSRui Paulo if (allowed_groups) { 10165b9c547cSRui Paulo int i; 10175b9c547cSRui Paulo for (i = 0; allowed_groups[i] > 0; i++) { 10185b9c547cSRui Paulo if (allowed_groups[i] == group) 10195b9c547cSRui Paulo break; 10205b9c547cSRui Paulo } 10215b9c547cSRui Paulo if (allowed_groups[i] != group) { 10225b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " 10235b9c547cSRui Paulo "enabled in the current configuration", 10245b9c547cSRui Paulo group); 10255b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 10265b9c547cSRui Paulo } 10275b9c547cSRui Paulo } 10285b9c547cSRui Paulo 10295b9c547cSRui Paulo if (sae->state == SAE_COMMITTED && group != sae->group) { 10305b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); 10315b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 10325b9c547cSRui Paulo } 10335b9c547cSRui Paulo 10345b9c547cSRui Paulo if (group != sae->group && sae_set_group(sae, group) < 0) { 10355b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", 10365b9c547cSRui Paulo group); 10375b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 10385b9c547cSRui Paulo } 10395b9c547cSRui Paulo 10405b9c547cSRui Paulo if (sae->tmp == NULL) { 10415b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); 10425b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10435b9c547cSRui Paulo } 10445b9c547cSRui Paulo 10455b9c547cSRui Paulo if (sae->tmp->dh && !allowed_groups) { 10465b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " 10475b9c547cSRui Paulo "explicit configuration enabling it", group); 10485b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 10495b9c547cSRui Paulo } 10505b9c547cSRui Paulo 10515b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 10525b9c547cSRui Paulo } 10535b9c547cSRui Paulo 10545b9c547cSRui Paulo 105585732ac8SCy Schubert static int sae_is_password_id_elem(const u8 *pos, const u8 *end) 105685732ac8SCy Schubert { 105785732ac8SCy Schubert return end - pos >= 3 && 105885732ac8SCy Schubert pos[0] == WLAN_EID_EXTENSION && 105985732ac8SCy Schubert pos[1] >= 1 && 106085732ac8SCy Schubert end - pos - 2 >= pos[1] && 106185732ac8SCy Schubert pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; 106285732ac8SCy Schubert } 106385732ac8SCy Schubert 106485732ac8SCy Schubert 10655b9c547cSRui Paulo static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, 10665b9c547cSRui Paulo const u8 *end, const u8 **token, 10675b9c547cSRui Paulo size_t *token_len) 10685b9c547cSRui Paulo { 106985732ac8SCy Schubert size_t scalar_elem_len, tlen; 107085732ac8SCy Schubert const u8 *elem; 107185732ac8SCy Schubert 107285732ac8SCy Schubert if (token) 107385732ac8SCy Schubert *token = NULL; 107485732ac8SCy Schubert if (token_len) 107585732ac8SCy Schubert *token_len = 0; 107685732ac8SCy Schubert 107785732ac8SCy Schubert scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; 107885732ac8SCy Schubert if (scalar_elem_len >= (size_t) (end - *pos)) 107985732ac8SCy Schubert return; /* No extra data beyond peer scalar and element */ 108085732ac8SCy Schubert 108185732ac8SCy Schubert /* It is a bit difficult to parse this now that there is an 108285732ac8SCy Schubert * optional variable length Anti-Clogging Token field and 108385732ac8SCy Schubert * optional variable length Password Identifier element in the 108485732ac8SCy Schubert * frame. We are sending out fixed length Anti-Clogging Token 108585732ac8SCy Schubert * fields, so use that length as a requirement for the received 108685732ac8SCy Schubert * token and check for the presence of possible Password 108785732ac8SCy Schubert * Identifier element based on the element header information. 108885732ac8SCy Schubert */ 108985732ac8SCy Schubert tlen = end - (*pos + scalar_elem_len); 109085732ac8SCy Schubert 109185732ac8SCy Schubert if (tlen < SHA256_MAC_LEN) { 109285732ac8SCy Schubert wpa_printf(MSG_DEBUG, 109385732ac8SCy Schubert "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", 109485732ac8SCy Schubert (unsigned int) tlen); 109585732ac8SCy Schubert return; 109685732ac8SCy Schubert } 109785732ac8SCy Schubert 109885732ac8SCy Schubert elem = *pos + scalar_elem_len; 109985732ac8SCy Schubert if (sae_is_password_id_elem(elem, end)) { 110085732ac8SCy Schubert /* Password Identifier element takes out all available 110185732ac8SCy Schubert * extra octets, so there can be no Anti-Clogging token in 110285732ac8SCy Schubert * this frame. */ 110385732ac8SCy Schubert return; 110485732ac8SCy Schubert } 110585732ac8SCy Schubert 110685732ac8SCy Schubert elem += SHA256_MAC_LEN; 110785732ac8SCy Schubert if (sae_is_password_id_elem(elem, end)) { 110885732ac8SCy Schubert /* Password Identifier element is included in the end, so 110985732ac8SCy Schubert * remove its length from the Anti-Clogging token field. */ 111085732ac8SCy Schubert tlen -= 2 + elem[1]; 111185732ac8SCy Schubert } 111285732ac8SCy Schubert 11135b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); 11145b9c547cSRui Paulo if (token) 11155b9c547cSRui Paulo *token = *pos; 11165b9c547cSRui Paulo if (token_len) 11175b9c547cSRui Paulo *token_len = tlen; 11185b9c547cSRui Paulo *pos += tlen; 11195b9c547cSRui Paulo } 11205b9c547cSRui Paulo 11215b9c547cSRui Paulo 11225b9c547cSRui Paulo static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, 11235b9c547cSRui Paulo const u8 *end) 11245b9c547cSRui Paulo { 11255b9c547cSRui Paulo struct crypto_bignum *peer_scalar; 11265b9c547cSRui Paulo 1127780fb4a2SCy Schubert if (sae->tmp->prime_len > end - *pos) { 11285b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); 11295b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11305b9c547cSRui Paulo } 11315b9c547cSRui Paulo 11325b9c547cSRui Paulo peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); 11335b9c547cSRui Paulo if (peer_scalar == NULL) 11345b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11355b9c547cSRui Paulo 11365b9c547cSRui Paulo /* 11375b9c547cSRui Paulo * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for 11385b9c547cSRui Paulo * the peer and it is in Authenticated state, the new Commit Message 11395b9c547cSRui Paulo * shall be dropped if the peer-scalar is identical to the one used in 11405b9c547cSRui Paulo * the existing protocol instance. 11415b9c547cSRui Paulo */ 11425b9c547cSRui Paulo if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && 11435b9c547cSRui Paulo crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { 11445b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " 11455b9c547cSRui Paulo "peer-commit-scalar"); 11465b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 11475b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11485b9c547cSRui Paulo } 11495b9c547cSRui Paulo 1150325151a3SRui Paulo /* 1 < scalar < r */ 11515b9c547cSRui Paulo if (crypto_bignum_is_zero(peer_scalar) || 1152325151a3SRui Paulo crypto_bignum_is_one(peer_scalar) || 11535b9c547cSRui Paulo crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { 11545b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); 11555b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 11565b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11575b9c547cSRui Paulo } 11585b9c547cSRui Paulo 11595b9c547cSRui Paulo 11605b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 11615b9c547cSRui Paulo sae->peer_commit_scalar = peer_scalar; 11625b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", 11635b9c547cSRui Paulo *pos, sae->tmp->prime_len); 11645b9c547cSRui Paulo *pos += sae->tmp->prime_len; 11655b9c547cSRui Paulo 11665b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 11675b9c547cSRui Paulo } 11685b9c547cSRui Paulo 11695b9c547cSRui Paulo 117085732ac8SCy Schubert static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, 11715b9c547cSRui Paulo const u8 *end) 11725b9c547cSRui Paulo { 11735b9c547cSRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 11745b9c547cSRui Paulo 117585732ac8SCy Schubert if (2 * sae->tmp->prime_len > end - *pos) { 11765b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 11775b9c547cSRui Paulo "commit-element"); 11785b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11795b9c547cSRui Paulo } 11805b9c547cSRui Paulo 11815b9c547cSRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 11825b9c547cSRui Paulo sae->tmp->prime_len) < 0) 11835b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11845b9c547cSRui Paulo 11855b9c547cSRui Paulo /* element x and y coordinates < p */ 118685732ac8SCy Schubert if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || 118785732ac8SCy Schubert os_memcmp(*pos + sae->tmp->prime_len, prime, 11885b9c547cSRui Paulo sae->tmp->prime_len) >= 0) { 11895b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " 11905b9c547cSRui Paulo "element"); 11915b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11925b9c547cSRui Paulo } 11935b9c547cSRui Paulo 11945b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", 119585732ac8SCy Schubert *pos, sae->tmp->prime_len); 11965b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", 119785732ac8SCy Schubert *pos + sae->tmp->prime_len, sae->tmp->prime_len); 11985b9c547cSRui Paulo 11995b9c547cSRui Paulo crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); 12005b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc = 120185732ac8SCy Schubert crypto_ec_point_from_bin(sae->tmp->ec, *pos); 12025b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ecc == NULL) 12035b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 12045b9c547cSRui Paulo 12055b9c547cSRui Paulo if (!crypto_ec_point_is_on_curve(sae->tmp->ec, 12065b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc)) { 12075b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); 12085b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 12095b9c547cSRui Paulo } 12105b9c547cSRui Paulo 121185732ac8SCy Schubert *pos += 2 * sae->tmp->prime_len; 121285732ac8SCy Schubert 12135b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 12145b9c547cSRui Paulo } 12155b9c547cSRui Paulo 12165b9c547cSRui Paulo 121785732ac8SCy Schubert static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, 12185b9c547cSRui Paulo const u8 *end) 12195b9c547cSRui Paulo { 1220325151a3SRui Paulo struct crypto_bignum *res, *one; 1221325151a3SRui Paulo const u8 one_bin[1] = { 0x01 }; 12225b9c547cSRui Paulo 122385732ac8SCy Schubert if (sae->tmp->prime_len > end - *pos) { 12245b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 12255b9c547cSRui Paulo "commit-element"); 12265b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 12275b9c547cSRui Paulo } 122885732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, 12295b9c547cSRui Paulo sae->tmp->prime_len); 12305b9c547cSRui Paulo 12315b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); 12325b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc = 123385732ac8SCy Schubert crypto_bignum_init_set(*pos, sae->tmp->prime_len); 12345b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ffc == NULL) 12355b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 1236325151a3SRui Paulo /* 1 < element < p - 1 */ 1237325151a3SRui Paulo res = crypto_bignum_init(); 1238325151a3SRui Paulo one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); 1239325151a3SRui Paulo if (!res || !one || 1240325151a3SRui Paulo crypto_bignum_sub(sae->tmp->prime, one, res) || 1241325151a3SRui Paulo crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || 12425b9c547cSRui Paulo crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || 1243325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { 1244325151a3SRui Paulo crypto_bignum_deinit(res, 0); 1245325151a3SRui Paulo crypto_bignum_deinit(one, 0); 12465b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); 12475b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 12485b9c547cSRui Paulo } 1249325151a3SRui Paulo crypto_bignum_deinit(one, 0); 12505b9c547cSRui Paulo 12515b9c547cSRui Paulo /* scalar-op(r, ELEMENT) = 1 modulo p */ 1252325151a3SRui Paulo if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, 12535b9c547cSRui Paulo sae->tmp->order, sae->tmp->prime, res) < 0 || 12545b9c547cSRui Paulo !crypto_bignum_is_one(res)) { 12555b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); 12565b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 12575b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 12585b9c547cSRui Paulo } 12595b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 12605b9c547cSRui Paulo 126185732ac8SCy Schubert *pos += sae->tmp->prime_len; 126285732ac8SCy Schubert 12635b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 12645b9c547cSRui Paulo } 12655b9c547cSRui Paulo 12665b9c547cSRui Paulo 126785732ac8SCy Schubert static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, 12685b9c547cSRui Paulo const u8 *end) 12695b9c547cSRui Paulo { 12705b9c547cSRui Paulo if (sae->tmp->dh) 12715b9c547cSRui Paulo return sae_parse_commit_element_ffc(sae, pos, end); 12725b9c547cSRui Paulo return sae_parse_commit_element_ecc(sae, pos, end); 12735b9c547cSRui Paulo } 12745b9c547cSRui Paulo 12755b9c547cSRui Paulo 127685732ac8SCy Schubert static int sae_parse_password_identifier(struct sae_data *sae, 127785732ac8SCy Schubert const u8 *pos, const u8 *end) 127885732ac8SCy Schubert { 127985732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 128085732ac8SCy Schubert pos, end - pos); 128185732ac8SCy Schubert if (!sae_is_password_id_elem(pos, end)) { 128285732ac8SCy Schubert if (sae->tmp->pw_id) { 128385732ac8SCy Schubert wpa_printf(MSG_DEBUG, 128485732ac8SCy Schubert "SAE: No Password Identifier included, but expected one (%s)", 128585732ac8SCy Schubert sae->tmp->pw_id); 128685732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 128785732ac8SCy Schubert } 128885732ac8SCy Schubert os_free(sae->tmp->pw_id); 128985732ac8SCy Schubert sae->tmp->pw_id = NULL; 129085732ac8SCy Schubert return WLAN_STATUS_SUCCESS; /* No Password Identifier */ 129185732ac8SCy Schubert } 129285732ac8SCy Schubert 129385732ac8SCy Schubert if (sae->tmp->pw_id && 129485732ac8SCy Schubert (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) || 129585732ac8SCy Schubert os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) { 129685732ac8SCy Schubert wpa_printf(MSG_DEBUG, 129785732ac8SCy Schubert "SAE: The included Password Identifier does not match the expected one (%s)", 129885732ac8SCy Schubert sae->tmp->pw_id); 129985732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 130085732ac8SCy Schubert } 130185732ac8SCy Schubert 130285732ac8SCy Schubert os_free(sae->tmp->pw_id); 130385732ac8SCy Schubert sae->tmp->pw_id = os_malloc(pos[1]); 130485732ac8SCy Schubert if (!sae->tmp->pw_id) 130585732ac8SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 130685732ac8SCy Schubert os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1); 130785732ac8SCy Schubert sae->tmp->pw_id[pos[1] - 1] = '\0'; 130885732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier", 130985732ac8SCy Schubert sae->tmp->pw_id, pos[1] - 1); 131085732ac8SCy Schubert return WLAN_STATUS_SUCCESS; 131185732ac8SCy Schubert } 131285732ac8SCy Schubert 131385732ac8SCy Schubert 13145b9c547cSRui Paulo u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, 13155b9c547cSRui Paulo const u8 **token, size_t *token_len, int *allowed_groups) 13165b9c547cSRui Paulo { 13175b9c547cSRui Paulo const u8 *pos = data, *end = data + len; 13185b9c547cSRui Paulo u16 res; 13195b9c547cSRui Paulo 13205b9c547cSRui Paulo /* Check Finite Cyclic Group */ 1321780fb4a2SCy Schubert if (end - pos < 2) 13225b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 13235b9c547cSRui Paulo res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); 13245b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 13255b9c547cSRui Paulo return res; 13265b9c547cSRui Paulo pos += 2; 13275b9c547cSRui Paulo 13285b9c547cSRui Paulo /* Optional Anti-Clogging Token */ 13295b9c547cSRui Paulo sae_parse_commit_token(sae, &pos, end, token, token_len); 13305b9c547cSRui Paulo 13315b9c547cSRui Paulo /* commit-scalar */ 13325b9c547cSRui Paulo res = sae_parse_commit_scalar(sae, &pos, end); 13335b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 13345b9c547cSRui Paulo return res; 13355b9c547cSRui Paulo 13365b9c547cSRui Paulo /* commit-element */ 133785732ac8SCy Schubert res = sae_parse_commit_element(sae, &pos, end); 133885732ac8SCy Schubert if (res != WLAN_STATUS_SUCCESS) 133985732ac8SCy Schubert return res; 134085732ac8SCy Schubert 134185732ac8SCy Schubert /* Optional Password Identifier element */ 134285732ac8SCy Schubert res = sae_parse_password_identifier(sae, pos, end); 1343325151a3SRui Paulo if (res != WLAN_STATUS_SUCCESS) 1344325151a3SRui Paulo return res; 1345325151a3SRui Paulo 1346325151a3SRui Paulo /* 1347325151a3SRui Paulo * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as 1348325151a3SRui Paulo * the values we sent which would be evidence of a reflection attack. 1349325151a3SRui Paulo */ 1350325151a3SRui Paulo if (!sae->tmp->own_commit_scalar || 1351325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_scalar, 1352325151a3SRui Paulo sae->peer_commit_scalar) != 0 || 1353325151a3SRui Paulo (sae->tmp->dh && 1354325151a3SRui Paulo (!sae->tmp->own_commit_element_ffc || 1355325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, 1356325151a3SRui Paulo sae->tmp->peer_commit_element_ffc) != 0)) || 1357325151a3SRui Paulo (sae->tmp->ec && 1358325151a3SRui Paulo (!sae->tmp->own_commit_element_ecc || 1359325151a3SRui Paulo crypto_ec_point_cmp(sae->tmp->ec, 1360325151a3SRui Paulo sae->tmp->own_commit_element_ecc, 1361325151a3SRui Paulo sae->tmp->peer_commit_element_ecc) != 0))) 1362325151a3SRui Paulo return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ 1363325151a3SRui Paulo 1364325151a3SRui Paulo /* 1365325151a3SRui Paulo * This is a reflection attack - return special value to trigger caller 1366325151a3SRui Paulo * to silently discard the frame instead of replying with a specific 1367325151a3SRui Paulo * status code. 1368325151a3SRui Paulo */ 1369325151a3SRui Paulo return SAE_SILENTLY_DISCARD; 13705b9c547cSRui Paulo } 13715b9c547cSRui Paulo 13725b9c547cSRui Paulo 13735b9c547cSRui Paulo static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, 13745b9c547cSRui Paulo const struct crypto_bignum *scalar1, 13755b9c547cSRui Paulo const u8 *element1, size_t element1_len, 13765b9c547cSRui Paulo const struct crypto_bignum *scalar2, 13775b9c547cSRui Paulo const u8 *element2, size_t element2_len, 13785b9c547cSRui Paulo u8 *confirm) 13795b9c547cSRui Paulo { 13805b9c547cSRui Paulo const u8 *addr[5]; 13815b9c547cSRui Paulo size_t len[5]; 13825b9c547cSRui Paulo u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; 13835b9c547cSRui Paulo 13845b9c547cSRui Paulo /* Confirm 13855b9c547cSRui Paulo * CN(key, X, Y, Z, ...) = 13865b9c547cSRui Paulo * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) 13875b9c547cSRui Paulo * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, 13885b9c547cSRui Paulo * peer-commit-scalar, PEER-COMMIT-ELEMENT) 13895b9c547cSRui Paulo * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, 13905b9c547cSRui Paulo * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) 13915b9c547cSRui Paulo */ 13925b9c547cSRui Paulo addr[0] = sc; 13935b9c547cSRui Paulo len[0] = 2; 13945b9c547cSRui Paulo crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), 13955b9c547cSRui Paulo sae->tmp->prime_len); 13965b9c547cSRui Paulo addr[1] = scalar_b1; 13975b9c547cSRui Paulo len[1] = sae->tmp->prime_len; 13985b9c547cSRui Paulo addr[2] = element1; 13995b9c547cSRui Paulo len[2] = element1_len; 14005b9c547cSRui Paulo crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), 14015b9c547cSRui Paulo sae->tmp->prime_len); 14025b9c547cSRui Paulo addr[3] = scalar_b2; 14035b9c547cSRui Paulo len[3] = sae->tmp->prime_len; 14045b9c547cSRui Paulo addr[4] = element2; 14055b9c547cSRui Paulo len[4] = element2_len; 14065b9c547cSRui Paulo hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, 14075b9c547cSRui Paulo confirm); 14085b9c547cSRui Paulo } 14095b9c547cSRui Paulo 14105b9c547cSRui Paulo 14115b9c547cSRui Paulo static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, 14125b9c547cSRui Paulo const struct crypto_bignum *scalar1, 14135b9c547cSRui Paulo const struct crypto_ec_point *element1, 14145b9c547cSRui Paulo const struct crypto_bignum *scalar2, 14155b9c547cSRui Paulo const struct crypto_ec_point *element2, 14165b9c547cSRui Paulo u8 *confirm) 14175b9c547cSRui Paulo { 14185b9c547cSRui Paulo u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; 14195b9c547cSRui Paulo u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; 14205b9c547cSRui Paulo 14215b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, 14225b9c547cSRui Paulo element_b1 + sae->tmp->prime_len); 14235b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, 14245b9c547cSRui Paulo element_b2 + sae->tmp->prime_len); 14255b9c547cSRui Paulo 14265b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, 14275b9c547cSRui Paulo scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); 14285b9c547cSRui Paulo } 14295b9c547cSRui Paulo 14305b9c547cSRui Paulo 14315b9c547cSRui Paulo static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, 14325b9c547cSRui Paulo const struct crypto_bignum *scalar1, 14335b9c547cSRui Paulo const struct crypto_bignum *element1, 14345b9c547cSRui Paulo const struct crypto_bignum *scalar2, 14355b9c547cSRui Paulo const struct crypto_bignum *element2, 14365b9c547cSRui Paulo u8 *confirm) 14375b9c547cSRui Paulo { 14385b9c547cSRui Paulo u8 element_b1[SAE_MAX_PRIME_LEN]; 14395b9c547cSRui Paulo u8 element_b2[SAE_MAX_PRIME_LEN]; 14405b9c547cSRui Paulo 14415b9c547cSRui Paulo crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), 14425b9c547cSRui Paulo sae->tmp->prime_len); 14435b9c547cSRui Paulo crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), 14445b9c547cSRui Paulo sae->tmp->prime_len); 14455b9c547cSRui Paulo 14465b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, 14475b9c547cSRui Paulo scalar2, element_b2, sae->tmp->prime_len, confirm); 14485b9c547cSRui Paulo } 14495b9c547cSRui Paulo 14505b9c547cSRui Paulo 14515b9c547cSRui Paulo void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) 14525b9c547cSRui Paulo { 14535b9c547cSRui Paulo const u8 *sc; 14545b9c547cSRui Paulo 14555b9c547cSRui Paulo if (sae->tmp == NULL) 14565b9c547cSRui Paulo return; 14575b9c547cSRui Paulo 14585b9c547cSRui Paulo /* Send-Confirm */ 14595b9c547cSRui Paulo sc = wpabuf_put(buf, 0); 14605b9c547cSRui Paulo wpabuf_put_le16(buf, sae->send_confirm); 146185732ac8SCy Schubert if (sae->send_confirm < 0xffff) 14625b9c547cSRui Paulo sae->send_confirm++; 14635b9c547cSRui Paulo 14645b9c547cSRui Paulo if (sae->tmp->ec) 14655b9c547cSRui Paulo sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, 14665b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 14675b9c547cSRui Paulo sae->peer_commit_scalar, 14685b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 14695b9c547cSRui Paulo wpabuf_put(buf, SHA256_MAC_LEN)); 14705b9c547cSRui Paulo else 14715b9c547cSRui Paulo sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, 14725b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 14735b9c547cSRui Paulo sae->peer_commit_scalar, 14745b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 14755b9c547cSRui Paulo wpabuf_put(buf, SHA256_MAC_LEN)); 14765b9c547cSRui Paulo } 14775b9c547cSRui Paulo 14785b9c547cSRui Paulo 14795b9c547cSRui Paulo int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) 14805b9c547cSRui Paulo { 14815b9c547cSRui Paulo u8 verifier[SHA256_MAC_LEN]; 14825b9c547cSRui Paulo 14835b9c547cSRui Paulo if (len < 2 + SHA256_MAC_LEN) { 14845b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); 14855b9c547cSRui Paulo return -1; 14865b9c547cSRui Paulo } 14875b9c547cSRui Paulo 14885b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); 14895b9c547cSRui Paulo 14904bc52338SCy Schubert if (!sae->tmp || !sae->peer_commit_scalar || 14914bc52338SCy Schubert !sae->tmp->own_commit_scalar) { 14925b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); 14935b9c547cSRui Paulo return -1; 14945b9c547cSRui Paulo } 14955b9c547cSRui Paulo 14964bc52338SCy Schubert if (sae->tmp->ec) { 14974bc52338SCy Schubert if (!sae->tmp->peer_commit_element_ecc || 14984bc52338SCy Schubert !sae->tmp->own_commit_element_ecc) 14994bc52338SCy Schubert return -1; 15005b9c547cSRui Paulo sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, 15015b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 15025b9c547cSRui Paulo sae->tmp->own_commit_scalar, 15035b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 15045b9c547cSRui Paulo verifier); 15054bc52338SCy Schubert } else { 15064bc52338SCy Schubert if (!sae->tmp->peer_commit_element_ffc || 15074bc52338SCy Schubert !sae->tmp->own_commit_element_ffc) 15084bc52338SCy Schubert return -1; 15095b9c547cSRui Paulo sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, 15105b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 15115b9c547cSRui Paulo sae->tmp->own_commit_scalar, 15125b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 15135b9c547cSRui Paulo verifier); 15144bc52338SCy Schubert } 15155b9c547cSRui Paulo 15165b9c547cSRui Paulo if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) { 15175b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); 15185b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", 15195b9c547cSRui Paulo data + 2, SHA256_MAC_LEN); 15205b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", 15215b9c547cSRui Paulo verifier, SHA256_MAC_LEN); 15225b9c547cSRui Paulo return -1; 15235b9c547cSRui Paulo } 15245b9c547cSRui Paulo 15255b9c547cSRui Paulo return 0; 15265b9c547cSRui Paulo } 152785732ac8SCy Schubert 152885732ac8SCy Schubert 152985732ac8SCy Schubert const char * sae_state_txt(enum sae_state state) 153085732ac8SCy Schubert { 153185732ac8SCy Schubert switch (state) { 153285732ac8SCy Schubert case SAE_NOTHING: 153385732ac8SCy Schubert return "Nothing"; 153485732ac8SCy Schubert case SAE_COMMITTED: 153585732ac8SCy Schubert return "Committed"; 153685732ac8SCy Schubert case SAE_CONFIRMED: 153785732ac8SCy Schubert return "Confirmed"; 153885732ac8SCy Schubert case SAE_ACCEPTED: 153985732ac8SCy Schubert return "Accepted"; 154085732ac8SCy Schubert } 154185732ac8SCy Schubert return "?"; 154285732ac8SCy Schubert } 1543