15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * Simultaneous authentication of equals 3*325151a3SRui Paulo * Copyright (c) 2012-2015, 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) { 325b9c547cSRui Paulo sae->group = group; 335b9c547cSRui Paulo tmp->prime_len = crypto_ec_prime_len(tmp->ec); 345b9c547cSRui Paulo tmp->prime = crypto_ec_get_prime(tmp->ec); 355b9c547cSRui Paulo tmp->order = crypto_ec_get_order(tmp->ec); 365b9c547cSRui Paulo return 0; 375b9c547cSRui Paulo } 385b9c547cSRui Paulo 395b9c547cSRui Paulo /* Not an ECC group, check FFC */ 405b9c547cSRui Paulo tmp->dh = dh_groups_get(group); 415b9c547cSRui Paulo if (tmp->dh) { 425b9c547cSRui Paulo sae->group = group; 435b9c547cSRui Paulo tmp->prime_len = tmp->dh->prime_len; 445b9c547cSRui Paulo if (tmp->prime_len > SAE_MAX_PRIME_LEN) { 455b9c547cSRui Paulo sae_clear_data(sae); 465b9c547cSRui Paulo return -1; 475b9c547cSRui Paulo } 485b9c547cSRui Paulo 495b9c547cSRui Paulo tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, 505b9c547cSRui Paulo tmp->prime_len); 515b9c547cSRui Paulo if (tmp->prime_buf == NULL) { 525b9c547cSRui Paulo sae_clear_data(sae); 535b9c547cSRui Paulo return -1; 545b9c547cSRui Paulo } 555b9c547cSRui Paulo tmp->prime = tmp->prime_buf; 565b9c547cSRui Paulo 575b9c547cSRui Paulo tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, 585b9c547cSRui Paulo tmp->dh->order_len); 595b9c547cSRui Paulo if (tmp->order_buf == NULL) { 605b9c547cSRui Paulo sae_clear_data(sae); 615b9c547cSRui Paulo return -1; 625b9c547cSRui Paulo } 635b9c547cSRui Paulo tmp->order = tmp->order_buf; 645b9c547cSRui Paulo 655b9c547cSRui Paulo return 0; 665b9c547cSRui Paulo } 675b9c547cSRui Paulo 685b9c547cSRui Paulo /* Unsupported group */ 695b9c547cSRui Paulo return -1; 705b9c547cSRui Paulo } 715b9c547cSRui Paulo 725b9c547cSRui Paulo 735b9c547cSRui Paulo void sae_clear_temp_data(struct sae_data *sae) 745b9c547cSRui Paulo { 755b9c547cSRui Paulo struct sae_temporary_data *tmp; 765b9c547cSRui Paulo if (sae == NULL || sae->tmp == NULL) 775b9c547cSRui Paulo return; 785b9c547cSRui Paulo tmp = sae->tmp; 795b9c547cSRui Paulo crypto_ec_deinit(tmp->ec); 805b9c547cSRui Paulo crypto_bignum_deinit(tmp->prime_buf, 0); 815b9c547cSRui Paulo crypto_bignum_deinit(tmp->order_buf, 0); 825b9c547cSRui Paulo crypto_bignum_deinit(tmp->sae_rand, 1); 835b9c547cSRui Paulo crypto_bignum_deinit(tmp->pwe_ffc, 1); 845b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_scalar, 0); 855b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); 865b9c547cSRui Paulo crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); 875b9c547cSRui Paulo crypto_ec_point_deinit(tmp->pwe_ecc, 1); 885b9c547cSRui Paulo crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); 895b9c547cSRui Paulo crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); 905b9c547cSRui Paulo wpabuf_free(tmp->anti_clogging_token); 915b9c547cSRui Paulo bin_clear_free(tmp, sizeof(*tmp)); 925b9c547cSRui Paulo sae->tmp = NULL; 935b9c547cSRui Paulo } 945b9c547cSRui Paulo 955b9c547cSRui Paulo 965b9c547cSRui Paulo void sae_clear_data(struct sae_data *sae) 975b9c547cSRui Paulo { 985b9c547cSRui Paulo if (sae == NULL) 995b9c547cSRui Paulo return; 1005b9c547cSRui Paulo sae_clear_temp_data(sae); 1015b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 1025b9c547cSRui Paulo os_memset(sae, 0, sizeof(*sae)); 1035b9c547cSRui Paulo } 1045b9c547cSRui Paulo 1055b9c547cSRui Paulo 1065b9c547cSRui Paulo static void buf_shift_right(u8 *buf, size_t len, size_t bits) 1075b9c547cSRui Paulo { 1085b9c547cSRui Paulo size_t i; 1095b9c547cSRui Paulo for (i = len - 1; i > 0; i--) 1105b9c547cSRui Paulo buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits); 1115b9c547cSRui Paulo buf[0] >>= bits; 1125b9c547cSRui Paulo } 1135b9c547cSRui Paulo 1145b9c547cSRui Paulo 1155b9c547cSRui Paulo static struct crypto_bignum * sae_get_rand(struct sae_data *sae) 1165b9c547cSRui Paulo { 1175b9c547cSRui Paulo u8 val[SAE_MAX_PRIME_LEN]; 1185b9c547cSRui Paulo int iter = 0; 1195b9c547cSRui Paulo struct crypto_bignum *bn = NULL; 1205b9c547cSRui Paulo int order_len_bits = crypto_bignum_bits(sae->tmp->order); 1215b9c547cSRui Paulo size_t order_len = (order_len_bits + 7) / 8; 1225b9c547cSRui Paulo 1235b9c547cSRui Paulo if (order_len > sizeof(val)) 1245b9c547cSRui Paulo return NULL; 1255b9c547cSRui Paulo 1265b9c547cSRui Paulo for (;;) { 127*325151a3SRui Paulo if (iter++ > 100 || random_get_bytes(val, order_len) < 0) 1285b9c547cSRui Paulo return NULL; 1295b9c547cSRui Paulo if (order_len_bits % 8) 1305b9c547cSRui Paulo buf_shift_right(val, order_len, 8 - order_len_bits % 8); 1315b9c547cSRui Paulo bn = crypto_bignum_init_set(val, order_len); 1325b9c547cSRui Paulo if (bn == NULL) 1335b9c547cSRui Paulo return NULL; 1345b9c547cSRui Paulo if (crypto_bignum_is_zero(bn) || 1355b9c547cSRui Paulo crypto_bignum_is_one(bn) || 1365b9c547cSRui Paulo crypto_bignum_cmp(bn, sae->tmp->order) >= 0) { 1375b9c547cSRui Paulo crypto_bignum_deinit(bn, 0); 1385b9c547cSRui Paulo continue; 1395b9c547cSRui Paulo } 1405b9c547cSRui Paulo break; 1415b9c547cSRui Paulo } 1425b9c547cSRui Paulo 1435b9c547cSRui Paulo os_memset(val, 0, order_len); 1445b9c547cSRui Paulo return bn; 1455b9c547cSRui Paulo } 1465b9c547cSRui Paulo 1475b9c547cSRui Paulo 1485b9c547cSRui Paulo static struct crypto_bignum * sae_get_rand_and_mask(struct sae_data *sae) 1495b9c547cSRui Paulo { 1505b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->sae_rand, 1); 1515b9c547cSRui Paulo sae->tmp->sae_rand = sae_get_rand(sae); 1525b9c547cSRui Paulo if (sae->tmp->sae_rand == NULL) 1535b9c547cSRui Paulo return NULL; 1545b9c547cSRui Paulo return sae_get_rand(sae); 1555b9c547cSRui Paulo } 1565b9c547cSRui Paulo 1575b9c547cSRui Paulo 1585b9c547cSRui Paulo static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) 1595b9c547cSRui Paulo { 1605b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR 1615b9c547cSRui Paulo " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); 1625b9c547cSRui Paulo if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { 1635b9c547cSRui Paulo os_memcpy(key, addr1, ETH_ALEN); 1645b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); 1655b9c547cSRui Paulo } else { 1665b9c547cSRui Paulo os_memcpy(key, addr2, ETH_ALEN); 1675b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); 1685b9c547cSRui Paulo } 1695b9c547cSRui Paulo } 1705b9c547cSRui Paulo 1715b9c547cSRui Paulo 172*325151a3SRui Paulo static struct crypto_bignum * 173*325151a3SRui Paulo get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, 174*325151a3SRui Paulo int *r_odd) 1755b9c547cSRui Paulo { 176*325151a3SRui Paulo for (;;) { 177*325151a3SRui Paulo struct crypto_bignum *r; 178*325151a3SRui Paulo u8 tmp[SAE_MAX_ECC_PRIME_LEN]; 179*325151a3SRui Paulo 180*325151a3SRui Paulo if (random_get_bytes(tmp, prime_len) < 0) 181*325151a3SRui Paulo break; 182*325151a3SRui Paulo if (prime_bits % 8) 183*325151a3SRui Paulo buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); 184*325151a3SRui Paulo if (os_memcmp(tmp, prime, prime_len) >= 0) 185*325151a3SRui Paulo continue; 186*325151a3SRui Paulo r = crypto_bignum_init_set(tmp, prime_len); 187*325151a3SRui Paulo if (!r) 188*325151a3SRui Paulo break; 189*325151a3SRui Paulo if (crypto_bignum_is_zero(r)) { 190*325151a3SRui Paulo crypto_bignum_deinit(r, 0); 191*325151a3SRui Paulo continue; 192*325151a3SRui Paulo } 193*325151a3SRui Paulo 194*325151a3SRui Paulo *r_odd = tmp[prime_len - 1] & 0x01; 195*325151a3SRui Paulo return r; 196*325151a3SRui Paulo } 197*325151a3SRui Paulo 198*325151a3SRui Paulo return NULL; 199*325151a3SRui Paulo } 200*325151a3SRui Paulo 201*325151a3SRui Paulo 202*325151a3SRui Paulo static int is_quadratic_residue_blind(struct sae_data *sae, 203*325151a3SRui Paulo const u8 *prime, size_t bits, 204*325151a3SRui Paulo const struct crypto_bignum *qr, 205*325151a3SRui Paulo const struct crypto_bignum *qnr, 206*325151a3SRui Paulo const struct crypto_bignum *y_sqr) 207*325151a3SRui Paulo { 208*325151a3SRui Paulo struct crypto_bignum *r, *num; 209*325151a3SRui Paulo int r_odd, check, res = -1; 210*325151a3SRui Paulo 211*325151a3SRui Paulo /* 212*325151a3SRui Paulo * Use the blinding technique to mask y_sqr while determining 213*325151a3SRui Paulo * whether it is a quadratic residue modulo p to avoid leaking 214*325151a3SRui Paulo * timing information while determining the Legendre symbol. 215*325151a3SRui Paulo * 216*325151a3SRui Paulo * v = y_sqr 217*325151a3SRui Paulo * r = a random number between 1 and p-1, inclusive 218*325151a3SRui Paulo * num = (v * r * r) modulo p 219*325151a3SRui Paulo */ 220*325151a3SRui Paulo r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd); 221*325151a3SRui Paulo if (!r) 222*325151a3SRui Paulo return -1; 223*325151a3SRui Paulo 224*325151a3SRui Paulo num = crypto_bignum_init(); 225*325151a3SRui Paulo if (!num || 226*325151a3SRui Paulo crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 || 227*325151a3SRui Paulo crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) 228*325151a3SRui Paulo goto fail; 229*325151a3SRui Paulo 230*325151a3SRui Paulo if (r_odd) { 231*325151a3SRui Paulo /* 232*325151a3SRui Paulo * num = (num * qr) module p 233*325151a3SRui Paulo * LGR(num, p) = 1 ==> quadratic residue 234*325151a3SRui Paulo */ 235*325151a3SRui Paulo if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0) 236*325151a3SRui Paulo goto fail; 237*325151a3SRui Paulo check = 1; 238*325151a3SRui Paulo } else { 239*325151a3SRui Paulo /* 240*325151a3SRui Paulo * num = (num * qnr) module p 241*325151a3SRui Paulo * LGR(num, p) = -1 ==> quadratic residue 242*325151a3SRui Paulo */ 243*325151a3SRui Paulo if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0) 244*325151a3SRui Paulo goto fail; 245*325151a3SRui Paulo check = -1; 246*325151a3SRui Paulo } 247*325151a3SRui Paulo 248*325151a3SRui Paulo res = crypto_bignum_legendre(num, sae->tmp->prime); 249*325151a3SRui Paulo if (res == -2) { 250*325151a3SRui Paulo res = -1; 251*325151a3SRui Paulo goto fail; 252*325151a3SRui Paulo } 253*325151a3SRui Paulo res = res == check; 254*325151a3SRui Paulo fail: 255*325151a3SRui Paulo crypto_bignum_deinit(num, 1); 256*325151a3SRui Paulo crypto_bignum_deinit(r, 1); 257*325151a3SRui Paulo return res; 258*325151a3SRui Paulo } 259*325151a3SRui Paulo 260*325151a3SRui Paulo 261*325151a3SRui Paulo static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, 262*325151a3SRui Paulo const u8 *prime, 263*325151a3SRui Paulo const struct crypto_bignum *qr, 264*325151a3SRui Paulo const struct crypto_bignum *qnr, 265*325151a3SRui Paulo struct crypto_bignum **ret_x_cand) 266*325151a3SRui Paulo { 267*325151a3SRui Paulo u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; 268*325151a3SRui Paulo struct crypto_bignum *y_sqr, *x_cand; 269*325151a3SRui Paulo int res; 2705b9c547cSRui Paulo size_t bits; 2715b9c547cSRui Paulo 272*325151a3SRui Paulo *ret_x_cand = NULL; 2735b9c547cSRui Paulo 2745b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 2755b9c547cSRui Paulo 2765b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 2775b9c547cSRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec); 2785b9c547cSRui Paulo sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 2795b9c547cSRui Paulo prime, sae->tmp->prime_len, pwd_value, bits); 2805b9c547cSRui Paulo if (bits % 8) 2815b9c547cSRui Paulo buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); 2825b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", 2835b9c547cSRui Paulo pwd_value, sae->tmp->prime_len); 2845b9c547cSRui Paulo 2855b9c547cSRui Paulo if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) 2865b9c547cSRui Paulo return 0; 2875b9c547cSRui Paulo 288*325151a3SRui Paulo x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 289*325151a3SRui Paulo if (!x_cand) 2905b9c547cSRui Paulo return -1; 291*325151a3SRui Paulo y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); 292*325151a3SRui Paulo if (!y_sqr) { 293*325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 294*325151a3SRui Paulo return -1; 2955b9c547cSRui Paulo } 2965b9c547cSRui Paulo 297*325151a3SRui Paulo res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); 298*325151a3SRui Paulo crypto_bignum_deinit(y_sqr, 1); 299*325151a3SRui Paulo if (res <= 0) { 300*325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 301*325151a3SRui Paulo return res; 302*325151a3SRui Paulo } 3035b9c547cSRui Paulo 304*325151a3SRui Paulo *ret_x_cand = x_cand; 3055b9c547cSRui Paulo return 1; 3065b9c547cSRui Paulo } 3075b9c547cSRui Paulo 3085b9c547cSRui Paulo 3095b9c547cSRui Paulo static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, 3105b9c547cSRui Paulo struct crypto_bignum *pwe) 3115b9c547cSRui Paulo { 3125b9c547cSRui Paulo u8 pwd_value[SAE_MAX_PRIME_LEN]; 3135b9c547cSRui Paulo size_t bits = sae->tmp->prime_len * 8; 3145b9c547cSRui Paulo u8 exp[1]; 3155b9c547cSRui Paulo struct crypto_bignum *a, *b; 3165b9c547cSRui Paulo int res; 3175b9c547cSRui Paulo 3185b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 3195b9c547cSRui Paulo 3205b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 3215b9c547cSRui Paulo sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 3225b9c547cSRui Paulo sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, 3235b9c547cSRui Paulo bits); 3245b9c547cSRui Paulo if (bits % 8) 3255b9c547cSRui Paulo buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); 3265b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, 3275b9c547cSRui Paulo sae->tmp->prime_len); 3285b9c547cSRui Paulo 3295b9c547cSRui Paulo if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0) 3305b9c547cSRui Paulo { 3315b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p"); 3325b9c547cSRui Paulo return 0; 3335b9c547cSRui Paulo } 3345b9c547cSRui Paulo 3355b9c547cSRui Paulo /* PWE = pwd-value^((p-1)/r) modulo p */ 3365b9c547cSRui Paulo 3375b9c547cSRui Paulo a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 3385b9c547cSRui Paulo 3395b9c547cSRui Paulo if (sae->tmp->dh->safe_prime) { 3405b9c547cSRui Paulo /* 3415b9c547cSRui Paulo * r = (p-1)/2 for the group used here, so this becomes: 3425b9c547cSRui Paulo * PWE = pwd-value^2 modulo p 3435b9c547cSRui Paulo */ 3445b9c547cSRui Paulo exp[0] = 2; 3455b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 3465b9c547cSRui Paulo } else { 3475b9c547cSRui Paulo /* Calculate exponent: (p-1)/r */ 3485b9c547cSRui Paulo exp[0] = 1; 3495b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp)); 3505b9c547cSRui Paulo if (b == NULL || 3515b9c547cSRui Paulo crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || 3525b9c547cSRui Paulo crypto_bignum_div(b, sae->tmp->order, b) < 0) { 3535b9c547cSRui Paulo crypto_bignum_deinit(b, 0); 3545b9c547cSRui Paulo b = NULL; 3555b9c547cSRui Paulo } 3565b9c547cSRui Paulo } 3575b9c547cSRui Paulo 3585b9c547cSRui Paulo if (a == NULL || b == NULL) 3595b9c547cSRui Paulo res = -1; 3605b9c547cSRui Paulo else 3615b9c547cSRui Paulo res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); 3625b9c547cSRui Paulo 3635b9c547cSRui Paulo crypto_bignum_deinit(a, 0); 3645b9c547cSRui Paulo crypto_bignum_deinit(b, 0); 3655b9c547cSRui Paulo 3665b9c547cSRui Paulo if (res < 0) { 3675b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE"); 3685b9c547cSRui Paulo return -1; 3695b9c547cSRui Paulo } 3705b9c547cSRui Paulo 3715b9c547cSRui Paulo /* if (PWE > 1) --> found */ 3725b9c547cSRui Paulo if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) { 3735b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE <= 1"); 3745b9c547cSRui Paulo return 0; 3755b9c547cSRui Paulo } 3765b9c547cSRui Paulo 3775b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE found"); 3785b9c547cSRui Paulo return 1; 3795b9c547cSRui Paulo } 3805b9c547cSRui Paulo 3815b9c547cSRui Paulo 382*325151a3SRui Paulo static int get_random_qr_qnr(const u8 *prime, size_t prime_len, 383*325151a3SRui Paulo const struct crypto_bignum *prime_bn, 384*325151a3SRui Paulo size_t prime_bits, struct crypto_bignum **qr, 385*325151a3SRui Paulo struct crypto_bignum **qnr) 386*325151a3SRui Paulo { 387*325151a3SRui Paulo *qr = NULL; 388*325151a3SRui Paulo *qnr = NULL; 389*325151a3SRui Paulo 390*325151a3SRui Paulo while (!(*qr) || !(*qnr)) { 391*325151a3SRui Paulo u8 tmp[SAE_MAX_ECC_PRIME_LEN]; 392*325151a3SRui Paulo struct crypto_bignum *q; 393*325151a3SRui Paulo int res; 394*325151a3SRui Paulo 395*325151a3SRui Paulo if (random_get_bytes(tmp, prime_len) < 0) 396*325151a3SRui Paulo break; 397*325151a3SRui Paulo if (prime_bits % 8) 398*325151a3SRui Paulo buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); 399*325151a3SRui Paulo if (os_memcmp(tmp, prime, prime_len) >= 0) 400*325151a3SRui Paulo continue; 401*325151a3SRui Paulo q = crypto_bignum_init_set(tmp, prime_len); 402*325151a3SRui Paulo if (!q) 403*325151a3SRui Paulo break; 404*325151a3SRui Paulo res = crypto_bignum_legendre(q, prime_bn); 405*325151a3SRui Paulo 406*325151a3SRui Paulo if (res == 1 && !(*qr)) 407*325151a3SRui Paulo *qr = q; 408*325151a3SRui Paulo else if (res == -1 && !(*qnr)) 409*325151a3SRui Paulo *qnr = q; 410*325151a3SRui Paulo else 411*325151a3SRui Paulo crypto_bignum_deinit(q, 0); 412*325151a3SRui Paulo } 413*325151a3SRui Paulo 414*325151a3SRui Paulo return (*qr && *qnr) ? 0 : -1; 415*325151a3SRui Paulo } 416*325151a3SRui Paulo 417*325151a3SRui Paulo 4185b9c547cSRui Paulo static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, 4195b9c547cSRui Paulo const u8 *addr2, const u8 *password, 4205b9c547cSRui Paulo size_t password_len) 4215b9c547cSRui Paulo { 422*325151a3SRui Paulo u8 counter, k = 40; 4235b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 4245b9c547cSRui Paulo const u8 *addr[2]; 4255b9c547cSRui Paulo size_t len[2]; 426*325151a3SRui Paulo u8 dummy_password[32]; 427*325151a3SRui Paulo size_t dummy_password_len; 428*325151a3SRui Paulo int pwd_seed_odd = 0; 429*325151a3SRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 430*325151a3SRui Paulo size_t prime_len; 431*325151a3SRui Paulo struct crypto_bignum *x = NULL, *qr, *qnr; 432*325151a3SRui Paulo size_t bits; 433*325151a3SRui Paulo int res; 4345b9c547cSRui Paulo 435*325151a3SRui Paulo dummy_password_len = password_len; 436*325151a3SRui Paulo if (dummy_password_len > sizeof(dummy_password)) 437*325151a3SRui Paulo dummy_password_len = sizeof(dummy_password); 438*325151a3SRui Paulo if (random_get_bytes(dummy_password, dummy_password_len) < 0) 4395b9c547cSRui Paulo return -1; 440*325151a3SRui Paulo 441*325151a3SRui Paulo prime_len = sae->tmp->prime_len; 442*325151a3SRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 443*325151a3SRui Paulo prime_len) < 0) 444*325151a3SRui Paulo return -1; 445*325151a3SRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec); 446*325151a3SRui Paulo 447*325151a3SRui Paulo /* 448*325151a3SRui Paulo * Create a random quadratic residue (qr) and quadratic non-residue 449*325151a3SRui Paulo * (qnr) modulo p for blinding purposes during the loop. 450*325151a3SRui Paulo */ 451*325151a3SRui Paulo if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, 452*325151a3SRui Paulo &qr, &qnr) < 0) 4535b9c547cSRui Paulo return -1; 4545b9c547cSRui Paulo 4555b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 4565b9c547cSRui Paulo password, password_len); 4575b9c547cSRui Paulo 4585b9c547cSRui Paulo /* 4595b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 460*325151a3SRui Paulo * base = password 4615b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 462*325151a3SRui Paulo * base || counter) 4635b9c547cSRui Paulo */ 4645b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 4655b9c547cSRui Paulo 4665b9c547cSRui Paulo addr[0] = password; 4675b9c547cSRui Paulo len[0] = password_len; 4685b9c547cSRui Paulo addr[1] = &counter; 4695b9c547cSRui Paulo len[1] = sizeof(counter); 4705b9c547cSRui Paulo 4715b9c547cSRui Paulo /* 4725b9c547cSRui Paulo * Continue for at least k iterations to protect against side-channel 4735b9c547cSRui Paulo * attacks that attempt to determine the number of iterations required 4745b9c547cSRui Paulo * in the loop. 4755b9c547cSRui Paulo */ 476*325151a3SRui Paulo for (counter = 1; counter <= k || !x; counter++) { 4775b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 478*325151a3SRui Paulo struct crypto_bignum *x_cand; 4795b9c547cSRui Paulo 4805b9c547cSRui Paulo if (counter > 200) { 4815b9c547cSRui Paulo /* This should not happen in practice */ 4825b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 4835b9c547cSRui Paulo break; 4845b9c547cSRui Paulo } 4855b9c547cSRui Paulo 4865b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); 4875b9c547cSRui Paulo if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, 4885b9c547cSRui Paulo pwd_seed) < 0) 4895b9c547cSRui Paulo break; 490*325151a3SRui Paulo 4915b9c547cSRui Paulo res = sae_test_pwd_seed_ecc(sae, pwd_seed, 492*325151a3SRui Paulo prime, qr, qnr, &x_cand); 4935b9c547cSRui Paulo if (res < 0) 494*325151a3SRui Paulo goto fail; 495*325151a3SRui Paulo if (res > 0 && !x) { 496*325151a3SRui Paulo wpa_printf(MSG_DEBUG, 497*325151a3SRui Paulo "SAE: Selected pwd-seed with counter %u", 498*325151a3SRui Paulo counter); 499*325151a3SRui Paulo x = x_cand; 500*325151a3SRui Paulo pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; 501*325151a3SRui Paulo os_memset(pwd_seed, 0, sizeof(pwd_seed)); 502*325151a3SRui Paulo 503*325151a3SRui Paulo /* 504*325151a3SRui Paulo * Use a dummy password for the following rounds, if 505*325151a3SRui Paulo * any. 506*325151a3SRui Paulo */ 507*325151a3SRui Paulo addr[0] = dummy_password; 508*325151a3SRui Paulo len[0] = dummy_password_len; 509*325151a3SRui Paulo } else if (res > 0) { 510*325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 5115b9c547cSRui Paulo } 5125b9c547cSRui Paulo } 5135b9c547cSRui Paulo 514*325151a3SRui Paulo if (!x) { 515*325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); 516*325151a3SRui Paulo res = -1; 517*325151a3SRui Paulo goto fail; 518*325151a3SRui Paulo } 5195b9c547cSRui Paulo 520*325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 521*325151a3SRui Paulo sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); 522*325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 523*325151a3SRui Paulo res = -1; 524*325151a3SRui Paulo else 525*325151a3SRui Paulo res = crypto_ec_point_solve_y_coord(sae->tmp->ec, 526*325151a3SRui Paulo sae->tmp->pwe_ecc, x, 527*325151a3SRui Paulo pwd_seed_odd); 528*325151a3SRui Paulo crypto_bignum_deinit(x, 1); 529*325151a3SRui Paulo if (res < 0) { 530*325151a3SRui Paulo /* 531*325151a3SRui Paulo * This should not happen since we already checked that there 532*325151a3SRui Paulo * is a result. 533*325151a3SRui Paulo */ 534*325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); 535*325151a3SRui Paulo } 536*325151a3SRui Paulo 537*325151a3SRui Paulo fail: 538*325151a3SRui Paulo crypto_bignum_deinit(qr, 0); 539*325151a3SRui Paulo crypto_bignum_deinit(qnr, 0); 540*325151a3SRui Paulo 541*325151a3SRui Paulo return res; 5425b9c547cSRui Paulo } 5435b9c547cSRui Paulo 5445b9c547cSRui Paulo 5455b9c547cSRui Paulo static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, 5465b9c547cSRui Paulo const u8 *addr2, const u8 *password, 5475b9c547cSRui Paulo size_t password_len) 5485b9c547cSRui Paulo { 5495b9c547cSRui Paulo u8 counter; 5505b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 5515b9c547cSRui Paulo const u8 *addr[2]; 5525b9c547cSRui Paulo size_t len[2]; 5535b9c547cSRui Paulo int found = 0; 5545b9c547cSRui Paulo 5555b9c547cSRui Paulo if (sae->tmp->pwe_ffc == NULL) { 5565b9c547cSRui Paulo sae->tmp->pwe_ffc = crypto_bignum_init(); 5575b9c547cSRui Paulo if (sae->tmp->pwe_ffc == NULL) 5585b9c547cSRui Paulo return -1; 5595b9c547cSRui Paulo } 5605b9c547cSRui Paulo 5615b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 5625b9c547cSRui Paulo password, password_len); 5635b9c547cSRui Paulo 5645b9c547cSRui Paulo /* 5655b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm) 5665b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 5675b9c547cSRui Paulo * password || counter) 5685b9c547cSRui Paulo */ 5695b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs); 5705b9c547cSRui Paulo 5715b9c547cSRui Paulo addr[0] = password; 5725b9c547cSRui Paulo len[0] = password_len; 5735b9c547cSRui Paulo addr[1] = &counter; 5745b9c547cSRui Paulo len[1] = sizeof(counter); 5755b9c547cSRui Paulo 5765b9c547cSRui Paulo for (counter = 1; !found; counter++) { 5775b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 5785b9c547cSRui Paulo int res; 5795b9c547cSRui Paulo 5805b9c547cSRui Paulo if (counter > 200) { 5815b9c547cSRui Paulo /* This should not happen in practice */ 5825b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 5835b9c547cSRui Paulo break; 5845b9c547cSRui Paulo } 5855b9c547cSRui Paulo 5865b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); 5875b9c547cSRui Paulo if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, 5885b9c547cSRui Paulo pwd_seed) < 0) 5895b9c547cSRui Paulo break; 5905b9c547cSRui Paulo res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); 5915b9c547cSRui Paulo if (res < 0) 5925b9c547cSRui Paulo break; 5935b9c547cSRui Paulo if (res > 0) { 5945b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Use this PWE"); 5955b9c547cSRui Paulo found = 1; 5965b9c547cSRui Paulo } 5975b9c547cSRui Paulo } 5985b9c547cSRui Paulo 5995b9c547cSRui Paulo return found ? 0 : -1; 6005b9c547cSRui Paulo } 6015b9c547cSRui Paulo 6025b9c547cSRui Paulo 6035b9c547cSRui Paulo static int sae_derive_commit_element_ecc(struct sae_data *sae, 6045b9c547cSRui Paulo struct crypto_bignum *mask) 6055b9c547cSRui Paulo { 6065b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 6075b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) { 6085b9c547cSRui Paulo sae->tmp->own_commit_element_ecc = 6095b9c547cSRui Paulo crypto_ec_point_init(sae->tmp->ec); 6105b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) 6115b9c547cSRui Paulo return -1; 6125b9c547cSRui Paulo } 6135b9c547cSRui Paulo 6145b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, 6155b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0 || 6165b9c547cSRui Paulo crypto_ec_point_invert(sae->tmp->ec, 6175b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0) { 6185b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 6195b9c547cSRui Paulo return -1; 6205b9c547cSRui Paulo } 6215b9c547cSRui Paulo 6225b9c547cSRui Paulo return 0; 6235b9c547cSRui Paulo } 6245b9c547cSRui Paulo 6255b9c547cSRui Paulo 6265b9c547cSRui Paulo static int sae_derive_commit_element_ffc(struct sae_data *sae, 6275b9c547cSRui Paulo struct crypto_bignum *mask) 6285b9c547cSRui Paulo { 6295b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 6305b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) { 6315b9c547cSRui Paulo sae->tmp->own_commit_element_ffc = crypto_bignum_init(); 6325b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) 6335b9c547cSRui Paulo return -1; 6345b9c547cSRui Paulo } 6355b9c547cSRui Paulo 6365b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, 6375b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0 || 6385b9c547cSRui Paulo crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, 6395b9c547cSRui Paulo sae->tmp->prime, 6405b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0) { 6415b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 6425b9c547cSRui Paulo return -1; 6435b9c547cSRui Paulo } 6445b9c547cSRui Paulo 6455b9c547cSRui Paulo return 0; 6465b9c547cSRui Paulo } 6475b9c547cSRui Paulo 6485b9c547cSRui Paulo 6495b9c547cSRui Paulo static int sae_derive_commit(struct sae_data *sae) 6505b9c547cSRui Paulo { 6515b9c547cSRui Paulo struct crypto_bignum *mask; 6525b9c547cSRui Paulo int ret = -1; 653*325151a3SRui Paulo unsigned int counter = 0; 654*325151a3SRui Paulo 655*325151a3SRui Paulo do { 656*325151a3SRui Paulo counter++; 657*325151a3SRui Paulo if (counter > 100) { 658*325151a3SRui Paulo /* 659*325151a3SRui Paulo * This cannot really happen in practice if the random 660*325151a3SRui Paulo * number generator is working. Anyway, to avoid even a 661*325151a3SRui Paulo * theoretical infinite loop, break out after 100 662*325151a3SRui Paulo * attemps. 663*325151a3SRui Paulo */ 664*325151a3SRui Paulo return -1; 665*325151a3SRui Paulo } 6665b9c547cSRui Paulo 6675b9c547cSRui Paulo mask = sae_get_rand_and_mask(sae); 6685b9c547cSRui Paulo if (mask == NULL) { 6695b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask"); 6705b9c547cSRui Paulo return -1; 6715b9c547cSRui Paulo } 6725b9c547cSRui Paulo 6735b9c547cSRui Paulo /* commit-scalar = (rand + mask) modulo r */ 6745b9c547cSRui Paulo if (!sae->tmp->own_commit_scalar) { 6755b9c547cSRui Paulo sae->tmp->own_commit_scalar = crypto_bignum_init(); 6765b9c547cSRui Paulo if (!sae->tmp->own_commit_scalar) 6775b9c547cSRui Paulo goto fail; 6785b9c547cSRui Paulo } 6795b9c547cSRui Paulo crypto_bignum_add(sae->tmp->sae_rand, mask, 6805b9c547cSRui Paulo sae->tmp->own_commit_scalar); 6815b9c547cSRui Paulo crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order, 6825b9c547cSRui Paulo sae->tmp->own_commit_scalar); 683*325151a3SRui Paulo } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) || 684*325151a3SRui Paulo crypto_bignum_is_one(sae->tmp->own_commit_scalar)); 6855b9c547cSRui Paulo 686*325151a3SRui Paulo if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) || 687*325151a3SRui Paulo (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)) 6885b9c547cSRui Paulo goto fail; 6895b9c547cSRui Paulo 6905b9c547cSRui Paulo ret = 0; 6915b9c547cSRui Paulo fail: 6925b9c547cSRui Paulo crypto_bignum_deinit(mask, 1); 6935b9c547cSRui Paulo return ret; 6945b9c547cSRui Paulo } 6955b9c547cSRui Paulo 6965b9c547cSRui Paulo 6975b9c547cSRui Paulo int sae_prepare_commit(const u8 *addr1, const u8 *addr2, 6985b9c547cSRui Paulo const u8 *password, size_t password_len, 6995b9c547cSRui Paulo struct sae_data *sae) 7005b9c547cSRui Paulo { 701*325151a3SRui Paulo if (sae->tmp == NULL || 702*325151a3SRui Paulo (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, 703*325151a3SRui Paulo password_len) < 0) || 704*325151a3SRui Paulo (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, 705*325151a3SRui Paulo password_len) < 0) || 706*325151a3SRui Paulo sae_derive_commit(sae) < 0) 7075b9c547cSRui Paulo return -1; 7085b9c547cSRui Paulo return 0; 7095b9c547cSRui Paulo } 7105b9c547cSRui Paulo 7115b9c547cSRui Paulo 7125b9c547cSRui Paulo static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) 7135b9c547cSRui Paulo { 7145b9c547cSRui Paulo struct crypto_ec_point *K; 7155b9c547cSRui Paulo int ret = -1; 7165b9c547cSRui Paulo 7175b9c547cSRui Paulo K = crypto_ec_point_init(sae->tmp->ec); 7185b9c547cSRui Paulo if (K == NULL) 7195b9c547cSRui Paulo goto fail; 7205b9c547cSRui Paulo 7215b9c547cSRui Paulo /* 7225b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 7235b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 7245b9c547cSRui Paulo * If K is identity element (point-at-infinity), reject 7255b9c547cSRui Paulo * k = F(K) (= x coordinate) 7265b9c547cSRui Paulo */ 7275b9c547cSRui Paulo 7285b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, 7295b9c547cSRui Paulo sae->peer_commit_scalar, K) < 0 || 7305b9c547cSRui Paulo crypto_ec_point_add(sae->tmp->ec, K, 7315b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, K) < 0 || 7325b9c547cSRui Paulo crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || 7335b9c547cSRui Paulo crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || 7345b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { 7355b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 7365b9c547cSRui Paulo goto fail; 7375b9c547cSRui Paulo } 7385b9c547cSRui Paulo 7395b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 7405b9c547cSRui Paulo 7415b9c547cSRui Paulo ret = 0; 7425b9c547cSRui Paulo fail: 7435b9c547cSRui Paulo crypto_ec_point_deinit(K, 1); 7445b9c547cSRui Paulo return ret; 7455b9c547cSRui Paulo } 7465b9c547cSRui Paulo 7475b9c547cSRui Paulo 7485b9c547cSRui Paulo static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) 7495b9c547cSRui Paulo { 7505b9c547cSRui Paulo struct crypto_bignum *K; 7515b9c547cSRui Paulo int ret = -1; 7525b9c547cSRui Paulo 7535b9c547cSRui Paulo K = crypto_bignum_init(); 7545b9c547cSRui Paulo if (K == NULL) 7555b9c547cSRui Paulo goto fail; 7565b9c547cSRui Paulo 7575b9c547cSRui Paulo /* 7585b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 7595b9c547cSRui Paulo * PEER-COMMIT-ELEMENT))) 7605b9c547cSRui Paulo * If K is identity element (one), reject. 7615b9c547cSRui Paulo * k = F(K) (= x coordinate) 7625b9c547cSRui Paulo */ 7635b9c547cSRui Paulo 7645b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, 7655b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 7665b9c547cSRui Paulo crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, 7675b9c547cSRui Paulo sae->tmp->prime, K) < 0 || 7685b9c547cSRui Paulo crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 7695b9c547cSRui Paulo || 7705b9c547cSRui Paulo crypto_bignum_is_one(K) || 7715b9c547cSRui Paulo crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < 7725b9c547cSRui Paulo 0) { 7735b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 7745b9c547cSRui Paulo goto fail; 7755b9c547cSRui Paulo } 7765b9c547cSRui Paulo 7775b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 7785b9c547cSRui Paulo 7795b9c547cSRui Paulo ret = 0; 7805b9c547cSRui Paulo fail: 7815b9c547cSRui Paulo crypto_bignum_deinit(K, 1); 7825b9c547cSRui Paulo return ret; 7835b9c547cSRui Paulo } 7845b9c547cSRui Paulo 7855b9c547cSRui Paulo 7865b9c547cSRui Paulo static int sae_derive_keys(struct sae_data *sae, const u8 *k) 7875b9c547cSRui Paulo { 7885b9c547cSRui Paulo u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; 7895b9c547cSRui Paulo u8 keyseed[SHA256_MAC_LEN]; 7905b9c547cSRui Paulo u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; 7915b9c547cSRui Paulo struct crypto_bignum *tmp; 7925b9c547cSRui Paulo int ret = -1; 7935b9c547cSRui Paulo 7945b9c547cSRui Paulo tmp = crypto_bignum_init(); 7955b9c547cSRui Paulo if (tmp == NULL) 7965b9c547cSRui Paulo goto fail; 7975b9c547cSRui Paulo 7985b9c547cSRui Paulo /* keyseed = H(<0>32, k) 7995b9c547cSRui Paulo * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK", 8005b9c547cSRui Paulo * (commit-scalar + peer-commit-scalar) modulo r) 8015b9c547cSRui Paulo * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) 8025b9c547cSRui Paulo */ 8035b9c547cSRui Paulo 8045b9c547cSRui Paulo os_memset(null_key, 0, sizeof(null_key)); 8055b9c547cSRui Paulo hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, 8065b9c547cSRui Paulo keyseed); 8075b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); 8085b9c547cSRui Paulo 8095b9c547cSRui Paulo crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, 8105b9c547cSRui Paulo tmp); 8115b9c547cSRui Paulo crypto_bignum_mod(tmp, sae->tmp->order, tmp); 8125b9c547cSRui Paulo crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len); 8135b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); 8145b9c547cSRui Paulo sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", 8155b9c547cSRui Paulo val, sae->tmp->prime_len, keys, sizeof(keys)); 8165b9c547cSRui Paulo os_memset(keyseed, 0, sizeof(keyseed)); 8175b9c547cSRui Paulo os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); 8185b9c547cSRui Paulo os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); 8195b9c547cSRui Paulo os_memset(keys, 0, sizeof(keys)); 8205b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); 8215b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); 8225b9c547cSRui Paulo 8235b9c547cSRui Paulo ret = 0; 8245b9c547cSRui Paulo fail: 8255b9c547cSRui Paulo crypto_bignum_deinit(tmp, 0); 8265b9c547cSRui Paulo return ret; 8275b9c547cSRui Paulo } 8285b9c547cSRui Paulo 8295b9c547cSRui Paulo 8305b9c547cSRui Paulo int sae_process_commit(struct sae_data *sae) 8315b9c547cSRui Paulo { 8325b9c547cSRui Paulo u8 k[SAE_MAX_PRIME_LEN]; 8335b9c547cSRui Paulo if (sae->tmp == NULL || 8345b9c547cSRui Paulo (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || 8355b9c547cSRui Paulo (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || 8365b9c547cSRui Paulo sae_derive_keys(sae, k) < 0) 8375b9c547cSRui Paulo return -1; 8385b9c547cSRui Paulo return 0; 8395b9c547cSRui Paulo } 8405b9c547cSRui Paulo 8415b9c547cSRui Paulo 8425b9c547cSRui Paulo void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, 8435b9c547cSRui Paulo const struct wpabuf *token) 8445b9c547cSRui Paulo { 8455b9c547cSRui Paulo u8 *pos; 8465b9c547cSRui Paulo 8475b9c547cSRui Paulo if (sae->tmp == NULL) 8485b9c547cSRui Paulo return; 8495b9c547cSRui Paulo 8505b9c547cSRui Paulo wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ 8515b9c547cSRui Paulo if (token) { 8525b9c547cSRui Paulo wpabuf_put_buf(buf, token); 8535b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", 8545b9c547cSRui Paulo wpabuf_head(token), wpabuf_len(token)); 8555b9c547cSRui Paulo } 8565b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 8575b9c547cSRui Paulo crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, 8585b9c547cSRui Paulo sae->tmp->prime_len, sae->tmp->prime_len); 8595b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", 8605b9c547cSRui Paulo pos, sae->tmp->prime_len); 8615b9c547cSRui Paulo if (sae->tmp->ec) { 8625b9c547cSRui Paulo pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); 8635b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, 8645b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 8655b9c547cSRui Paulo pos, pos + sae->tmp->prime_len); 8665b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", 8675b9c547cSRui Paulo pos, sae->tmp->prime_len); 8685b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", 8695b9c547cSRui Paulo pos + sae->tmp->prime_len, sae->tmp->prime_len); 8705b9c547cSRui Paulo } else { 8715b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 8725b9c547cSRui Paulo crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, 8735b9c547cSRui Paulo sae->tmp->prime_len, sae->tmp->prime_len); 8745b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", 8755b9c547cSRui Paulo pos, sae->tmp->prime_len); 8765b9c547cSRui Paulo } 8775b9c547cSRui Paulo } 8785b9c547cSRui Paulo 8795b9c547cSRui Paulo 8805b9c547cSRui Paulo u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) 8815b9c547cSRui Paulo { 8825b9c547cSRui Paulo if (allowed_groups) { 8835b9c547cSRui Paulo int i; 8845b9c547cSRui Paulo for (i = 0; allowed_groups[i] > 0; i++) { 8855b9c547cSRui Paulo if (allowed_groups[i] == group) 8865b9c547cSRui Paulo break; 8875b9c547cSRui Paulo } 8885b9c547cSRui Paulo if (allowed_groups[i] != group) { 8895b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " 8905b9c547cSRui Paulo "enabled in the current configuration", 8915b9c547cSRui Paulo group); 8925b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 8935b9c547cSRui Paulo } 8945b9c547cSRui Paulo } 8955b9c547cSRui Paulo 8965b9c547cSRui Paulo if (sae->state == SAE_COMMITTED && group != sae->group) { 8975b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); 8985b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 8995b9c547cSRui Paulo } 9005b9c547cSRui Paulo 9015b9c547cSRui Paulo if (group != sae->group && sae_set_group(sae, group) < 0) { 9025b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", 9035b9c547cSRui Paulo group); 9045b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 9055b9c547cSRui Paulo } 9065b9c547cSRui Paulo 9075b9c547cSRui Paulo if (sae->tmp == NULL) { 9085b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); 9095b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9105b9c547cSRui Paulo } 9115b9c547cSRui Paulo 9125b9c547cSRui Paulo if (sae->tmp->dh && !allowed_groups) { 9135b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " 9145b9c547cSRui Paulo "explicit configuration enabling it", group); 9155b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 9165b9c547cSRui Paulo } 9175b9c547cSRui Paulo 9185b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 9195b9c547cSRui Paulo } 9205b9c547cSRui Paulo 9215b9c547cSRui Paulo 9225b9c547cSRui Paulo static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, 9235b9c547cSRui Paulo const u8 *end, const u8 **token, 9245b9c547cSRui Paulo size_t *token_len) 9255b9c547cSRui Paulo { 9265b9c547cSRui Paulo if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) { 9275b9c547cSRui Paulo size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * 9285b9c547cSRui Paulo sae->tmp->prime_len); 9295b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); 9305b9c547cSRui Paulo if (token) 9315b9c547cSRui Paulo *token = *pos; 9325b9c547cSRui Paulo if (token_len) 9335b9c547cSRui Paulo *token_len = tlen; 9345b9c547cSRui Paulo *pos += tlen; 9355b9c547cSRui Paulo } else { 9365b9c547cSRui Paulo if (token) 9375b9c547cSRui Paulo *token = NULL; 9385b9c547cSRui Paulo if (token_len) 9395b9c547cSRui Paulo *token_len = 0; 9405b9c547cSRui Paulo } 9415b9c547cSRui Paulo } 9425b9c547cSRui Paulo 9435b9c547cSRui Paulo 9445b9c547cSRui Paulo static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, 9455b9c547cSRui Paulo const u8 *end) 9465b9c547cSRui Paulo { 9475b9c547cSRui Paulo struct crypto_bignum *peer_scalar; 9485b9c547cSRui Paulo 9495b9c547cSRui Paulo if (*pos + sae->tmp->prime_len > end) { 9505b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); 9515b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9525b9c547cSRui Paulo } 9535b9c547cSRui Paulo 9545b9c547cSRui Paulo peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); 9555b9c547cSRui Paulo if (peer_scalar == NULL) 9565b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9575b9c547cSRui Paulo 9585b9c547cSRui Paulo /* 9595b9c547cSRui Paulo * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for 9605b9c547cSRui Paulo * the peer and it is in Authenticated state, the new Commit Message 9615b9c547cSRui Paulo * shall be dropped if the peer-scalar is identical to the one used in 9625b9c547cSRui Paulo * the existing protocol instance. 9635b9c547cSRui Paulo */ 9645b9c547cSRui Paulo if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && 9655b9c547cSRui Paulo crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { 9665b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " 9675b9c547cSRui Paulo "peer-commit-scalar"); 9685b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 9695b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9705b9c547cSRui Paulo } 9715b9c547cSRui Paulo 972*325151a3SRui Paulo /* 1 < scalar < r */ 9735b9c547cSRui Paulo if (crypto_bignum_is_zero(peer_scalar) || 974*325151a3SRui Paulo crypto_bignum_is_one(peer_scalar) || 9755b9c547cSRui Paulo crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { 9765b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); 9775b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 9785b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9795b9c547cSRui Paulo } 9805b9c547cSRui Paulo 9815b9c547cSRui Paulo 9825b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 9835b9c547cSRui Paulo sae->peer_commit_scalar = peer_scalar; 9845b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", 9855b9c547cSRui Paulo *pos, sae->tmp->prime_len); 9865b9c547cSRui Paulo *pos += sae->tmp->prime_len; 9875b9c547cSRui Paulo 9885b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 9895b9c547cSRui Paulo } 9905b9c547cSRui Paulo 9915b9c547cSRui Paulo 9925b9c547cSRui Paulo static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, 9935b9c547cSRui Paulo const u8 *end) 9945b9c547cSRui Paulo { 9955b9c547cSRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 9965b9c547cSRui Paulo 9975b9c547cSRui Paulo if (pos + 2 * sae->tmp->prime_len > end) { 9985b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 9995b9c547cSRui Paulo "commit-element"); 10005b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10015b9c547cSRui Paulo } 10025b9c547cSRui Paulo 10035b9c547cSRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 10045b9c547cSRui Paulo sae->tmp->prime_len) < 0) 10055b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10065b9c547cSRui Paulo 10075b9c547cSRui Paulo /* element x and y coordinates < p */ 10085b9c547cSRui Paulo if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 || 10095b9c547cSRui Paulo os_memcmp(pos + sae->tmp->prime_len, prime, 10105b9c547cSRui Paulo sae->tmp->prime_len) >= 0) { 10115b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " 10125b9c547cSRui Paulo "element"); 10135b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10145b9c547cSRui Paulo } 10155b9c547cSRui Paulo 10165b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", 10175b9c547cSRui Paulo pos, sae->tmp->prime_len); 10185b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", 10195b9c547cSRui Paulo pos + sae->tmp->prime_len, sae->tmp->prime_len); 10205b9c547cSRui Paulo 10215b9c547cSRui Paulo crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); 10225b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc = 10235b9c547cSRui Paulo crypto_ec_point_from_bin(sae->tmp->ec, pos); 10245b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ecc == NULL) 10255b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10265b9c547cSRui Paulo 10275b9c547cSRui Paulo if (!crypto_ec_point_is_on_curve(sae->tmp->ec, 10285b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc)) { 10295b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); 10305b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10315b9c547cSRui Paulo } 10325b9c547cSRui Paulo 10335b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 10345b9c547cSRui Paulo } 10355b9c547cSRui Paulo 10365b9c547cSRui Paulo 10375b9c547cSRui Paulo static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, 10385b9c547cSRui Paulo const u8 *end) 10395b9c547cSRui Paulo { 1040*325151a3SRui Paulo struct crypto_bignum *res, *one; 1041*325151a3SRui Paulo const u8 one_bin[1] = { 0x01 }; 10425b9c547cSRui Paulo 10435b9c547cSRui Paulo if (pos + sae->tmp->prime_len > end) { 10445b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 10455b9c547cSRui Paulo "commit-element"); 10465b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10475b9c547cSRui Paulo } 10485b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos, 10495b9c547cSRui Paulo sae->tmp->prime_len); 10505b9c547cSRui Paulo 10515b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); 10525b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc = 10535b9c547cSRui Paulo crypto_bignum_init_set(pos, sae->tmp->prime_len); 10545b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ffc == NULL) 10555b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 1056*325151a3SRui Paulo /* 1 < element < p - 1 */ 1057*325151a3SRui Paulo res = crypto_bignum_init(); 1058*325151a3SRui Paulo one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); 1059*325151a3SRui Paulo if (!res || !one || 1060*325151a3SRui Paulo crypto_bignum_sub(sae->tmp->prime, one, res) || 1061*325151a3SRui Paulo crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || 10625b9c547cSRui Paulo crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || 1063*325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { 1064*325151a3SRui Paulo crypto_bignum_deinit(res, 0); 1065*325151a3SRui Paulo crypto_bignum_deinit(one, 0); 10665b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); 10675b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10685b9c547cSRui Paulo } 1069*325151a3SRui Paulo crypto_bignum_deinit(one, 0); 10705b9c547cSRui Paulo 10715b9c547cSRui Paulo /* scalar-op(r, ELEMENT) = 1 modulo p */ 1072*325151a3SRui Paulo if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, 10735b9c547cSRui Paulo sae->tmp->order, sae->tmp->prime, res) < 0 || 10745b9c547cSRui Paulo !crypto_bignum_is_one(res)) { 10755b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); 10765b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 10775b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10785b9c547cSRui Paulo } 10795b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 10805b9c547cSRui Paulo 10815b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 10825b9c547cSRui Paulo } 10835b9c547cSRui Paulo 10845b9c547cSRui Paulo 10855b9c547cSRui Paulo static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, 10865b9c547cSRui Paulo const u8 *end) 10875b9c547cSRui Paulo { 10885b9c547cSRui Paulo if (sae->tmp->dh) 10895b9c547cSRui Paulo return sae_parse_commit_element_ffc(sae, pos, end); 10905b9c547cSRui Paulo return sae_parse_commit_element_ecc(sae, pos, end); 10915b9c547cSRui Paulo } 10925b9c547cSRui Paulo 10935b9c547cSRui Paulo 10945b9c547cSRui Paulo u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, 10955b9c547cSRui Paulo const u8 **token, size_t *token_len, int *allowed_groups) 10965b9c547cSRui Paulo { 10975b9c547cSRui Paulo const u8 *pos = data, *end = data + len; 10985b9c547cSRui Paulo u16 res; 10995b9c547cSRui Paulo 11005b9c547cSRui Paulo /* Check Finite Cyclic Group */ 11015b9c547cSRui Paulo if (pos + 2 > end) 11025b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11035b9c547cSRui Paulo res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); 11045b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 11055b9c547cSRui Paulo return res; 11065b9c547cSRui Paulo pos += 2; 11075b9c547cSRui Paulo 11085b9c547cSRui Paulo /* Optional Anti-Clogging Token */ 11095b9c547cSRui Paulo sae_parse_commit_token(sae, &pos, end, token, token_len); 11105b9c547cSRui Paulo 11115b9c547cSRui Paulo /* commit-scalar */ 11125b9c547cSRui Paulo res = sae_parse_commit_scalar(sae, &pos, end); 11135b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 11145b9c547cSRui Paulo return res; 11155b9c547cSRui Paulo 11165b9c547cSRui Paulo /* commit-element */ 1117*325151a3SRui Paulo res = sae_parse_commit_element(sae, pos, end); 1118*325151a3SRui Paulo if (res != WLAN_STATUS_SUCCESS) 1119*325151a3SRui Paulo return res; 1120*325151a3SRui Paulo 1121*325151a3SRui Paulo /* 1122*325151a3SRui Paulo * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as 1123*325151a3SRui Paulo * the values we sent which would be evidence of a reflection attack. 1124*325151a3SRui Paulo */ 1125*325151a3SRui Paulo if (!sae->tmp->own_commit_scalar || 1126*325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_scalar, 1127*325151a3SRui Paulo sae->peer_commit_scalar) != 0 || 1128*325151a3SRui Paulo (sae->tmp->dh && 1129*325151a3SRui Paulo (!sae->tmp->own_commit_element_ffc || 1130*325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, 1131*325151a3SRui Paulo sae->tmp->peer_commit_element_ffc) != 0)) || 1132*325151a3SRui Paulo (sae->tmp->ec && 1133*325151a3SRui Paulo (!sae->tmp->own_commit_element_ecc || 1134*325151a3SRui Paulo crypto_ec_point_cmp(sae->tmp->ec, 1135*325151a3SRui Paulo sae->tmp->own_commit_element_ecc, 1136*325151a3SRui Paulo sae->tmp->peer_commit_element_ecc) != 0))) 1137*325151a3SRui Paulo return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ 1138*325151a3SRui Paulo 1139*325151a3SRui Paulo /* 1140*325151a3SRui Paulo * This is a reflection attack - return special value to trigger caller 1141*325151a3SRui Paulo * to silently discard the frame instead of replying with a specific 1142*325151a3SRui Paulo * status code. 1143*325151a3SRui Paulo */ 1144*325151a3SRui Paulo return SAE_SILENTLY_DISCARD; 11455b9c547cSRui Paulo } 11465b9c547cSRui Paulo 11475b9c547cSRui Paulo 11485b9c547cSRui Paulo static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, 11495b9c547cSRui Paulo const struct crypto_bignum *scalar1, 11505b9c547cSRui Paulo const u8 *element1, size_t element1_len, 11515b9c547cSRui Paulo const struct crypto_bignum *scalar2, 11525b9c547cSRui Paulo const u8 *element2, size_t element2_len, 11535b9c547cSRui Paulo u8 *confirm) 11545b9c547cSRui Paulo { 11555b9c547cSRui Paulo const u8 *addr[5]; 11565b9c547cSRui Paulo size_t len[5]; 11575b9c547cSRui Paulo u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; 11585b9c547cSRui Paulo 11595b9c547cSRui Paulo /* Confirm 11605b9c547cSRui Paulo * CN(key, X, Y, Z, ...) = 11615b9c547cSRui Paulo * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) 11625b9c547cSRui Paulo * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, 11635b9c547cSRui Paulo * peer-commit-scalar, PEER-COMMIT-ELEMENT) 11645b9c547cSRui Paulo * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, 11655b9c547cSRui Paulo * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) 11665b9c547cSRui Paulo */ 11675b9c547cSRui Paulo addr[0] = sc; 11685b9c547cSRui Paulo len[0] = 2; 11695b9c547cSRui Paulo crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), 11705b9c547cSRui Paulo sae->tmp->prime_len); 11715b9c547cSRui Paulo addr[1] = scalar_b1; 11725b9c547cSRui Paulo len[1] = sae->tmp->prime_len; 11735b9c547cSRui Paulo addr[2] = element1; 11745b9c547cSRui Paulo len[2] = element1_len; 11755b9c547cSRui Paulo crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), 11765b9c547cSRui Paulo sae->tmp->prime_len); 11775b9c547cSRui Paulo addr[3] = scalar_b2; 11785b9c547cSRui Paulo len[3] = sae->tmp->prime_len; 11795b9c547cSRui Paulo addr[4] = element2; 11805b9c547cSRui Paulo len[4] = element2_len; 11815b9c547cSRui Paulo hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, 11825b9c547cSRui Paulo confirm); 11835b9c547cSRui Paulo } 11845b9c547cSRui Paulo 11855b9c547cSRui Paulo 11865b9c547cSRui Paulo static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, 11875b9c547cSRui Paulo const struct crypto_bignum *scalar1, 11885b9c547cSRui Paulo const struct crypto_ec_point *element1, 11895b9c547cSRui Paulo const struct crypto_bignum *scalar2, 11905b9c547cSRui Paulo const struct crypto_ec_point *element2, 11915b9c547cSRui Paulo u8 *confirm) 11925b9c547cSRui Paulo { 11935b9c547cSRui Paulo u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; 11945b9c547cSRui Paulo u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; 11955b9c547cSRui Paulo 11965b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, 11975b9c547cSRui Paulo element_b1 + sae->tmp->prime_len); 11985b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, 11995b9c547cSRui Paulo element_b2 + sae->tmp->prime_len); 12005b9c547cSRui Paulo 12015b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, 12025b9c547cSRui Paulo scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); 12035b9c547cSRui Paulo } 12045b9c547cSRui Paulo 12055b9c547cSRui Paulo 12065b9c547cSRui Paulo static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, 12075b9c547cSRui Paulo const struct crypto_bignum *scalar1, 12085b9c547cSRui Paulo const struct crypto_bignum *element1, 12095b9c547cSRui Paulo const struct crypto_bignum *scalar2, 12105b9c547cSRui Paulo const struct crypto_bignum *element2, 12115b9c547cSRui Paulo u8 *confirm) 12125b9c547cSRui Paulo { 12135b9c547cSRui Paulo u8 element_b1[SAE_MAX_PRIME_LEN]; 12145b9c547cSRui Paulo u8 element_b2[SAE_MAX_PRIME_LEN]; 12155b9c547cSRui Paulo 12165b9c547cSRui Paulo crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), 12175b9c547cSRui Paulo sae->tmp->prime_len); 12185b9c547cSRui Paulo crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), 12195b9c547cSRui Paulo sae->tmp->prime_len); 12205b9c547cSRui Paulo 12215b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, 12225b9c547cSRui Paulo scalar2, element_b2, sae->tmp->prime_len, confirm); 12235b9c547cSRui Paulo } 12245b9c547cSRui Paulo 12255b9c547cSRui Paulo 12265b9c547cSRui Paulo void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) 12275b9c547cSRui Paulo { 12285b9c547cSRui Paulo const u8 *sc; 12295b9c547cSRui Paulo 12305b9c547cSRui Paulo if (sae->tmp == NULL) 12315b9c547cSRui Paulo return; 12325b9c547cSRui Paulo 12335b9c547cSRui Paulo /* Send-Confirm */ 12345b9c547cSRui Paulo sc = wpabuf_put(buf, 0); 12355b9c547cSRui Paulo wpabuf_put_le16(buf, sae->send_confirm); 12365b9c547cSRui Paulo sae->send_confirm++; 12375b9c547cSRui Paulo 12385b9c547cSRui Paulo if (sae->tmp->ec) 12395b9c547cSRui Paulo sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, 12405b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 12415b9c547cSRui Paulo sae->peer_commit_scalar, 12425b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 12435b9c547cSRui Paulo wpabuf_put(buf, SHA256_MAC_LEN)); 12445b9c547cSRui Paulo else 12455b9c547cSRui Paulo sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, 12465b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 12475b9c547cSRui Paulo sae->peer_commit_scalar, 12485b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 12495b9c547cSRui Paulo wpabuf_put(buf, SHA256_MAC_LEN)); 12505b9c547cSRui Paulo } 12515b9c547cSRui Paulo 12525b9c547cSRui Paulo 12535b9c547cSRui Paulo int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) 12545b9c547cSRui Paulo { 12555b9c547cSRui Paulo u8 verifier[SHA256_MAC_LEN]; 12565b9c547cSRui Paulo 12575b9c547cSRui Paulo if (len < 2 + SHA256_MAC_LEN) { 12585b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); 12595b9c547cSRui Paulo return -1; 12605b9c547cSRui Paulo } 12615b9c547cSRui Paulo 12625b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); 12635b9c547cSRui Paulo 12645b9c547cSRui Paulo if (sae->tmp == NULL) { 12655b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); 12665b9c547cSRui Paulo return -1; 12675b9c547cSRui Paulo } 12685b9c547cSRui Paulo 12695b9c547cSRui Paulo if (sae->tmp->ec) 12705b9c547cSRui Paulo sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, 12715b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 12725b9c547cSRui Paulo sae->tmp->own_commit_scalar, 12735b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 12745b9c547cSRui Paulo verifier); 12755b9c547cSRui Paulo else 12765b9c547cSRui Paulo sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, 12775b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 12785b9c547cSRui Paulo sae->tmp->own_commit_scalar, 12795b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 12805b9c547cSRui Paulo verifier); 12815b9c547cSRui Paulo 12825b9c547cSRui Paulo if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) { 12835b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); 12845b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", 12855b9c547cSRui Paulo data + 2, SHA256_MAC_LEN); 12865b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", 12875b9c547cSRui Paulo verifier, SHA256_MAC_LEN); 12885b9c547cSRui Paulo return -1; 12895b9c547cSRui Paulo } 12905b9c547cSRui Paulo 12915b9c547cSRui Paulo return 0; 12925b9c547cSRui Paulo } 1293