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