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