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" 125b9c547cSRui Paulo #include "crypto/crypto.h" 135b9c547cSRui Paulo #include "crypto/sha256.h" 145b9c547cSRui Paulo #include "crypto/random.h" 155b9c547cSRui Paulo #include "crypto/dh_groups.h" 165b9c547cSRui Paulo #include "ieee802_11_defs.h" 175b9c547cSRui Paulo #include "sae.h" 185b9c547cSRui Paulo 195b9c547cSRui Paulo 205b9c547cSRui Paulo int sae_set_group(struct sae_data *sae, int group) 215b9c547cSRui Paulo { 225b9c547cSRui Paulo struct sae_temporary_data *tmp; 235b9c547cSRui Paulo 245b9c547cSRui Paulo sae_clear_data(sae); 255b9c547cSRui Paulo tmp = sae->tmp = os_zalloc(sizeof(*tmp)); 265b9c547cSRui Paulo if (tmp == NULL) 275b9c547cSRui Paulo return -1; 285b9c547cSRui Paulo 295b9c547cSRui Paulo /* First, check if this is an ECC group */ 305b9c547cSRui Paulo tmp->ec = crypto_ec_init(group); 315b9c547cSRui Paulo if (tmp->ec) { 32*85732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", 33*85732ac8SCy Schubert group); 345b9c547cSRui Paulo sae->group = group; 355b9c547cSRui Paulo tmp->prime_len = crypto_ec_prime_len(tmp->ec); 365b9c547cSRui Paulo tmp->prime = crypto_ec_get_prime(tmp->ec); 375b9c547cSRui Paulo tmp->order = crypto_ec_get_order(tmp->ec); 385b9c547cSRui Paulo return 0; 395b9c547cSRui Paulo } 405b9c547cSRui Paulo 415b9c547cSRui Paulo /* Not an ECC group, check FFC */ 425b9c547cSRui Paulo tmp->dh = dh_groups_get(group); 435b9c547cSRui Paulo if (tmp->dh) { 44*85732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", 45*85732ac8SCy Schubert group); 465b9c547cSRui Paulo sae->group = group; 475b9c547cSRui Paulo tmp->prime_len = tmp->dh->prime_len; 485b9c547cSRui Paulo if (tmp->prime_len > SAE_MAX_PRIME_LEN) { 495b9c547cSRui Paulo sae_clear_data(sae); 505b9c547cSRui Paulo return -1; 515b9c547cSRui Paulo } 525b9c547cSRui Paulo 535b9c547cSRui Paulo tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, 545b9c547cSRui Paulo tmp->prime_len); 555b9c547cSRui Paulo if (tmp->prime_buf == NULL) { 565b9c547cSRui Paulo sae_clear_data(sae); 575b9c547cSRui Paulo return -1; 585b9c547cSRui Paulo } 595b9c547cSRui Paulo tmp->prime = tmp->prime_buf; 605b9c547cSRui Paulo 615b9c547cSRui Paulo tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, 625b9c547cSRui Paulo tmp->dh->order_len); 635b9c547cSRui Paulo if (tmp->order_buf == NULL) { 645b9c547cSRui Paulo sae_clear_data(sae); 655b9c547cSRui Paulo return -1; 665b9c547cSRui Paulo } 675b9c547cSRui Paulo tmp->order = tmp->order_buf; 685b9c547cSRui Paulo 695b9c547cSRui Paulo return 0; 705b9c547cSRui Paulo } 715b9c547cSRui Paulo 725b9c547cSRui Paulo /* Unsupported group */ 73*85732ac8SCy Schubert wpa_printf(MSG_DEBUG, 74*85732ac8SCy Schubert "SAE: Group %d not supported by the crypto library", group); 755b9c547cSRui Paulo return -1; 765b9c547cSRui Paulo } 775b9c547cSRui Paulo 785b9c547cSRui Paulo 795b9c547cSRui Paulo void sae_clear_temp_data(struct sae_data *sae) 805b9c547cSRui Paulo { 815b9c547cSRui Paulo struct sae_temporary_data *tmp; 825b9c547cSRui Paulo if (sae == NULL || sae->tmp == NULL) 835b9c547cSRui Paulo return; 845b9c547cSRui Paulo tmp = sae->tmp; 855b9c547cSRui Paulo crypto_ec_deinit(tmp->ec); 865b9c547cSRui Paulo crypto_bignum_deinit(tmp->prime_buf, 0); 875b9c547cSRui Paulo crypto_bignum_deinit(tmp->order_buf, 0); 885b9c547cSRui Paulo crypto_bignum_deinit(tmp->sae_rand, 1); 895b9c547cSRui Paulo crypto_bignum_deinit(tmp->pwe_ffc, 1); 905b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_scalar, 0); 915b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); 925b9c547cSRui Paulo crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); 935b9c547cSRui Paulo crypto_ec_point_deinit(tmp->pwe_ecc, 1); 945b9c547cSRui Paulo crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); 955b9c547cSRui Paulo crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); 965b9c547cSRui Paulo wpabuf_free(tmp->anti_clogging_token); 97*85732ac8SCy Schubert os_free(tmp->pw_id); 985b9c547cSRui Paulo bin_clear_free(tmp, sizeof(*tmp)); 995b9c547cSRui Paulo sae->tmp = NULL; 1005b9c547cSRui Paulo } 1015b9c547cSRui Paulo 1025b9c547cSRui Paulo 1035b9c547cSRui Paulo void sae_clear_data(struct sae_data *sae) 1045b9c547cSRui Paulo { 1055b9c547cSRui Paulo if (sae == NULL) 1065b9c547cSRui Paulo return; 1075b9c547cSRui Paulo sae_clear_temp_data(sae); 1085b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 1095b9c547cSRui Paulo os_memset(sae, 0, sizeof(*sae)); 1105b9c547cSRui Paulo } 1115b9c547cSRui Paulo 1125b9c547cSRui Paulo 1135b9c547cSRui Paulo static void buf_shift_right(u8 *buf, size_t len, size_t bits) 1145b9c547cSRui Paulo { 1155b9c547cSRui Paulo size_t i; 1165b9c547cSRui Paulo for (i = len - 1; i > 0; i--) 1175b9c547cSRui Paulo buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); 1185b9c547cSRui Paulo buf[0] >>= bits; 1195b9c547cSRui Paulo } 1205b9c547cSRui Paulo 1215b9c547cSRui Paulo 1225b9c547cSRui Paulo static struct crypto_bignum * sae_get_rand(struct sae_data *sae) 1235b9c547cSRui Paulo { 1245b9c547cSRui Paulo u8 val[SAE_MAX_PRIME_LEN]; 1255b9c547cSRui Paulo int iter = 0; 1265b9c547cSRui Paulo struct crypto_bignum *bn = NULL; 1275b9c547cSRui Paulo int order_len_bits = crypto_bignum_bits(sae->tmp->order); 1285b9c547cSRui Paulo size_t order_len = (order_len_bits + 7) / 8; 1295b9c547cSRui Paulo 1305b9c547cSRui Paulo if (order_len > sizeof(val)) 1315b9c547cSRui Paulo return NULL; 1325b9c547cSRui Paulo 1335b9c547cSRui Paulo for (;;) { 134325151a3SRui Paulo if (iter++ > 100 || random_get_bytes(val, order_len) < 0) 1355b9c547cSRui Paulo return NULL; 1365b9c547cSRui Paulo if (order_len_bits % 8) 1375b9c547cSRui Paulo buf_shift_right(val, order_len, 8 - order_len_bits % 8); 1385b9c547cSRui Paulo bn = crypto_bignum_init_set(val, order_len); 1395b9c547cSRui Paulo if (bn == NULL) 1405b9c547cSRui Paulo return NULL; 1415b9c547cSRui Paulo if (crypto_bignum_is_zero(bn) || 1425b9c547cSRui Paulo crypto_bignum_is_one(bn) || 1435b9c547cSRui Paulo crypto_bignum_cmp(bn, sae->tmp->order) >= 0) { 1445b9c547cSRui Paulo crypto_bignum_deinit(bn, 0); 1455b9c547cSRui Paulo continue; 1465b9c547cSRui Paulo } 1475b9c547cSRui Paulo break; 1485b9c547cSRui Paulo } 1495b9c547cSRui Paulo 1505b9c547cSRui Paulo os_memset(val, 0, order_len); 1515b9c547cSRui Paulo return bn; 1525b9c547cSRui Paulo } 1535b9c547cSRui Paulo 1545b9c547cSRui Paulo 1555b9c547cSRui Paulo static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) 1565b9c547cSRui Paulo { 1575b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->sae_rand, 1); 1585b9c547cSRui Paulo sae->tmp->sae_rand = sae_get_rand(sae); 1595b9c547cSRui Paulo if (sae->tmp->sae_rand == NULL) 1605b9c547cSRui Paulo return NULL; 1615b9c547cSRui Paulo return sae_get_rand(sae); 1625b9c547cSRui Paulo } 1635b9c547cSRui Paulo 1645b9c547cSRui Paulo 1655b9c547cSRui Paulo static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) 1665b9c547cSRui Paulo { 1675b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR 1685b9c547cSRui Paulo " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); 1695b9c547cSRui Paulo if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { 1705b9c547cSRui Paulo os_memcpy(key, addr1, ETH_ALEN); 1715b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); 1725b9c547cSRui Paulo } else { 1735b9c547cSRui Paulo os_memcpy(key, addr2, ETH_ALEN); 1745b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); 1755b9c547cSRui Paulo } 1765b9c547cSRui Paulo } 1775b9c547cSRui Paulo 1785b9c547cSRui Paulo 179325151a3SRui Paulo static struct crypto_bignum * 180325151a3SRui Paulo get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, 181325151a3SRui Paulo int *r_odd) 1825b9c547cSRui Paulo { 183325151a3SRui Paulo for (;;) { 184325151a3SRui Paulo struct crypto_bignum *r; 185325151a3SRui Paulo u8 tmp[SAE_MAX_ECC_PRIME_LEN]; 186325151a3SRui Paulo 187325151a3SRui Paulo if (random_get_bytes(tmp, prime_len) < 0) 188325151a3SRui Paulo break; 189325151a3SRui Paulo if (prime_bits % 8) 190325151a3SRui Paulo buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); 191325151a3SRui Paulo if (os_memcmp(tmp, prime, prime_len) >= 0) 192325151a3SRui Paulo continue; 193325151a3SRui Paulo r = crypto_bignum_init_set(tmp, prime_len); 194325151a3SRui Paulo if (!r) 195325151a3SRui Paulo break; 196325151a3SRui Paulo if (crypto_bignum_is_zero(r)) { 197325151a3SRui Paulo crypto_bignum_deinit(r, 0); 198325151a3SRui Paulo continue; 199325151a3SRui Paulo } 200325151a3SRui Paulo 201325151a3SRui Paulo *r_odd = tmp[prime_len - 1] & 0x01; 202325151a3SRui Paulo return r; 203325151a3SRui Paulo } 204325151a3SRui Paulo 205325151a3SRui Paulo return NULL; 206325151a3SRui Paulo } 207325151a3SRui Paulo 208325151a3SRui Paulo 209325151a3SRui Paulo static int is_quadratic_residue_blind(struct sae_data *sae, 210325151a3SRui Paulo const u8 *prime, size_t bits, 211325151a3SRui Paulo const struct crypto_bignum *qr, 212325151a3SRui Paulo const struct crypto_bignum *qnr, 213325151a3SRui Paulo const struct crypto_bignum *y_sqr) 214325151a3SRui Paulo { 215325151a3SRui Paulo struct crypto_bignum *r, *num; 216325151a3SRui Paulo int r_odd, check, res = -1; 217325151a3SRui Paulo 218325151a3SRui Paulo /* 219325151a3SRui Paulo * Use the blinding technique to mask y_sqr while determining 220325151a3SRui Paulo * whether it is a quadratic residue modulo p to avoid leaking 221325151a3SRui Paulo * timing information while determining the Legendre symbol. 222325151a3SRui Paulo * 223325151a3SRui Paulo * v = y_sqr 224325151a3SRui Paulo * r = a random number between 1 and p-1, inclusive 225325151a3SRui Paulo * num = (v * r * r) modulo p 226325151a3SRui Paulo */ 227325151a3SRui Paulo r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd); 228325151a3SRui Paulo if (!r) 229325151a3SRui Paulo return -1; 230325151a3SRui Paulo 231325151a3SRui Paulo num = crypto_bignum_init(); 232325151a3SRui Paulo if (!num || 233325151a3SRui Paulo crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 || 234325151a3SRui Paulo crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) 235325151a3SRui Paulo goto fail; 236325151a3SRui Paulo 237325151a3SRui Paulo if (r_odd) { 238325151a3SRui Paulo /* 239325151a3SRui Paulo * num = (num * qr) module p 240325151a3SRui Paulo * LGR(num, p) = 1 ==> quadratic residue 241325151a3SRui Paulo */ 242325151a3SRui Paulo if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0) 243325151a3SRui Paulo goto fail; 244325151a3SRui Paulo check = 1; 245325151a3SRui Paulo } else { 246325151a3SRui Paulo /* 247325151a3SRui Paulo * num = (num * qnr) module p 248325151a3SRui Paulo * LGR(num, p) = -1 ==> quadratic residue 249325151a3SRui Paulo */ 250325151a3SRui Paulo if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0) 251325151a3SRui Paulo goto fail; 252325151a3SRui Paulo check = -1; 253325151a3SRui Paulo } 254325151a3SRui Paulo 255325151a3SRui Paulo res = crypto_bignum_legendre(num, sae->tmp->prime); 256325151a3SRui Paulo if (res == -2) { 257325151a3SRui Paulo res = -1; 258325151a3SRui Paulo goto fail; 259325151a3SRui Paulo } 260325151a3SRui Paulo res = res == check; 261325151a3SRui Paulo fail: 262325151a3SRui Paulo crypto_bignum_deinit(num, 1); 263325151a3SRui Paulo crypto_bignum_deinit(r, 1); 264325151a3SRui Paulo return res; 265325151a3SRui Paulo } 266325151a3SRui Paulo 267325151a3SRui Paulo 268325151a3SRui Paulo static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, 269325151a3SRui Paulo const u8 *prime, 270325151a3SRui Paulo const struct crypto_bignum *qr, 271325151a3SRui Paulo const struct crypto_bignum *qnr, 272325151a3SRui Paulo struct crypto_bignum **ret_x_cand) 273325151a3SRui Paulo { 274325151a3SRui Paulo u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; 275325151a3SRui Paulo struct crypto_bignum *y_sqr, *x_cand; 276325151a3SRui Paulo int res; 2775b9c547cSRui Paulo size_t bits; 2785b9c547cSRui Paulo 279325151a3SRui Paulo *ret_x_cand = NULL; 2805b9c547cSRui Paulo 2815b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 2825b9c547cSRui Paulo 2835b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 2845b9c547cSRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec); 285780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 286780fb4a2SCy Schubert prime, sae->tmp->prime_len, pwd_value, bits) < 0) 287780fb4a2SCy Schubert return -1; 2885b9c547cSRui Paulo if (bits % 8) 2895b9c547cSRui Paulo buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); 2905b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", 2915b9c547cSRui Paulo pwd_value, sae->tmp->prime_len); 2925b9c547cSRui Paulo 2935b9c547cSRui Paulo if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) 2945b9c547cSRui Paulo return 0; 2955b9c547cSRui Paulo 296325151a3SRui Paulo x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 297325151a3SRui Paulo if (!x_cand) 2985b9c547cSRui Paulo return -1; 299325151a3SRui Paulo y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); 300325151a3SRui Paulo if (!y_sqr) { 301325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 302325151a3SRui Paulo return -1; 3035b9c547cSRui Paulo } 3045b9c547cSRui Paulo 305325151a3SRui Paulo res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); 306325151a3SRui Paulo crypto_bignum_deinit(y_sqr, 1); 307325151a3SRui Paulo if (res <= 0) { 308325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 309325151a3SRui Paulo return res; 310325151a3SRui Paulo } 3115b9c547cSRui Paulo 312325151a3SRui Paulo *ret_x_cand = x_cand; 3135b9c547cSRui Paulo return 1; 3145b9c547cSRui Paulo } 3155b9c547cSRui Paulo 3165b9c547cSRui Paulo 3175b9c547cSRui Paulo static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, 3185b9c547cSRui Paulo struct crypto_bignum *pwe) 3195b9c547cSRui Paulo { 3205b9c547cSRui Paulo u8 pwd_value[SAE_MAX_PRIME_LEN]; 3215b9c547cSRui Paulo size_t bits = sae->tmp->prime_len * 8; 3225b9c547cSRui Paulo u8 exp[1]; 3235b9c547cSRui Paulo struct crypto_bignum *a, *b; 3245b9c547cSRui Paulo int res; 3255b9c547cSRui Paulo 3265b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 3275b9c547cSRui Paulo 3285b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 329780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 3305b9c547cSRui Paulo sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, 331780fb4a2SCy Schubert bits) < 0) 332780fb4a2SCy Schubert return -1; 3335b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, 3345b9c547cSRui Paulo sae->tmp->prime_len); 3355b9c547cSRui Paulo 3365b9c547cSRui Paulo if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0) 3375b9c547cSRui Paulo { 3385b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p"); 3395b9c547cSRui Paulo return 0; 3405b9c547cSRui Paulo } 3415b9c547cSRui Paulo 3425b9c547cSRui Paulo /* PWE = pwd-value^((p-1)/r) modulo p */ 3435b9c547cSRui Paulo 3445b9c547cSRui Paulo a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 3455b9c547cSRui Paulo 3465b9c547cSRui Paulo if (sae->tmp->dh->safe_prime) { 3475b9c547cSRui Paulo /* 3485b9c547cSRui Paulo * r = (p-1)/2 for the group used here, so this becomes: 3495b9c547cSRui Paulo * PWE = pwd-value^2 modulo p 3505b9c547cSRui Paulo */ 3515b9c547cSRui Paulo exp[0] = 2; 3525b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 3535b9c547cSRui Paulo } else { 3545b9c547cSRui Paulo /* Calculate exponent: (p-1)/r */ 3555b9c547cSRui Paulo exp[0] = 1; 3565b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 3575b9c547cSRui Paulo if (b == NULL || 3585b9c547cSRui Paulo crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || 3595b9c547cSRui Paulo crypto_bignum_div(b, sae->tmp->order, b) < 0) { 3605b9c547cSRui Paulo crypto_bignum_deinit(b, 0); 3615b9c547cSRui Paulo b = NULL; 3625b9c547cSRui Paulo } 3635b9c547cSRui Paulo } 3645b9c547cSRui Paulo 3655b9c547cSRui Paulo if (a == NULL || b == NULL) 3665b9c547cSRui Paulo res = -1; 3675b9c547cSRui Paulo else 3685b9c547cSRui Paulo res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); 3695b9c547cSRui Paulo 3705b9c547cSRui Paulo crypto_bignum_deinit(a, 0); 3715b9c547cSRui Paulo crypto_bignum_deinit(b, 0); 3725b9c547cSRui Paulo 3735b9c547cSRui Paulo if (res < 0) { 3745b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); 3755b9c547cSRui Paulo return -1; 3765b9c547cSRui Paulo } 3775b9c547cSRui Paulo 3785b9c547cSRui Paulo /* if (PWE > 1) --> found */ 3795b9c547cSRui Paulo if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) { 3805b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE <= 1"); 3815b9c547cSRui Paulo return 0; 3825b9c547cSRui Paulo } 3835b9c547cSRui Paulo 3845b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE found"); 3855b9c547cSRui Paulo return 1; 3865b9c547cSRui Paulo } 3875b9c547cSRui Paulo 3885b9c547cSRui Paulo 389325151a3SRui Paulo static int get_random_qr_qnr(const u8 *prime, size_t prime_len, 390325151a3SRui Paulo const struct crypto_bignum *prime_bn, 391325151a3SRui Paulo size_t prime_bits, struct crypto_bignum **qr, 392325151a3SRui Paulo struct crypto_bignum **qnr) 393325151a3SRui Paulo { 394325151a3SRui Paulo *qr = NULL; 395325151a3SRui Paulo *qnr = NULL; 396325151a3SRui Paulo 397325151a3SRui Paulo while (!(*qr) || !(*qnr)) { 398325151a3SRui Paulo u8 tmp[SAE_MAX_ECC_PRIME_LEN]; 399325151a3SRui Paulo struct crypto_bignum *q; 400325151a3SRui Paulo int res; 401325151a3SRui Paulo 402325151a3SRui Paulo if (random_get_bytes(tmp, prime_len) < 0) 403325151a3SRui Paulo break; 404325151a3SRui Paulo if (prime_bits % 8) 405325151a3SRui Paulo buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); 406325151a3SRui Paulo if (os_memcmp(tmp, prime, prime_len) >= 0) 407325151a3SRui Paulo continue; 408325151a3SRui Paulo q = crypto_bignum_init_set(tmp, prime_len); 409325151a3SRui Paulo if (!q) 410325151a3SRui Paulo break; 411325151a3SRui Paulo res = crypto_bignum_legendre(q, prime_bn); 412325151a3SRui Paulo 413325151a3SRui Paulo if (res == 1 && !(*qr)) 414325151a3SRui Paulo *qr = q; 415325151a3SRui Paulo else if (res == -1 && !(*qnr)) 416325151a3SRui Paulo *qnr = q; 417325151a3SRui Paulo else 418325151a3SRui Paulo crypto_bignum_deinit(q, 0); 419325151a3SRui Paulo } 420325151a3SRui Paulo 421325151a3SRui Paulo return (*qr && *qnr) ? 0 : -1; 422325151a3SRui Paulo } 423325151a3SRui Paulo 424325151a3SRui Paulo 4255b9c547cSRui Paulo static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, 4265b9c547cSRui Paulo const u8 *addr2, const u8 *password, 427*85732ac8SCy Schubert size_t password_len, const char *identifier) 4285b9c547cSRui Paulo { 429325151a3SRui Paulo u8 counter, k = 40; 4305b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 431*85732ac8SCy Schubert const u8 *addr[3]; 432*85732ac8SCy Schubert size_t len[3]; 433*85732ac8SCy Schubert size_t num_elem; 434325151a3SRui Paulo u8 dummy_password[32]; 435325151a3SRui Paulo size_t dummy_password_len; 436325151a3SRui Paulo int pwd_seed_odd = 0; 437325151a3SRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 438325151a3SRui Paulo size_t prime_len; 439325151a3SRui Paulo struct crypto_bignum *x = NULL, *qr, *qnr; 440325151a3SRui Paulo size_t bits; 441325151a3SRui Paulo int res; 4425b9c547cSRui Paulo 443325151a3SRui Paulo dummy_password_len = password_len; 444325151a3SRui Paulo if (dummy_password_len > sizeof(dummy_password)) 445325151a3SRui Paulo dummy_password_len = sizeof(dummy_password); 446325151a3SRui Paulo if (random_get_bytes(dummy_password, dummy_password_len) < 0) 4475b9c547cSRui Paulo return -1; 448325151a3SRui Paulo 449325151a3SRui Paulo prime_len = sae->tmp->prime_len; 450325151a3SRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 451325151a3SRui Paulo prime_len) < 0) 452325151a3SRui Paulo return -1; 453325151a3SRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec); 454325151a3SRui Paulo 455325151a3SRui Paulo /* 456325151a3SRui Paulo * Create a random quadratic residue (qr) and quadratic non-residue 457325151a3SRui Paulo * (qnr) modulo p for blinding purposes during the loop. 458325151a3SRui Paulo */ 459325151a3SRui Paulo if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, 460325151a3SRui Paulo &qr, &qnr) < 0) 4615b9c547cSRui Paulo return -1; 4625b9c547cSRui Paulo 4635b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 4645b9c547cSRui Paulo password, password_len); 465*85732ac8SCy Schubert if (identifier) 466*85732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", 467*85732ac8SCy Schubert identifier); 4685b9c547cSRui Paulo 4695b9c547cSRui Paulo /* 4705b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 471*85732ac8SCy Schubert * base = password [|| identifier] 4725b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 473325151a3SRui Paulo * base || counter) 4745b9c547cSRui Paulo */ 4755b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 4765b9c547cSRui Paulo 4775b9c547cSRui Paulo addr[0] = password; 4785b9c547cSRui Paulo len[0] = password_len; 479*85732ac8SCy Schubert num_elem = 1; 480*85732ac8SCy Schubert if (identifier) { 481*85732ac8SCy Schubert addr[num_elem] = (const u8 *) identifier; 482*85732ac8SCy Schubert len[num_elem] = os_strlen(identifier); 483*85732ac8SCy Schubert num_elem++; 484*85732ac8SCy Schubert } 485*85732ac8SCy Schubert addr[num_elem] = &counter; 486*85732ac8SCy Schubert len[num_elem] = sizeof(counter); 487*85732ac8SCy Schubert num_elem++; 4885b9c547cSRui Paulo 4895b9c547cSRui Paulo /* 4905b9c547cSRui Paulo * Continue for at least k iterations to protect against side-channel 4915b9c547cSRui Paulo * attacks that attempt to determine the number of iterations required 4925b9c547cSRui Paulo * in the loop. 4935b9c547cSRui Paulo */ 494325151a3SRui Paulo for (counter = 1; counter <= k || !x; counter++) { 4955b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 496325151a3SRui Paulo struct crypto_bignum *x_cand; 4975b9c547cSRui Paulo 4985b9c547cSRui Paulo if (counter > 200) { 4995b9c547cSRui Paulo /* This should not happen in practice */ 5005b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 5015b9c547cSRui Paulo break; 5025b9c547cSRui Paulo } 5035b9c547cSRui Paulo 5045b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); 505*85732ac8SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, 506*85732ac8SCy Schubert addr, len, pwd_seed) < 0) 5075b9c547cSRui Paulo break; 508325151a3SRui Paulo 5095b9c547cSRui Paulo res = sae_test_pwd_seed_ecc(sae, pwd_seed, 510325151a3SRui Paulo prime, qr, qnr, &x_cand); 5115b9c547cSRui Paulo if (res < 0) 512325151a3SRui Paulo goto fail; 513325151a3SRui Paulo if (res > 0 && !x) { 514325151a3SRui Paulo wpa_printf(MSG_DEBUG, 515325151a3SRui Paulo "SAE: Selected pwd-seed with counter %u", 516325151a3SRui Paulo counter); 517325151a3SRui Paulo x = x_cand; 518325151a3SRui Paulo pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; 519325151a3SRui Paulo os_memset(pwd_seed, 0, sizeof(pwd_seed)); 520325151a3SRui Paulo 521325151a3SRui Paulo /* 522325151a3SRui Paulo * Use a dummy password for the following rounds, if 523325151a3SRui Paulo * any. 524325151a3SRui Paulo */ 525325151a3SRui Paulo addr[0] = dummy_password; 526325151a3SRui Paulo len[0] = dummy_password_len; 527325151a3SRui Paulo } else if (res > 0) { 528325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 5295b9c547cSRui Paulo } 5305b9c547cSRui Paulo } 5315b9c547cSRui Paulo 532325151a3SRui Paulo if (!x) { 533325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); 534325151a3SRui Paulo res = -1; 535325151a3SRui Paulo goto fail; 536325151a3SRui Paulo } 5375b9c547cSRui Paulo 538325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 539325151a3SRui Paulo sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); 540325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 541325151a3SRui Paulo res = -1; 542325151a3SRui Paulo else 543325151a3SRui Paulo res = crypto_ec_point_solve_y_coord(sae->tmp->ec, 544325151a3SRui Paulo sae->tmp->pwe_ecc, x, 545325151a3SRui Paulo pwd_seed_odd); 546325151a3SRui Paulo crypto_bignum_deinit(x, 1); 547325151a3SRui Paulo if (res < 0) { 548325151a3SRui Paulo /* 549325151a3SRui Paulo * This should not happen since we already checked that there 550325151a3SRui Paulo * is a result. 551325151a3SRui Paulo */ 552325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); 553325151a3SRui Paulo } 554325151a3SRui Paulo 555325151a3SRui Paulo fail: 556325151a3SRui Paulo crypto_bignum_deinit(qr, 0); 557325151a3SRui Paulo crypto_bignum_deinit(qnr, 0); 558325151a3SRui Paulo 559325151a3SRui Paulo return res; 5605b9c547cSRui Paulo } 5615b9c547cSRui Paulo 5625b9c547cSRui Paulo 5635b9c547cSRui Paulo static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, 5645b9c547cSRui Paulo const u8 *addr2, const u8 *password, 565*85732ac8SCy Schubert size_t password_len, const char *identifier) 5665b9c547cSRui Paulo { 5675b9c547cSRui Paulo u8 counter; 5685b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 569*85732ac8SCy Schubert const u8 *addr[3]; 570*85732ac8SCy Schubert size_t len[3]; 571*85732ac8SCy Schubert size_t num_elem; 5725b9c547cSRui Paulo int found = 0; 5735b9c547cSRui Paulo 5745b9c547cSRui Paulo if (sae->tmp->pwe_ffc == NULL) { 5755b9c547cSRui Paulo sae->tmp->pwe_ffc = crypto_bignum_init(); 5765b9c547cSRui Paulo if (sae->tmp->pwe_ffc == NULL) 5775b9c547cSRui Paulo return -1; 5785b9c547cSRui Paulo } 5795b9c547cSRui Paulo 5805b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 5815b9c547cSRui Paulo password, password_len); 5825b9c547cSRui Paulo 5835b9c547cSRui Paulo /* 5845b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 5855b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 586*85732ac8SCy Schubert * password [|| identifier] || counter) 5875b9c547cSRui Paulo */ 5885b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 5895b9c547cSRui Paulo 5905b9c547cSRui Paulo addr[0] = password; 5915b9c547cSRui Paulo len[0] = password_len; 592*85732ac8SCy Schubert num_elem = 1; 593*85732ac8SCy Schubert if (identifier) { 594*85732ac8SCy Schubert addr[num_elem] = (const u8 *) identifier; 595*85732ac8SCy Schubert len[num_elem] = os_strlen(identifier); 596*85732ac8SCy Schubert num_elem++; 597*85732ac8SCy Schubert } 598*85732ac8SCy Schubert addr[num_elem] = &counter; 599*85732ac8SCy Schubert len[num_elem] = sizeof(counter); 600*85732ac8SCy Schubert num_elem++; 6015b9c547cSRui Paulo 6025b9c547cSRui Paulo for (counter = 1; !found; counter++) { 6035b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 6045b9c547cSRui Paulo int res; 6055b9c547cSRui Paulo 6065b9c547cSRui Paulo if (counter > 200) { 6075b9c547cSRui Paulo /* This should not happen in practice */ 6085b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 6095b9c547cSRui Paulo break; 6105b9c547cSRui Paulo } 6115b9c547cSRui Paulo 6125b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); 613*85732ac8SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, 614*85732ac8SCy Schubert addr, len, pwd_seed) < 0) 6155b9c547cSRui Paulo break; 6165b9c547cSRui Paulo res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); 6175b9c547cSRui Paulo if (res < 0) 6185b9c547cSRui Paulo break; 6195b9c547cSRui Paulo if (res > 0) { 6205b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); 6215b9c547cSRui Paulo found = 1; 6225b9c547cSRui Paulo } 6235b9c547cSRui Paulo } 6245b9c547cSRui Paulo 6255b9c547cSRui Paulo return found ? 0 : -1; 6265b9c547cSRui Paulo } 6275b9c547cSRui Paulo 6285b9c547cSRui Paulo 6295b9c547cSRui Paulo static int sae_derive_commit_element_ecc(struct sae_data *sae, 6305b9c547cSRui Paulo struct crypto_bignum *mask) 6315b9c547cSRui Paulo { 6325b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 6335b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) { 6345b9c547cSRui Paulo sae->tmp->own_commit_element_ecc = 6355b9c547cSRui Paulo crypto_ec_point_init(sae->tmp->ec); 6365b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) 6375b9c547cSRui Paulo return -1; 6385b9c547cSRui Paulo } 6395b9c547cSRui Paulo 6405b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, 6415b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0 || 6425b9c547cSRui Paulo crypto_ec_point_invert(sae->tmp->ec, 6435b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0) { 6445b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 6455b9c547cSRui Paulo return -1; 6465b9c547cSRui Paulo } 6475b9c547cSRui Paulo 6485b9c547cSRui Paulo return 0; 6495b9c547cSRui Paulo } 6505b9c547cSRui Paulo 6515b9c547cSRui Paulo 6525b9c547cSRui Paulo static int sae_derive_commit_element_ffc(struct sae_data *sae, 6535b9c547cSRui Paulo struct crypto_bignum *mask) 6545b9c547cSRui Paulo { 6555b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 6565b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) { 6575b9c547cSRui Paulo sae->tmp->own_commit_element_ffc = crypto_bignum_init(); 6585b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) 6595b9c547cSRui Paulo return -1; 6605b9c547cSRui Paulo } 6615b9c547cSRui Paulo 6625b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, 6635b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0 || 6645b9c547cSRui Paulo crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, 6655b9c547cSRui Paulo sae->tmp->prime, 6665b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0) { 6675b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 6685b9c547cSRui Paulo return -1; 6695b9c547cSRui Paulo } 6705b9c547cSRui Paulo 6715b9c547cSRui Paulo return 0; 6725b9c547cSRui Paulo } 6735b9c547cSRui Paulo 6745b9c547cSRui Paulo 6755b9c547cSRui Paulo static int sae_derive_commit(struct sae_data *sae) 6765b9c547cSRui Paulo { 6775b9c547cSRui Paulo struct crypto_bignum *mask; 6785b9c547cSRui Paulo int ret = -1; 679325151a3SRui Paulo unsigned int counter = 0; 680325151a3SRui Paulo 681325151a3SRui Paulo do { 682325151a3SRui Paulo counter++; 683325151a3SRui Paulo if (counter > 100) { 684325151a3SRui Paulo /* 685325151a3SRui Paulo * This cannot really happen in practice if the random 686325151a3SRui Paulo * number generator is working. Anyway, to avoid even a 687325151a3SRui Paulo * theoretical infinite loop, break out after 100 688325151a3SRui Paulo * attemps. 689325151a3SRui Paulo */ 690325151a3SRui Paulo return -1; 691325151a3SRui Paulo } 6925b9c547cSRui Paulo 6935b9c547cSRui Paulo mask = sae_get_rand_and_mask(sae); 6945b9c547cSRui Paulo if (mask == NULL) { 6955b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); 6965b9c547cSRui Paulo return -1; 6975b9c547cSRui Paulo } 6985b9c547cSRui Paulo 6995b9c547cSRui Paulo /* commit-scalar = (rand + mask) modulo r */ 7005b9c547cSRui Paulo if (!sae->tmp->own_commit_scalar) { 7015b9c547cSRui Paulo sae->tmp->own_commit_scalar = crypto_bignum_init(); 7025b9c547cSRui Paulo if (!sae->tmp->own_commit_scalar) 7035b9c547cSRui Paulo goto fail; 7045b9c547cSRui Paulo } 7055b9c547cSRui Paulo crypto_bignum_add(sae->tmp->sae_rand, mask, 7065b9c547cSRui Paulo sae->tmp->own_commit_scalar); 7075b9c547cSRui Paulo crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, 7085b9c547cSRui Paulo sae->tmp->own_commit_scalar); 709325151a3SRui Paulo } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) || 710325151a3SRui Paulo crypto_bignum_is_one(sae->tmp->own_commit_scalar)); 7115b9c547cSRui Paulo 712325151a3SRui Paulo if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) || 713325151a3SRui Paulo (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)) 7145b9c547cSRui Paulo goto fail; 7155b9c547cSRui Paulo 7165b9c547cSRui Paulo ret = 0; 7175b9c547cSRui Paulo fail: 7185b9c547cSRui Paulo crypto_bignum_deinit(mask, 1); 7195b9c547cSRui Paulo return ret; 7205b9c547cSRui Paulo } 7215b9c547cSRui Paulo 7225b9c547cSRui Paulo 7235b9c547cSRui Paulo int sae_prepare_commit(const u8 *addr1, const u8 *addr2, 7245b9c547cSRui Paulo const u8 *password, size_t password_len, 725*85732ac8SCy Schubert const char *identifier, struct sae_data *sae) 7265b9c547cSRui Paulo { 727325151a3SRui Paulo if (sae->tmp == NULL || 728325151a3SRui Paulo (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, 729*85732ac8SCy Schubert password_len, 730*85732ac8SCy Schubert identifier) < 0) || 731325151a3SRui Paulo (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, 732*85732ac8SCy Schubert password_len, 733*85732ac8SCy Schubert identifier) < 0) || 734325151a3SRui Paulo sae_derive_commit(sae) < 0) 7355b9c547cSRui Paulo return -1; 7365b9c547cSRui Paulo return 0; 7375b9c547cSRui Paulo } 7385b9c547cSRui Paulo 7395b9c547cSRui Paulo 7405b9c547cSRui Paulo static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) 7415b9c547cSRui Paulo { 7425b9c547cSRui Paulo struct crypto_ec_point *K; 7435b9c547cSRui Paulo int ret = -1; 7445b9c547cSRui Paulo 7455b9c547cSRui Paulo K = crypto_ec_point_init(sae->tmp->ec); 7465b9c547cSRui Paulo if (K == NULL) 7475b9c547cSRui Paulo goto fail; 7485b9c547cSRui Paulo 7495b9c547cSRui Paulo /* 7505b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 7515b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 7525b9c547cSRui Paulo * If K is identity element (point-at-infinity), reject 7535b9c547cSRui Paulo * k = F(K) (= x coordinate) 7545b9c547cSRui Paulo */ 7555b9c547cSRui Paulo 7565b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, 7575b9c547cSRui Paulo sae->peer_commit_scalar, K) < 0 || 7585b9c547cSRui Paulo crypto_ec_point_add(sae->tmp->ec, K, 7595b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, K) < 0 || 7605b9c547cSRui Paulo crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || 7615b9c547cSRui Paulo crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || 7625b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { 7635b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 7645b9c547cSRui Paulo goto fail; 7655b9c547cSRui Paulo } 7665b9c547cSRui Paulo 7675b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 7685b9c547cSRui Paulo 7695b9c547cSRui Paulo ret = 0; 7705b9c547cSRui Paulo fail: 7715b9c547cSRui Paulo crypto_ec_point_deinit(K, 1); 7725b9c547cSRui Paulo return ret; 7735b9c547cSRui Paulo } 7745b9c547cSRui Paulo 7755b9c547cSRui Paulo 7765b9c547cSRui Paulo static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) 7775b9c547cSRui Paulo { 7785b9c547cSRui Paulo struct crypto_bignum *K; 7795b9c547cSRui Paulo int ret = -1; 7805b9c547cSRui Paulo 7815b9c547cSRui Paulo K = crypto_bignum_init(); 7825b9c547cSRui Paulo if (K == NULL) 7835b9c547cSRui Paulo goto fail; 7845b9c547cSRui Paulo 7855b9c547cSRui Paulo /* 7865b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 7875b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 7885b9c547cSRui Paulo * If K is identity element (one), reject. 7895b9c547cSRui Paulo * k = F(K) (= x coordinate) 7905b9c547cSRui Paulo */ 7915b9c547cSRui Paulo 7925b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, 7935b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 7945b9c547cSRui Paulo crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, 7955b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 7965b9c547cSRui Paulo crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 7975b9c547cSRui Paulo || 7985b9c547cSRui Paulo crypto_bignum_is_one(K) || 7995b9c547cSRui Paulo crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < 8005b9c547cSRui Paulo 0) { 8015b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 8025b9c547cSRui Paulo goto fail; 8035b9c547cSRui Paulo } 8045b9c547cSRui Paulo 8055b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 8065b9c547cSRui Paulo 8075b9c547cSRui Paulo ret = 0; 8085b9c547cSRui Paulo fail: 8095b9c547cSRui Paulo crypto_bignum_deinit(K, 1); 8105b9c547cSRui Paulo return ret; 8115b9c547cSRui Paulo } 8125b9c547cSRui Paulo 8135b9c547cSRui Paulo 8145b9c547cSRui Paulo static int sae_derive_keys(struct sae_data *sae, const u8 *k) 8155b9c547cSRui Paulo { 8165b9c547cSRui Paulo u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; 8175b9c547cSRui Paulo u8 keyseed[SHA256_MAC_LEN]; 8185b9c547cSRui Paulo u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; 8195b9c547cSRui Paulo struct crypto_bignum *tmp; 8205b9c547cSRui Paulo int ret = -1; 8215b9c547cSRui Paulo 8225b9c547cSRui Paulo tmp = crypto_bignum_init(); 8235b9c547cSRui Paulo if (tmp == NULL) 8245b9c547cSRui Paulo goto fail; 8255b9c547cSRui Paulo 8265b9c547cSRui Paulo /* keyseed = H(<0>32, k) 8275b9c547cSRui Paulo * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK", 8285b9c547cSRui Paulo * (commit-scalar + peer-commit-scalar) modulo r) 8295b9c547cSRui Paulo * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) 8305b9c547cSRui Paulo */ 8315b9c547cSRui Paulo 8325b9c547cSRui Paulo os_memset(null_key, 0, sizeof(null_key)); 8335b9c547cSRui Paulo hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, 8345b9c547cSRui Paulo keyseed); 8355b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); 8365b9c547cSRui Paulo 8375b9c547cSRui Paulo crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, 8385b9c547cSRui Paulo tmp); 8395b9c547cSRui Paulo crypto_bignum_mod(tmp, sae->tmp->order, tmp); 8405b9c547cSRui Paulo crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); 8415b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); 842780fb4a2SCy Schubert if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", 843780fb4a2SCy Schubert val, sae->tmp->prime_len, keys, sizeof(keys)) < 0) 844780fb4a2SCy Schubert goto fail; 8455b9c547cSRui Paulo os_memset(keyseed, 0, sizeof(keyseed)); 8465b9c547cSRui Paulo os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); 8475b9c547cSRui Paulo os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); 848780fb4a2SCy Schubert os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); 8495b9c547cSRui Paulo os_memset(keys, 0, sizeof(keys)); 8505b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); 8515b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); 8525b9c547cSRui Paulo 8535b9c547cSRui Paulo ret = 0; 8545b9c547cSRui Paulo fail: 8555b9c547cSRui Paulo crypto_bignum_deinit(tmp, 0); 8565b9c547cSRui Paulo return ret; 8575b9c547cSRui Paulo } 8585b9c547cSRui Paulo 8595b9c547cSRui Paulo 8605b9c547cSRui Paulo int sae_process_commit(struct sae_data *sae) 8615b9c547cSRui Paulo { 8625b9c547cSRui Paulo u8 k[SAE_MAX_PRIME_LEN]; 8635b9c547cSRui Paulo if (sae->tmp == NULL || 8645b9c547cSRui Paulo (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || 8655b9c547cSRui Paulo (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || 8665b9c547cSRui Paulo sae_derive_keys(sae, k) < 0) 8675b9c547cSRui Paulo return -1; 8685b9c547cSRui Paulo return 0; 8695b9c547cSRui Paulo } 8705b9c547cSRui Paulo 8715b9c547cSRui Paulo 8725b9c547cSRui Paulo void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, 873*85732ac8SCy Schubert const struct wpabuf *token, const char *identifier) 8745b9c547cSRui Paulo { 8755b9c547cSRui Paulo u8 *pos; 8765b9c547cSRui Paulo 8775b9c547cSRui Paulo if (sae->tmp == NULL) 8785b9c547cSRui Paulo return; 8795b9c547cSRui Paulo 8805b9c547cSRui Paulo wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ 8815b9c547cSRui Paulo if (token) { 8825b9c547cSRui Paulo wpabuf_put_buf(buf, token); 8835b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", 8845b9c547cSRui Paulo wpabuf_head(token), wpabuf_len(token)); 8855b9c547cSRui Paulo } 8865b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 8875b9c547cSRui Paulo crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, 8885b9c547cSRui Paulo sae->tmp->prime_len, sae->tmp->prime_len); 8895b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", 8905b9c547cSRui Paulo pos, sae->tmp->prime_len); 8915b9c547cSRui Paulo if (sae->tmp->ec) { 8925b9c547cSRui Paulo pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); 8935b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, 8945b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 8955b9c547cSRui Paulo pos, pos + sae->tmp->prime_len); 8965b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", 8975b9c547cSRui Paulo pos, sae->tmp->prime_len); 8985b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", 8995b9c547cSRui Paulo pos + sae->tmp->prime_len, sae->tmp->prime_len); 9005b9c547cSRui Paulo } else { 9015b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 9025b9c547cSRui Paulo crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, 9035b9c547cSRui Paulo sae->tmp->prime_len, sae->tmp->prime_len); 9045b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", 9055b9c547cSRui Paulo pos, sae->tmp->prime_len); 9065b9c547cSRui Paulo } 907*85732ac8SCy Schubert 908*85732ac8SCy Schubert if (identifier) { 909*85732ac8SCy Schubert /* Password Identifier element */ 910*85732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 911*85732ac8SCy Schubert wpabuf_put_u8(buf, 1 + os_strlen(identifier)); 912*85732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); 913*85732ac8SCy Schubert wpabuf_put_str(buf, identifier); 914*85732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", 915*85732ac8SCy Schubert identifier); 916*85732ac8SCy Schubert } 9175b9c547cSRui Paulo } 9185b9c547cSRui Paulo 9195b9c547cSRui Paulo 9205b9c547cSRui Paulo u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) 9215b9c547cSRui Paulo { 9225b9c547cSRui Paulo if (allowed_groups) { 9235b9c547cSRui Paulo int i; 9245b9c547cSRui Paulo for (i = 0; allowed_groups[i] > 0; i++) { 9255b9c547cSRui Paulo if (allowed_groups[i] == group) 9265b9c547cSRui Paulo break; 9275b9c547cSRui Paulo } 9285b9c547cSRui Paulo if (allowed_groups[i] != group) { 9295b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " 9305b9c547cSRui Paulo "enabled in the current configuration", 9315b9c547cSRui Paulo group); 9325b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 9335b9c547cSRui Paulo } 9345b9c547cSRui Paulo } 9355b9c547cSRui Paulo 9365b9c547cSRui Paulo if (sae->state == SAE_COMMITTED && group != sae->group) { 9375b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); 9385b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 9395b9c547cSRui Paulo } 9405b9c547cSRui Paulo 9415b9c547cSRui Paulo if (group != sae->group && sae_set_group(sae, group) < 0) { 9425b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", 9435b9c547cSRui Paulo group); 9445b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 9455b9c547cSRui Paulo } 9465b9c547cSRui Paulo 9475b9c547cSRui Paulo if (sae->tmp == NULL) { 9485b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); 9495b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9505b9c547cSRui Paulo } 9515b9c547cSRui Paulo 9525b9c547cSRui Paulo if (sae->tmp->dh && !allowed_groups) { 9535b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " 9545b9c547cSRui Paulo "explicit configuration enabling it", group); 9555b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 9565b9c547cSRui Paulo } 9575b9c547cSRui Paulo 9585b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 9595b9c547cSRui Paulo } 9605b9c547cSRui Paulo 9615b9c547cSRui Paulo 962*85732ac8SCy Schubert static int sae_is_password_id_elem(const u8 *pos, const u8 *end) 963*85732ac8SCy Schubert { 964*85732ac8SCy Schubert return end - pos >= 3 && 965*85732ac8SCy Schubert pos[0] == WLAN_EID_EXTENSION && 966*85732ac8SCy Schubert pos[1] >= 1 && 967*85732ac8SCy Schubert end - pos - 2 >= pos[1] && 968*85732ac8SCy Schubert pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; 969*85732ac8SCy Schubert } 970*85732ac8SCy Schubert 971*85732ac8SCy Schubert 9725b9c547cSRui Paulo static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, 9735b9c547cSRui Paulo const u8 *end, const u8 **token, 9745b9c547cSRui Paulo size_t *token_len) 9755b9c547cSRui Paulo { 976*85732ac8SCy Schubert size_t scalar_elem_len, tlen; 977*85732ac8SCy Schubert const u8 *elem; 978*85732ac8SCy Schubert 979*85732ac8SCy Schubert if (token) 980*85732ac8SCy Schubert *token = NULL; 981*85732ac8SCy Schubert if (token_len) 982*85732ac8SCy Schubert *token_len = 0; 983*85732ac8SCy Schubert 984*85732ac8SCy Schubert scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; 985*85732ac8SCy Schubert if (scalar_elem_len >= (size_t) (end - *pos)) 986*85732ac8SCy Schubert return; /* No extra data beyond peer scalar and element */ 987*85732ac8SCy Schubert 988*85732ac8SCy Schubert /* It is a bit difficult to parse this now that there is an 989*85732ac8SCy Schubert * optional variable length Anti-Clogging Token field and 990*85732ac8SCy Schubert * optional variable length Password Identifier element in the 991*85732ac8SCy Schubert * frame. We are sending out fixed length Anti-Clogging Token 992*85732ac8SCy Schubert * fields, so use that length as a requirement for the received 993*85732ac8SCy Schubert * token and check for the presence of possible Password 994*85732ac8SCy Schubert * Identifier element based on the element header information. 995*85732ac8SCy Schubert */ 996*85732ac8SCy Schubert tlen = end - (*pos + scalar_elem_len); 997*85732ac8SCy Schubert 998*85732ac8SCy Schubert if (tlen < SHA256_MAC_LEN) { 999*85732ac8SCy Schubert wpa_printf(MSG_DEBUG, 1000*85732ac8SCy Schubert "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", 1001*85732ac8SCy Schubert (unsigned int) tlen); 1002*85732ac8SCy Schubert return; 1003*85732ac8SCy Schubert } 1004*85732ac8SCy Schubert 1005*85732ac8SCy Schubert elem = *pos + scalar_elem_len; 1006*85732ac8SCy Schubert if (sae_is_password_id_elem(elem, end)) { 1007*85732ac8SCy Schubert /* Password Identifier element takes out all available 1008*85732ac8SCy Schubert * extra octets, so there can be no Anti-Clogging token in 1009*85732ac8SCy Schubert * this frame. */ 1010*85732ac8SCy Schubert return; 1011*85732ac8SCy Schubert } 1012*85732ac8SCy Schubert 1013*85732ac8SCy Schubert elem += SHA256_MAC_LEN; 1014*85732ac8SCy Schubert if (sae_is_password_id_elem(elem, end)) { 1015*85732ac8SCy Schubert /* Password Identifier element is included in the end, so 1016*85732ac8SCy Schubert * remove its length from the Anti-Clogging token field. */ 1017*85732ac8SCy Schubert tlen -= 2 + elem[1]; 1018*85732ac8SCy Schubert } 1019*85732ac8SCy Schubert 10205b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); 10215b9c547cSRui Paulo if (token) 10225b9c547cSRui Paulo *token = *pos; 10235b9c547cSRui Paulo if (token_len) 10245b9c547cSRui Paulo *token_len = tlen; 10255b9c547cSRui Paulo *pos += tlen; 10265b9c547cSRui Paulo } 10275b9c547cSRui Paulo 10285b9c547cSRui Paulo 10295b9c547cSRui Paulo static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, 10305b9c547cSRui Paulo const u8 *end) 10315b9c547cSRui Paulo { 10325b9c547cSRui Paulo struct crypto_bignum *peer_scalar; 10335b9c547cSRui Paulo 1034780fb4a2SCy Schubert if (sae->tmp->prime_len > end - *pos) { 10355b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); 10365b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10375b9c547cSRui Paulo } 10385b9c547cSRui Paulo 10395b9c547cSRui Paulo peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); 10405b9c547cSRui Paulo if (peer_scalar == NULL) 10415b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10425b9c547cSRui Paulo 10435b9c547cSRui Paulo /* 10445b9c547cSRui Paulo * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for 10455b9c547cSRui Paulo * the peer and it is in Authenticated state, the new Commit Message 10465b9c547cSRui Paulo * shall be dropped if the peer-scalar is identical to the one used in 10475b9c547cSRui Paulo * the existing protocol instance. 10485b9c547cSRui Paulo */ 10495b9c547cSRui Paulo if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && 10505b9c547cSRui Paulo crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { 10515b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " 10525b9c547cSRui Paulo "peer-commit-scalar"); 10535b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 10545b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10555b9c547cSRui Paulo } 10565b9c547cSRui Paulo 1057325151a3SRui Paulo /* 1 < scalar < r */ 10585b9c547cSRui Paulo if (crypto_bignum_is_zero(peer_scalar) || 1059325151a3SRui Paulo crypto_bignum_is_one(peer_scalar) || 10605b9c547cSRui Paulo crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { 10615b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); 10625b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 10635b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10645b9c547cSRui Paulo } 10655b9c547cSRui Paulo 10665b9c547cSRui Paulo 10675b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 10685b9c547cSRui Paulo sae->peer_commit_scalar = peer_scalar; 10695b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", 10705b9c547cSRui Paulo *pos, sae->tmp->prime_len); 10715b9c547cSRui Paulo *pos += sae->tmp->prime_len; 10725b9c547cSRui Paulo 10735b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 10745b9c547cSRui Paulo } 10755b9c547cSRui Paulo 10765b9c547cSRui Paulo 1077*85732ac8SCy Schubert static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, 10785b9c547cSRui Paulo const u8 *end) 10795b9c547cSRui Paulo { 10805b9c547cSRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 10815b9c547cSRui Paulo 1082*85732ac8SCy Schubert if (2 * sae->tmp->prime_len > end - *pos) { 10835b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 10845b9c547cSRui Paulo "commit-element"); 10855b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10865b9c547cSRui Paulo } 10875b9c547cSRui Paulo 10885b9c547cSRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 10895b9c547cSRui Paulo sae->tmp->prime_len) < 0) 10905b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10915b9c547cSRui Paulo 10925b9c547cSRui Paulo /* element x and y coordinates < p */ 1093*85732ac8SCy Schubert if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || 1094*85732ac8SCy Schubert os_memcmp(*pos + sae->tmp->prime_len, prime, 10955b9c547cSRui Paulo sae->tmp->prime_len) >= 0) { 10965b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " 10975b9c547cSRui Paulo "element"); 10985b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10995b9c547cSRui Paulo } 11005b9c547cSRui Paulo 11015b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", 1102*85732ac8SCy Schubert *pos, sae->tmp->prime_len); 11035b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", 1104*85732ac8SCy Schubert *pos + sae->tmp->prime_len, sae->tmp->prime_len); 11055b9c547cSRui Paulo 11065b9c547cSRui Paulo crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); 11075b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc = 1108*85732ac8SCy Schubert crypto_ec_point_from_bin(sae->tmp->ec, *pos); 11095b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ecc == NULL) 11105b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11115b9c547cSRui Paulo 11125b9c547cSRui Paulo if (!crypto_ec_point_is_on_curve(sae->tmp->ec, 11135b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc)) { 11145b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); 11155b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11165b9c547cSRui Paulo } 11175b9c547cSRui Paulo 1118*85732ac8SCy Schubert *pos += 2 * sae->tmp->prime_len; 1119*85732ac8SCy Schubert 11205b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 11215b9c547cSRui Paulo } 11225b9c547cSRui Paulo 11235b9c547cSRui Paulo 1124*85732ac8SCy Schubert static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, 11255b9c547cSRui Paulo const u8 *end) 11265b9c547cSRui Paulo { 1127325151a3SRui Paulo struct crypto_bignum *res, *one; 1128325151a3SRui Paulo const u8 one_bin[1] = { 0x01 }; 11295b9c547cSRui Paulo 1130*85732ac8SCy Schubert if (sae->tmp->prime_len > end - *pos) { 11315b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 11325b9c547cSRui Paulo "commit-element"); 11335b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11345b9c547cSRui Paulo } 1135*85732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, 11365b9c547cSRui Paulo sae->tmp->prime_len); 11375b9c547cSRui Paulo 11385b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); 11395b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc = 1140*85732ac8SCy Schubert crypto_bignum_init_set(*pos, sae->tmp->prime_len); 11415b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ffc == NULL) 11425b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 1143325151a3SRui Paulo /* 1 < element < p - 1 */ 1144325151a3SRui Paulo res = crypto_bignum_init(); 1145325151a3SRui Paulo one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); 1146325151a3SRui Paulo if (!res || !one || 1147325151a3SRui Paulo crypto_bignum_sub(sae->tmp->prime, one, res) || 1148325151a3SRui Paulo crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || 11495b9c547cSRui Paulo crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || 1150325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { 1151325151a3SRui Paulo crypto_bignum_deinit(res, 0); 1152325151a3SRui Paulo crypto_bignum_deinit(one, 0); 11535b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); 11545b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11555b9c547cSRui Paulo } 1156325151a3SRui Paulo crypto_bignum_deinit(one, 0); 11575b9c547cSRui Paulo 11585b9c547cSRui Paulo /* scalar-op(r, ELEMENT) = 1 modulo p */ 1159325151a3SRui Paulo if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, 11605b9c547cSRui Paulo sae->tmp->order, sae->tmp->prime, res) < 0 || 11615b9c547cSRui Paulo !crypto_bignum_is_one(res)) { 11625b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); 11635b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 11645b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11655b9c547cSRui Paulo } 11665b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 11675b9c547cSRui Paulo 1168*85732ac8SCy Schubert *pos += sae->tmp->prime_len; 1169*85732ac8SCy Schubert 11705b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 11715b9c547cSRui Paulo } 11725b9c547cSRui Paulo 11735b9c547cSRui Paulo 1174*85732ac8SCy Schubert static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, 11755b9c547cSRui Paulo const u8 *end) 11765b9c547cSRui Paulo { 11775b9c547cSRui Paulo if (sae->tmp->dh) 11785b9c547cSRui Paulo return sae_parse_commit_element_ffc(sae, pos, end); 11795b9c547cSRui Paulo return sae_parse_commit_element_ecc(sae, pos, end); 11805b9c547cSRui Paulo } 11815b9c547cSRui Paulo 11825b9c547cSRui Paulo 1183*85732ac8SCy Schubert static int sae_parse_password_identifier(struct sae_data *sae, 1184*85732ac8SCy Schubert const u8 *pos, const u8 *end) 1185*85732ac8SCy Schubert { 1186*85732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 1187*85732ac8SCy Schubert pos, end - pos); 1188*85732ac8SCy Schubert if (!sae_is_password_id_elem(pos, end)) { 1189*85732ac8SCy Schubert if (sae->tmp->pw_id) { 1190*85732ac8SCy Schubert wpa_printf(MSG_DEBUG, 1191*85732ac8SCy Schubert "SAE: No Password Identifier included, but expected one (%s)", 1192*85732ac8SCy Schubert sae->tmp->pw_id); 1193*85732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 1194*85732ac8SCy Schubert } 1195*85732ac8SCy Schubert os_free(sae->tmp->pw_id); 1196*85732ac8SCy Schubert sae->tmp->pw_id = NULL; 1197*85732ac8SCy Schubert return WLAN_STATUS_SUCCESS; /* No Password Identifier */ 1198*85732ac8SCy Schubert } 1199*85732ac8SCy Schubert 1200*85732ac8SCy Schubert if (sae->tmp->pw_id && 1201*85732ac8SCy Schubert (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) || 1202*85732ac8SCy Schubert os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) { 1203*85732ac8SCy Schubert wpa_printf(MSG_DEBUG, 1204*85732ac8SCy Schubert "SAE: The included Password Identifier does not match the expected one (%s)", 1205*85732ac8SCy Schubert sae->tmp->pw_id); 1206*85732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 1207*85732ac8SCy Schubert } 1208*85732ac8SCy Schubert 1209*85732ac8SCy Schubert os_free(sae->tmp->pw_id); 1210*85732ac8SCy Schubert sae->tmp->pw_id = os_malloc(pos[1]); 1211*85732ac8SCy Schubert if (!sae->tmp->pw_id) 1212*85732ac8SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE; 1213*85732ac8SCy Schubert os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1); 1214*85732ac8SCy Schubert sae->tmp->pw_id[pos[1] - 1] = '\0'; 1215*85732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier", 1216*85732ac8SCy Schubert sae->tmp->pw_id, pos[1] - 1); 1217*85732ac8SCy Schubert return WLAN_STATUS_SUCCESS; 1218*85732ac8SCy Schubert } 1219*85732ac8SCy Schubert 1220*85732ac8SCy Schubert 12215b9c547cSRui Paulo u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, 12225b9c547cSRui Paulo const u8 **token, size_t *token_len, int *allowed_groups) 12235b9c547cSRui Paulo { 12245b9c547cSRui Paulo const u8 *pos = data, *end = data + len; 12255b9c547cSRui Paulo u16 res; 12265b9c547cSRui Paulo 12275b9c547cSRui Paulo /* Check Finite Cyclic Group */ 1228780fb4a2SCy Schubert if (end - pos < 2) 12295b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 12305b9c547cSRui Paulo res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); 12315b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 12325b9c547cSRui Paulo return res; 12335b9c547cSRui Paulo pos += 2; 12345b9c547cSRui Paulo 12355b9c547cSRui Paulo /* Optional Anti-Clogging Token */ 12365b9c547cSRui Paulo sae_parse_commit_token(sae, &pos, end, token, token_len); 12375b9c547cSRui Paulo 12385b9c547cSRui Paulo /* commit-scalar */ 12395b9c547cSRui Paulo res = sae_parse_commit_scalar(sae, &pos, end); 12405b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 12415b9c547cSRui Paulo return res; 12425b9c547cSRui Paulo 12435b9c547cSRui Paulo /* commit-element */ 1244*85732ac8SCy Schubert res = sae_parse_commit_element(sae, &pos, end); 1245*85732ac8SCy Schubert if (res != WLAN_STATUS_SUCCESS) 1246*85732ac8SCy Schubert return res; 1247*85732ac8SCy Schubert 1248*85732ac8SCy Schubert /* Optional Password Identifier element */ 1249*85732ac8SCy Schubert res = sae_parse_password_identifier(sae, pos, end); 1250325151a3SRui Paulo if (res != WLAN_STATUS_SUCCESS) 1251325151a3SRui Paulo return res; 1252325151a3SRui Paulo 1253325151a3SRui Paulo /* 1254325151a3SRui Paulo * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as 1255325151a3SRui Paulo * the values we sent which would be evidence of a reflection attack. 1256325151a3SRui Paulo */ 1257325151a3SRui Paulo if (!sae->tmp->own_commit_scalar || 1258325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_scalar, 1259325151a3SRui Paulo sae->peer_commit_scalar) != 0 || 1260325151a3SRui Paulo (sae->tmp->dh && 1261325151a3SRui Paulo (!sae->tmp->own_commit_element_ffc || 1262325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, 1263325151a3SRui Paulo sae->tmp->peer_commit_element_ffc) != 0)) || 1264325151a3SRui Paulo (sae->tmp->ec && 1265325151a3SRui Paulo (!sae->tmp->own_commit_element_ecc || 1266325151a3SRui Paulo crypto_ec_point_cmp(sae->tmp->ec, 1267325151a3SRui Paulo sae->tmp->own_commit_element_ecc, 1268325151a3SRui Paulo sae->tmp->peer_commit_element_ecc) != 0))) 1269325151a3SRui Paulo return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ 1270325151a3SRui Paulo 1271325151a3SRui Paulo /* 1272325151a3SRui Paulo * This is a reflection attack - return special value to trigger caller 1273325151a3SRui Paulo * to silently discard the frame instead of replying with a specific 1274325151a3SRui Paulo * status code. 1275325151a3SRui Paulo */ 1276325151a3SRui Paulo return SAE_SILENTLY_DISCARD; 12775b9c547cSRui Paulo } 12785b9c547cSRui Paulo 12795b9c547cSRui Paulo 12805b9c547cSRui Paulo static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, 12815b9c547cSRui Paulo const struct crypto_bignum *scalar1, 12825b9c547cSRui Paulo const u8 *element1, size_t element1_len, 12835b9c547cSRui Paulo const struct crypto_bignum *scalar2, 12845b9c547cSRui Paulo const u8 *element2, size_t element2_len, 12855b9c547cSRui Paulo u8 *confirm) 12865b9c547cSRui Paulo { 12875b9c547cSRui Paulo const u8 *addr[5]; 12885b9c547cSRui Paulo size_t len[5]; 12895b9c547cSRui Paulo u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; 12905b9c547cSRui Paulo 12915b9c547cSRui Paulo /* Confirm 12925b9c547cSRui Paulo * CN(key, X, Y, Z, ...) = 12935b9c547cSRui Paulo * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) 12945b9c547cSRui Paulo * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, 12955b9c547cSRui Paulo * peer-commit-scalar, PEER-COMMIT-ELEMENT) 12965b9c547cSRui Paulo * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, 12975b9c547cSRui Paulo * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) 12985b9c547cSRui Paulo */ 12995b9c547cSRui Paulo addr[0] = sc; 13005b9c547cSRui Paulo len[0] = 2; 13015b9c547cSRui Paulo crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), 13025b9c547cSRui Paulo sae->tmp->prime_len); 13035b9c547cSRui Paulo addr[1] = scalar_b1; 13045b9c547cSRui Paulo len[1] = sae->tmp->prime_len; 13055b9c547cSRui Paulo addr[2] = element1; 13065b9c547cSRui Paulo len[2] = element1_len; 13075b9c547cSRui Paulo crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), 13085b9c547cSRui Paulo sae->tmp->prime_len); 13095b9c547cSRui Paulo addr[3] = scalar_b2; 13105b9c547cSRui Paulo len[3] = sae->tmp->prime_len; 13115b9c547cSRui Paulo addr[4] = element2; 13125b9c547cSRui Paulo len[4] = element2_len; 13135b9c547cSRui Paulo hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, 13145b9c547cSRui Paulo confirm); 13155b9c547cSRui Paulo } 13165b9c547cSRui Paulo 13175b9c547cSRui Paulo 13185b9c547cSRui Paulo static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, 13195b9c547cSRui Paulo const struct crypto_bignum *scalar1, 13205b9c547cSRui Paulo const struct crypto_ec_point *element1, 13215b9c547cSRui Paulo const struct crypto_bignum *scalar2, 13225b9c547cSRui Paulo const struct crypto_ec_point *element2, 13235b9c547cSRui Paulo u8 *confirm) 13245b9c547cSRui Paulo { 13255b9c547cSRui Paulo u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; 13265b9c547cSRui Paulo u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; 13275b9c547cSRui Paulo 13285b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, 13295b9c547cSRui Paulo element_b1 + sae->tmp->prime_len); 13305b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, 13315b9c547cSRui Paulo element_b2 + sae->tmp->prime_len); 13325b9c547cSRui Paulo 13335b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, 13345b9c547cSRui Paulo scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); 13355b9c547cSRui Paulo } 13365b9c547cSRui Paulo 13375b9c547cSRui Paulo 13385b9c547cSRui Paulo static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, 13395b9c547cSRui Paulo const struct crypto_bignum *scalar1, 13405b9c547cSRui Paulo const struct crypto_bignum *element1, 13415b9c547cSRui Paulo const struct crypto_bignum *scalar2, 13425b9c547cSRui Paulo const struct crypto_bignum *element2, 13435b9c547cSRui Paulo u8 *confirm) 13445b9c547cSRui Paulo { 13455b9c547cSRui Paulo u8 element_b1[SAE_MAX_PRIME_LEN]; 13465b9c547cSRui Paulo u8 element_b2[SAE_MAX_PRIME_LEN]; 13475b9c547cSRui Paulo 13485b9c547cSRui Paulo crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), 13495b9c547cSRui Paulo sae->tmp->prime_len); 13505b9c547cSRui Paulo crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), 13515b9c547cSRui Paulo sae->tmp->prime_len); 13525b9c547cSRui Paulo 13535b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, 13545b9c547cSRui Paulo scalar2, element_b2, sae->tmp->prime_len, confirm); 13555b9c547cSRui Paulo } 13565b9c547cSRui Paulo 13575b9c547cSRui Paulo 13585b9c547cSRui Paulo void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) 13595b9c547cSRui Paulo { 13605b9c547cSRui Paulo const u8 *sc; 13615b9c547cSRui Paulo 13625b9c547cSRui Paulo if (sae->tmp == NULL) 13635b9c547cSRui Paulo return; 13645b9c547cSRui Paulo 13655b9c547cSRui Paulo /* Send-Confirm */ 13665b9c547cSRui Paulo sc = wpabuf_put(buf, 0); 13675b9c547cSRui Paulo wpabuf_put_le16(buf, sae->send_confirm); 1368*85732ac8SCy Schubert if (sae->send_confirm < 0xffff) 13695b9c547cSRui Paulo sae->send_confirm++; 13705b9c547cSRui Paulo 13715b9c547cSRui Paulo if (sae->tmp->ec) 13725b9c547cSRui Paulo sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, 13735b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 13745b9c547cSRui Paulo sae->peer_commit_scalar, 13755b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 13765b9c547cSRui Paulo wpabuf_put(buf, SHA256_MAC_LEN)); 13775b9c547cSRui Paulo else 13785b9c547cSRui Paulo sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, 13795b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 13805b9c547cSRui Paulo sae->peer_commit_scalar, 13815b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 13825b9c547cSRui Paulo wpabuf_put(buf, SHA256_MAC_LEN)); 13835b9c547cSRui Paulo } 13845b9c547cSRui Paulo 13855b9c547cSRui Paulo 13865b9c547cSRui Paulo int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) 13875b9c547cSRui Paulo { 13885b9c547cSRui Paulo u8 verifier[SHA256_MAC_LEN]; 13895b9c547cSRui Paulo 13905b9c547cSRui Paulo if (len < 2 + SHA256_MAC_LEN) { 13915b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); 13925b9c547cSRui Paulo return -1; 13935b9c547cSRui Paulo } 13945b9c547cSRui Paulo 13955b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); 13965b9c547cSRui Paulo 13975b9c547cSRui Paulo if (sae->tmp == NULL) { 13985b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); 13995b9c547cSRui Paulo return -1; 14005b9c547cSRui Paulo } 14015b9c547cSRui Paulo 14025b9c547cSRui Paulo if (sae->tmp->ec) 14035b9c547cSRui Paulo sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, 14045b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 14055b9c547cSRui Paulo sae->tmp->own_commit_scalar, 14065b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 14075b9c547cSRui Paulo verifier); 14085b9c547cSRui Paulo else 14095b9c547cSRui Paulo sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, 14105b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 14115b9c547cSRui Paulo sae->tmp->own_commit_scalar, 14125b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 14135b9c547cSRui Paulo verifier); 14145b9c547cSRui Paulo 14155b9c547cSRui Paulo if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) { 14165b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); 14175b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", 14185b9c547cSRui Paulo data + 2, SHA256_MAC_LEN); 14195b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", 14205b9c547cSRui Paulo verifier, SHA256_MAC_LEN); 14215b9c547cSRui Paulo return -1; 14225b9c547cSRui Paulo } 14235b9c547cSRui Paulo 14245b9c547cSRui Paulo return 0; 14255b9c547cSRui Paulo } 1426*85732ac8SCy Schubert 1427*85732ac8SCy Schubert 1428*85732ac8SCy Schubert const char * sae_state_txt(enum sae_state state) 1429*85732ac8SCy Schubert { 1430*85732ac8SCy Schubert switch (state) { 1431*85732ac8SCy Schubert case SAE_NOTHING: 1432*85732ac8SCy Schubert return "Nothing"; 1433*85732ac8SCy Schubert case SAE_COMMITTED: 1434*85732ac8SCy Schubert return "Committed"; 1435*85732ac8SCy Schubert case SAE_CONFIRMED: 1436*85732ac8SCy Schubert return "Confirmed"; 1437*85732ac8SCy Schubert case SAE_ACCEPTED: 1438*85732ac8SCy Schubert return "Accepted"; 1439*85732ac8SCy Schubert } 1440*85732ac8SCy Schubert return "?"; 1441*85732ac8SCy Schubert } 1442