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