15b9c547cSRui Paulo /* 25b9c547cSRui Paulo * Simultaneous authentication of equals 3*780fb4a2SCy 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) { 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 (;;) { 127325151a3SRui 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 172325151a3SRui Paulo static struct crypto_bignum * 173325151a3SRui Paulo get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits, 174325151a3SRui Paulo int *r_odd) 1755b9c547cSRui Paulo { 176325151a3SRui Paulo for (;;) { 177325151a3SRui Paulo struct crypto_bignum *r; 178325151a3SRui Paulo u8 tmp[SAE_MAX_ECC_PRIME_LEN]; 179325151a3SRui Paulo 180325151a3SRui Paulo if (random_get_bytes(tmp, prime_len) < 0) 181325151a3SRui Paulo break; 182325151a3SRui Paulo if (prime_bits % 8) 183325151a3SRui Paulo buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); 184325151a3SRui Paulo if (os_memcmp(tmp, prime, prime_len) >= 0) 185325151a3SRui Paulo continue; 186325151a3SRui Paulo r = crypto_bignum_init_set(tmp, prime_len); 187325151a3SRui Paulo if (!r) 188325151a3SRui Paulo break; 189325151a3SRui Paulo if (crypto_bignum_is_zero(r)) { 190325151a3SRui Paulo crypto_bignum_deinit(r, 0); 191325151a3SRui Paulo continue; 192325151a3SRui Paulo } 193325151a3SRui Paulo 194325151a3SRui Paulo *r_odd = tmp[prime_len - 1] & 0x01; 195325151a3SRui Paulo return r; 196325151a3SRui Paulo } 197325151a3SRui Paulo 198325151a3SRui Paulo return NULL; 199325151a3SRui Paulo } 200325151a3SRui Paulo 201325151a3SRui Paulo 202325151a3SRui Paulo static int is_quadratic_residue_blind(struct sae_data *sae, 203325151a3SRui Paulo const u8 *prime, size_t bits, 204325151a3SRui Paulo const struct crypto_bignum *qr, 205325151a3SRui Paulo const struct crypto_bignum *qnr, 206325151a3SRui Paulo const struct crypto_bignum *y_sqr) 207325151a3SRui Paulo { 208325151a3SRui Paulo struct crypto_bignum *r, *num; 209325151a3SRui Paulo int r_odd, check, res = -1; 210325151a3SRui Paulo 211325151a3SRui Paulo /* 212325151a3SRui Paulo * Use the blinding technique to mask y_sqr while determining 213325151a3SRui Paulo * whether it is a quadratic residue modulo p to avoid leaking 214325151a3SRui Paulo * timing information while determining the Legendre symbol. 215325151a3SRui Paulo * 216325151a3SRui Paulo * v = y_sqr 217325151a3SRui Paulo * r = a random number between 1 and p-1, inclusive 218325151a3SRui Paulo * num = (v * r * r) modulo p 219325151a3SRui Paulo */ 220325151a3SRui Paulo r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd); 221325151a3SRui Paulo if (!r) 222325151a3SRui Paulo return -1; 223325151a3SRui Paulo 224325151a3SRui Paulo num = crypto_bignum_init(); 225325151a3SRui Paulo if (!num || 226325151a3SRui Paulo crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 || 227325151a3SRui Paulo crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0) 228325151a3SRui Paulo goto fail; 229325151a3SRui Paulo 230325151a3SRui Paulo if (r_odd) { 231325151a3SRui Paulo /* 232325151a3SRui Paulo * num = (num * qr) module p 233325151a3SRui Paulo * LGR(num, p) = 1 ==> quadratic residue 234325151a3SRui Paulo */ 235325151a3SRui Paulo if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0) 236325151a3SRui Paulo goto fail; 237325151a3SRui Paulo check = 1; 238325151a3SRui Paulo } else { 239325151a3SRui Paulo /* 240325151a3SRui Paulo * num = (num * qnr) module p 241325151a3SRui Paulo * LGR(num, p) = -1 ==> quadratic residue 242325151a3SRui Paulo */ 243325151a3SRui Paulo if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0) 244325151a3SRui Paulo goto fail; 245325151a3SRui Paulo check = -1; 246325151a3SRui Paulo } 247325151a3SRui Paulo 248325151a3SRui Paulo res = crypto_bignum_legendre(num, sae->tmp->prime); 249325151a3SRui Paulo if (res == -2) { 250325151a3SRui Paulo res = -1; 251325151a3SRui Paulo goto fail; 252325151a3SRui Paulo } 253325151a3SRui Paulo res = res == check; 254325151a3SRui Paulo fail: 255325151a3SRui Paulo crypto_bignum_deinit(num, 1); 256325151a3SRui Paulo crypto_bignum_deinit(r, 1); 257325151a3SRui Paulo return res; 258325151a3SRui Paulo } 259325151a3SRui Paulo 260325151a3SRui Paulo 261325151a3SRui Paulo static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, 262325151a3SRui Paulo const u8 *prime, 263325151a3SRui Paulo const struct crypto_bignum *qr, 264325151a3SRui Paulo const struct crypto_bignum *qnr, 265325151a3SRui Paulo struct crypto_bignum **ret_x_cand) 266325151a3SRui Paulo { 267325151a3SRui Paulo u8 pwd_value[SAE_MAX_ECC_PRIME_LEN]; 268325151a3SRui Paulo struct crypto_bignum *y_sqr, *x_cand; 269325151a3SRui Paulo int res; 2705b9c547cSRui Paulo size_t bits; 2715b9c547cSRui Paulo 272325151a3SRui 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); 278*780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 279*780fb4a2SCy Schubert prime, sae->tmp->prime_len, pwd_value, bits) < 0) 280*780fb4a2SCy Schubert return -1; 2815b9c547cSRui Paulo if (bits % 8) 2825b9c547cSRui Paulo buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8); 2835b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", 2845b9c547cSRui Paulo pwd_value, sae->tmp->prime_len); 2855b9c547cSRui Paulo 2865b9c547cSRui Paulo if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0) 2875b9c547cSRui Paulo return 0; 2885b9c547cSRui Paulo 289325151a3SRui Paulo x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 290325151a3SRui Paulo if (!x_cand) 2915b9c547cSRui Paulo return -1; 292325151a3SRui Paulo y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); 293325151a3SRui Paulo if (!y_sqr) { 294325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 295325151a3SRui Paulo return -1; 2965b9c547cSRui Paulo } 2975b9c547cSRui Paulo 298325151a3SRui Paulo res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr); 299325151a3SRui Paulo crypto_bignum_deinit(y_sqr, 1); 300325151a3SRui Paulo if (res <= 0) { 301325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 302325151a3SRui Paulo return res; 303325151a3SRui Paulo } 3045b9c547cSRui Paulo 305325151a3SRui Paulo *ret_x_cand = x_cand; 3065b9c547cSRui Paulo return 1; 3075b9c547cSRui Paulo } 3085b9c547cSRui Paulo 3095b9c547cSRui Paulo 3105b9c547cSRui Paulo static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, 3115b9c547cSRui Paulo struct crypto_bignum *pwe) 3125b9c547cSRui Paulo { 3135b9c547cSRui Paulo u8 pwd_value[SAE_MAX_PRIME_LEN]; 3145b9c547cSRui Paulo size_t bits = sae->tmp->prime_len * 8; 3155b9c547cSRui Paulo u8 exp[1]; 3165b9c547cSRui Paulo struct crypto_bignum *a, *b; 3175b9c547cSRui Paulo int res; 3185b9c547cSRui Paulo 3195b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 3205b9c547cSRui Paulo 3215b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 322*780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 3235b9c547cSRui Paulo sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, 324*780fb4a2SCy Schubert bits) < 0) 325*780fb4a2SCy Schubert return -1; 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 382325151a3SRui Paulo static int get_random_qr_qnr(const u8 *prime, size_t prime_len, 383325151a3SRui Paulo const struct crypto_bignum *prime_bn, 384325151a3SRui Paulo size_t prime_bits, struct crypto_bignum **qr, 385325151a3SRui Paulo struct crypto_bignum **qnr) 386325151a3SRui Paulo { 387325151a3SRui Paulo *qr = NULL; 388325151a3SRui Paulo *qnr = NULL; 389325151a3SRui Paulo 390325151a3SRui Paulo while (!(*qr) || !(*qnr)) { 391325151a3SRui Paulo u8 tmp[SAE_MAX_ECC_PRIME_LEN]; 392325151a3SRui Paulo struct crypto_bignum *q; 393325151a3SRui Paulo int res; 394325151a3SRui Paulo 395325151a3SRui Paulo if (random_get_bytes(tmp, prime_len) < 0) 396325151a3SRui Paulo break; 397325151a3SRui Paulo if (prime_bits % 8) 398325151a3SRui Paulo buf_shift_right(tmp, prime_len, 8 - prime_bits % 8); 399325151a3SRui Paulo if (os_memcmp(tmp, prime, prime_len) >= 0) 400325151a3SRui Paulo continue; 401325151a3SRui Paulo q = crypto_bignum_init_set(tmp, prime_len); 402325151a3SRui Paulo if (!q) 403325151a3SRui Paulo break; 404325151a3SRui Paulo res = crypto_bignum_legendre(q, prime_bn); 405325151a3SRui Paulo 406325151a3SRui Paulo if (res == 1 && !(*qr)) 407325151a3SRui Paulo *qr = q; 408325151a3SRui Paulo else if (res == -1 && !(*qnr)) 409325151a3SRui Paulo *qnr = q; 410325151a3SRui Paulo else 411325151a3SRui Paulo crypto_bignum_deinit(q, 0); 412325151a3SRui Paulo } 413325151a3SRui Paulo 414325151a3SRui Paulo return (*qr && *qnr) ? 0 : -1; 415325151a3SRui Paulo } 416325151a3SRui Paulo 417325151a3SRui 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 { 422325151a3SRui Paulo u8 counter, k = 40; 4235b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN]; 4245b9c547cSRui Paulo const u8 *addr[2]; 4255b9c547cSRui Paulo size_t len[2]; 426325151a3SRui Paulo u8 dummy_password[32]; 427325151a3SRui Paulo size_t dummy_password_len; 428325151a3SRui Paulo int pwd_seed_odd = 0; 429325151a3SRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 430325151a3SRui Paulo size_t prime_len; 431325151a3SRui Paulo struct crypto_bignum *x = NULL, *qr, *qnr; 432325151a3SRui Paulo size_t bits; 433325151a3SRui Paulo int res; 4345b9c547cSRui Paulo 435325151a3SRui Paulo dummy_password_len = password_len; 436325151a3SRui Paulo if (dummy_password_len > sizeof(dummy_password)) 437325151a3SRui Paulo dummy_password_len = sizeof(dummy_password); 438325151a3SRui Paulo if (random_get_bytes(dummy_password, dummy_password_len) < 0) 4395b9c547cSRui Paulo return -1; 440325151a3SRui Paulo 441325151a3SRui Paulo prime_len = sae->tmp->prime_len; 442325151a3SRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 443325151a3SRui Paulo prime_len) < 0) 444325151a3SRui Paulo return -1; 445325151a3SRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec); 446325151a3SRui Paulo 447325151a3SRui Paulo /* 448325151a3SRui Paulo * Create a random quadratic residue (qr) and quadratic non-residue 449325151a3SRui Paulo * (qnr) modulo p for blinding purposes during the loop. 450325151a3SRui Paulo */ 451325151a3SRui Paulo if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits, 452325151a3SRui 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) 460325151a3SRui Paulo * base = password 4615b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 462325151a3SRui 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 */ 476325151a3SRui Paulo for (counter = 1; counter <= k || !x; counter++) { 4775b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN]; 478325151a3SRui 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; 490325151a3SRui Paulo 4915b9c547cSRui Paulo res = sae_test_pwd_seed_ecc(sae, pwd_seed, 492325151a3SRui Paulo prime, qr, qnr, &x_cand); 4935b9c547cSRui Paulo if (res < 0) 494325151a3SRui Paulo goto fail; 495325151a3SRui Paulo if (res > 0 && !x) { 496325151a3SRui Paulo wpa_printf(MSG_DEBUG, 497325151a3SRui Paulo "SAE: Selected pwd-seed with counter %u", 498325151a3SRui Paulo counter); 499325151a3SRui Paulo x = x_cand; 500325151a3SRui Paulo pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01; 501325151a3SRui Paulo os_memset(pwd_seed, 0, sizeof(pwd_seed)); 502325151a3SRui Paulo 503325151a3SRui Paulo /* 504325151a3SRui Paulo * Use a dummy password for the following rounds, if 505325151a3SRui Paulo * any. 506325151a3SRui Paulo */ 507325151a3SRui Paulo addr[0] = dummy_password; 508325151a3SRui Paulo len[0] = dummy_password_len; 509325151a3SRui Paulo } else if (res > 0) { 510325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1); 5115b9c547cSRui Paulo } 5125b9c547cSRui Paulo } 5135b9c547cSRui Paulo 514325151a3SRui Paulo if (!x) { 515325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); 516325151a3SRui Paulo res = -1; 517325151a3SRui Paulo goto fail; 518325151a3SRui Paulo } 5195b9c547cSRui Paulo 520325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 521325151a3SRui Paulo sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); 522325151a3SRui Paulo if (!sae->tmp->pwe_ecc) 523325151a3SRui Paulo res = -1; 524325151a3SRui Paulo else 525325151a3SRui Paulo res = crypto_ec_point_solve_y_coord(sae->tmp->ec, 526325151a3SRui Paulo sae->tmp->pwe_ecc, x, 527325151a3SRui Paulo pwd_seed_odd); 528325151a3SRui Paulo crypto_bignum_deinit(x, 1); 529325151a3SRui Paulo if (res < 0) { 530325151a3SRui Paulo /* 531325151a3SRui Paulo * This should not happen since we already checked that there 532325151a3SRui Paulo * is a result. 533325151a3SRui Paulo */ 534325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); 535325151a3SRui Paulo } 536325151a3SRui Paulo 537325151a3SRui Paulo fail: 538325151a3SRui Paulo crypto_bignum_deinit(qr, 0); 539325151a3SRui Paulo crypto_bignum_deinit(qnr, 0); 540325151a3SRui Paulo 541325151a3SRui 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; 653325151a3SRui Paulo unsigned int counter = 0; 654325151a3SRui Paulo 655325151a3SRui Paulo do { 656325151a3SRui Paulo counter++; 657325151a3SRui Paulo if (counter > 100) { 658325151a3SRui Paulo /* 659325151a3SRui Paulo * This cannot really happen in practice if the random 660325151a3SRui Paulo * number generator is working. Anyway, to avoid even a 661325151a3SRui Paulo * theoretical infinite loop, break out after 100 662325151a3SRui Paulo * attemps. 663325151a3SRui Paulo */ 664325151a3SRui Paulo return -1; 665325151a3SRui 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); 683325151a3SRui Paulo } while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) || 684325151a3SRui Paulo crypto_bignum_is_one(sae->tmp->own_commit_scalar)); 6855b9c547cSRui Paulo 686325151a3SRui Paulo if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) || 687325151a3SRui 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 { 701325151a3SRui Paulo if (sae->tmp == NULL || 702325151a3SRui Paulo (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, 703325151a3SRui Paulo password_len) < 0) || 704325151a3SRui Paulo (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, 705325151a3SRui Paulo password_len) < 0) || 706325151a3SRui 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); 814*780fb4a2SCy Schubert if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", 815*780fb4a2SCy Schubert val, sae->tmp->prime_len, keys, sizeof(keys)) < 0) 816*780fb4a2SCy Schubert goto fail; 8175b9c547cSRui Paulo os_memset(keyseed, 0, sizeof(keyseed)); 8185b9c547cSRui Paulo os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); 8195b9c547cSRui Paulo os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); 820*780fb4a2SCy Schubert os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); 8215b9c547cSRui Paulo os_memset(keys, 0, sizeof(keys)); 8225b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); 8235b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); 8245b9c547cSRui Paulo 8255b9c547cSRui Paulo ret = 0; 8265b9c547cSRui Paulo fail: 8275b9c547cSRui Paulo crypto_bignum_deinit(tmp, 0); 8285b9c547cSRui Paulo return ret; 8295b9c547cSRui Paulo } 8305b9c547cSRui Paulo 8315b9c547cSRui Paulo 8325b9c547cSRui Paulo int sae_process_commit(struct sae_data *sae) 8335b9c547cSRui Paulo { 8345b9c547cSRui Paulo u8 k[SAE_MAX_PRIME_LEN]; 8355b9c547cSRui Paulo if (sae->tmp == NULL || 8365b9c547cSRui Paulo (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || 8375b9c547cSRui Paulo (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || 8385b9c547cSRui Paulo sae_derive_keys(sae, k) < 0) 8395b9c547cSRui Paulo return -1; 8405b9c547cSRui Paulo return 0; 8415b9c547cSRui Paulo } 8425b9c547cSRui Paulo 8435b9c547cSRui Paulo 8445b9c547cSRui Paulo void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, 8455b9c547cSRui Paulo const struct wpabuf *token) 8465b9c547cSRui Paulo { 8475b9c547cSRui Paulo u8 *pos; 8485b9c547cSRui Paulo 8495b9c547cSRui Paulo if (sae->tmp == NULL) 8505b9c547cSRui Paulo return; 8515b9c547cSRui Paulo 8525b9c547cSRui Paulo wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ 8535b9c547cSRui Paulo if (token) { 8545b9c547cSRui Paulo wpabuf_put_buf(buf, token); 8555b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", 8565b9c547cSRui Paulo wpabuf_head(token), wpabuf_len(token)); 8575b9c547cSRui Paulo } 8585b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 8595b9c547cSRui Paulo crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, 8605b9c547cSRui Paulo sae->tmp->prime_len, sae->tmp->prime_len); 8615b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", 8625b9c547cSRui Paulo pos, sae->tmp->prime_len); 8635b9c547cSRui Paulo if (sae->tmp->ec) { 8645b9c547cSRui Paulo pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); 8655b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, 8665b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 8675b9c547cSRui Paulo pos, pos + sae->tmp->prime_len); 8685b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", 8695b9c547cSRui Paulo pos, sae->tmp->prime_len); 8705b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", 8715b9c547cSRui Paulo pos + sae->tmp->prime_len, sae->tmp->prime_len); 8725b9c547cSRui Paulo } else { 8735b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len); 8745b9c547cSRui Paulo crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, 8755b9c547cSRui Paulo sae->tmp->prime_len, sae->tmp->prime_len); 8765b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", 8775b9c547cSRui Paulo pos, sae->tmp->prime_len); 8785b9c547cSRui Paulo } 8795b9c547cSRui Paulo } 8805b9c547cSRui Paulo 8815b9c547cSRui Paulo 8825b9c547cSRui Paulo u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) 8835b9c547cSRui Paulo { 8845b9c547cSRui Paulo if (allowed_groups) { 8855b9c547cSRui Paulo int i; 8865b9c547cSRui Paulo for (i = 0; allowed_groups[i] > 0; i++) { 8875b9c547cSRui Paulo if (allowed_groups[i] == group) 8885b9c547cSRui Paulo break; 8895b9c547cSRui Paulo } 8905b9c547cSRui Paulo if (allowed_groups[i] != group) { 8915b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " 8925b9c547cSRui Paulo "enabled in the current configuration", 8935b9c547cSRui Paulo group); 8945b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 8955b9c547cSRui Paulo } 8965b9c547cSRui Paulo } 8975b9c547cSRui Paulo 8985b9c547cSRui Paulo if (sae->state == SAE_COMMITTED && group != sae->group) { 8995b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); 9005b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 9015b9c547cSRui Paulo } 9025b9c547cSRui Paulo 9035b9c547cSRui Paulo if (group != sae->group && sae_set_group(sae, group) < 0) { 9045b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", 9055b9c547cSRui Paulo group); 9065b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 9075b9c547cSRui Paulo } 9085b9c547cSRui Paulo 9095b9c547cSRui Paulo if (sae->tmp == NULL) { 9105b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); 9115b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9125b9c547cSRui Paulo } 9135b9c547cSRui Paulo 9145b9c547cSRui Paulo if (sae->tmp->dh && !allowed_groups) { 9155b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " 9165b9c547cSRui Paulo "explicit configuration enabling it", group); 9175b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 9185b9c547cSRui Paulo } 9195b9c547cSRui Paulo 9205b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 9215b9c547cSRui Paulo } 9225b9c547cSRui Paulo 9235b9c547cSRui Paulo 9245b9c547cSRui Paulo static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, 9255b9c547cSRui Paulo const u8 *end, const u8 **token, 9265b9c547cSRui Paulo size_t *token_len) 9275b9c547cSRui Paulo { 928*780fb4a2SCy Schubert if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) { 9295b9c547cSRui Paulo size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * 9305b9c547cSRui Paulo sae->tmp->prime_len); 9315b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); 9325b9c547cSRui Paulo if (token) 9335b9c547cSRui Paulo *token = *pos; 9345b9c547cSRui Paulo if (token_len) 9355b9c547cSRui Paulo *token_len = tlen; 9365b9c547cSRui Paulo *pos += tlen; 9375b9c547cSRui Paulo } else { 9385b9c547cSRui Paulo if (token) 9395b9c547cSRui Paulo *token = NULL; 9405b9c547cSRui Paulo if (token_len) 9415b9c547cSRui Paulo *token_len = 0; 9425b9c547cSRui Paulo } 9435b9c547cSRui Paulo } 9445b9c547cSRui Paulo 9455b9c547cSRui Paulo 9465b9c547cSRui Paulo static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, 9475b9c547cSRui Paulo const u8 *end) 9485b9c547cSRui Paulo { 9495b9c547cSRui Paulo struct crypto_bignum *peer_scalar; 9505b9c547cSRui Paulo 951*780fb4a2SCy Schubert if (sae->tmp->prime_len > end - *pos) { 9525b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); 9535b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9545b9c547cSRui Paulo } 9555b9c547cSRui Paulo 9565b9c547cSRui Paulo peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); 9575b9c547cSRui Paulo if (peer_scalar == NULL) 9585b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9595b9c547cSRui Paulo 9605b9c547cSRui Paulo /* 9615b9c547cSRui Paulo * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for 9625b9c547cSRui Paulo * the peer and it is in Authenticated state, the new Commit Message 9635b9c547cSRui Paulo * shall be dropped if the peer-scalar is identical to the one used in 9645b9c547cSRui Paulo * the existing protocol instance. 9655b9c547cSRui Paulo */ 9665b9c547cSRui Paulo if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && 9675b9c547cSRui Paulo crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { 9685b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " 9695b9c547cSRui Paulo "peer-commit-scalar"); 9705b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 9715b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9725b9c547cSRui Paulo } 9735b9c547cSRui Paulo 974325151a3SRui Paulo /* 1 < scalar < r */ 9755b9c547cSRui Paulo if (crypto_bignum_is_zero(peer_scalar) || 976325151a3SRui Paulo crypto_bignum_is_one(peer_scalar) || 9775b9c547cSRui Paulo crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { 9785b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); 9795b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0); 9805b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 9815b9c547cSRui Paulo } 9825b9c547cSRui Paulo 9835b9c547cSRui Paulo 9845b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0); 9855b9c547cSRui Paulo sae->peer_commit_scalar = peer_scalar; 9865b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", 9875b9c547cSRui Paulo *pos, sae->tmp->prime_len); 9885b9c547cSRui Paulo *pos += sae->tmp->prime_len; 9895b9c547cSRui Paulo 9905b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 9915b9c547cSRui Paulo } 9925b9c547cSRui Paulo 9935b9c547cSRui Paulo 9945b9c547cSRui Paulo static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, 9955b9c547cSRui Paulo const u8 *end) 9965b9c547cSRui Paulo { 9975b9c547cSRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN]; 9985b9c547cSRui Paulo 999*780fb4a2SCy Schubert if (2 * sae->tmp->prime_len > end - pos) { 10005b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 10015b9c547cSRui Paulo "commit-element"); 10025b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10035b9c547cSRui Paulo } 10045b9c547cSRui Paulo 10055b9c547cSRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 10065b9c547cSRui Paulo sae->tmp->prime_len) < 0) 10075b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10085b9c547cSRui Paulo 10095b9c547cSRui Paulo /* element x and y coordinates < p */ 10105b9c547cSRui Paulo if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 || 10115b9c547cSRui Paulo os_memcmp(pos + sae->tmp->prime_len, prime, 10125b9c547cSRui Paulo sae->tmp->prime_len) >= 0) { 10135b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " 10145b9c547cSRui Paulo "element"); 10155b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10165b9c547cSRui Paulo } 10175b9c547cSRui Paulo 10185b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", 10195b9c547cSRui Paulo pos, sae->tmp->prime_len); 10205b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", 10215b9c547cSRui Paulo pos + sae->tmp->prime_len, sae->tmp->prime_len); 10225b9c547cSRui Paulo 10235b9c547cSRui Paulo crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); 10245b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc = 10255b9c547cSRui Paulo crypto_ec_point_from_bin(sae->tmp->ec, pos); 10265b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ecc == NULL) 10275b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10285b9c547cSRui Paulo 10295b9c547cSRui Paulo if (!crypto_ec_point_is_on_curve(sae->tmp->ec, 10305b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc)) { 10315b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); 10325b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10335b9c547cSRui Paulo } 10345b9c547cSRui Paulo 10355b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 10365b9c547cSRui Paulo } 10375b9c547cSRui Paulo 10385b9c547cSRui Paulo 10395b9c547cSRui Paulo static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, 10405b9c547cSRui Paulo const u8 *end) 10415b9c547cSRui Paulo { 1042325151a3SRui Paulo struct crypto_bignum *res, *one; 1043325151a3SRui Paulo const u8 one_bin[1] = { 0x01 }; 10445b9c547cSRui Paulo 1045*780fb4a2SCy Schubert if (sae->tmp->prime_len > end - pos) { 10465b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 10475b9c547cSRui Paulo "commit-element"); 10485b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10495b9c547cSRui Paulo } 10505b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos, 10515b9c547cSRui Paulo sae->tmp->prime_len); 10525b9c547cSRui Paulo 10535b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); 10545b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc = 10555b9c547cSRui Paulo crypto_bignum_init_set(pos, sae->tmp->prime_len); 10565b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ffc == NULL) 10575b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 1058325151a3SRui Paulo /* 1 < element < p - 1 */ 1059325151a3SRui Paulo res = crypto_bignum_init(); 1060325151a3SRui Paulo one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); 1061325151a3SRui Paulo if (!res || !one || 1062325151a3SRui Paulo crypto_bignum_sub(sae->tmp->prime, one, res) || 1063325151a3SRui Paulo crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || 10645b9c547cSRui Paulo crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || 1065325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { 1066325151a3SRui Paulo crypto_bignum_deinit(res, 0); 1067325151a3SRui Paulo crypto_bignum_deinit(one, 0); 10685b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); 10695b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10705b9c547cSRui Paulo } 1071325151a3SRui Paulo crypto_bignum_deinit(one, 0); 10725b9c547cSRui Paulo 10735b9c547cSRui Paulo /* scalar-op(r, ELEMENT) = 1 modulo p */ 1074325151a3SRui Paulo if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, 10755b9c547cSRui Paulo sae->tmp->order, sae->tmp->prime, res) < 0 || 10765b9c547cSRui Paulo !crypto_bignum_is_one(res)) { 10775b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); 10785b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 10795b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 10805b9c547cSRui Paulo } 10815b9c547cSRui Paulo crypto_bignum_deinit(res, 0); 10825b9c547cSRui Paulo 10835b9c547cSRui Paulo return WLAN_STATUS_SUCCESS; 10845b9c547cSRui Paulo } 10855b9c547cSRui Paulo 10865b9c547cSRui Paulo 10875b9c547cSRui Paulo static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, 10885b9c547cSRui Paulo const u8 *end) 10895b9c547cSRui Paulo { 10905b9c547cSRui Paulo if (sae->tmp->dh) 10915b9c547cSRui Paulo return sae_parse_commit_element_ffc(sae, pos, end); 10925b9c547cSRui Paulo return sae_parse_commit_element_ecc(sae, pos, end); 10935b9c547cSRui Paulo } 10945b9c547cSRui Paulo 10955b9c547cSRui Paulo 10965b9c547cSRui Paulo u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, 10975b9c547cSRui Paulo const u8 **token, size_t *token_len, int *allowed_groups) 10985b9c547cSRui Paulo { 10995b9c547cSRui Paulo const u8 *pos = data, *end = data + len; 11005b9c547cSRui Paulo u16 res; 11015b9c547cSRui Paulo 11025b9c547cSRui Paulo /* Check Finite Cyclic Group */ 1103*780fb4a2SCy Schubert if (end - pos < 2) 11045b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 11055b9c547cSRui Paulo res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); 11065b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 11075b9c547cSRui Paulo return res; 11085b9c547cSRui Paulo pos += 2; 11095b9c547cSRui Paulo 11105b9c547cSRui Paulo /* Optional Anti-Clogging Token */ 11115b9c547cSRui Paulo sae_parse_commit_token(sae, &pos, end, token, token_len); 11125b9c547cSRui Paulo 11135b9c547cSRui Paulo /* commit-scalar */ 11145b9c547cSRui Paulo res = sae_parse_commit_scalar(sae, &pos, end); 11155b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS) 11165b9c547cSRui Paulo return res; 11175b9c547cSRui Paulo 11185b9c547cSRui Paulo /* commit-element */ 1119325151a3SRui Paulo res = sae_parse_commit_element(sae, pos, end); 1120325151a3SRui Paulo if (res != WLAN_STATUS_SUCCESS) 1121325151a3SRui Paulo return res; 1122325151a3SRui Paulo 1123325151a3SRui Paulo /* 1124325151a3SRui Paulo * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as 1125325151a3SRui Paulo * the values we sent which would be evidence of a reflection attack. 1126325151a3SRui Paulo */ 1127325151a3SRui Paulo if (!sae->tmp->own_commit_scalar || 1128325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_scalar, 1129325151a3SRui Paulo sae->peer_commit_scalar) != 0 || 1130325151a3SRui Paulo (sae->tmp->dh && 1131325151a3SRui Paulo (!sae->tmp->own_commit_element_ffc || 1132325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, 1133325151a3SRui Paulo sae->tmp->peer_commit_element_ffc) != 0)) || 1134325151a3SRui Paulo (sae->tmp->ec && 1135325151a3SRui Paulo (!sae->tmp->own_commit_element_ecc || 1136325151a3SRui Paulo crypto_ec_point_cmp(sae->tmp->ec, 1137325151a3SRui Paulo sae->tmp->own_commit_element_ecc, 1138325151a3SRui Paulo sae->tmp->peer_commit_element_ecc) != 0))) 1139325151a3SRui Paulo return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ 1140325151a3SRui Paulo 1141325151a3SRui Paulo /* 1142325151a3SRui Paulo * This is a reflection attack - return special value to trigger caller 1143325151a3SRui Paulo * to silently discard the frame instead of replying with a specific 1144325151a3SRui Paulo * status code. 1145325151a3SRui Paulo */ 1146325151a3SRui Paulo return SAE_SILENTLY_DISCARD; 11475b9c547cSRui Paulo } 11485b9c547cSRui Paulo 11495b9c547cSRui Paulo 11505b9c547cSRui Paulo static void sae_cn_confirm(struct sae_data *sae, const u8 *sc, 11515b9c547cSRui Paulo const struct crypto_bignum *scalar1, 11525b9c547cSRui Paulo const u8 *element1, size_t element1_len, 11535b9c547cSRui Paulo const struct crypto_bignum *scalar2, 11545b9c547cSRui Paulo const u8 *element2, size_t element2_len, 11555b9c547cSRui Paulo u8 *confirm) 11565b9c547cSRui Paulo { 11575b9c547cSRui Paulo const u8 *addr[5]; 11585b9c547cSRui Paulo size_t len[5]; 11595b9c547cSRui Paulo u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; 11605b9c547cSRui Paulo 11615b9c547cSRui Paulo /* Confirm 11625b9c547cSRui Paulo * CN(key, X, Y, Z, ...) = 11635b9c547cSRui Paulo * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) 11645b9c547cSRui Paulo * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, 11655b9c547cSRui Paulo * peer-commit-scalar, PEER-COMMIT-ELEMENT) 11665b9c547cSRui Paulo * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, 11675b9c547cSRui Paulo * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) 11685b9c547cSRui Paulo */ 11695b9c547cSRui Paulo addr[0] = sc; 11705b9c547cSRui Paulo len[0] = 2; 11715b9c547cSRui Paulo crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), 11725b9c547cSRui Paulo sae->tmp->prime_len); 11735b9c547cSRui Paulo addr[1] = scalar_b1; 11745b9c547cSRui Paulo len[1] = sae->tmp->prime_len; 11755b9c547cSRui Paulo addr[2] = element1; 11765b9c547cSRui Paulo len[2] = element1_len; 11775b9c547cSRui Paulo crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), 11785b9c547cSRui Paulo sae->tmp->prime_len); 11795b9c547cSRui Paulo addr[3] = scalar_b2; 11805b9c547cSRui Paulo len[3] = sae->tmp->prime_len; 11815b9c547cSRui Paulo addr[4] = element2; 11825b9c547cSRui Paulo len[4] = element2_len; 11835b9c547cSRui Paulo hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, 11845b9c547cSRui Paulo confirm); 11855b9c547cSRui Paulo } 11865b9c547cSRui Paulo 11875b9c547cSRui Paulo 11885b9c547cSRui Paulo static void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, 11895b9c547cSRui Paulo const struct crypto_bignum *scalar1, 11905b9c547cSRui Paulo const struct crypto_ec_point *element1, 11915b9c547cSRui Paulo const struct crypto_bignum *scalar2, 11925b9c547cSRui Paulo const struct crypto_ec_point *element2, 11935b9c547cSRui Paulo u8 *confirm) 11945b9c547cSRui Paulo { 11955b9c547cSRui Paulo u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; 11965b9c547cSRui Paulo u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; 11975b9c547cSRui Paulo 11985b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, 11995b9c547cSRui Paulo element_b1 + sae->tmp->prime_len); 12005b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, 12015b9c547cSRui Paulo element_b2 + sae->tmp->prime_len); 12025b9c547cSRui Paulo 12035b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, 12045b9c547cSRui Paulo scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); 12055b9c547cSRui Paulo } 12065b9c547cSRui Paulo 12075b9c547cSRui Paulo 12085b9c547cSRui Paulo static void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, 12095b9c547cSRui Paulo const struct crypto_bignum *scalar1, 12105b9c547cSRui Paulo const struct crypto_bignum *element1, 12115b9c547cSRui Paulo const struct crypto_bignum *scalar2, 12125b9c547cSRui Paulo const struct crypto_bignum *element2, 12135b9c547cSRui Paulo u8 *confirm) 12145b9c547cSRui Paulo { 12155b9c547cSRui Paulo u8 element_b1[SAE_MAX_PRIME_LEN]; 12165b9c547cSRui Paulo u8 element_b2[SAE_MAX_PRIME_LEN]; 12175b9c547cSRui Paulo 12185b9c547cSRui Paulo crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), 12195b9c547cSRui Paulo sae->tmp->prime_len); 12205b9c547cSRui Paulo crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), 12215b9c547cSRui Paulo sae->tmp->prime_len); 12225b9c547cSRui Paulo 12235b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, 12245b9c547cSRui Paulo scalar2, element_b2, sae->tmp->prime_len, confirm); 12255b9c547cSRui Paulo } 12265b9c547cSRui Paulo 12275b9c547cSRui Paulo 12285b9c547cSRui Paulo void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) 12295b9c547cSRui Paulo { 12305b9c547cSRui Paulo const u8 *sc; 12315b9c547cSRui Paulo 12325b9c547cSRui Paulo if (sae->tmp == NULL) 12335b9c547cSRui Paulo return; 12345b9c547cSRui Paulo 12355b9c547cSRui Paulo /* Send-Confirm */ 12365b9c547cSRui Paulo sc = wpabuf_put(buf, 0); 12375b9c547cSRui Paulo wpabuf_put_le16(buf, sae->send_confirm); 12385b9c547cSRui Paulo sae->send_confirm++; 12395b9c547cSRui Paulo 12405b9c547cSRui Paulo if (sae->tmp->ec) 12415b9c547cSRui Paulo sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, 12425b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 12435b9c547cSRui Paulo sae->peer_commit_scalar, 12445b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 12455b9c547cSRui Paulo wpabuf_put(buf, SHA256_MAC_LEN)); 12465b9c547cSRui Paulo else 12475b9c547cSRui Paulo sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, 12485b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 12495b9c547cSRui Paulo sae->peer_commit_scalar, 12505b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 12515b9c547cSRui Paulo wpabuf_put(buf, SHA256_MAC_LEN)); 12525b9c547cSRui Paulo } 12535b9c547cSRui Paulo 12545b9c547cSRui Paulo 12555b9c547cSRui Paulo int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) 12565b9c547cSRui Paulo { 12575b9c547cSRui Paulo u8 verifier[SHA256_MAC_LEN]; 12585b9c547cSRui Paulo 12595b9c547cSRui Paulo if (len < 2 + SHA256_MAC_LEN) { 12605b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); 12615b9c547cSRui Paulo return -1; 12625b9c547cSRui Paulo } 12635b9c547cSRui Paulo 12645b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); 12655b9c547cSRui Paulo 12665b9c547cSRui Paulo if (sae->tmp == NULL) { 12675b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); 12685b9c547cSRui Paulo return -1; 12695b9c547cSRui Paulo } 12705b9c547cSRui Paulo 12715b9c547cSRui Paulo if (sae->tmp->ec) 12725b9c547cSRui Paulo sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, 12735b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, 12745b9c547cSRui Paulo sae->tmp->own_commit_scalar, 12755b9c547cSRui Paulo sae->tmp->own_commit_element_ecc, 12765b9c547cSRui Paulo verifier); 12775b9c547cSRui Paulo else 12785b9c547cSRui Paulo sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, 12795b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc, 12805b9c547cSRui Paulo sae->tmp->own_commit_scalar, 12815b9c547cSRui Paulo sae->tmp->own_commit_element_ffc, 12825b9c547cSRui Paulo verifier); 12835b9c547cSRui Paulo 12845b9c547cSRui Paulo if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) { 12855b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); 12865b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", 12875b9c547cSRui Paulo data + 2, SHA256_MAC_LEN); 12885b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", 12895b9c547cSRui Paulo verifier, SHA256_MAC_LEN); 12905b9c547cSRui Paulo return -1; 12915b9c547cSRui Paulo } 12925b9c547cSRui Paulo 12935b9c547cSRui Paulo return 0; 12945b9c547cSRui Paulo } 1295