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"
12*a90b9d01SCy Schubert #include "common/defs.h"
13*a90b9d01SCy Schubert #include "common/wpa_common.h"
144bc52338SCy Schubert #include "utils/const_time.h"
155b9c547cSRui Paulo #include "crypto/crypto.h"
165b9c547cSRui Paulo #include "crypto/sha256.h"
17c1d255d3SCy Schubert #include "crypto/sha384.h"
18c1d255d3SCy Schubert #include "crypto/sha512.h"
195b9c547cSRui Paulo #include "crypto/random.h"
205b9c547cSRui Paulo #include "crypto/dh_groups.h"
215b9c547cSRui Paulo #include "ieee802_11_defs.h"
22206b73d0SCy Schubert #include "dragonfly.h"
235b9c547cSRui Paulo #include "sae.h"
245b9c547cSRui Paulo
255b9c547cSRui Paulo
sae_set_group(struct sae_data * sae,int group)265b9c547cSRui Paulo int sae_set_group(struct sae_data *sae, int group)
275b9c547cSRui Paulo {
285b9c547cSRui Paulo struct sae_temporary_data *tmp;
295b9c547cSRui Paulo
30206b73d0SCy Schubert #ifdef CONFIG_TESTING_OPTIONS
31206b73d0SCy Schubert /* Allow all groups for testing purposes in non-production builds. */
32206b73d0SCy Schubert #else /* CONFIG_TESTING_OPTIONS */
33206b73d0SCy Schubert if (!dragonfly_suitable_group(group, 0)) {
344bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group);
354bc52338SCy Schubert return -1;
364bc52338SCy Schubert }
37206b73d0SCy Schubert #endif /* CONFIG_TESTING_OPTIONS */
384bc52338SCy Schubert
395b9c547cSRui Paulo sae_clear_data(sae);
405b9c547cSRui Paulo tmp = sae->tmp = os_zalloc(sizeof(*tmp));
415b9c547cSRui Paulo if (tmp == NULL)
425b9c547cSRui Paulo return -1;
435b9c547cSRui Paulo
445b9c547cSRui Paulo /* First, check if this is an ECC group */
455b9c547cSRui Paulo tmp->ec = crypto_ec_init(group);
465b9c547cSRui Paulo if (tmp->ec) {
4785732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d",
4885732ac8SCy Schubert group);
495b9c547cSRui Paulo sae->group = group;
505b9c547cSRui Paulo tmp->prime_len = crypto_ec_prime_len(tmp->ec);
515b9c547cSRui Paulo tmp->prime = crypto_ec_get_prime(tmp->ec);
52206b73d0SCy Schubert tmp->order_len = crypto_ec_order_len(tmp->ec);
535b9c547cSRui Paulo tmp->order = crypto_ec_get_order(tmp->ec);
545b9c547cSRui Paulo return 0;
555b9c547cSRui Paulo }
565b9c547cSRui Paulo
575b9c547cSRui Paulo /* Not an ECC group, check FFC */
585b9c547cSRui Paulo tmp->dh = dh_groups_get(group);
595b9c547cSRui Paulo if (tmp->dh) {
6085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d",
6185732ac8SCy Schubert group);
625b9c547cSRui Paulo sae->group = group;
635b9c547cSRui Paulo tmp->prime_len = tmp->dh->prime_len;
645b9c547cSRui Paulo if (tmp->prime_len > SAE_MAX_PRIME_LEN) {
655b9c547cSRui Paulo sae_clear_data(sae);
665b9c547cSRui Paulo return -1;
675b9c547cSRui Paulo }
685b9c547cSRui Paulo
695b9c547cSRui Paulo tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime,
705b9c547cSRui Paulo tmp->prime_len);
715b9c547cSRui Paulo if (tmp->prime_buf == NULL) {
725b9c547cSRui Paulo sae_clear_data(sae);
735b9c547cSRui Paulo return -1;
745b9c547cSRui Paulo }
755b9c547cSRui Paulo tmp->prime = tmp->prime_buf;
765b9c547cSRui Paulo
77206b73d0SCy Schubert tmp->order_len = tmp->dh->order_len;
785b9c547cSRui Paulo tmp->order_buf = crypto_bignum_init_set(tmp->dh->order,
795b9c547cSRui Paulo tmp->dh->order_len);
805b9c547cSRui Paulo if (tmp->order_buf == NULL) {
815b9c547cSRui Paulo sae_clear_data(sae);
825b9c547cSRui Paulo return -1;
835b9c547cSRui Paulo }
845b9c547cSRui Paulo tmp->order = tmp->order_buf;
855b9c547cSRui Paulo
865b9c547cSRui Paulo return 0;
875b9c547cSRui Paulo }
885b9c547cSRui Paulo
895b9c547cSRui Paulo /* Unsupported group */
9085732ac8SCy Schubert wpa_printf(MSG_DEBUG,
9185732ac8SCy Schubert "SAE: Group %d not supported by the crypto library", group);
925b9c547cSRui Paulo return -1;
935b9c547cSRui Paulo }
945b9c547cSRui Paulo
955b9c547cSRui Paulo
sae_clear_temp_data(struct sae_data * sae)965b9c547cSRui Paulo void sae_clear_temp_data(struct sae_data *sae)
975b9c547cSRui Paulo {
985b9c547cSRui Paulo struct sae_temporary_data *tmp;
995b9c547cSRui Paulo if (sae == NULL || sae->tmp == NULL)
1005b9c547cSRui Paulo return;
1015b9c547cSRui Paulo tmp = sae->tmp;
1025b9c547cSRui Paulo crypto_ec_deinit(tmp->ec);
1035b9c547cSRui Paulo crypto_bignum_deinit(tmp->prime_buf, 0);
1045b9c547cSRui Paulo crypto_bignum_deinit(tmp->order_buf, 0);
1055b9c547cSRui Paulo crypto_bignum_deinit(tmp->sae_rand, 1);
1065b9c547cSRui Paulo crypto_bignum_deinit(tmp->pwe_ffc, 1);
1075b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_scalar, 0);
1085b9c547cSRui Paulo crypto_bignum_deinit(tmp->own_commit_element_ffc, 0);
1095b9c547cSRui Paulo crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0);
1105b9c547cSRui Paulo crypto_ec_point_deinit(tmp->pwe_ecc, 1);
1115b9c547cSRui Paulo crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
1125b9c547cSRui Paulo crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
1135b9c547cSRui Paulo wpabuf_free(tmp->anti_clogging_token);
114c1d255d3SCy Schubert wpabuf_free(tmp->own_rejected_groups);
115c1d255d3SCy Schubert wpabuf_free(tmp->peer_rejected_groups);
11685732ac8SCy Schubert os_free(tmp->pw_id);
1175b9c547cSRui Paulo bin_clear_free(tmp, sizeof(*tmp));
1185b9c547cSRui Paulo sae->tmp = NULL;
1195b9c547cSRui Paulo }
1205b9c547cSRui Paulo
1215b9c547cSRui Paulo
sae_clear_data(struct sae_data * sae)1225b9c547cSRui Paulo void sae_clear_data(struct sae_data *sae)
1235b9c547cSRui Paulo {
1245b9c547cSRui Paulo if (sae == NULL)
1255b9c547cSRui Paulo return;
1265b9c547cSRui Paulo sae_clear_temp_data(sae);
1275b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0);
128c1d255d3SCy Schubert crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0);
1295b9c547cSRui Paulo os_memset(sae, 0, sizeof(*sae));
1305b9c547cSRui Paulo }
1315b9c547cSRui Paulo
1325b9c547cSRui Paulo
sae_pwd_seed_key(const u8 * addr1,const u8 * addr2,u8 * key)1335b9c547cSRui Paulo static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key)
1345b9c547cSRui Paulo {
1355b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR
1365b9c547cSRui Paulo " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2));
1375b9c547cSRui Paulo if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
1385b9c547cSRui Paulo os_memcpy(key, addr1, ETH_ALEN);
1395b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN);
1405b9c547cSRui Paulo } else {
1415b9c547cSRui Paulo os_memcpy(key, addr2, ETH_ALEN);
1425b9c547cSRui Paulo os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN);
1435b9c547cSRui Paulo }
1445b9c547cSRui Paulo }
1455b9c547cSRui Paulo
1465b9c547cSRui Paulo
sae_test_pwd_seed_ecc(struct sae_data * sae,const u8 * pwd_seed,const u8 * prime,const u8 * qr,const u8 * qnr,u8 * pwd_value)147325151a3SRui Paulo static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
1484bc52338SCy Schubert const u8 *prime, const u8 *qr, const u8 *qnr,
1494bc52338SCy Schubert u8 *pwd_value)
150325151a3SRui Paulo {
151325151a3SRui Paulo struct crypto_bignum *y_sqr, *x_cand;
152325151a3SRui Paulo int res;
1535b9c547cSRui Paulo size_t bits;
154206b73d0SCy Schubert int cmp_prime;
155206b73d0SCy Schubert unsigned int in_range;
1565b9c547cSRui Paulo
1575b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
1585b9c547cSRui Paulo
1595b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
1605b9c547cSRui Paulo bits = crypto_ec_prime_len_bits(sae->tmp->ec);
161780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
162780fb4a2SCy Schubert prime, sae->tmp->prime_len, pwd_value, bits) < 0)
163780fb4a2SCy Schubert return -1;
1645b9c547cSRui Paulo if (bits % 8)
1654bc52338SCy Schubert buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
1665b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
1675b9c547cSRui Paulo pwd_value, sae->tmp->prime_len);
1685b9c547cSRui Paulo
169206b73d0SCy Schubert cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len);
170206b73d0SCy Schubert /* Create a const_time mask for selection based on prf result
171206b73d0SCy Schubert * being smaller than prime. */
172206b73d0SCy Schubert in_range = const_time_fill_msb((unsigned int) cmp_prime);
173206b73d0SCy Schubert /* The algorithm description would skip the next steps if
174c1d255d3SCy Schubert * cmp_prime >= 0 (return 0 here), but go through them regardless to
175206b73d0SCy Schubert * minimize externally observable differences in behavior. */
1765b9c547cSRui Paulo
177325151a3SRui Paulo x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
178325151a3SRui Paulo if (!x_cand)
1795b9c547cSRui Paulo return -1;
180325151a3SRui Paulo y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
181325151a3SRui Paulo crypto_bignum_deinit(x_cand, 1);
1824bc52338SCy Schubert if (!y_sqr)
183325151a3SRui Paulo return -1;
1845b9c547cSRui Paulo
185206b73d0SCy Schubert res = dragonfly_is_quadratic_residue_blind(sae->tmp->ec, qr, qnr,
186206b73d0SCy Schubert y_sqr);
187325151a3SRui Paulo crypto_bignum_deinit(y_sqr, 1);
188206b73d0SCy Schubert if (res < 0)
189325151a3SRui Paulo return res;
190206b73d0SCy Schubert return const_time_select_int(in_range, res, 0);
191325151a3SRui Paulo }
1925b9c547cSRui Paulo
1935b9c547cSRui Paulo
1944bc52338SCy Schubert /* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
1954bc52338SCy Schubert * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
sae_test_pwd_seed_ffc(struct sae_data * sae,const u8 * pwd_seed,struct crypto_bignum * pwe)1965b9c547cSRui Paulo static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
1975b9c547cSRui Paulo struct crypto_bignum *pwe)
1985b9c547cSRui Paulo {
1995b9c547cSRui Paulo u8 pwd_value[SAE_MAX_PRIME_LEN];
2005b9c547cSRui Paulo size_t bits = sae->tmp->prime_len * 8;
2015b9c547cSRui Paulo u8 exp[1];
2024bc52338SCy Schubert struct crypto_bignum *a, *b = NULL;
2034bc52338SCy Schubert int res, is_val;
2044bc52338SCy Schubert u8 pwd_value_valid;
2055b9c547cSRui Paulo
2065b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
2075b9c547cSRui Paulo
2085b9c547cSRui Paulo /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
209780fb4a2SCy Schubert if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
2105b9c547cSRui Paulo sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
211780fb4a2SCy Schubert bits) < 0)
212780fb4a2SCy Schubert return -1;
2135b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
2145b9c547cSRui Paulo sae->tmp->prime_len);
2155b9c547cSRui Paulo
2164bc52338SCy Schubert /* Check whether pwd-value < p */
2174bc52338SCy Schubert res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
2184bc52338SCy Schubert sae->tmp->prime_len);
2194bc52338SCy Schubert /* pwd-value >= p is invalid, so res is < 0 for the valid cases and
2204bc52338SCy Schubert * the negative sign can be used to fill the mask for constant time
2214bc52338SCy Schubert * selection */
2224bc52338SCy Schubert pwd_value_valid = const_time_fill_msb(res);
2234bc52338SCy Schubert
2244bc52338SCy Schubert /* If pwd-value >= p, force pwd-value to be < p and perform the
2254bc52338SCy Schubert * calculations anyway to hide timing difference. The derived PWE will
2264bc52338SCy Schubert * be ignored in that case. */
2274bc52338SCy Schubert pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
2285b9c547cSRui Paulo
2295b9c547cSRui Paulo /* PWE = pwd-value^((p-1)/r) modulo p */
2305b9c547cSRui Paulo
2314bc52338SCy Schubert res = -1;
2325b9c547cSRui Paulo a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
2334bc52338SCy Schubert if (!a)
2344bc52338SCy Schubert goto fail;
2355b9c547cSRui Paulo
2364bc52338SCy Schubert /* This is an optimization based on the used group that does not depend
2374bc52338SCy Schubert * on the password in any way, so it is fine to use separate branches
2384bc52338SCy Schubert * for this step without constant time operations. */
2395b9c547cSRui Paulo if (sae->tmp->dh->safe_prime) {
2405b9c547cSRui Paulo /*
2415b9c547cSRui Paulo * r = (p-1)/2 for the group used here, so this becomes:
2425b9c547cSRui Paulo * PWE = pwd-value^2 modulo p
2435b9c547cSRui Paulo */
2445b9c547cSRui Paulo exp[0] = 2;
2455b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp));
2465b9c547cSRui Paulo } else {
2475b9c547cSRui Paulo /* Calculate exponent: (p-1)/r */
2485b9c547cSRui Paulo exp[0] = 1;
2495b9c547cSRui Paulo b = crypto_bignum_init_set(exp, sizeof(exp));
2505b9c547cSRui Paulo if (b == NULL ||
2515b9c547cSRui Paulo crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
2524bc52338SCy Schubert crypto_bignum_div(b, sae->tmp->order, b) < 0)
2534bc52338SCy Schubert goto fail;
2545b9c547cSRui Paulo }
2555b9c547cSRui Paulo
2564bc52338SCy Schubert if (!b)
2574bc52338SCy Schubert goto fail;
2584bc52338SCy Schubert
2595b9c547cSRui Paulo res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
2604bc52338SCy Schubert if (res < 0)
2614bc52338SCy Schubert goto fail;
2625b9c547cSRui Paulo
2634bc52338SCy Schubert /* There were no fatal errors in calculations, so determine the return
2644bc52338SCy Schubert * value using constant time operations. We get here for number of
2654bc52338SCy Schubert * invalid cases which are cleared here after having performed all the
2664bc52338SCy Schubert * computation. PWE is valid if pwd-value was less than prime and
2674bc52338SCy Schubert * PWE > 1. Start with pwd-value check first and then use constant time
2684bc52338SCy Schubert * operations to clear res to 0 if PWE is 0 or 1.
2694bc52338SCy Schubert */
2704bc52338SCy Schubert res = const_time_select_u8(pwd_value_valid, 1, 0);
2714bc52338SCy Schubert is_val = crypto_bignum_is_zero(pwe);
2724bc52338SCy Schubert res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
2734bc52338SCy Schubert is_val = crypto_bignum_is_one(pwe);
2744bc52338SCy Schubert res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
2755b9c547cSRui Paulo
2764bc52338SCy Schubert fail:
2774bc52338SCy Schubert crypto_bignum_deinit(a, 1);
2784bc52338SCy Schubert crypto_bignum_deinit(b, 1);
2794bc52338SCy Schubert return res;
2805b9c547cSRui Paulo }
2815b9c547cSRui Paulo
2825b9c547cSRui Paulo
sae_derive_pwe_ecc(struct sae_data * sae,const u8 * addr1,const u8 * addr2,const u8 * password,size_t password_len)2835b9c547cSRui Paulo static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
2845b9c547cSRui Paulo const u8 *addr2, const u8 *password,
285c1d255d3SCy Schubert size_t password_len)
2865b9c547cSRui Paulo {
287206b73d0SCy Schubert u8 counter, k;
2885b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN];
289c1d255d3SCy Schubert const u8 *addr[2];
290c1d255d3SCy Schubert size_t len[2];
2914b72b91aSCy Schubert u8 *stub_password, *tmp_password;
292325151a3SRui Paulo int pwd_seed_odd = 0;
293325151a3SRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN];
294325151a3SRui Paulo size_t prime_len;
295ec080394SCy Schubert struct crypto_bignum *x = NULL, *y = NULL, *qr = NULL, *qnr = NULL;
2964bc52338SCy Schubert u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
2974bc52338SCy Schubert u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
2984bc52338SCy Schubert u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
2994bc52338SCy Schubert u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
300ec080394SCy Schubert u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN];
3014bc52338SCy Schubert int res = -1;
3024bc52338SCy Schubert u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
3034bc52338SCy Schubert * mask */
304ec080394SCy Schubert unsigned int is_eq;
3055b9c547cSRui Paulo
3064bc52338SCy Schubert os_memset(x_bin, 0, sizeof(x_bin));
3074bc52338SCy Schubert
3084b72b91aSCy Schubert stub_password = os_malloc(password_len);
3094bc52338SCy Schubert tmp_password = os_malloc(password_len);
3104b72b91aSCy Schubert if (!stub_password || !tmp_password ||
3114b72b91aSCy Schubert random_get_bytes(stub_password, password_len) < 0)
3124bc52338SCy Schubert goto fail;
313325151a3SRui Paulo
314325151a3SRui Paulo prime_len = sae->tmp->prime_len;
315325151a3SRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
316325151a3SRui Paulo prime_len) < 0)
3174bc52338SCy Schubert goto fail;
318325151a3SRui Paulo
319325151a3SRui Paulo /*
320325151a3SRui Paulo * Create a random quadratic residue (qr) and quadratic non-residue
321325151a3SRui Paulo * (qnr) modulo p for blinding purposes during the loop.
322325151a3SRui Paulo */
323206b73d0SCy Schubert if (dragonfly_get_random_qr_qnr(sae->tmp->prime, &qr, &qnr) < 0 ||
3244bc52338SCy Schubert crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
3254bc52338SCy Schubert crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
3264bc52338SCy Schubert goto fail;
3275b9c547cSRui Paulo
3285b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
3295b9c547cSRui Paulo password, password_len);
3305b9c547cSRui Paulo
3315b9c547cSRui Paulo /*
3325b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm)
333c1d255d3SCy Schubert * base = password
3345b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
335325151a3SRui Paulo * base || counter)
3365b9c547cSRui Paulo */
3375b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs);
3385b9c547cSRui Paulo
3394bc52338SCy Schubert addr[0] = tmp_password;
3405b9c547cSRui Paulo len[0] = password_len;
341c1d255d3SCy Schubert addr[1] = &counter;
342c1d255d3SCy Schubert len[1] = sizeof(counter);
3435b9c547cSRui Paulo
3445b9c547cSRui Paulo /*
3455b9c547cSRui Paulo * Continue for at least k iterations to protect against side-channel
3465b9c547cSRui Paulo * attacks that attempt to determine the number of iterations required
3475b9c547cSRui Paulo * in the loop.
3485b9c547cSRui Paulo */
349206b73d0SCy Schubert k = dragonfly_min_pwe_loop_iter(sae->group);
350206b73d0SCy Schubert
3514bc52338SCy Schubert for (counter = 1; counter <= k || !found; counter++) {
3525b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN];
3535b9c547cSRui Paulo
3545b9c547cSRui Paulo if (counter > 200) {
3555b9c547cSRui Paulo /* This should not happen in practice */
3565b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
3575b9c547cSRui Paulo break;
3585b9c547cSRui Paulo }
3595b9c547cSRui Paulo
3604bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
3614b72b91aSCy Schubert const_time_select_bin(found, stub_password, password,
3624bc52338SCy Schubert password_len, tmp_password);
363c1d255d3SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), 2,
36485732ac8SCy Schubert addr, len, pwd_seed) < 0)
3655b9c547cSRui Paulo break;
366325151a3SRui Paulo
3675b9c547cSRui Paulo res = sae_test_pwd_seed_ecc(sae, pwd_seed,
3684bc52338SCy Schubert prime, qr_bin, qnr_bin, x_cand_bin);
3694bc52338SCy Schubert const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
3704bc52338SCy Schubert x_bin);
3714bc52338SCy Schubert pwd_seed_odd = const_time_select_u8(
3724bc52338SCy Schubert found, pwd_seed_odd,
3734bc52338SCy Schubert pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
3744bc52338SCy Schubert os_memset(pwd_seed, 0, sizeof(pwd_seed));
3755b9c547cSRui Paulo if (res < 0)
376325151a3SRui Paulo goto fail;
3774bc52338SCy Schubert /* Need to minimize differences in handling res == 0 and 1 here
3784bc52338SCy Schubert * to avoid differences in timing and instruction cache access,
3794bc52338SCy Schubert * so use const_time_select_*() to make local copies of the
3804bc52338SCy Schubert * values based on whether this loop iteration was the one that
3814bc52338SCy Schubert * found the pwd-seed/x. */
382325151a3SRui Paulo
3834bc52338SCy Schubert /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
3844bc52338SCy Schubert * (with res converted to 0/0xff) handles this in constant time.
385325151a3SRui Paulo */
3864bc52338SCy Schubert found |= res * 0xff;
3874bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
3884bc52338SCy Schubert res, found);
3895b9c547cSRui Paulo }
3905b9c547cSRui Paulo
3914bc52338SCy Schubert if (!found) {
392325151a3SRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
393325151a3SRui Paulo res = -1;
394325151a3SRui Paulo goto fail;
395325151a3SRui Paulo }
3965b9c547cSRui Paulo
3974bc52338SCy Schubert x = crypto_bignum_init_set(x_bin, prime_len);
3984bc52338SCy Schubert if (!x) {
3994bc52338SCy Schubert res = -1;
4004bc52338SCy Schubert goto fail;
4014bc52338SCy Schubert }
4024bc52338SCy Schubert
403ec080394SCy Schubert /* y = sqrt(x^3 + ax + b) mod p
404ec080394SCy Schubert * if LSB(save) == LSB(y): PWE = (x, y)
405ec080394SCy Schubert * else: PWE = (x, p - y)
406ec080394SCy Schubert *
407ec080394SCy Schubert * Calculate y and the two possible values for PWE and after that,
408ec080394SCy Schubert * use constant time selection to copy the correct alternative.
40964e33c5cSCy Schubert */
410ec080394SCy Schubert y = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x);
411ec080394SCy Schubert if (!y ||
412ec080394SCy Schubert dragonfly_sqrt(sae->tmp->ec, y, y) < 0 ||
413ec080394SCy Schubert crypto_bignum_to_bin(y, x_y, SAE_MAX_ECC_PRIME_LEN,
414ec080394SCy Schubert prime_len) < 0 ||
415ec080394SCy Schubert crypto_bignum_sub(sae->tmp->prime, y, y) < 0 ||
416ec080394SCy Schubert crypto_bignum_to_bin(y, x_y + SAE_MAX_ECC_PRIME_LEN,
417ec080394SCy Schubert SAE_MAX_ECC_PRIME_LEN, prime_len) < 0) {
41864e33c5cSCy Schubert wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
419ec080394SCy Schubert goto fail;
420ec080394SCy Schubert }
421ec080394SCy Schubert
422ec080394SCy Schubert is_eq = const_time_eq(pwd_seed_odd, x_y[prime_len - 1] & 0x01);
423ec080394SCy Schubert const_time_select_bin(is_eq, x_y, x_y + SAE_MAX_ECC_PRIME_LEN,
424ec080394SCy Schubert prime_len, x_y + prime_len);
425ec080394SCy Schubert os_memcpy(x_y, x_bin, prime_len);
426ec080394SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PWE", x_y, 2 * prime_len);
427ec080394SCy Schubert crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1);
428ec080394SCy Schubert sae->tmp->pwe_ecc = crypto_ec_point_from_bin(sae->tmp->ec, x_y);
429ec080394SCy Schubert if (!sae->tmp->pwe_ecc) {
430ec080394SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
431ec080394SCy Schubert res = -1;
432325151a3SRui Paulo }
433325151a3SRui Paulo
434325151a3SRui Paulo fail:
435ec080394SCy Schubert forced_memzero(x_y, sizeof(x_y));
436325151a3SRui Paulo crypto_bignum_deinit(qr, 0);
437325151a3SRui Paulo crypto_bignum_deinit(qnr, 0);
438ec080394SCy Schubert crypto_bignum_deinit(y, 1);
4394b72b91aSCy Schubert os_free(stub_password);
4404bc52338SCy Schubert bin_clear_free(tmp_password, password_len);
4414bc52338SCy Schubert crypto_bignum_deinit(x, 1);
4424bc52338SCy Schubert os_memset(x_bin, 0, sizeof(x_bin));
4434bc52338SCy Schubert os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
444325151a3SRui Paulo
445325151a3SRui Paulo return res;
4465b9c547cSRui Paulo }
4475b9c547cSRui Paulo
4485b9c547cSRui Paulo
sae_derive_pwe_ffc(struct sae_data * sae,const u8 * addr1,const u8 * addr2,const u8 * password,size_t password_len)4495b9c547cSRui Paulo static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
4505b9c547cSRui Paulo const u8 *addr2, const u8 *password,
451c1d255d3SCy Schubert size_t password_len)
4525b9c547cSRui Paulo {
4534bc52338SCy Schubert u8 counter, k, sel_counter = 0;
4545b9c547cSRui Paulo u8 addrs[2 * ETH_ALEN];
455c1d255d3SCy Schubert const u8 *addr[2];
456c1d255d3SCy Schubert size_t len[2];
4574bc52338SCy Schubert u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
4584bc52338SCy Schubert * mask */
4594bc52338SCy Schubert u8 mask;
4604bc52338SCy Schubert struct crypto_bignum *pwe;
461*a90b9d01SCy Schubert size_t prime_len = sae->tmp->prime_len;
4624bc52338SCy Schubert u8 *pwe_buf;
4635b9c547cSRui Paulo
4644bc52338SCy Schubert crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
4654bc52338SCy Schubert sae->tmp->pwe_ffc = NULL;
4664bc52338SCy Schubert
4674bc52338SCy Schubert /* Allocate a buffer to maintain selected and candidate PWE for constant
4684bc52338SCy Schubert * time selection. */
4694bc52338SCy Schubert pwe_buf = os_zalloc(prime_len * 2);
4704bc52338SCy Schubert pwe = crypto_bignum_init();
4714bc52338SCy Schubert if (!pwe_buf || !pwe)
4724bc52338SCy Schubert goto fail;
4735b9c547cSRui Paulo
4745b9c547cSRui Paulo wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
4755b9c547cSRui Paulo password, password_len);
4765b9c547cSRui Paulo
4775b9c547cSRui Paulo /*
4785b9c547cSRui Paulo * H(salt, ikm) = HMAC-SHA256(salt, ikm)
4795b9c547cSRui Paulo * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
480c1d255d3SCy Schubert * password || counter)
4815b9c547cSRui Paulo */
4825b9c547cSRui Paulo sae_pwd_seed_key(addr1, addr2, addrs);
4835b9c547cSRui Paulo
4845b9c547cSRui Paulo addr[0] = password;
4855b9c547cSRui Paulo len[0] = password_len;
486c1d255d3SCy Schubert addr[1] = &counter;
487c1d255d3SCy Schubert len[1] = sizeof(counter);
4885b9c547cSRui Paulo
489206b73d0SCy Schubert k = dragonfly_min_pwe_loop_iter(sae->group);
4904bc52338SCy Schubert
4914bc52338SCy Schubert for (counter = 1; counter <= k || !found; counter++) {
4925b9c547cSRui Paulo u8 pwd_seed[SHA256_MAC_LEN];
4935b9c547cSRui Paulo int res;
4945b9c547cSRui Paulo
4955b9c547cSRui Paulo if (counter > 200) {
4965b9c547cSRui Paulo /* This should not happen in practice */
4975b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE");
4985b9c547cSRui Paulo break;
4995b9c547cSRui Paulo }
5005b9c547cSRui Paulo
5014bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter);
502c1d255d3SCy Schubert if (hmac_sha256_vector(addrs, sizeof(addrs), 2,
50385732ac8SCy Schubert addr, len, pwd_seed) < 0)
5045b9c547cSRui Paulo break;
5054bc52338SCy Schubert res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
5064bc52338SCy Schubert /* res is -1 for fatal failure, 0 if a valid PWE was not found,
5074bc52338SCy Schubert * or 1 if a valid PWE was found. */
5085b9c547cSRui Paulo if (res < 0)
5095b9c547cSRui Paulo break;
5104bc52338SCy Schubert /* Store the candidate PWE into the second half of pwe_buf and
5114bc52338SCy Schubert * the selected PWE in the beginning of pwe_buf using constant
5124bc52338SCy Schubert * time selection. */
5134bc52338SCy Schubert if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len,
5144bc52338SCy Schubert prime_len) < 0)
5154bc52338SCy Schubert break;
5164bc52338SCy Schubert const_time_select_bin(found, pwe_buf, pwe_buf + prime_len,
5174bc52338SCy Schubert prime_len, pwe_buf);
5184bc52338SCy Schubert sel_counter = const_time_select_u8(found, sel_counter, counter);
5194bc52338SCy Schubert mask = const_time_eq_u8(res, 1);
5204bc52338SCy Schubert found = const_time_select_u8(found, found, mask);
5215b9c547cSRui Paulo }
5225b9c547cSRui Paulo
5234bc52338SCy Schubert if (!found)
5244bc52338SCy Schubert goto fail;
5254bc52338SCy Schubert
5264bc52338SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
5274bc52338SCy Schubert sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
5284bc52338SCy Schubert fail:
5294bc52338SCy Schubert crypto_bignum_deinit(pwe, 1);
5304bc52338SCy Schubert bin_clear_free(pwe_buf, prime_len * 2);
5314bc52338SCy Schubert return sae->tmp->pwe_ffc ? 0 : -1;
5325b9c547cSRui Paulo }
5335b9c547cSRui Paulo
5345b9c547cSRui Paulo
hkdf_extract(size_t hash_len,const u8 * salt,size_t salt_len,size_t num_elem,const u8 * addr[],const size_t len[],u8 * prk)535c1d255d3SCy Schubert static int hkdf_extract(size_t hash_len, const u8 *salt, size_t salt_len,
536c1d255d3SCy Schubert size_t num_elem, const u8 *addr[], const size_t len[],
537c1d255d3SCy Schubert u8 *prk)
538c1d255d3SCy Schubert {
539c1d255d3SCy Schubert if (hash_len == 32)
540c1d255d3SCy Schubert return hmac_sha256_vector(salt, salt_len, num_elem, addr, len,
541c1d255d3SCy Schubert prk);
542c1d255d3SCy Schubert #ifdef CONFIG_SHA384
543c1d255d3SCy Schubert if (hash_len == 48)
544c1d255d3SCy Schubert return hmac_sha384_vector(salt, salt_len, num_elem, addr, len,
545c1d255d3SCy Schubert prk);
546c1d255d3SCy Schubert #endif /* CONFIG_SHA384 */
547c1d255d3SCy Schubert #ifdef CONFIG_SHA512
548c1d255d3SCy Schubert if (hash_len == 64)
549c1d255d3SCy Schubert return hmac_sha512_vector(salt, salt_len, num_elem, addr, len,
550c1d255d3SCy Schubert prk);
551c1d255d3SCy Schubert #endif /* CONFIG_SHA512 */
552c1d255d3SCy Schubert return -1;
553c1d255d3SCy Schubert }
554c1d255d3SCy Schubert
555c1d255d3SCy Schubert
hkdf_expand(size_t hash_len,const u8 * prk,size_t prk_len,const char * info,u8 * okm,size_t okm_len)556c1d255d3SCy Schubert static int hkdf_expand(size_t hash_len, const u8 *prk, size_t prk_len,
557c1d255d3SCy Schubert const char *info, u8 *okm, size_t okm_len)
558c1d255d3SCy Schubert {
559c1d255d3SCy Schubert size_t info_len = os_strlen(info);
560c1d255d3SCy Schubert
561c1d255d3SCy Schubert if (hash_len == 32)
562c1d255d3SCy Schubert return hmac_sha256_kdf(prk, prk_len, NULL,
563c1d255d3SCy Schubert (const u8 *) info, info_len,
564c1d255d3SCy Schubert okm, okm_len);
565c1d255d3SCy Schubert #ifdef CONFIG_SHA384
566c1d255d3SCy Schubert if (hash_len == 48)
567c1d255d3SCy Schubert return hmac_sha384_kdf(prk, prk_len, NULL,
568c1d255d3SCy Schubert (const u8 *) info, info_len,
569c1d255d3SCy Schubert okm, okm_len);
570c1d255d3SCy Schubert #endif /* CONFIG_SHA384 */
571c1d255d3SCy Schubert #ifdef CONFIG_SHA512
572c1d255d3SCy Schubert if (hash_len == 64)
573c1d255d3SCy Schubert return hmac_sha512_kdf(prk, prk_len, NULL,
574c1d255d3SCy Schubert (const u8 *) info, info_len,
575c1d255d3SCy Schubert okm, okm_len);
576c1d255d3SCy Schubert #endif /* CONFIG_SHA512 */
577c1d255d3SCy Schubert return -1;
578c1d255d3SCy Schubert }
579c1d255d3SCy Schubert
580c1d255d3SCy Schubert
sswu_curve_param(int group,int * z)581c1d255d3SCy Schubert static int sswu_curve_param(int group, int *z)
582c1d255d3SCy Schubert {
583c1d255d3SCy Schubert switch (group) {
584c1d255d3SCy Schubert case 19:
585c1d255d3SCy Schubert *z = -10;
586c1d255d3SCy Schubert return 0;
587c1d255d3SCy Schubert case 20:
588c1d255d3SCy Schubert *z = -12;
589c1d255d3SCy Schubert return 0;
590c1d255d3SCy Schubert case 21:
591c1d255d3SCy Schubert *z = -4;
592c1d255d3SCy Schubert return 0;
593c1d255d3SCy Schubert case 25:
594c1d255d3SCy Schubert case 29:
595c1d255d3SCy Schubert *z = -5;
596c1d255d3SCy Schubert return 0;
597c1d255d3SCy Schubert case 26:
598c1d255d3SCy Schubert *z = 31;
599c1d255d3SCy Schubert return 0;
600c1d255d3SCy Schubert case 28:
601c1d255d3SCy Schubert *z = -2;
602c1d255d3SCy Schubert return 0;
603c1d255d3SCy Schubert case 30:
604c1d255d3SCy Schubert *z = 7;
605c1d255d3SCy Schubert return 0;
606*a90b9d01SCy Schubert default:
607c1d255d3SCy Schubert return -1;
608c1d255d3SCy Schubert }
609*a90b9d01SCy Schubert }
610c1d255d3SCy Schubert
611c1d255d3SCy Schubert
debug_print_bignum(const char * title,const struct crypto_bignum * a,size_t prime_len)612c1d255d3SCy Schubert static void debug_print_bignum(const char *title, const struct crypto_bignum *a,
613c1d255d3SCy Schubert size_t prime_len)
614c1d255d3SCy Schubert {
615c1d255d3SCy Schubert u8 *bin;
616c1d255d3SCy Schubert
617c1d255d3SCy Schubert bin = os_malloc(prime_len);
618c1d255d3SCy Schubert if (bin && crypto_bignum_to_bin(a, bin, prime_len, prime_len) >= 0)
619c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, title, bin, prime_len);
620c1d255d3SCy Schubert else
621c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "Could not print bignum (%s)", title);
622c1d255d3SCy Schubert bin_clear_free(bin, prime_len);
623c1d255d3SCy Schubert }
624c1d255d3SCy Schubert
625c1d255d3SCy Schubert
sswu(struct crypto_ec * ec,int group,const struct crypto_bignum * u)626c1d255d3SCy Schubert static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group,
627c1d255d3SCy Schubert const struct crypto_bignum *u)
628c1d255d3SCy Schubert {
629c1d255d3SCy Schubert int z_int;
630c1d255d3SCy Schubert const struct crypto_bignum *a, *b, *prime;
631c1d255d3SCy Schubert struct crypto_bignum *u2, *t1, *t2, *z, *t, *zero, *one, *two, *three,
632c1d255d3SCy Schubert *x1a, *x1b, *y = NULL;
633c1d255d3SCy Schubert struct crypto_bignum *x1 = NULL, *x2, *gx1, *gx2, *v = NULL;
634c1d255d3SCy Schubert unsigned int m_is_zero, is_qr, is_eq;
635c1d255d3SCy Schubert size_t prime_len;
636c1d255d3SCy Schubert u8 bin[SAE_MAX_ECC_PRIME_LEN];
637c1d255d3SCy Schubert u8 bin1[SAE_MAX_ECC_PRIME_LEN];
638c1d255d3SCy Schubert u8 bin2[SAE_MAX_ECC_PRIME_LEN];
639c1d255d3SCy Schubert u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN];
640c1d255d3SCy Schubert struct crypto_ec_point *p = NULL;
641c1d255d3SCy Schubert
642c1d255d3SCy Schubert if (sswu_curve_param(group, &z_int) < 0)
643c1d255d3SCy Schubert return NULL;
644c1d255d3SCy Schubert
645c1d255d3SCy Schubert prime = crypto_ec_get_prime(ec);
646c1d255d3SCy Schubert prime_len = crypto_ec_prime_len(ec);
647c1d255d3SCy Schubert a = crypto_ec_get_a(ec);
648c1d255d3SCy Schubert b = crypto_ec_get_b(ec);
649c1d255d3SCy Schubert
650c1d255d3SCy Schubert u2 = crypto_bignum_init();
651c1d255d3SCy Schubert t1 = crypto_bignum_init();
652c1d255d3SCy Schubert t2 = crypto_bignum_init();
653c1d255d3SCy Schubert z = crypto_bignum_init_uint(abs(z_int));
654c1d255d3SCy Schubert t = crypto_bignum_init();
655c1d255d3SCy Schubert zero = crypto_bignum_init_uint(0);
656c1d255d3SCy Schubert one = crypto_bignum_init_uint(1);
657c1d255d3SCy Schubert two = crypto_bignum_init_uint(2);
658c1d255d3SCy Schubert three = crypto_bignum_init_uint(3);
659c1d255d3SCy Schubert x1a = crypto_bignum_init();
660c1d255d3SCy Schubert x1b = crypto_bignum_init();
661c1d255d3SCy Schubert x2 = crypto_bignum_init();
662c1d255d3SCy Schubert gx1 = crypto_bignum_init();
663c1d255d3SCy Schubert gx2 = crypto_bignum_init();
664c1d255d3SCy Schubert if (!u2 || !t1 || !t2 || !z || !t || !zero || !one || !two || !three ||
665c1d255d3SCy Schubert !x1a || !x1b || !x2 || !gx1 || !gx2)
666c1d255d3SCy Schubert goto fail;
667c1d255d3SCy Schubert
668c1d255d3SCy Schubert if (z_int < 0 && crypto_bignum_sub(prime, z, z) < 0)
669c1d255d3SCy Schubert goto fail;
670c1d255d3SCy Schubert
671c1d255d3SCy Schubert /* m = z^2 * u^4 + z * u^2 */
672c1d255d3SCy Schubert /* --> tmp = z * u^2, m = tmp^2 + tmp */
673c1d255d3SCy Schubert
674c1d255d3SCy Schubert /* u2 = u^2
675c1d255d3SCy Schubert * t1 = z * u2
676c1d255d3SCy Schubert * t2 = t1^2
677c1d255d3SCy Schubert * m = t1 = t1 + t2 */
678c1d255d3SCy Schubert if (crypto_bignum_sqrmod(u, prime, u2) < 0 ||
679c1d255d3SCy Schubert crypto_bignum_mulmod(z, u2, prime, t1) < 0 ||
680c1d255d3SCy Schubert crypto_bignum_sqrmod(t1, prime, t2) < 0 ||
681c1d255d3SCy Schubert crypto_bignum_addmod(t1, t2, prime, t1) < 0)
682c1d255d3SCy Schubert goto fail;
683c1d255d3SCy Schubert debug_print_bignum("SSWU: m", t1, prime_len);
684c1d255d3SCy Schubert
685c1d255d3SCy Schubert /* l = CEQ(m, 0)
686c1d255d3SCy Schubert * t = CSEL(l, 0, inverse(m); where inverse(x) is calculated as
687c1d255d3SCy Schubert * x^(p-2) modulo p which will handle m == 0 case correctly */
688c1d255d3SCy Schubert /* TODO: Make sure crypto_bignum_is_zero() is constant time */
689c1d255d3SCy Schubert m_is_zero = const_time_eq(crypto_bignum_is_zero(t1), 1);
690c1d255d3SCy Schubert /* t = m^(p-2) modulo p */
691c1d255d3SCy Schubert if (crypto_bignum_sub(prime, two, t2) < 0 ||
692c1d255d3SCy Schubert crypto_bignum_exptmod(t1, t2, prime, t) < 0)
693c1d255d3SCy Schubert goto fail;
694c1d255d3SCy Schubert debug_print_bignum("SSWU: t", t, prime_len);
695c1d255d3SCy Schubert
696c1d255d3SCy Schubert /* b / (z * a) */
697c1d255d3SCy Schubert if (crypto_bignum_mulmod(z, a, prime, t1) < 0 ||
698c1d255d3SCy Schubert crypto_bignum_inverse(t1, prime, t1) < 0 ||
699c1d255d3SCy Schubert crypto_bignum_mulmod(b, t1, prime, x1a) < 0)
700c1d255d3SCy Schubert goto fail;
701c1d255d3SCy Schubert debug_print_bignum("SSWU: x1a = b / (z * a)", x1a, prime_len);
702c1d255d3SCy Schubert
703c1d255d3SCy Schubert /* (-b/a) * (1 + t) */
704c1d255d3SCy Schubert if (crypto_bignum_sub(prime, b, t1) < 0 ||
705c1d255d3SCy Schubert crypto_bignum_inverse(a, prime, t2) < 0 ||
706c1d255d3SCy Schubert crypto_bignum_mulmod(t1, t2, prime, t1) < 0 ||
707c1d255d3SCy Schubert crypto_bignum_addmod(one, t, prime, t2) < 0 ||
708c1d255d3SCy Schubert crypto_bignum_mulmod(t1, t2, prime, x1b) < 0)
709c1d255d3SCy Schubert goto fail;
710c1d255d3SCy Schubert debug_print_bignum("SSWU: x1b = (-b/a) * (1 + t)", x1b, prime_len);
711c1d255d3SCy Schubert
712c1d255d3SCy Schubert /* x1 = CSEL(CEQ(m, 0), x1a, x1b) */
713c1d255d3SCy Schubert if (crypto_bignum_to_bin(x1a, bin1, sizeof(bin1), prime_len) < 0 ||
714c1d255d3SCy Schubert crypto_bignum_to_bin(x1b, bin2, sizeof(bin2), prime_len) < 0)
715c1d255d3SCy Schubert goto fail;
716c1d255d3SCy Schubert const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin);
717c1d255d3SCy Schubert x1 = crypto_bignum_init_set(bin, prime_len);
718c1d255d3SCy Schubert if (!x1)
719c1d255d3SCy Schubert goto fail;
720c1d255d3SCy Schubert debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len);
721c1d255d3SCy Schubert
722c1d255d3SCy Schubert /* gx1 = x1^3 + a * x1 + b */
723c1d255d3SCy Schubert if (crypto_bignum_exptmod(x1, three, prime, t1) < 0 ||
724c1d255d3SCy Schubert crypto_bignum_mulmod(a, x1, prime, t2) < 0 ||
725c1d255d3SCy Schubert crypto_bignum_addmod(t1, t2, prime, t1) < 0 ||
726c1d255d3SCy Schubert crypto_bignum_addmod(t1, b, prime, gx1) < 0)
727c1d255d3SCy Schubert goto fail;
728c1d255d3SCy Schubert debug_print_bignum("SSWU: gx1 = x1^3 + a * x1 + b", gx1, prime_len);
729c1d255d3SCy Schubert
730c1d255d3SCy Schubert /* x2 = z * u^2 * x1 */
731c1d255d3SCy Schubert if (crypto_bignum_mulmod(z, u2, prime, t1) < 0 ||
732c1d255d3SCy Schubert crypto_bignum_mulmod(t1, x1, prime, x2) < 0)
733c1d255d3SCy Schubert goto fail;
734c1d255d3SCy Schubert debug_print_bignum("SSWU: x2 = z * u^2 * x1", x2, prime_len);
735c1d255d3SCy Schubert
736c1d255d3SCy Schubert /* gx2 = x2^3 + a * x2 + b */
737c1d255d3SCy Schubert if (crypto_bignum_exptmod(x2, three, prime, t1) < 0 ||
738c1d255d3SCy Schubert crypto_bignum_mulmod(a, x2, prime, t2) < 0 ||
739c1d255d3SCy Schubert crypto_bignum_addmod(t1, t2, prime, t1) < 0 ||
740c1d255d3SCy Schubert crypto_bignum_addmod(t1, b, prime, gx2) < 0)
741c1d255d3SCy Schubert goto fail;
742c1d255d3SCy Schubert debug_print_bignum("SSWU: gx2 = x2^3 + a * x2 + b", gx2, prime_len);
743c1d255d3SCy Schubert
744c1d255d3SCy Schubert /* l = gx1 is a quadratic residue modulo p
745c1d255d3SCy Schubert * --> gx1^((p-1)/2) modulo p is zero or one */
746c1d255d3SCy Schubert if (crypto_bignum_sub(prime, one, t1) < 0 ||
747c1d255d3SCy Schubert crypto_bignum_rshift(t1, 1, t1) < 0 ||
748c1d255d3SCy Schubert crypto_bignum_exptmod(gx1, t1, prime, t1) < 0)
749c1d255d3SCy Schubert goto fail;
750c1d255d3SCy Schubert debug_print_bignum("SSWU: gx1^((p-1)/2) modulo p", t1, prime_len);
751c1d255d3SCy Schubert is_qr = const_time_eq(crypto_bignum_is_zero(t1) |
752c1d255d3SCy Schubert crypto_bignum_is_one(t1), 1);
753c1d255d3SCy Schubert
754c1d255d3SCy Schubert /* v = CSEL(l, gx1, gx2) */
755c1d255d3SCy Schubert if (crypto_bignum_to_bin(gx1, bin1, sizeof(bin1), prime_len) < 0 ||
756c1d255d3SCy Schubert crypto_bignum_to_bin(gx2, bin2, sizeof(bin2), prime_len) < 0)
757c1d255d3SCy Schubert goto fail;
758c1d255d3SCy Schubert const_time_select_bin(is_qr, bin1, bin2, prime_len, bin);
759c1d255d3SCy Schubert v = crypto_bignum_init_set(bin, prime_len);
760c1d255d3SCy Schubert if (!v)
761c1d255d3SCy Schubert goto fail;
762c1d255d3SCy Schubert debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len);
763c1d255d3SCy Schubert
764c1d255d3SCy Schubert /* x = CSEL(l, x1, x2) */
765c1d255d3SCy Schubert if (crypto_bignum_to_bin(x1, bin1, sizeof(bin1), prime_len) < 0 ||
766c1d255d3SCy Schubert crypto_bignum_to_bin(x2, bin2, sizeof(bin2), prime_len) < 0)
767c1d255d3SCy Schubert goto fail;
768c1d255d3SCy Schubert const_time_select_bin(is_qr, bin1, bin2, prime_len, x_y);
769c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SSWU: x = CSEL(l, x1, x2)", x_y, prime_len);
770c1d255d3SCy Schubert
771ec080394SCy Schubert /* y = sqrt(v) */
772c1d255d3SCy Schubert y = crypto_bignum_init();
773ec080394SCy Schubert if (!y || dragonfly_sqrt(ec, v, y) < 0)
774c1d255d3SCy Schubert goto fail;
775c1d255d3SCy Schubert debug_print_bignum("SSWU: y = sqrt(v)", y, prime_len);
776c1d255d3SCy Schubert
777c1d255d3SCy Schubert /* l = CEQ(LSB(u), LSB(y)) */
778c1d255d3SCy Schubert if (crypto_bignum_to_bin(u, bin1, sizeof(bin1), prime_len) < 0 ||
779c1d255d3SCy Schubert crypto_bignum_to_bin(y, bin2, sizeof(bin2), prime_len) < 0)
780c1d255d3SCy Schubert goto fail;
781c1d255d3SCy Schubert is_eq = const_time_eq(bin1[prime_len - 1] & 0x01,
782c1d255d3SCy Schubert bin2[prime_len - 1] & 0x01);
783c1d255d3SCy Schubert
784c1d255d3SCy Schubert /* P = CSEL(l, (x,y), (x, p-y)) */
785c1d255d3SCy Schubert if (crypto_bignum_sub(prime, y, t1) < 0)
786c1d255d3SCy Schubert goto fail;
787c1d255d3SCy Schubert debug_print_bignum("SSWU: p - y", t1, prime_len);
788c1d255d3SCy Schubert if (crypto_bignum_to_bin(y, bin1, sizeof(bin1), prime_len) < 0 ||
789c1d255d3SCy Schubert crypto_bignum_to_bin(t1, bin2, sizeof(bin2), prime_len) < 0)
790c1d255d3SCy Schubert goto fail;
791c1d255d3SCy Schubert const_time_select_bin(is_eq, bin1, bin2, prime_len, &x_y[prime_len]);
792c1d255d3SCy Schubert
793c1d255d3SCy Schubert /* output P */
794c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SSWU: P.x", x_y, prime_len);
795c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SSWU: P.y", &x_y[prime_len], prime_len);
796c1d255d3SCy Schubert p = crypto_ec_point_from_bin(ec, x_y);
797c1d255d3SCy Schubert
798c1d255d3SCy Schubert fail:
799c1d255d3SCy Schubert crypto_bignum_deinit(u2, 1);
800c1d255d3SCy Schubert crypto_bignum_deinit(t1, 1);
801c1d255d3SCy Schubert crypto_bignum_deinit(t2, 1);
802c1d255d3SCy Schubert crypto_bignum_deinit(z, 0);
803c1d255d3SCy Schubert crypto_bignum_deinit(t, 1);
804c1d255d3SCy Schubert crypto_bignum_deinit(x1a, 1);
805c1d255d3SCy Schubert crypto_bignum_deinit(x1b, 1);
806c1d255d3SCy Schubert crypto_bignum_deinit(x1, 1);
807c1d255d3SCy Schubert crypto_bignum_deinit(x2, 1);
808c1d255d3SCy Schubert crypto_bignum_deinit(gx1, 1);
809c1d255d3SCy Schubert crypto_bignum_deinit(gx2, 1);
810c1d255d3SCy Schubert crypto_bignum_deinit(y, 1);
811c1d255d3SCy Schubert crypto_bignum_deinit(v, 1);
812c1d255d3SCy Schubert crypto_bignum_deinit(zero, 0);
813c1d255d3SCy Schubert crypto_bignum_deinit(one, 0);
814c1d255d3SCy Schubert crypto_bignum_deinit(two, 0);
815c1d255d3SCy Schubert crypto_bignum_deinit(three, 0);
816c1d255d3SCy Schubert forced_memzero(bin, sizeof(bin));
817c1d255d3SCy Schubert forced_memzero(bin1, sizeof(bin1));
818c1d255d3SCy Schubert forced_memzero(bin2, sizeof(bin2));
819c1d255d3SCy Schubert forced_memzero(x_y, sizeof(x_y));
820c1d255d3SCy Schubert return p;
821c1d255d3SCy Schubert }
822c1d255d3SCy Schubert
823c1d255d3SCy Schubert
sae_pwd_seed(size_t hash_len,const u8 * ssid,size_t ssid_len,const u8 * password,size_t password_len,const char * identifier,u8 * pwd_seed)824c1d255d3SCy Schubert static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len,
825c1d255d3SCy Schubert const u8 *password, size_t password_len,
826c1d255d3SCy Schubert const char *identifier, u8 *pwd_seed)
827c1d255d3SCy Schubert {
828c1d255d3SCy Schubert const u8 *addr[2];
829c1d255d3SCy Schubert size_t len[2];
830c1d255d3SCy Schubert size_t num_elem;
831c1d255d3SCy Schubert
832c1d255d3SCy Schubert /* pwd-seed = HKDF-Extract(ssid, password [ || identifier ]) */
833c1d255d3SCy Schubert addr[0] = password;
834c1d255d3SCy Schubert len[0] = password_len;
835c1d255d3SCy Schubert num_elem = 1;
836c1d255d3SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "SAE: SSID", ssid, ssid_len);
837c1d255d3SCy Schubert wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
838c1d255d3SCy Schubert password, password_len);
839c1d255d3SCy Schubert if (identifier) {
840c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: password identifier: %s",
841c1d255d3SCy Schubert identifier);
842c1d255d3SCy Schubert addr[num_elem] = (const u8 *) identifier;
843c1d255d3SCy Schubert len[num_elem] = os_strlen(identifier);
844c1d255d3SCy Schubert num_elem++;
845c1d255d3SCy Schubert }
846c1d255d3SCy Schubert if (hkdf_extract(hash_len, ssid, ssid_len, num_elem, addr, len,
847c1d255d3SCy Schubert pwd_seed) < 0)
848c1d255d3SCy Schubert return -1;
849c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, hash_len);
850c1d255d3SCy Schubert return 0;
851c1d255d3SCy Schubert }
852c1d255d3SCy Schubert
853c1d255d3SCy Schubert
sae_ecc_prime_len_2_hash_len(size_t prime_len)854c1d255d3SCy Schubert size_t sae_ecc_prime_len_2_hash_len(size_t prime_len)
855c1d255d3SCy Schubert {
856c1d255d3SCy Schubert if (prime_len <= 256 / 8)
857c1d255d3SCy Schubert return 32;
858c1d255d3SCy Schubert if (prime_len <= 384 / 8)
859c1d255d3SCy Schubert return 48;
860c1d255d3SCy Schubert return 64;
861c1d255d3SCy Schubert }
862c1d255d3SCy Schubert
863c1d255d3SCy Schubert
864c1d255d3SCy Schubert static struct crypto_ec_point *
sae_derive_pt_ecc(struct crypto_ec * ec,int group,const u8 * ssid,size_t ssid_len,const u8 * password,size_t password_len,const char * identifier)865c1d255d3SCy Schubert sae_derive_pt_ecc(struct crypto_ec *ec, int group,
866c1d255d3SCy Schubert const u8 *ssid, size_t ssid_len,
867c1d255d3SCy Schubert const u8 *password, size_t password_len,
868c1d255d3SCy Schubert const char *identifier)
869c1d255d3SCy Schubert {
870c1d255d3SCy Schubert u8 pwd_seed[64];
871c1d255d3SCy Schubert u8 pwd_value[SAE_MAX_ECC_PRIME_LEN * 2];
872c1d255d3SCy Schubert size_t pwd_value_len, hash_len, prime_len;
873c1d255d3SCy Schubert const struct crypto_bignum *prime;
874c1d255d3SCy Schubert struct crypto_bignum *bn = NULL;
875c1d255d3SCy Schubert struct crypto_ec_point *p1 = NULL, *p2 = NULL, *pt = NULL;
876c1d255d3SCy Schubert
877c1d255d3SCy Schubert prime = crypto_ec_get_prime(ec);
878c1d255d3SCy Schubert prime_len = crypto_ec_prime_len(ec);
879c1d255d3SCy Schubert if (prime_len > SAE_MAX_ECC_PRIME_LEN)
880c1d255d3SCy Schubert goto fail;
881c1d255d3SCy Schubert hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
882c1d255d3SCy Schubert
883c1d255d3SCy Schubert /* len = olen(p) + ceil(olen(p)/2) */
884c1d255d3SCy Schubert pwd_value_len = prime_len + (prime_len + 1) / 2;
885c1d255d3SCy Schubert
886c1d255d3SCy Schubert if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len,
887c1d255d3SCy Schubert identifier, pwd_seed) < 0)
888c1d255d3SCy Schubert goto fail;
889c1d255d3SCy Schubert
890c1d255d3SCy Schubert /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u1 P1", len)
891c1d255d3SCy Schubert */
892c1d255d3SCy Schubert if (hkdf_expand(hash_len, pwd_seed, hash_len,
893c1d255d3SCy Schubert "SAE Hash to Element u1 P1", pwd_value, pwd_value_len) <
894c1d255d3SCy Schubert 0)
895c1d255d3SCy Schubert goto fail;
896c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u1 P1)",
897c1d255d3SCy Schubert pwd_value, pwd_value_len);
898c1d255d3SCy Schubert
899c1d255d3SCy Schubert /* u1 = pwd-value modulo p */
900c1d255d3SCy Schubert bn = crypto_bignum_init_set(pwd_value, pwd_value_len);
901c1d255d3SCy Schubert if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 ||
902c1d255d3SCy Schubert crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value),
903c1d255d3SCy Schubert prime_len) < 0)
904c1d255d3SCy Schubert goto fail;
905c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: u1", pwd_value, prime_len);
906c1d255d3SCy Schubert
907c1d255d3SCy Schubert /* P1 = SSWU(u1) */
908c1d255d3SCy Schubert p1 = sswu(ec, group, bn);
909c1d255d3SCy Schubert if (!p1)
910c1d255d3SCy Schubert goto fail;
911c1d255d3SCy Schubert
912c1d255d3SCy Schubert /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u2 P2", len)
913c1d255d3SCy Schubert */
914c1d255d3SCy Schubert if (hkdf_expand(hash_len, pwd_seed, hash_len,
915c1d255d3SCy Schubert "SAE Hash to Element u2 P2", pwd_value,
916c1d255d3SCy Schubert pwd_value_len) < 0)
917c1d255d3SCy Schubert goto fail;
918c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u2 P2)",
919c1d255d3SCy Schubert pwd_value, pwd_value_len);
920c1d255d3SCy Schubert
921c1d255d3SCy Schubert /* u2 = pwd-value modulo p */
922c1d255d3SCy Schubert crypto_bignum_deinit(bn, 1);
923c1d255d3SCy Schubert bn = crypto_bignum_init_set(pwd_value, pwd_value_len);
924c1d255d3SCy Schubert if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 ||
925c1d255d3SCy Schubert crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value),
926c1d255d3SCy Schubert prime_len) < 0)
927c1d255d3SCy Schubert goto fail;
928c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: u2", pwd_value, prime_len);
929c1d255d3SCy Schubert
930c1d255d3SCy Schubert /* P2 = SSWU(u2) */
931c1d255d3SCy Schubert p2 = sswu(ec, group, bn);
932c1d255d3SCy Schubert if (!p2)
933c1d255d3SCy Schubert goto fail;
934c1d255d3SCy Schubert
935c1d255d3SCy Schubert /* PT = elem-op(P1, P2) */
936c1d255d3SCy Schubert pt = crypto_ec_point_init(ec);
937c1d255d3SCy Schubert if (!pt)
938c1d255d3SCy Schubert goto fail;
939c1d255d3SCy Schubert if (crypto_ec_point_add(ec, p1, p2, pt) < 0) {
940c1d255d3SCy Schubert crypto_ec_point_deinit(pt, 1);
941c1d255d3SCy Schubert pt = NULL;
942c1d255d3SCy Schubert }
943c1d255d3SCy Schubert
944c1d255d3SCy Schubert fail:
945c1d255d3SCy Schubert forced_memzero(pwd_seed, sizeof(pwd_seed));
946c1d255d3SCy Schubert forced_memzero(pwd_value, sizeof(pwd_value));
947c1d255d3SCy Schubert crypto_bignum_deinit(bn, 1);
948c1d255d3SCy Schubert crypto_ec_point_deinit(p1, 1);
949c1d255d3SCy Schubert crypto_ec_point_deinit(p2, 1);
950c1d255d3SCy Schubert return pt;
951c1d255d3SCy Schubert }
952c1d255d3SCy Schubert
953c1d255d3SCy Schubert
sae_ffc_prime_len_2_hash_len(size_t prime_len)954c1d255d3SCy Schubert size_t sae_ffc_prime_len_2_hash_len(size_t prime_len)
955c1d255d3SCy Schubert {
956c1d255d3SCy Schubert if (prime_len <= 2048 / 8)
957c1d255d3SCy Schubert return 32;
958c1d255d3SCy Schubert if (prime_len <= 3072 / 8)
959c1d255d3SCy Schubert return 48;
960c1d255d3SCy Schubert return 64;
961c1d255d3SCy Schubert }
962c1d255d3SCy Schubert
963c1d255d3SCy Schubert
964c1d255d3SCy Schubert static struct crypto_bignum *
sae_derive_pt_ffc(const struct dh_group * dh,int group,const u8 * ssid,size_t ssid_len,const u8 * password,size_t password_len,const char * identifier)965c1d255d3SCy Schubert sae_derive_pt_ffc(const struct dh_group *dh, int group,
966c1d255d3SCy Schubert const u8 *ssid, size_t ssid_len,
967c1d255d3SCy Schubert const u8 *password, size_t password_len,
968c1d255d3SCy Schubert const char *identifier)
969c1d255d3SCy Schubert {
970c1d255d3SCy Schubert size_t hash_len, prime_len, pwd_value_len;
971c1d255d3SCy Schubert struct crypto_bignum *prime, *order;
972c1d255d3SCy Schubert struct crypto_bignum *one = NULL, *two = NULL, *bn = NULL, *tmp = NULL,
973c1d255d3SCy Schubert *pt = NULL;
974c1d255d3SCy Schubert u8 pwd_seed[64];
975c1d255d3SCy Schubert u8 pwd_value[SAE_MAX_PRIME_LEN + SAE_MAX_PRIME_LEN / 2];
976c1d255d3SCy Schubert
977c1d255d3SCy Schubert prime = crypto_bignum_init_set(dh->prime, dh->prime_len);
978c1d255d3SCy Schubert order = crypto_bignum_init_set(dh->order, dh->order_len);
979c1d255d3SCy Schubert if (!prime || !order)
980c1d255d3SCy Schubert goto fail;
981c1d255d3SCy Schubert prime_len = dh->prime_len;
982c1d255d3SCy Schubert if (prime_len > SAE_MAX_PRIME_LEN)
983c1d255d3SCy Schubert goto fail;
984c1d255d3SCy Schubert hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
985c1d255d3SCy Schubert
986c1d255d3SCy Schubert /* len = olen(p) + ceil(olen(p)/2) */
987c1d255d3SCy Schubert pwd_value_len = prime_len + (prime_len + 1) / 2;
988c1d255d3SCy Schubert if (pwd_value_len > sizeof(pwd_value))
989c1d255d3SCy Schubert goto fail;
990c1d255d3SCy Schubert
991c1d255d3SCy Schubert if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len,
992c1d255d3SCy Schubert identifier, pwd_seed) < 0)
993c1d255d3SCy Schubert goto fail;
994c1d255d3SCy Schubert
995c1d255d3SCy Schubert /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element", len) */
996c1d255d3SCy Schubert if (hkdf_expand(hash_len, pwd_seed, hash_len,
997c1d255d3SCy Schubert "SAE Hash to Element", pwd_value, pwd_value_len) < 0)
998c1d255d3SCy Schubert goto fail;
999c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
1000c1d255d3SCy Schubert pwd_value, pwd_value_len);
1001c1d255d3SCy Schubert
1002c1d255d3SCy Schubert /* pwd-value = (pwd-value modulo (p-2)) + 2 */
1003c1d255d3SCy Schubert bn = crypto_bignum_init_set(pwd_value, pwd_value_len);
1004c1d255d3SCy Schubert one = crypto_bignum_init_uint(1);
1005c1d255d3SCy Schubert two = crypto_bignum_init_uint(2);
1006c1d255d3SCy Schubert tmp = crypto_bignum_init();
1007c1d255d3SCy Schubert if (!bn || !one || !two || !tmp ||
1008c1d255d3SCy Schubert crypto_bignum_sub(prime, two, tmp) < 0 ||
1009c1d255d3SCy Schubert crypto_bignum_mod(bn, tmp, bn) < 0 ||
1010c1d255d3SCy Schubert crypto_bignum_add(bn, two, bn) < 0 ||
1011c1d255d3SCy Schubert crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value),
1012c1d255d3SCy Schubert prime_len) < 0)
1013c1d255d3SCy Schubert goto fail;
1014c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value(reduced)",
1015c1d255d3SCy Schubert pwd_value, prime_len);
1016c1d255d3SCy Schubert
1017c1d255d3SCy Schubert /* PT = pwd-value^((p-1)/q) modulo p */
1018c1d255d3SCy Schubert pt = crypto_bignum_init();
1019c1d255d3SCy Schubert if (!pt ||
1020c1d255d3SCy Schubert crypto_bignum_sub(prime, one, tmp) < 0 ||
1021c1d255d3SCy Schubert crypto_bignum_div(tmp, order, tmp) < 0 ||
1022c1d255d3SCy Schubert crypto_bignum_exptmod(bn, tmp, prime, pt) < 0) {
1023c1d255d3SCy Schubert crypto_bignum_deinit(pt, 1);
1024c1d255d3SCy Schubert pt = NULL;
1025c1d255d3SCy Schubert goto fail;
1026c1d255d3SCy Schubert }
1027c1d255d3SCy Schubert debug_print_bignum("SAE: PT", pt, prime_len);
1028c1d255d3SCy Schubert
1029c1d255d3SCy Schubert fail:
1030c1d255d3SCy Schubert forced_memzero(pwd_seed, sizeof(pwd_seed));
1031c1d255d3SCy Schubert forced_memzero(pwd_value, sizeof(pwd_value));
1032c1d255d3SCy Schubert crypto_bignum_deinit(bn, 1);
1033c1d255d3SCy Schubert crypto_bignum_deinit(tmp, 1);
1034c1d255d3SCy Schubert crypto_bignum_deinit(one, 0);
1035c1d255d3SCy Schubert crypto_bignum_deinit(two, 0);
1036c1d255d3SCy Schubert crypto_bignum_deinit(prime, 0);
1037c1d255d3SCy Schubert crypto_bignum_deinit(order, 0);
1038c1d255d3SCy Schubert return pt;
1039c1d255d3SCy Schubert }
1040c1d255d3SCy Schubert
1041c1d255d3SCy Schubert
1042c1d255d3SCy Schubert static struct sae_pt *
sae_derive_pt_group(int group,const u8 * ssid,size_t ssid_len,const u8 * password,size_t password_len,const char * identifier)1043c1d255d3SCy Schubert sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len,
1044c1d255d3SCy Schubert const u8 *password, size_t password_len,
1045c1d255d3SCy Schubert const char *identifier)
1046c1d255d3SCy Schubert {
1047c1d255d3SCy Schubert struct sae_pt *pt;
1048c1d255d3SCy Schubert
1049c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group);
1050c1d255d3SCy Schubert
1051c1d255d3SCy Schubert if (ssid_len > 32)
1052c1d255d3SCy Schubert return NULL;
1053c1d255d3SCy Schubert
1054c1d255d3SCy Schubert pt = os_zalloc(sizeof(*pt));
1055c1d255d3SCy Schubert if (!pt)
1056c1d255d3SCy Schubert return NULL;
1057c1d255d3SCy Schubert
1058c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK
1059c1d255d3SCy Schubert os_memcpy(pt->ssid, ssid, ssid_len);
1060c1d255d3SCy Schubert pt->ssid_len = ssid_len;
1061c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */
1062c1d255d3SCy Schubert pt->group = group;
1063c1d255d3SCy Schubert pt->ec = crypto_ec_init(group);
1064c1d255d3SCy Schubert if (pt->ec) {
1065c1d255d3SCy Schubert pt->ecc_pt = sae_derive_pt_ecc(pt->ec, group, ssid, ssid_len,
1066c1d255d3SCy Schubert password, password_len,
1067c1d255d3SCy Schubert identifier);
1068c1d255d3SCy Schubert if (!pt->ecc_pt) {
1069c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT");
1070c1d255d3SCy Schubert goto fail;
1071c1d255d3SCy Schubert }
1072c1d255d3SCy Schubert
1073c1d255d3SCy Schubert return pt;
1074c1d255d3SCy Schubert }
1075c1d255d3SCy Schubert
1076c1d255d3SCy Schubert pt->dh = dh_groups_get(group);
1077c1d255d3SCy Schubert if (!pt->dh) {
1078c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Unsupported group %d", group);
1079c1d255d3SCy Schubert goto fail;
1080c1d255d3SCy Schubert }
1081c1d255d3SCy Schubert
1082c1d255d3SCy Schubert pt->ffc_pt = sae_derive_pt_ffc(pt->dh, group, ssid, ssid_len,
1083c1d255d3SCy Schubert password, password_len, identifier);
1084c1d255d3SCy Schubert if (!pt->ffc_pt) {
1085c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT");
1086c1d255d3SCy Schubert goto fail;
1087c1d255d3SCy Schubert }
1088c1d255d3SCy Schubert
1089c1d255d3SCy Schubert return pt;
1090c1d255d3SCy Schubert fail:
1091c1d255d3SCy Schubert sae_deinit_pt(pt);
1092c1d255d3SCy Schubert return NULL;
1093c1d255d3SCy Schubert }
1094c1d255d3SCy Schubert
1095c1d255d3SCy Schubert
sae_derive_pt(int * groups,const u8 * ssid,size_t ssid_len,const u8 * password,size_t password_len,const char * identifier)1096c1d255d3SCy Schubert struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len,
1097c1d255d3SCy Schubert const u8 *password, size_t password_len,
1098c1d255d3SCy Schubert const char *identifier)
1099c1d255d3SCy Schubert {
1100c1d255d3SCy Schubert struct sae_pt *pt = NULL, *last = NULL, *tmp;
1101c1d255d3SCy Schubert int default_groups[] = { 19, 0 };
1102c1d255d3SCy Schubert int i;
1103c1d255d3SCy Schubert
1104c1d255d3SCy Schubert if (!groups)
1105c1d255d3SCy Schubert groups = default_groups;
1106c1d255d3SCy Schubert for (i = 0; groups[i] > 0; i++) {
1107c1d255d3SCy Schubert tmp = sae_derive_pt_group(groups[i], ssid, ssid_len, password,
1108c1d255d3SCy Schubert password_len, identifier);
1109c1d255d3SCy Schubert if (!tmp)
1110c1d255d3SCy Schubert continue;
1111c1d255d3SCy Schubert
1112c1d255d3SCy Schubert if (last)
1113c1d255d3SCy Schubert last->next = tmp;
1114c1d255d3SCy Schubert else
1115c1d255d3SCy Schubert pt = tmp;
1116c1d255d3SCy Schubert last = tmp;
1117c1d255d3SCy Schubert }
1118c1d255d3SCy Schubert
1119c1d255d3SCy Schubert return pt;
1120c1d255d3SCy Schubert }
1121c1d255d3SCy Schubert
1122c1d255d3SCy Schubert
sae_max_min_addr(const u8 * addr[],size_t len[],const u8 * addr1,const u8 * addr2)1123c1d255d3SCy Schubert static void sae_max_min_addr(const u8 *addr[], size_t len[],
1124c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2)
1125c1d255d3SCy Schubert {
1126c1d255d3SCy Schubert len[0] = ETH_ALEN;
1127c1d255d3SCy Schubert len[1] = ETH_ALEN;
1128c1d255d3SCy Schubert if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) {
1129c1d255d3SCy Schubert addr[0] = addr1;
1130c1d255d3SCy Schubert addr[1] = addr2;
1131c1d255d3SCy Schubert } else {
1132c1d255d3SCy Schubert addr[0] = addr2;
1133c1d255d3SCy Schubert addr[1] = addr1;
1134c1d255d3SCy Schubert }
1135c1d255d3SCy Schubert }
1136c1d255d3SCy Schubert
1137c1d255d3SCy Schubert
1138c1d255d3SCy Schubert struct crypto_ec_point *
sae_derive_pwe_from_pt_ecc(const struct sae_pt * pt,const u8 * addr1,const u8 * addr2)1139c1d255d3SCy Schubert sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt,
1140c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2)
1141c1d255d3SCy Schubert {
1142c1d255d3SCy Schubert u8 bin[SAE_MAX_ECC_PRIME_LEN * 2];
1143c1d255d3SCy Schubert size_t prime_len;
1144c1d255d3SCy Schubert const u8 *addr[2];
1145c1d255d3SCy Schubert size_t len[2];
1146c1d255d3SCy Schubert u8 salt[64], hash[64];
1147c1d255d3SCy Schubert size_t hash_len;
1148c1d255d3SCy Schubert const struct crypto_bignum *order;
1149c1d255d3SCy Schubert struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL;
1150c1d255d3SCy Schubert struct crypto_ec_point *pwe = NULL;
1151c1d255d3SCy Schubert
1152c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT");
1153c1d255d3SCy Schubert prime_len = crypto_ec_prime_len(pt->ec);
1154c1d255d3SCy Schubert if (crypto_ec_point_to_bin(pt->ec, pt->ecc_pt,
1155c1d255d3SCy Schubert bin, bin + prime_len) < 0)
1156c1d255d3SCy Schubert return NULL;
1157c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PT.x", bin, prime_len);
1158c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PT.y", bin + prime_len, prime_len);
1159c1d255d3SCy Schubert
1160c1d255d3SCy Schubert sae_max_min_addr(addr, len, addr1, addr2);
1161c1d255d3SCy Schubert
1162c1d255d3SCy Schubert /* val = H(0^n,
1163c1d255d3SCy Schubert * MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */
1164c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))");
1165c1d255d3SCy Schubert hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
1166c1d255d3SCy Schubert os_memset(salt, 0, hash_len);
1167c1d255d3SCy Schubert if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0)
1168c1d255d3SCy Schubert goto fail;
1169c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len);
1170c1d255d3SCy Schubert
1171c1d255d3SCy Schubert /* val = val modulo (q - 1) + 1 */
1172c1d255d3SCy Schubert order = crypto_ec_get_order(pt->ec);
1173c1d255d3SCy Schubert tmp = crypto_bignum_init();
1174c1d255d3SCy Schubert val = crypto_bignum_init_set(hash, hash_len);
1175c1d255d3SCy Schubert one = crypto_bignum_init_uint(1);
1176c1d255d3SCy Schubert if (!tmp || !val || !one ||
1177c1d255d3SCy Schubert crypto_bignum_sub(order, one, tmp) < 0 ||
1178c1d255d3SCy Schubert crypto_bignum_mod(val, tmp, val) < 0 ||
1179c1d255d3SCy Schubert crypto_bignum_add(val, one, val) < 0)
1180c1d255d3SCy Schubert goto fail;
1181c1d255d3SCy Schubert debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len);
1182c1d255d3SCy Schubert
1183c1d255d3SCy Schubert /* PWE = scalar-op(val, PT) */
1184c1d255d3SCy Schubert pwe = crypto_ec_point_init(pt->ec);
1185c1d255d3SCy Schubert if (!pwe ||
1186c1d255d3SCy Schubert crypto_ec_point_mul(pt->ec, pt->ecc_pt, val, pwe) < 0 ||
1187c1d255d3SCy Schubert crypto_ec_point_to_bin(pt->ec, pwe, bin, bin + prime_len) < 0) {
1188c1d255d3SCy Schubert crypto_ec_point_deinit(pwe, 1);
1189c1d255d3SCy Schubert pwe = NULL;
1190c1d255d3SCy Schubert goto fail;
1191c1d255d3SCy Schubert }
1192c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.x", bin, prime_len);
1193c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.y", bin + prime_len, prime_len);
1194c1d255d3SCy Schubert
1195c1d255d3SCy Schubert fail:
1196c1d255d3SCy Schubert crypto_bignum_deinit(tmp, 1);
1197c1d255d3SCy Schubert crypto_bignum_deinit(val, 1);
1198c1d255d3SCy Schubert crypto_bignum_deinit(one, 0);
1199c1d255d3SCy Schubert return pwe;
1200c1d255d3SCy Schubert }
1201c1d255d3SCy Schubert
1202c1d255d3SCy Schubert
1203c1d255d3SCy Schubert struct crypto_bignum *
sae_derive_pwe_from_pt_ffc(const struct sae_pt * pt,const u8 * addr1,const u8 * addr2)1204c1d255d3SCy Schubert sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt,
1205c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2)
1206c1d255d3SCy Schubert {
1207c1d255d3SCy Schubert size_t prime_len;
1208c1d255d3SCy Schubert const u8 *addr[2];
1209c1d255d3SCy Schubert size_t len[2];
1210c1d255d3SCy Schubert u8 salt[64], hash[64];
1211c1d255d3SCy Schubert size_t hash_len;
1212c1d255d3SCy Schubert struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL;
1213c1d255d3SCy Schubert struct crypto_bignum *pwe = NULL, *order = NULL, *prime = NULL;
1214c1d255d3SCy Schubert
1215c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT");
1216c1d255d3SCy Schubert prime = crypto_bignum_init_set(pt->dh->prime, pt->dh->prime_len);
1217c1d255d3SCy Schubert order = crypto_bignum_init_set(pt->dh->order, pt->dh->order_len);
1218c1d255d3SCy Schubert if (!prime || !order)
1219c1d255d3SCy Schubert goto fail;
1220c1d255d3SCy Schubert prime_len = pt->dh->prime_len;
1221c1d255d3SCy Schubert
1222c1d255d3SCy Schubert sae_max_min_addr(addr, len, addr1, addr2);
1223c1d255d3SCy Schubert
1224c1d255d3SCy Schubert /* val = H(0^n,
1225c1d255d3SCy Schubert * MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */
1226c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))");
1227c1d255d3SCy Schubert hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
1228c1d255d3SCy Schubert os_memset(salt, 0, hash_len);
1229c1d255d3SCy Schubert if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0)
1230c1d255d3SCy Schubert goto fail;
1231c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len);
1232c1d255d3SCy Schubert
1233c1d255d3SCy Schubert /* val = val modulo (q - 1) + 1 */
1234c1d255d3SCy Schubert tmp = crypto_bignum_init();
1235c1d255d3SCy Schubert val = crypto_bignum_init_set(hash, hash_len);
1236c1d255d3SCy Schubert one = crypto_bignum_init_uint(1);
1237c1d255d3SCy Schubert if (!tmp || !val || !one ||
1238c1d255d3SCy Schubert crypto_bignum_sub(order, one, tmp) < 0 ||
1239c1d255d3SCy Schubert crypto_bignum_mod(val, tmp, val) < 0 ||
1240c1d255d3SCy Schubert crypto_bignum_add(val, one, val) < 0)
1241c1d255d3SCy Schubert goto fail;
1242c1d255d3SCy Schubert debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len);
1243c1d255d3SCy Schubert
1244c1d255d3SCy Schubert /* PWE = scalar-op(val, PT) */
1245c1d255d3SCy Schubert pwe = crypto_bignum_init();
1246c1d255d3SCy Schubert if (!pwe || crypto_bignum_exptmod(pt->ffc_pt, val, prime, pwe) < 0) {
1247c1d255d3SCy Schubert crypto_bignum_deinit(pwe, 1);
1248c1d255d3SCy Schubert pwe = NULL;
1249c1d255d3SCy Schubert goto fail;
1250c1d255d3SCy Schubert }
1251c1d255d3SCy Schubert debug_print_bignum("SAE: PWE", pwe, prime_len);
1252c1d255d3SCy Schubert
1253c1d255d3SCy Schubert fail:
1254c1d255d3SCy Schubert crypto_bignum_deinit(tmp, 1);
1255c1d255d3SCy Schubert crypto_bignum_deinit(val, 1);
1256c1d255d3SCy Schubert crypto_bignum_deinit(one, 0);
1257c1d255d3SCy Schubert crypto_bignum_deinit(prime, 0);
1258c1d255d3SCy Schubert crypto_bignum_deinit(order, 0);
1259c1d255d3SCy Schubert return pwe;
1260c1d255d3SCy Schubert }
1261c1d255d3SCy Schubert
1262c1d255d3SCy Schubert
sae_deinit_pt(struct sae_pt * pt)1263c1d255d3SCy Schubert void sae_deinit_pt(struct sae_pt *pt)
1264c1d255d3SCy Schubert {
1265c1d255d3SCy Schubert struct sae_pt *prev;
1266c1d255d3SCy Schubert
1267c1d255d3SCy Schubert while (pt) {
1268c1d255d3SCy Schubert crypto_ec_point_deinit(pt->ecc_pt, 1);
1269c1d255d3SCy Schubert crypto_bignum_deinit(pt->ffc_pt, 1);
1270c1d255d3SCy Schubert crypto_ec_deinit(pt->ec);
1271c1d255d3SCy Schubert prev = pt;
1272c1d255d3SCy Schubert pt = pt->next;
1273c1d255d3SCy Schubert os_free(prev);
1274c1d255d3SCy Schubert }
1275c1d255d3SCy Schubert }
1276c1d255d3SCy Schubert
1277c1d255d3SCy Schubert
sae_derive_commit_element_ecc(struct sae_data * sae,struct crypto_bignum * mask)12785b9c547cSRui Paulo static int sae_derive_commit_element_ecc(struct sae_data *sae,
12795b9c547cSRui Paulo struct crypto_bignum *mask)
12805b9c547cSRui Paulo {
12815b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
12825b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc) {
12835b9c547cSRui Paulo sae->tmp->own_commit_element_ecc =
12845b9c547cSRui Paulo crypto_ec_point_init(sae->tmp->ec);
12855b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ecc)
12865b9c547cSRui Paulo return -1;
12875b9c547cSRui Paulo }
12885b9c547cSRui Paulo
12895b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask,
12905b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0 ||
12915b9c547cSRui Paulo crypto_ec_point_invert(sae->tmp->ec,
12925b9c547cSRui Paulo sae->tmp->own_commit_element_ecc) < 0) {
12935b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
12945b9c547cSRui Paulo return -1;
12955b9c547cSRui Paulo }
12965b9c547cSRui Paulo
12975b9c547cSRui Paulo return 0;
12985b9c547cSRui Paulo }
12995b9c547cSRui Paulo
13005b9c547cSRui Paulo
sae_derive_commit_element_ffc(struct sae_data * sae,struct crypto_bignum * mask)13015b9c547cSRui Paulo static int sae_derive_commit_element_ffc(struct sae_data *sae,
13025b9c547cSRui Paulo struct crypto_bignum *mask)
13035b9c547cSRui Paulo {
13045b9c547cSRui Paulo /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */
13055b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc) {
13065b9c547cSRui Paulo sae->tmp->own_commit_element_ffc = crypto_bignum_init();
13075b9c547cSRui Paulo if (!sae->tmp->own_commit_element_ffc)
13085b9c547cSRui Paulo return -1;
13095b9c547cSRui Paulo }
13105b9c547cSRui Paulo
13115b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime,
13125b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0 ||
13135b9c547cSRui Paulo crypto_bignum_inverse(sae->tmp->own_commit_element_ffc,
13145b9c547cSRui Paulo sae->tmp->prime,
13155b9c547cSRui Paulo sae->tmp->own_commit_element_ffc) < 0) {
13165b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element");
13175b9c547cSRui Paulo return -1;
13185b9c547cSRui Paulo }
13195b9c547cSRui Paulo
13205b9c547cSRui Paulo return 0;
13215b9c547cSRui Paulo }
13225b9c547cSRui Paulo
13235b9c547cSRui Paulo
sae_derive_commit(struct sae_data * sae)13245b9c547cSRui Paulo static int sae_derive_commit(struct sae_data *sae)
13255b9c547cSRui Paulo {
13265b9c547cSRui Paulo struct crypto_bignum *mask;
1327206b73d0SCy Schubert int ret;
1328325151a3SRui Paulo
1329206b73d0SCy Schubert mask = crypto_bignum_init();
1330206b73d0SCy Schubert if (!sae->tmp->sae_rand)
1331206b73d0SCy Schubert sae->tmp->sae_rand = crypto_bignum_init();
13325b9c547cSRui Paulo if (!sae->tmp->own_commit_scalar)
1333206b73d0SCy Schubert sae->tmp->own_commit_scalar = crypto_bignum_init();
1334206b73d0SCy Schubert ret = !mask || !sae->tmp->sae_rand || !sae->tmp->own_commit_scalar ||
1335206b73d0SCy Schubert dragonfly_generate_scalar(sae->tmp->order, sae->tmp->sae_rand,
1336206b73d0SCy Schubert mask,
1337206b73d0SCy Schubert sae->tmp->own_commit_scalar) < 0 ||
1338206b73d0SCy Schubert (sae->tmp->ec &&
1339206b73d0SCy Schubert sae_derive_commit_element_ecc(sae, mask) < 0) ||
1340206b73d0SCy Schubert (sae->tmp->dh &&
1341206b73d0SCy Schubert sae_derive_commit_element_ffc(sae, mask) < 0);
13425b9c547cSRui Paulo crypto_bignum_deinit(mask, 1);
1343206b73d0SCy Schubert return ret ? -1 : 0;
13445b9c547cSRui Paulo }
13455b9c547cSRui Paulo
13465b9c547cSRui Paulo
sae_prepare_commit(const u8 * addr1,const u8 * addr2,const u8 * password,size_t password_len,struct sae_data * sae)13475b9c547cSRui Paulo int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
13485b9c547cSRui Paulo const u8 *password, size_t password_len,
1349c1d255d3SCy Schubert struct sae_data *sae)
13505b9c547cSRui Paulo {
1351325151a3SRui Paulo if (sae->tmp == NULL ||
1352325151a3SRui Paulo (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
1353c1d255d3SCy Schubert password_len) < 0) ||
1354325151a3SRui Paulo (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
1355c1d255d3SCy Schubert password_len) < 0))
13565b9c547cSRui Paulo return -1;
1357c1d255d3SCy Schubert
1358c1d255d3SCy Schubert sae->h2e = 0;
1359c1d255d3SCy Schubert sae->pk = 0;
1360c1d255d3SCy Schubert return sae_derive_commit(sae);
1361c1d255d3SCy Schubert }
1362c1d255d3SCy Schubert
1363c1d255d3SCy Schubert
sae_prepare_commit_pt(struct sae_data * sae,const struct sae_pt * pt,const u8 * addr1,const u8 * addr2,int * rejected_groups,const struct sae_pk * pk)1364c1d255d3SCy Schubert int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt,
1365c1d255d3SCy Schubert const u8 *addr1, const u8 *addr2,
1366c1d255d3SCy Schubert int *rejected_groups, const struct sae_pk *pk)
1367c1d255d3SCy Schubert {
1368c1d255d3SCy Schubert if (!sae->tmp)
1369c1d255d3SCy Schubert return -1;
1370c1d255d3SCy Schubert
1371c1d255d3SCy Schubert while (pt) {
1372c1d255d3SCy Schubert if (pt->group == sae->group)
1373c1d255d3SCy Schubert break;
1374c1d255d3SCy Schubert pt = pt->next;
1375c1d255d3SCy Schubert }
1376c1d255d3SCy Schubert if (!pt) {
1377c1d255d3SCy Schubert wpa_printf(MSG_INFO, "SAE: Could not find PT for group %u",
1378c1d255d3SCy Schubert sae->group);
1379c1d255d3SCy Schubert return -1;
1380c1d255d3SCy Schubert }
1381c1d255d3SCy Schubert
1382c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK
1383c1d255d3SCy Schubert os_memcpy(sae->tmp->ssid, pt->ssid, pt->ssid_len);
1384c1d255d3SCy Schubert sae->tmp->ssid_len = pt->ssid_len;
1385c1d255d3SCy Schubert sae->tmp->ap_pk = pk;
1386c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */
1387c1d255d3SCy Schubert sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0;
1388c1d255d3SCy Schubert wpabuf_free(sae->tmp->own_rejected_groups);
1389c1d255d3SCy Schubert sae->tmp->own_rejected_groups = NULL;
1390c1d255d3SCy Schubert if (rejected_groups) {
1391c1d255d3SCy Schubert int count, i;
1392c1d255d3SCy Schubert struct wpabuf *groups;
1393c1d255d3SCy Schubert
1394c1d255d3SCy Schubert count = int_array_len(rejected_groups);
1395c1d255d3SCy Schubert groups = wpabuf_alloc(count * 2);
1396c1d255d3SCy Schubert if (!groups)
1397c1d255d3SCy Schubert return -1;
1398c1d255d3SCy Schubert for (i = 0; i < count; i++)
1399c1d255d3SCy Schubert wpabuf_put_le16(groups, rejected_groups[i]);
1400c1d255d3SCy Schubert sae->tmp->own_rejected_groups = groups;
1401c1d255d3SCy Schubert }
1402c1d255d3SCy Schubert
1403c1d255d3SCy Schubert if (pt->ec) {
1404c1d255d3SCy Schubert crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1);
1405c1d255d3SCy Schubert sae->tmp->pwe_ecc = sae_derive_pwe_from_pt_ecc(pt, addr1,
1406c1d255d3SCy Schubert addr2);
1407c1d255d3SCy Schubert if (!sae->tmp->pwe_ecc)
1408c1d255d3SCy Schubert return -1;
1409c1d255d3SCy Schubert }
1410c1d255d3SCy Schubert
1411c1d255d3SCy Schubert if (pt->dh) {
1412c1d255d3SCy Schubert crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
1413c1d255d3SCy Schubert sae->tmp->pwe_ffc = sae_derive_pwe_from_pt_ffc(pt, addr1,
1414c1d255d3SCy Schubert addr2);
1415c1d255d3SCy Schubert if (!sae->tmp->pwe_ffc)
1416c1d255d3SCy Schubert return -1;
1417c1d255d3SCy Schubert }
1418c1d255d3SCy Schubert
1419c1d255d3SCy Schubert sae->h2e = 1;
1420c1d255d3SCy Schubert return sae_derive_commit(sae);
14215b9c547cSRui Paulo }
14225b9c547cSRui Paulo
14235b9c547cSRui Paulo
sae_derive_k_ecc(struct sae_data * sae,u8 * k)14245b9c547cSRui Paulo static int sae_derive_k_ecc(struct sae_data *sae, u8 *k)
14255b9c547cSRui Paulo {
14265b9c547cSRui Paulo struct crypto_ec_point *K;
14275b9c547cSRui Paulo int ret = -1;
14285b9c547cSRui Paulo
14295b9c547cSRui Paulo K = crypto_ec_point_init(sae->tmp->ec);
14305b9c547cSRui Paulo if (K == NULL)
14315b9c547cSRui Paulo goto fail;
14325b9c547cSRui Paulo
14335b9c547cSRui Paulo /*
14345b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
14355b9c547cSRui Paulo * PEER-COMMIT-ELEMENT)))
14365b9c547cSRui Paulo * If K is identity element (point-at-infinity), reject
14375b9c547cSRui Paulo * k = F(K) (= x coordinate)
14385b9c547cSRui Paulo */
14395b9c547cSRui Paulo
14405b9c547cSRui Paulo if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc,
14415b9c547cSRui Paulo sae->peer_commit_scalar, K) < 0 ||
14425b9c547cSRui Paulo crypto_ec_point_add(sae->tmp->ec, K,
14435b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc, K) < 0 ||
14445b9c547cSRui Paulo crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 ||
14455b9c547cSRui Paulo crypto_ec_point_is_at_infinity(sae->tmp->ec, K) ||
14465b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) {
14475b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
14485b9c547cSRui Paulo goto fail;
14495b9c547cSRui Paulo }
14505b9c547cSRui Paulo
14515b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
14525b9c547cSRui Paulo
14535b9c547cSRui Paulo ret = 0;
14545b9c547cSRui Paulo fail:
14555b9c547cSRui Paulo crypto_ec_point_deinit(K, 1);
14565b9c547cSRui Paulo return ret;
14575b9c547cSRui Paulo }
14585b9c547cSRui Paulo
14595b9c547cSRui Paulo
sae_derive_k_ffc(struct sae_data * sae,u8 * k)14605b9c547cSRui Paulo static int sae_derive_k_ffc(struct sae_data *sae, u8 *k)
14615b9c547cSRui Paulo {
14625b9c547cSRui Paulo struct crypto_bignum *K;
14635b9c547cSRui Paulo int ret = -1;
14645b9c547cSRui Paulo
14655b9c547cSRui Paulo K = crypto_bignum_init();
14665b9c547cSRui Paulo if (K == NULL)
14675b9c547cSRui Paulo goto fail;
14685b9c547cSRui Paulo
14695b9c547cSRui Paulo /*
14705b9c547cSRui Paulo * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
14715b9c547cSRui Paulo * PEER-COMMIT-ELEMENT)))
14725b9c547cSRui Paulo * If K is identity element (one), reject.
14735b9c547cSRui Paulo * k = F(K) (= x coordinate)
14745b9c547cSRui Paulo */
14755b9c547cSRui Paulo
14765b9c547cSRui Paulo if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar,
14775b9c547cSRui Paulo sae->tmp->prime, K) < 0 ||
14785b9c547cSRui Paulo crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc,
14795b9c547cSRui Paulo sae->tmp->prime, K) < 0 ||
14805b9c547cSRui Paulo crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0
14815b9c547cSRui Paulo ||
14825b9c547cSRui Paulo crypto_bignum_is_one(K) ||
14835b9c547cSRui Paulo crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) <
14845b9c547cSRui Paulo 0) {
14855b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k");
14865b9c547cSRui Paulo goto fail;
14875b9c547cSRui Paulo }
14885b9c547cSRui Paulo
14895b9c547cSRui Paulo wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len);
14905b9c547cSRui Paulo
14915b9c547cSRui Paulo ret = 0;
14925b9c547cSRui Paulo fail:
14935b9c547cSRui Paulo crypto_bignum_deinit(K, 1);
14945b9c547cSRui Paulo return ret;
14955b9c547cSRui Paulo }
14965b9c547cSRui Paulo
14975b9c547cSRui Paulo
sae_kdf_hash(size_t hash_len,const u8 * k,const char * label,const u8 * context,size_t context_len,u8 * out,size_t out_len)1498c1d255d3SCy Schubert static int sae_kdf_hash(size_t hash_len, const u8 *k, const char *label,
1499c1d255d3SCy Schubert const u8 *context, size_t context_len,
1500c1d255d3SCy Schubert u8 *out, size_t out_len)
1501c1d255d3SCy Schubert {
1502c1d255d3SCy Schubert if (hash_len == 32)
1503c1d255d3SCy Schubert return sha256_prf(k, hash_len, label,
1504c1d255d3SCy Schubert context, context_len, out, out_len);
1505c1d255d3SCy Schubert #ifdef CONFIG_SHA384
1506c1d255d3SCy Schubert if (hash_len == 48)
1507c1d255d3SCy Schubert return sha384_prf(k, hash_len, label,
1508c1d255d3SCy Schubert context, context_len, out, out_len);
1509c1d255d3SCy Schubert #endif /* CONFIG_SHA384 */
1510c1d255d3SCy Schubert #ifdef CONFIG_SHA512
1511c1d255d3SCy Schubert if (hash_len == 64)
1512c1d255d3SCy Schubert return sha512_prf(k, hash_len, label,
1513c1d255d3SCy Schubert context, context_len, out, out_len);
1514c1d255d3SCy Schubert #endif /* CONFIG_SHA512 */
1515c1d255d3SCy Schubert return -1;
1516c1d255d3SCy Schubert }
1517c1d255d3SCy Schubert
1518c1d255d3SCy Schubert
sae_derive_keys(struct sae_data * sae,const u8 * k)15195b9c547cSRui Paulo static int sae_derive_keys(struct sae_data *sae, const u8 *k)
15205b9c547cSRui Paulo {
1521c1d255d3SCy Schubert u8 zero[SAE_MAX_HASH_LEN], val[SAE_MAX_PRIME_LEN];
1522c1d255d3SCy Schubert const u8 *salt;
1523c1d255d3SCy Schubert struct wpabuf *rejected_groups = NULL;
1524c1d255d3SCy Schubert u8 keyseed[SAE_MAX_HASH_LEN];
1525*a90b9d01SCy Schubert u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN_MAX];
15265b9c547cSRui Paulo struct crypto_bignum *tmp;
15275b9c547cSRui Paulo int ret = -1;
1528c1d255d3SCy Schubert size_t hash_len, salt_len, prime_len = sae->tmp->prime_len;
1529*a90b9d01SCy Schubert size_t pmk_len;
1530c1d255d3SCy Schubert const u8 *addr[1];
1531c1d255d3SCy Schubert size_t len[1];
15325b9c547cSRui Paulo
15335b9c547cSRui Paulo tmp = crypto_bignum_init();
15345b9c547cSRui Paulo if (tmp == NULL)
15355b9c547cSRui Paulo goto fail;
15365b9c547cSRui Paulo
1537c1d255d3SCy Schubert /* keyseed = H(salt, k)
1538c1d255d3SCy Schubert * KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK",
15395b9c547cSRui Paulo * (commit-scalar + peer-commit-scalar) modulo r)
15405b9c547cSRui Paulo * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128)
1541c1d255d3SCy Schubert *
1542c1d255d3SCy Schubert * When SAE-PK is used,
1543c1d255d3SCy Schubert * KCK || PMK || KEK = KDF-Hash-Length(keyseed, "SAE-PK keys", context)
15445b9c547cSRui Paulo */
1545c1d255d3SCy Schubert if (!sae->h2e)
1546c1d255d3SCy Schubert hash_len = SHA256_MAC_LEN;
1547c1d255d3SCy Schubert else if (sae->tmp->dh)
1548c1d255d3SCy Schubert hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
1549c1d255d3SCy Schubert else
1550c1d255d3SCy Schubert hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
1551*a90b9d01SCy Schubert if (wpa_key_mgmt_sae_ext_key(sae->akmp))
1552*a90b9d01SCy Schubert pmk_len = hash_len;
1553*a90b9d01SCy Schubert else
1554*a90b9d01SCy Schubert pmk_len = SAE_PMK_LEN;
1555*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Derive keys - H2E=%d AKMP=0x%x = %08x (%s)",
1556*a90b9d01SCy Schubert sae->h2e, sae->akmp,
1557*a90b9d01SCy Schubert wpa_akm_to_suite(sae->akmp),
1558*a90b9d01SCy Schubert wpa_key_mgmt_txt(sae->akmp, WPA_PROTO_RSN));
1559c1d255d3SCy Schubert if (sae->h2e && (sae->tmp->own_rejected_groups ||
1560c1d255d3SCy Schubert sae->tmp->peer_rejected_groups)) {
1561c1d255d3SCy Schubert struct wpabuf *own, *peer;
15625b9c547cSRui Paulo
1563c1d255d3SCy Schubert own = sae->tmp->own_rejected_groups;
1564c1d255d3SCy Schubert peer = sae->tmp->peer_rejected_groups;
1565c1d255d3SCy Schubert salt_len = 0;
1566c1d255d3SCy Schubert if (own)
1567c1d255d3SCy Schubert salt_len += wpabuf_len(own);
1568c1d255d3SCy Schubert if (peer)
1569c1d255d3SCy Schubert salt_len += wpabuf_len(peer);
1570c1d255d3SCy Schubert rejected_groups = wpabuf_alloc(salt_len);
1571c1d255d3SCy Schubert if (!rejected_groups)
1572c1d255d3SCy Schubert goto fail;
1573c1d255d3SCy Schubert if (sae->tmp->own_addr_higher) {
1574c1d255d3SCy Schubert if (own)
1575c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, own);
1576c1d255d3SCy Schubert if (peer)
1577c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, peer);
1578c1d255d3SCy Schubert } else {
1579c1d255d3SCy Schubert if (peer)
1580c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, peer);
1581c1d255d3SCy Schubert if (own)
1582c1d255d3SCy Schubert wpabuf_put_buf(rejected_groups, own);
1583c1d255d3SCy Schubert }
1584c1d255d3SCy Schubert salt = wpabuf_head(rejected_groups);
1585c1d255d3SCy Schubert salt_len = wpabuf_len(rejected_groups);
1586c1d255d3SCy Schubert } else {
1587c1d255d3SCy Schubert os_memset(zero, 0, hash_len);
1588c1d255d3SCy Schubert salt = zero;
1589c1d255d3SCy Schubert salt_len = hash_len;
1590c1d255d3SCy Schubert }
1591c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: salt for keyseed derivation",
1592c1d255d3SCy Schubert salt, salt_len);
1593c1d255d3SCy Schubert addr[0] = k;
1594c1d255d3SCy Schubert len[0] = prime_len;
1595c1d255d3SCy Schubert if (hkdf_extract(hash_len, salt, salt_len, 1, addr, len, keyseed) < 0)
1596c1d255d3SCy Schubert goto fail;
1597c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, hash_len);
15985b9c547cSRui Paulo
1599c1d255d3SCy Schubert if (crypto_bignum_add(sae->tmp->own_commit_scalar,
1600c1d255d3SCy Schubert sae->peer_commit_scalar, tmp) < 0 ||
1601c1d255d3SCy Schubert crypto_bignum_mod(tmp, sae->tmp->order, tmp) < 0)
1602c1d255d3SCy Schubert goto fail;
1603206b73d0SCy Schubert /* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit
1604206b73d0SCy Schubert * string that is needed for KCK, PMK, and PMKID derivation, but it
1605206b73d0SCy Schubert * seems to make most sense to encode the
1606206b73d0SCy Schubert * (commit-scalar + peer-commit-scalar) mod r part as a bit string by
1607206b73d0SCy Schubert * zero padding it from left to the length of the order (in full
1608206b73d0SCy Schubert * octets). */
1609*a90b9d01SCy Schubert if (crypto_bignum_to_bin(tmp, val, sizeof(val),
1610*a90b9d01SCy Schubert sae->tmp->order_len) < 0)
1611*a90b9d01SCy Schubert goto fail;
16125b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
1613c1d255d3SCy Schubert
1614c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK
1615c1d255d3SCy Schubert if (sae->pk) {
1616c1d255d3SCy Schubert if (sae_kdf_hash(hash_len, keyseed, "SAE-PK keys",
1617c1d255d3SCy Schubert val, sae->tmp->order_len,
1618*a90b9d01SCy Schubert keys, 2 * hash_len + pmk_len) < 0)
1619780fb4a2SCy Schubert goto fail;
1620c1d255d3SCy Schubert } else {
1621c1d255d3SCy Schubert if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
1622c1d255d3SCy Schubert val, sae->tmp->order_len,
1623*a90b9d01SCy Schubert keys, hash_len + pmk_len) < 0)
1624c1d255d3SCy Schubert goto fail;
1625c1d255d3SCy Schubert }
1626c1d255d3SCy Schubert #else /* CONFIG_SAE_PK */
1627c1d255d3SCy Schubert if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
1628c1d255d3SCy Schubert val, sae->tmp->order_len,
1629*a90b9d01SCy Schubert keys, hash_len + pmk_len) < 0)
1630c1d255d3SCy Schubert goto fail;
1631c1d255d3SCy Schubert #endif /* !CONFIG_SAE_PK */
1632c1d255d3SCy Schubert
1633c1d255d3SCy Schubert forced_memzero(keyseed, sizeof(keyseed));
1634c1d255d3SCy Schubert os_memcpy(sae->tmp->kck, keys, hash_len);
1635c1d255d3SCy Schubert sae->tmp->kck_len = hash_len;
1636*a90b9d01SCy Schubert os_memcpy(sae->pmk, keys + hash_len, pmk_len);
1637*a90b9d01SCy Schubert sae->pmk_len = pmk_len;
1638780fb4a2SCy Schubert os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
1639c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK
1640c1d255d3SCy Schubert if (sae->pk) {
1641c1d255d3SCy Schubert os_memcpy(sae->tmp->kek, keys + hash_len + SAE_PMK_LEN,
1642c1d255d3SCy Schubert hash_len);
1643c1d255d3SCy Schubert sae->tmp->kek_len = hash_len;
1644c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: KEK for SAE-PK",
1645c1d255d3SCy Schubert sae->tmp->kek, sae->tmp->kek_len);
1646c1d255d3SCy Schubert }
1647c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */
1648c1d255d3SCy Schubert forced_memzero(keys, sizeof(keys));
1649c1d255d3SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: KCK",
1650c1d255d3SCy Schubert sae->tmp->kck, sae->tmp->kck_len);
1651*a90b9d01SCy Schubert wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, sae->pmk_len);
16525b9c547cSRui Paulo
16535b9c547cSRui Paulo ret = 0;
16545b9c547cSRui Paulo fail:
1655c1d255d3SCy Schubert wpabuf_free(rejected_groups);
16565b9c547cSRui Paulo crypto_bignum_deinit(tmp, 0);
16575b9c547cSRui Paulo return ret;
16585b9c547cSRui Paulo }
16595b9c547cSRui Paulo
16605b9c547cSRui Paulo
sae_process_commit(struct sae_data * sae)16615b9c547cSRui Paulo int sae_process_commit(struct sae_data *sae)
16625b9c547cSRui Paulo {
16635b9c547cSRui Paulo u8 k[SAE_MAX_PRIME_LEN];
16645b9c547cSRui Paulo if (sae->tmp == NULL ||
16655b9c547cSRui Paulo (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
16665b9c547cSRui Paulo (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
16675b9c547cSRui Paulo sae_derive_keys(sae, k) < 0)
16685b9c547cSRui Paulo return -1;
16695b9c547cSRui Paulo return 0;
16705b9c547cSRui Paulo }
16715b9c547cSRui Paulo
16725b9c547cSRui Paulo
sae_write_commit(struct sae_data * sae,struct wpabuf * buf,const struct wpabuf * token,const char * identifier)1673c1d255d3SCy Schubert int sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
167485732ac8SCy Schubert const struct wpabuf *token, const char *identifier)
16755b9c547cSRui Paulo {
16765b9c547cSRui Paulo u8 *pos;
16775b9c547cSRui Paulo
16785b9c547cSRui Paulo if (sae->tmp == NULL)
1679c1d255d3SCy Schubert return -1;
16805b9c547cSRui Paulo
16815b9c547cSRui Paulo wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
1682c1d255d3SCy Schubert if (!sae->h2e && token) {
16835b9c547cSRui Paulo wpabuf_put_buf(buf, token);
16845b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token",
16855b9c547cSRui Paulo wpabuf_head(token), wpabuf_len(token));
16865b9c547cSRui Paulo }
16875b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len);
1688c1d255d3SCy Schubert if (crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos,
1689c1d255d3SCy Schubert sae->tmp->prime_len, sae->tmp->prime_len) < 0)
1690c1d255d3SCy Schubert return -1;
16915b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar",
16925b9c547cSRui Paulo pos, sae->tmp->prime_len);
16935b9c547cSRui Paulo if (sae->tmp->ec) {
16945b9c547cSRui Paulo pos = wpabuf_put(buf, 2 * sae->tmp->prime_len);
1695c1d255d3SCy Schubert if (crypto_ec_point_to_bin(sae->tmp->ec,
16965b9c547cSRui Paulo sae->tmp->own_commit_element_ecc,
1697c1d255d3SCy Schubert pos, pos + sae->tmp->prime_len) < 0)
1698c1d255d3SCy Schubert return -1;
16995b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)",
17005b9c547cSRui Paulo pos, sae->tmp->prime_len);
17015b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)",
17025b9c547cSRui Paulo pos + sae->tmp->prime_len, sae->tmp->prime_len);
17035b9c547cSRui Paulo } else {
17045b9c547cSRui Paulo pos = wpabuf_put(buf, sae->tmp->prime_len);
1705c1d255d3SCy Schubert if (crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos,
1706c1d255d3SCy Schubert sae->tmp->prime_len,
1707c1d255d3SCy Schubert sae->tmp->prime_len) < 0)
1708c1d255d3SCy Schubert return -1;
17095b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: own commit-element",
17105b9c547cSRui Paulo pos, sae->tmp->prime_len);
17115b9c547cSRui Paulo }
171285732ac8SCy Schubert
171385732ac8SCy Schubert if (identifier) {
171485732ac8SCy Schubert /* Password Identifier element */
171585732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
171685732ac8SCy Schubert wpabuf_put_u8(buf, 1 + os_strlen(identifier));
171785732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER);
171885732ac8SCy Schubert wpabuf_put_str(buf, identifier);
171985732ac8SCy Schubert wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s",
172085732ac8SCy Schubert identifier);
172185732ac8SCy Schubert }
1722c1d255d3SCy Schubert
1723c1d255d3SCy Schubert if (sae->h2e && sae->tmp->own_rejected_groups) {
1724c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups",
1725c1d255d3SCy Schubert sae->tmp->own_rejected_groups);
1726c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
1727c1d255d3SCy Schubert wpabuf_put_u8(buf,
1728c1d255d3SCy Schubert 1 + wpabuf_len(sae->tmp->own_rejected_groups));
1729c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_REJECTED_GROUPS);
1730c1d255d3SCy Schubert wpabuf_put_buf(buf, sae->tmp->own_rejected_groups);
1731c1d255d3SCy Schubert }
1732c1d255d3SCy Schubert
1733c1d255d3SCy Schubert if (sae->h2e && token) {
1734c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
1735c1d255d3SCy Schubert wpabuf_put_u8(buf, 1 + wpabuf_len(token));
1736c1d255d3SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
1737c1d255d3SCy Schubert wpabuf_put_buf(buf, token);
1738c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG,
1739c1d255d3SCy Schubert "SAE: Anti-clogging token (in container)",
1740c1d255d3SCy Schubert token);
1741c1d255d3SCy Schubert }
1742c1d255d3SCy Schubert
1743*a90b9d01SCy Schubert if (wpa_key_mgmt_sae_ext_key(sae->akmp)) {
1744*a90b9d01SCy Schubert u32 suite = wpa_akm_to_suite(sae->akmp);
1745*a90b9d01SCy Schubert
1746*a90b9d01SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
1747*a90b9d01SCy Schubert wpabuf_put_u8(buf, 1 + RSN_SELECTOR_LEN);
1748*a90b9d01SCy Schubert wpabuf_put_u8(buf, WLAN_EID_EXT_AKM_SUITE_SELECTOR);
1749*a90b9d01SCy Schubert RSN_SELECTOR_PUT(wpabuf_put(buf, RSN_SELECTOR_LEN), suite);
1750*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "SAE: AKM Suite Selector: %08x", suite);
1751*a90b9d01SCy Schubert sae->own_akm_suite_selector = suite;
1752*a90b9d01SCy Schubert }
1753*a90b9d01SCy Schubert
1754c1d255d3SCy Schubert return 0;
17555b9c547cSRui Paulo }
17565b9c547cSRui Paulo
17575b9c547cSRui Paulo
sae_group_allowed(struct sae_data * sae,int * allowed_groups,u16 group)17585b9c547cSRui Paulo u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
17595b9c547cSRui Paulo {
17605b9c547cSRui Paulo if (allowed_groups) {
17615b9c547cSRui Paulo int i;
17625b9c547cSRui Paulo for (i = 0; allowed_groups[i] > 0; i++) {
17635b9c547cSRui Paulo if (allowed_groups[i] == group)
17645b9c547cSRui Paulo break;
17655b9c547cSRui Paulo }
17665b9c547cSRui Paulo if (allowed_groups[i] != group) {
17675b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not "
17685b9c547cSRui Paulo "enabled in the current configuration",
17695b9c547cSRui Paulo group);
17705b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
17715b9c547cSRui Paulo }
17725b9c547cSRui Paulo }
17735b9c547cSRui Paulo
17745b9c547cSRui Paulo if (sae->state == SAE_COMMITTED && group != sae->group) {
17755b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed");
17765b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
17775b9c547cSRui Paulo }
17785b9c547cSRui Paulo
17795b9c547cSRui Paulo if (group != sae->group && sae_set_group(sae, group) < 0) {
17805b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u",
17815b9c547cSRui Paulo group);
17825b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
17835b9c547cSRui Paulo }
17845b9c547cSRui Paulo
17855b9c547cSRui Paulo if (sae->tmp == NULL) {
17865b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized");
17875b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
17885b9c547cSRui Paulo }
17895b9c547cSRui Paulo
17905b9c547cSRui Paulo if (sae->tmp->dh && !allowed_groups) {
17915b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without "
17925b9c547cSRui Paulo "explicit configuration enabling it", group);
17935b9c547cSRui Paulo return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
17945b9c547cSRui Paulo }
17955b9c547cSRui Paulo
17965b9c547cSRui Paulo return WLAN_STATUS_SUCCESS;
17975b9c547cSRui Paulo }
17985b9c547cSRui Paulo
17995b9c547cSRui Paulo
sae_is_password_id_elem(const u8 * pos,const u8 * end)180085732ac8SCy Schubert static int sae_is_password_id_elem(const u8 *pos, const u8 *end)
180185732ac8SCy Schubert {
180285732ac8SCy Schubert return end - pos >= 3 &&
180385732ac8SCy Schubert pos[0] == WLAN_EID_EXTENSION &&
180485732ac8SCy Schubert pos[1] >= 1 &&
180585732ac8SCy Schubert end - pos - 2 >= pos[1] &&
180685732ac8SCy Schubert pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER;
180785732ac8SCy Schubert }
180885732ac8SCy Schubert
180985732ac8SCy Schubert
sae_is_rejected_groups_elem(const u8 * pos,const u8 * end)1810c1d255d3SCy Schubert static int sae_is_rejected_groups_elem(const u8 *pos, const u8 *end)
1811c1d255d3SCy Schubert {
1812c1d255d3SCy Schubert return end - pos >= 3 &&
1813c1d255d3SCy Schubert pos[0] == WLAN_EID_EXTENSION &&
1814c1d255d3SCy Schubert pos[1] >= 2 &&
1815c1d255d3SCy Schubert end - pos - 2 >= pos[1] &&
1816c1d255d3SCy Schubert pos[2] == WLAN_EID_EXT_REJECTED_GROUPS;
1817c1d255d3SCy Schubert }
1818c1d255d3SCy Schubert
1819c1d255d3SCy Schubert
sae_is_token_container_elem(const u8 * pos,const u8 * end)1820c1d255d3SCy Schubert static int sae_is_token_container_elem(const u8 *pos, const u8 *end)
1821c1d255d3SCy Schubert {
1822c1d255d3SCy Schubert return end - pos >= 3 &&
1823c1d255d3SCy Schubert pos[0] == WLAN_EID_EXTENSION &&
1824c1d255d3SCy Schubert pos[1] >= 1 &&
1825c1d255d3SCy Schubert end - pos - 2 >= pos[1] &&
1826c1d255d3SCy Schubert pos[2] == WLAN_EID_EXT_ANTI_CLOGGING_TOKEN;
1827c1d255d3SCy Schubert }
1828c1d255d3SCy Schubert
1829c1d255d3SCy Schubert
sae_is_akm_suite_selector_elem(const u8 * pos,const u8 * end)1830*a90b9d01SCy Schubert static int sae_is_akm_suite_selector_elem(const u8 *pos, const u8 *end)
1831*a90b9d01SCy Schubert {
1832*a90b9d01SCy Schubert return end - pos >= 2 + 1 + RSN_SELECTOR_LEN &&
1833*a90b9d01SCy Schubert pos[0] == WLAN_EID_EXTENSION &&
1834*a90b9d01SCy Schubert pos[1] >= 1 + RSN_SELECTOR_LEN &&
1835*a90b9d01SCy Schubert end - pos - 2 >= pos[1] &&
1836*a90b9d01SCy Schubert pos[2] == WLAN_EID_EXT_AKM_SUITE_SELECTOR;
1837*a90b9d01SCy Schubert }
1838*a90b9d01SCy Schubert
1839*a90b9d01SCy Schubert
sae_parse_commit_token(struct sae_data * sae,const u8 ** pos,const u8 * end,const u8 ** token,size_t * token_len,int h2e)18405b9c547cSRui Paulo static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
18415b9c547cSRui Paulo const u8 *end, const u8 **token,
1842c1d255d3SCy Schubert size_t *token_len, int h2e)
18435b9c547cSRui Paulo {
184485732ac8SCy Schubert size_t scalar_elem_len, tlen;
184585732ac8SCy Schubert
184685732ac8SCy Schubert if (token)
184785732ac8SCy Schubert *token = NULL;
184885732ac8SCy Schubert if (token_len)
184985732ac8SCy Schubert *token_len = 0;
185085732ac8SCy Schubert
1851c1d255d3SCy Schubert if (h2e)
1852c1d255d3SCy Schubert return; /* No Anti-Clogging Token field outside container IE */
1853c1d255d3SCy Schubert
185485732ac8SCy Schubert scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len;
185585732ac8SCy Schubert if (scalar_elem_len >= (size_t) (end - *pos))
185685732ac8SCy Schubert return; /* No extra data beyond peer scalar and element */
185785732ac8SCy Schubert
185885732ac8SCy Schubert tlen = end - (*pos + scalar_elem_len);
185985732ac8SCy Schubert
186085732ac8SCy Schubert if (tlen < SHA256_MAC_LEN) {
186185732ac8SCy Schubert wpa_printf(MSG_DEBUG,
186285732ac8SCy Schubert "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token",
186385732ac8SCy Schubert (unsigned int) tlen);
186485732ac8SCy Schubert return;
186585732ac8SCy Schubert }
186685732ac8SCy Schubert
18675b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
18685b9c547cSRui Paulo if (token)
18695b9c547cSRui Paulo *token = *pos;
18705b9c547cSRui Paulo if (token_len)
18715b9c547cSRui Paulo *token_len = tlen;
18725b9c547cSRui Paulo *pos += tlen;
18735b9c547cSRui Paulo }
18745b9c547cSRui Paulo
18755b9c547cSRui Paulo
sae_parse_token_container(struct sae_data * sae,const u8 * pos,const u8 * end,const u8 ** token,size_t * token_len)1876c1d255d3SCy Schubert static void sae_parse_token_container(struct sae_data *sae,
1877c1d255d3SCy Schubert const u8 *pos, const u8 *end,
1878c1d255d3SCy Schubert const u8 **token, size_t *token_len)
1879c1d255d3SCy Schubert {
1880c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
1881c1d255d3SCy Schubert pos, end - pos);
1882c1d255d3SCy Schubert if (!sae_is_token_container_elem(pos, end))
1883c1d255d3SCy Schubert return;
1884c1d255d3SCy Schubert *token = pos + 3;
1885c1d255d3SCy Schubert *token_len = pos[1] - 1;
1886c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token (in container)",
1887c1d255d3SCy Schubert *token, *token_len);
1888c1d255d3SCy Schubert }
1889c1d255d3SCy Schubert
1890c1d255d3SCy Schubert
sae_parse_commit_scalar(struct sae_data * sae,const u8 ** pos,const u8 * end)18915b9c547cSRui Paulo static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos,
18925b9c547cSRui Paulo const u8 *end)
18935b9c547cSRui Paulo {
18945b9c547cSRui Paulo struct crypto_bignum *peer_scalar;
18955b9c547cSRui Paulo
1896780fb4a2SCy Schubert if (sae->tmp->prime_len > end - *pos) {
18975b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
18985b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
18995b9c547cSRui Paulo }
19005b9c547cSRui Paulo
19015b9c547cSRui Paulo peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len);
19025b9c547cSRui Paulo if (peer_scalar == NULL)
19035b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
19045b9c547cSRui Paulo
19055b9c547cSRui Paulo /*
19065b9c547cSRui Paulo * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for
19075b9c547cSRui Paulo * the peer and it is in Authenticated state, the new Commit Message
19085b9c547cSRui Paulo * shall be dropped if the peer-scalar is identical to the one used in
19095b9c547cSRui Paulo * the existing protocol instance.
19105b9c547cSRui Paulo */
1911c1d255d3SCy Schubert if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar_accepted &&
1912c1d255d3SCy Schubert crypto_bignum_cmp(sae->peer_commit_scalar_accepted,
1913c1d255d3SCy Schubert peer_scalar) == 0) {
19145b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous "
19155b9c547cSRui Paulo "peer-commit-scalar");
19165b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0);
19175b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
19185b9c547cSRui Paulo }
19195b9c547cSRui Paulo
1920325151a3SRui Paulo /* 1 < scalar < r */
19215b9c547cSRui Paulo if (crypto_bignum_is_zero(peer_scalar) ||
1922325151a3SRui Paulo crypto_bignum_is_one(peer_scalar) ||
19235b9c547cSRui Paulo crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) {
19245b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
19255b9c547cSRui Paulo crypto_bignum_deinit(peer_scalar, 0);
19265b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
19275b9c547cSRui Paulo }
19285b9c547cSRui Paulo
19295b9c547cSRui Paulo
19305b9c547cSRui Paulo crypto_bignum_deinit(sae->peer_commit_scalar, 0);
19315b9c547cSRui Paulo sae->peer_commit_scalar = peer_scalar;
19325b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar",
19335b9c547cSRui Paulo *pos, sae->tmp->prime_len);
19345b9c547cSRui Paulo *pos += sae->tmp->prime_len;
19355b9c547cSRui Paulo
19365b9c547cSRui Paulo return WLAN_STATUS_SUCCESS;
19375b9c547cSRui Paulo }
19385b9c547cSRui Paulo
19395b9c547cSRui Paulo
sae_parse_commit_element_ecc(struct sae_data * sae,const u8 ** pos,const u8 * end)194085732ac8SCy Schubert static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos,
19415b9c547cSRui Paulo const u8 *end)
19425b9c547cSRui Paulo {
19435b9c547cSRui Paulo u8 prime[SAE_MAX_ECC_PRIME_LEN];
19445b9c547cSRui Paulo
194585732ac8SCy Schubert if (2 * sae->tmp->prime_len > end - *pos) {
19465b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
19475b9c547cSRui Paulo "commit-element");
19485b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
19495b9c547cSRui Paulo }
19505b9c547cSRui Paulo
19515b9c547cSRui Paulo if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
19525b9c547cSRui Paulo sae->tmp->prime_len) < 0)
19535b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
19545b9c547cSRui Paulo
19555b9c547cSRui Paulo /* element x and y coordinates < p */
195685732ac8SCy Schubert if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 ||
195785732ac8SCy Schubert os_memcmp(*pos + sae->tmp->prime_len, prime,
19585b9c547cSRui Paulo sae->tmp->prime_len) >= 0) {
19595b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
19605b9c547cSRui Paulo "element");
19615b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
19625b9c547cSRui Paulo }
19635b9c547cSRui Paulo
19645b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)",
196585732ac8SCy Schubert *pos, sae->tmp->prime_len);
19665b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)",
196785732ac8SCy Schubert *pos + sae->tmp->prime_len, sae->tmp->prime_len);
19685b9c547cSRui Paulo
19695b9c547cSRui Paulo crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0);
19705b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc =
197185732ac8SCy Schubert crypto_ec_point_from_bin(sae->tmp->ec, *pos);
1972*a90b9d01SCy Schubert if (!sae->tmp->peer_commit_element_ecc) {
1973*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Peer element is not a valid point");
19745b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
1975*a90b9d01SCy Schubert }
19765b9c547cSRui Paulo
19775b9c547cSRui Paulo if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
19785b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc)) {
19795b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
19805b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
19815b9c547cSRui Paulo }
19825b9c547cSRui Paulo
198385732ac8SCy Schubert *pos += 2 * sae->tmp->prime_len;
198485732ac8SCy Schubert
19855b9c547cSRui Paulo return WLAN_STATUS_SUCCESS;
19865b9c547cSRui Paulo }
19875b9c547cSRui Paulo
19885b9c547cSRui Paulo
sae_parse_commit_element_ffc(struct sae_data * sae,const u8 ** pos,const u8 * end)198985732ac8SCy Schubert static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos,
19905b9c547cSRui Paulo const u8 *end)
19915b9c547cSRui Paulo {
1992325151a3SRui Paulo struct crypto_bignum *res, *one;
1993325151a3SRui Paulo const u8 one_bin[1] = { 0x01 };
19945b9c547cSRui Paulo
199585732ac8SCy Schubert if (sae->tmp->prime_len > end - *pos) {
19965b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
19975b9c547cSRui Paulo "commit-element");
19985b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
19995b9c547cSRui Paulo }
200085732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos,
20015b9c547cSRui Paulo sae->tmp->prime_len);
20025b9c547cSRui Paulo
20035b9c547cSRui Paulo crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0);
20045b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc =
200585732ac8SCy Schubert crypto_bignum_init_set(*pos, sae->tmp->prime_len);
20065b9c547cSRui Paulo if (sae->tmp->peer_commit_element_ffc == NULL)
20075b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
2008325151a3SRui Paulo /* 1 < element < p - 1 */
2009325151a3SRui Paulo res = crypto_bignum_init();
2010325151a3SRui Paulo one = crypto_bignum_init_set(one_bin, sizeof(one_bin));
2011325151a3SRui Paulo if (!res || !one ||
2012325151a3SRui Paulo crypto_bignum_sub(sae->tmp->prime, one, res) ||
2013325151a3SRui Paulo crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
20145b9c547cSRui Paulo crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) ||
2015325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) {
2016325151a3SRui Paulo crypto_bignum_deinit(res, 0);
2017325151a3SRui Paulo crypto_bignum_deinit(one, 0);
20185b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element");
20195b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
20205b9c547cSRui Paulo }
2021325151a3SRui Paulo crypto_bignum_deinit(one, 0);
20225b9c547cSRui Paulo
20235b9c547cSRui Paulo /* scalar-op(r, ELEMENT) = 1 modulo p */
2024325151a3SRui Paulo if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc,
20255b9c547cSRui Paulo sae->tmp->order, sae->tmp->prime, res) < 0 ||
20265b9c547cSRui Paulo !crypto_bignum_is_one(res)) {
20275b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)");
20285b9c547cSRui Paulo crypto_bignum_deinit(res, 0);
20295b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
20305b9c547cSRui Paulo }
20315b9c547cSRui Paulo crypto_bignum_deinit(res, 0);
20325b9c547cSRui Paulo
203385732ac8SCy Schubert *pos += sae->tmp->prime_len;
203485732ac8SCy Schubert
20355b9c547cSRui Paulo return WLAN_STATUS_SUCCESS;
20365b9c547cSRui Paulo }
20375b9c547cSRui Paulo
20385b9c547cSRui Paulo
sae_parse_commit_element(struct sae_data * sae,const u8 ** pos,const u8 * end)203985732ac8SCy Schubert static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos,
20405b9c547cSRui Paulo const u8 *end)
20415b9c547cSRui Paulo {
20425b9c547cSRui Paulo if (sae->tmp->dh)
20435b9c547cSRui Paulo return sae_parse_commit_element_ffc(sae, pos, end);
20445b9c547cSRui Paulo return sae_parse_commit_element_ecc(sae, pos, end);
20455b9c547cSRui Paulo }
20465b9c547cSRui Paulo
20475b9c547cSRui Paulo
sae_parse_password_identifier(struct sae_data * sae,const u8 ** pos,const u8 * end)204885732ac8SCy Schubert static int sae_parse_password_identifier(struct sae_data *sae,
2049c1d255d3SCy Schubert const u8 **pos, const u8 *end)
205085732ac8SCy Schubert {
2051c1d255d3SCy Schubert const u8 *epos;
2052c1d255d3SCy Schubert u8 len;
2053c1d255d3SCy Schubert
205485732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
2055c1d255d3SCy Schubert *pos, end - *pos);
2056c1d255d3SCy Schubert if (!sae_is_password_id_elem(*pos, end)) {
205785732ac8SCy Schubert if (sae->tmp->pw_id) {
205885732ac8SCy Schubert wpa_printf(MSG_DEBUG,
205985732ac8SCy Schubert "SAE: No Password Identifier included, but expected one (%s)",
206085732ac8SCy Schubert sae->tmp->pw_id);
206185732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
206285732ac8SCy Schubert }
206385732ac8SCy Schubert os_free(sae->tmp->pw_id);
206485732ac8SCy Schubert sae->tmp->pw_id = NULL;
206585732ac8SCy Schubert return WLAN_STATUS_SUCCESS; /* No Password Identifier */
206685732ac8SCy Schubert }
206785732ac8SCy Schubert
2068c1d255d3SCy Schubert epos = *pos;
2069c1d255d3SCy Schubert epos++; /* skip IE type */
2070c1d255d3SCy Schubert len = *epos++; /* IE length */
2071c1d255d3SCy Schubert if (len > end - epos || len < 1)
2072c1d255d3SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE;
2073c1d255d3SCy Schubert epos++; /* skip ext ID */
2074c1d255d3SCy Schubert len--;
2075c1d255d3SCy Schubert
207685732ac8SCy Schubert if (sae->tmp->pw_id &&
2077c1d255d3SCy Schubert (len != os_strlen(sae->tmp->pw_id) ||
2078c1d255d3SCy Schubert os_memcmp(sae->tmp->pw_id, epos, len) != 0)) {
207985732ac8SCy Schubert wpa_printf(MSG_DEBUG,
208085732ac8SCy Schubert "SAE: The included Password Identifier does not match the expected one (%s)",
208185732ac8SCy Schubert sae->tmp->pw_id);
208285732ac8SCy Schubert return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
208385732ac8SCy Schubert }
208485732ac8SCy Schubert
208585732ac8SCy Schubert os_free(sae->tmp->pw_id);
2086c1d255d3SCy Schubert sae->tmp->pw_id = os_malloc(len + 1);
208785732ac8SCy Schubert if (!sae->tmp->pw_id)
208885732ac8SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE;
2089c1d255d3SCy Schubert os_memcpy(sae->tmp->pw_id, epos, len);
2090c1d255d3SCy Schubert sae->tmp->pw_id[len] = '\0';
209185732ac8SCy Schubert wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier",
2092c1d255d3SCy Schubert sae->tmp->pw_id, len);
2093c1d255d3SCy Schubert *pos = epos + len;
2094c1d255d3SCy Schubert return WLAN_STATUS_SUCCESS;
2095c1d255d3SCy Schubert }
2096c1d255d3SCy Schubert
2097c1d255d3SCy Schubert
sae_parse_rejected_groups(struct sae_data * sae,const u8 ** pos,const u8 * end)2098c1d255d3SCy Schubert static int sae_parse_rejected_groups(struct sae_data *sae,
2099c1d255d3SCy Schubert const u8 **pos, const u8 *end)
2100c1d255d3SCy Schubert {
2101c1d255d3SCy Schubert const u8 *epos;
2102c1d255d3SCy Schubert u8 len;
2103c1d255d3SCy Schubert
2104c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
2105c1d255d3SCy Schubert *pos, end - *pos);
2106*a90b9d01SCy Schubert if (!sae_is_rejected_groups_elem(*pos, end)) {
2107*a90b9d01SCy Schubert wpabuf_free(sae->tmp->peer_rejected_groups);
2108*a90b9d01SCy Schubert sae->tmp->peer_rejected_groups = NULL;
2109c1d255d3SCy Schubert return WLAN_STATUS_SUCCESS;
2110*a90b9d01SCy Schubert }
2111c1d255d3SCy Schubert
2112c1d255d3SCy Schubert epos = *pos;
2113c1d255d3SCy Schubert epos++; /* skip IE type */
2114c1d255d3SCy Schubert len = *epos++; /* IE length */
2115c1d255d3SCy Schubert if (len > end - epos || len < 1)
2116c1d255d3SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE;
2117c1d255d3SCy Schubert epos++; /* skip ext ID */
2118c1d255d3SCy Schubert len--;
2119*a90b9d01SCy Schubert if (len & 1) {
2120*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
2121*a90b9d01SCy Schubert "SAE: Invalid length of the Rejected Groups element payload: %u",
2122*a90b9d01SCy Schubert len);
2123*a90b9d01SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE;
2124*a90b9d01SCy Schubert }
2125c1d255d3SCy Schubert
2126c1d255d3SCy Schubert wpabuf_free(sae->tmp->peer_rejected_groups);
2127c1d255d3SCy Schubert sae->tmp->peer_rejected_groups = wpabuf_alloc(len);
2128c1d255d3SCy Schubert if (!sae->tmp->peer_rejected_groups)
2129c1d255d3SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE;
2130c1d255d3SCy Schubert wpabuf_put_data(sae->tmp->peer_rejected_groups, epos, len);
2131c1d255d3SCy Schubert wpa_hexdump_buf(MSG_DEBUG, "SAE: Received Rejected Groups list",
2132c1d255d3SCy Schubert sae->tmp->peer_rejected_groups);
2133c1d255d3SCy Schubert *pos = epos + len;
213485732ac8SCy Schubert return WLAN_STATUS_SUCCESS;
213585732ac8SCy Schubert }
213685732ac8SCy Schubert
213785732ac8SCy Schubert
sae_parse_akm_suite_selector(struct sae_data * sae,const u8 ** pos,const u8 * end)2138*a90b9d01SCy Schubert static int sae_parse_akm_suite_selector(struct sae_data *sae,
2139*a90b9d01SCy Schubert const u8 **pos, const u8 *end)
2140*a90b9d01SCy Schubert {
2141*a90b9d01SCy Schubert const u8 *epos;
2142*a90b9d01SCy Schubert u8 len;
2143*a90b9d01SCy Schubert
2144*a90b9d01SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
2145*a90b9d01SCy Schubert *pos, end - *pos);
2146*a90b9d01SCy Schubert if (!sae_is_akm_suite_selector_elem(*pos, end))
2147*a90b9d01SCy Schubert return WLAN_STATUS_SUCCESS;
2148*a90b9d01SCy Schubert
2149*a90b9d01SCy Schubert epos = *pos;
2150*a90b9d01SCy Schubert epos++; /* skip IE type */
2151*a90b9d01SCy Schubert len = *epos++; /* IE length */
2152*a90b9d01SCy Schubert if (len > end - epos || len < 1)
2153*a90b9d01SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE;
2154*a90b9d01SCy Schubert epos++; /* skip ext ID */
2155*a90b9d01SCy Schubert len--;
2156*a90b9d01SCy Schubert
2157*a90b9d01SCy Schubert if (len < RSN_SELECTOR_LEN)
2158*a90b9d01SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE;
2159*a90b9d01SCy Schubert sae->peer_akm_suite_selector = RSN_SELECTOR_GET(epos);
2160*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Received AKM Suite Selector: %08x",
2161*a90b9d01SCy Schubert sae->peer_akm_suite_selector);
2162*a90b9d01SCy Schubert *pos = epos + len;
2163*a90b9d01SCy Schubert return WLAN_STATUS_SUCCESS;
2164*a90b9d01SCy Schubert }
2165*a90b9d01SCy Schubert
2166*a90b9d01SCy Schubert
sae_parse_commit(struct sae_data * sae,const u8 * data,size_t len,const u8 ** token,size_t * token_len,int * allowed_groups,int h2e,int * ie_offset)21675b9c547cSRui Paulo u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
2168c1d255d3SCy Schubert const u8 **token, size_t *token_len, int *allowed_groups,
2169*a90b9d01SCy Schubert int h2e, int *ie_offset)
21705b9c547cSRui Paulo {
21715b9c547cSRui Paulo const u8 *pos = data, *end = data + len;
21725b9c547cSRui Paulo u16 res;
21735b9c547cSRui Paulo
21745b9c547cSRui Paulo /* Check Finite Cyclic Group */
2175780fb4a2SCy Schubert if (end - pos < 2)
21765b9c547cSRui Paulo return WLAN_STATUS_UNSPECIFIED_FAILURE;
21775b9c547cSRui Paulo res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
21785b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS)
21795b9c547cSRui Paulo return res;
21805b9c547cSRui Paulo pos += 2;
21815b9c547cSRui Paulo
21825b9c547cSRui Paulo /* Optional Anti-Clogging Token */
2183c1d255d3SCy Schubert sae_parse_commit_token(sae, &pos, end, token, token_len, h2e);
21845b9c547cSRui Paulo
21855b9c547cSRui Paulo /* commit-scalar */
21865b9c547cSRui Paulo res = sae_parse_commit_scalar(sae, &pos, end);
21875b9c547cSRui Paulo if (res != WLAN_STATUS_SUCCESS)
21885b9c547cSRui Paulo return res;
21895b9c547cSRui Paulo
21905b9c547cSRui Paulo /* commit-element */
219185732ac8SCy Schubert res = sae_parse_commit_element(sae, &pos, end);
219285732ac8SCy Schubert if (res != WLAN_STATUS_SUCCESS)
219385732ac8SCy Schubert return res;
219485732ac8SCy Schubert
2195*a90b9d01SCy Schubert if (ie_offset)
2196*a90b9d01SCy Schubert *ie_offset = pos - data;
2197*a90b9d01SCy Schubert
219885732ac8SCy Schubert /* Optional Password Identifier element */
2199c1d255d3SCy Schubert res = sae_parse_password_identifier(sae, &pos, end);
2200325151a3SRui Paulo if (res != WLAN_STATUS_SUCCESS)
2201325151a3SRui Paulo return res;
2202325151a3SRui Paulo
2203c1d255d3SCy Schubert /* Conditional Rejected Groups element */
2204c1d255d3SCy Schubert if (h2e) {
2205c1d255d3SCy Schubert res = sae_parse_rejected_groups(sae, &pos, end);
2206c1d255d3SCy Schubert if (res != WLAN_STATUS_SUCCESS)
2207c1d255d3SCy Schubert return res;
2208*a90b9d01SCy Schubert } else {
2209*a90b9d01SCy Schubert wpabuf_free(sae->tmp->peer_rejected_groups);
2210*a90b9d01SCy Schubert sae->tmp->peer_rejected_groups = NULL;
2211c1d255d3SCy Schubert }
2212c1d255d3SCy Schubert
2213c1d255d3SCy Schubert /* Optional Anti-Clogging Token Container element */
2214c1d255d3SCy Schubert if (h2e)
2215c1d255d3SCy Schubert sae_parse_token_container(sae, pos, end, token, token_len);
2216c1d255d3SCy Schubert
2217*a90b9d01SCy Schubert /* Conditional AKM Suite Selector element */
2218*a90b9d01SCy Schubert if (h2e) {
2219*a90b9d01SCy Schubert res = sae_parse_akm_suite_selector(sae, &pos, end);
2220*a90b9d01SCy Schubert if (res != WLAN_STATUS_SUCCESS)
2221*a90b9d01SCy Schubert return res;
2222*a90b9d01SCy Schubert }
2223*a90b9d01SCy Schubert
2224*a90b9d01SCy Schubert if (sae->own_akm_suite_selector &&
2225*a90b9d01SCy Schubert sae->own_akm_suite_selector != sae->peer_akm_suite_selector) {
2226*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
2227*a90b9d01SCy Schubert "SAE: AKM suite selector mismatch: own=%08x peer=%08x",
2228*a90b9d01SCy Schubert sae->own_akm_suite_selector,
2229*a90b9d01SCy Schubert sae->peer_akm_suite_selector);
2230*a90b9d01SCy Schubert return WLAN_STATUS_UNSPECIFIED_FAILURE;
2231*a90b9d01SCy Schubert }
2232*a90b9d01SCy Schubert
2233*a90b9d01SCy Schubert if (!sae->akmp) {
2234*a90b9d01SCy Schubert if (sae->peer_akm_suite_selector ==
2235*a90b9d01SCy Schubert RSN_AUTH_KEY_MGMT_SAE_EXT_KEY)
2236*a90b9d01SCy Schubert sae->akmp = WPA_KEY_MGMT_SAE_EXT_KEY;
2237*a90b9d01SCy Schubert else if (sae->peer_akm_suite_selector ==
2238*a90b9d01SCy Schubert RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY)
2239*a90b9d01SCy Schubert sae->akmp = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
2240*a90b9d01SCy Schubert }
2241*a90b9d01SCy Schubert
2242325151a3SRui Paulo /*
2243325151a3SRui Paulo * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
2244325151a3SRui Paulo * the values we sent which would be evidence of a reflection attack.
2245325151a3SRui Paulo */
2246325151a3SRui Paulo if (!sae->tmp->own_commit_scalar ||
2247325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_scalar,
2248325151a3SRui Paulo sae->peer_commit_scalar) != 0 ||
2249325151a3SRui Paulo (sae->tmp->dh &&
2250325151a3SRui Paulo (!sae->tmp->own_commit_element_ffc ||
2251325151a3SRui Paulo crypto_bignum_cmp(sae->tmp->own_commit_element_ffc,
2252325151a3SRui Paulo sae->tmp->peer_commit_element_ffc) != 0)) ||
2253325151a3SRui Paulo (sae->tmp->ec &&
2254325151a3SRui Paulo (!sae->tmp->own_commit_element_ecc ||
2255325151a3SRui Paulo crypto_ec_point_cmp(sae->tmp->ec,
2256325151a3SRui Paulo sae->tmp->own_commit_element_ecc,
2257325151a3SRui Paulo sae->tmp->peer_commit_element_ecc) != 0)))
2258325151a3SRui Paulo return WLAN_STATUS_SUCCESS; /* scalars/elements are different */
2259325151a3SRui Paulo
2260325151a3SRui Paulo /*
2261325151a3SRui Paulo * This is a reflection attack - return special value to trigger caller
2262325151a3SRui Paulo * to silently discard the frame instead of replying with a specific
2263325151a3SRui Paulo * status code.
2264325151a3SRui Paulo */
2265325151a3SRui Paulo return SAE_SILENTLY_DISCARD;
22665b9c547cSRui Paulo }
22675b9c547cSRui Paulo
22685b9c547cSRui Paulo
sae_cn_confirm(struct sae_data * sae,const u8 * sc,const struct crypto_bignum * scalar1,const u8 * element1,size_t element1_len,const struct crypto_bignum * scalar2,const u8 * element2,size_t element2_len,u8 * confirm)2269c1d255d3SCy Schubert static int sae_cn_confirm(struct sae_data *sae, const u8 *sc,
22705b9c547cSRui Paulo const struct crypto_bignum *scalar1,
22715b9c547cSRui Paulo const u8 *element1, size_t element1_len,
22725b9c547cSRui Paulo const struct crypto_bignum *scalar2,
22735b9c547cSRui Paulo const u8 *element2, size_t element2_len,
22745b9c547cSRui Paulo u8 *confirm)
22755b9c547cSRui Paulo {
22765b9c547cSRui Paulo const u8 *addr[5];
22775b9c547cSRui Paulo size_t len[5];
22785b9c547cSRui Paulo u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN];
22795b9c547cSRui Paulo
22805b9c547cSRui Paulo /* Confirm
22815b9c547cSRui Paulo * CN(key, X, Y, Z, ...) =
22825b9c547cSRui Paulo * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...)
22835b9c547cSRui Paulo * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT,
22845b9c547cSRui Paulo * peer-commit-scalar, PEER-COMMIT-ELEMENT)
22855b9c547cSRui Paulo * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar,
22865b9c547cSRui Paulo * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT)
22875b9c547cSRui Paulo */
2288c1d255d3SCy Schubert if (crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1),
2289c1d255d3SCy Schubert sae->tmp->prime_len) < 0 ||
2290c1d255d3SCy Schubert crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2),
2291c1d255d3SCy Schubert sae->tmp->prime_len) < 0)
2292c1d255d3SCy Schubert return -1;
22935b9c547cSRui Paulo addr[0] = sc;
22945b9c547cSRui Paulo len[0] = 2;
22955b9c547cSRui Paulo addr[1] = scalar_b1;
22965b9c547cSRui Paulo len[1] = sae->tmp->prime_len;
22975b9c547cSRui Paulo addr[2] = element1;
22985b9c547cSRui Paulo len[2] = element1_len;
22995b9c547cSRui Paulo addr[3] = scalar_b2;
23005b9c547cSRui Paulo len[3] = sae->tmp->prime_len;
23015b9c547cSRui Paulo addr[4] = element2;
23025b9c547cSRui Paulo len[4] = element2_len;
2303c1d255d3SCy Schubert return hkdf_extract(sae->tmp->kck_len, sae->tmp->kck, sae->tmp->kck_len,
2304c1d255d3SCy Schubert 5, addr, len, confirm);
23055b9c547cSRui Paulo }
23065b9c547cSRui Paulo
23075b9c547cSRui Paulo
sae_cn_confirm_ecc(struct sae_data * sae,const u8 * sc,const struct crypto_bignum * scalar1,const struct crypto_ec_point * element1,const struct crypto_bignum * scalar2,const struct crypto_ec_point * element2,u8 * confirm)2308c1d255d3SCy Schubert static int sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc,
23095b9c547cSRui Paulo const struct crypto_bignum *scalar1,
23105b9c547cSRui Paulo const struct crypto_ec_point *element1,
23115b9c547cSRui Paulo const struct crypto_bignum *scalar2,
23125b9c547cSRui Paulo const struct crypto_ec_point *element2,
23135b9c547cSRui Paulo u8 *confirm)
23145b9c547cSRui Paulo {
23155b9c547cSRui Paulo u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN];
23165b9c547cSRui Paulo u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN];
23175b9c547cSRui Paulo
2318c1d255d3SCy Schubert if (crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1,
2319c1d255d3SCy Schubert element_b1 + sae->tmp->prime_len) < 0 ||
23205b9c547cSRui Paulo crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2,
2321c1d255d3SCy Schubert element_b2 + sae->tmp->prime_len) < 0 ||
2322c1d255d3SCy Schubert sae_cn_confirm(sae, sc, scalar1, element_b1,
2323c1d255d3SCy Schubert 2 * sae->tmp->prime_len,
2324c1d255d3SCy Schubert scalar2, element_b2, 2 * sae->tmp->prime_len,
2325c1d255d3SCy Schubert confirm) < 0)
2326c1d255d3SCy Schubert return -1;
2327c1d255d3SCy Schubert return 0;
23285b9c547cSRui Paulo }
23295b9c547cSRui Paulo
23305b9c547cSRui Paulo
sae_cn_confirm_ffc(struct sae_data * sae,const u8 * sc,const struct crypto_bignum * scalar1,const struct crypto_bignum * element1,const struct crypto_bignum * scalar2,const struct crypto_bignum * element2,u8 * confirm)2331c1d255d3SCy Schubert static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc,
23325b9c547cSRui Paulo const struct crypto_bignum *scalar1,
23335b9c547cSRui Paulo const struct crypto_bignum *element1,
23345b9c547cSRui Paulo const struct crypto_bignum *scalar2,
23355b9c547cSRui Paulo const struct crypto_bignum *element2,
23365b9c547cSRui Paulo u8 *confirm)
23375b9c547cSRui Paulo {
23385b9c547cSRui Paulo u8 element_b1[SAE_MAX_PRIME_LEN];
23395b9c547cSRui Paulo u8 element_b2[SAE_MAX_PRIME_LEN];
23405b9c547cSRui Paulo
2341c1d255d3SCy Schubert if (crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1),
2342c1d255d3SCy Schubert sae->tmp->prime_len) < 0 ||
23435b9c547cSRui Paulo crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2),
2344c1d255d3SCy Schubert sae->tmp->prime_len) < 0 ||
23455b9c547cSRui Paulo sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len,
2346c1d255d3SCy Schubert scalar2, element_b2, sae->tmp->prime_len,
2347c1d255d3SCy Schubert confirm) < 0)
2348c1d255d3SCy Schubert return -1;
2349c1d255d3SCy Schubert return 0;
23505b9c547cSRui Paulo }
23515b9c547cSRui Paulo
23525b9c547cSRui Paulo
sae_write_confirm(struct sae_data * sae,struct wpabuf * buf)2353c1d255d3SCy Schubert int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
23545b9c547cSRui Paulo {
23555b9c547cSRui Paulo const u8 *sc;
2356c1d255d3SCy Schubert size_t hash_len;
2357c1d255d3SCy Schubert int res;
23585b9c547cSRui Paulo
23595b9c547cSRui Paulo if (sae->tmp == NULL)
2360c1d255d3SCy Schubert return -1;
2361c1d255d3SCy Schubert
2362c1d255d3SCy Schubert hash_len = sae->tmp->kck_len;
23635b9c547cSRui Paulo
23645b9c547cSRui Paulo /* Send-Confirm */
236585732ac8SCy Schubert if (sae->send_confirm < 0xffff)
23665b9c547cSRui Paulo sae->send_confirm++;
2367c1d255d3SCy Schubert sc = wpabuf_put(buf, 0);
2368c1d255d3SCy Schubert wpabuf_put_le16(buf, sae->send_confirm);
23695b9c547cSRui Paulo
23705b9c547cSRui Paulo if (sae->tmp->ec)
2371c1d255d3SCy Schubert res = sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar,
23725b9c547cSRui Paulo sae->tmp->own_commit_element_ecc,
23735b9c547cSRui Paulo sae->peer_commit_scalar,
23745b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc,
2375c1d255d3SCy Schubert wpabuf_put(buf, hash_len));
23765b9c547cSRui Paulo else
2377c1d255d3SCy Schubert res = sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar,
23785b9c547cSRui Paulo sae->tmp->own_commit_element_ffc,
23795b9c547cSRui Paulo sae->peer_commit_scalar,
23805b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc,
2381c1d255d3SCy Schubert wpabuf_put(buf, hash_len));
2382c1d255d3SCy Schubert if (res)
2383c1d255d3SCy Schubert return res;
2384c1d255d3SCy Schubert
2385c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK
2386c1d255d3SCy Schubert if (sae_write_confirm_pk(sae, buf) < 0)
2387c1d255d3SCy Schubert return -1;
2388c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */
2389c1d255d3SCy Schubert
2390c1d255d3SCy Schubert return 0;
23915b9c547cSRui Paulo }
23925b9c547cSRui Paulo
23935b9c547cSRui Paulo
sae_check_confirm(struct sae_data * sae,const u8 * data,size_t len,int * ie_offset)2394*a90b9d01SCy Schubert int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len,
2395*a90b9d01SCy Schubert int *ie_offset)
23965b9c547cSRui Paulo {
2397c1d255d3SCy Schubert u8 verifier[SAE_MAX_HASH_LEN];
2398c1d255d3SCy Schubert size_t hash_len;
23995b9c547cSRui Paulo
2400c1d255d3SCy Schubert if (!sae->tmp)
2401c1d255d3SCy Schubert return -1;
2402c1d255d3SCy Schubert
2403c1d255d3SCy Schubert hash_len = sae->tmp->kck_len;
2404c1d255d3SCy Schubert if (len < 2 + hash_len) {
24055b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Too short confirm message");
24065b9c547cSRui Paulo return -1;
24075b9c547cSRui Paulo }
24085b9c547cSRui Paulo
24095b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
24105b9c547cSRui Paulo
2411c1d255d3SCy Schubert if (!sae->peer_commit_scalar || !sae->tmp->own_commit_scalar) {
24125b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
24135b9c547cSRui Paulo return -1;
24145b9c547cSRui Paulo }
24155b9c547cSRui Paulo
24164bc52338SCy Schubert if (sae->tmp->ec) {
24174bc52338SCy Schubert if (!sae->tmp->peer_commit_element_ecc ||
2418c1d255d3SCy Schubert !sae->tmp->own_commit_element_ecc ||
24195b9c547cSRui Paulo sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
24205b9c547cSRui Paulo sae->tmp->peer_commit_element_ecc,
24215b9c547cSRui Paulo sae->tmp->own_commit_scalar,
24225b9c547cSRui Paulo sae->tmp->own_commit_element_ecc,
2423c1d255d3SCy Schubert verifier) < 0)
2424c1d255d3SCy Schubert return -1;
24254bc52338SCy Schubert } else {
24264bc52338SCy Schubert if (!sae->tmp->peer_commit_element_ffc ||
2427c1d255d3SCy Schubert !sae->tmp->own_commit_element_ffc ||
24285b9c547cSRui Paulo sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
24295b9c547cSRui Paulo sae->tmp->peer_commit_element_ffc,
24305b9c547cSRui Paulo sae->tmp->own_commit_scalar,
24315b9c547cSRui Paulo sae->tmp->own_commit_element_ffc,
2432c1d255d3SCy Schubert verifier) < 0)
24335b9c547cSRui Paulo return -1;
24345b9c547cSRui Paulo }
24355b9c547cSRui Paulo
2436c1d255d3SCy Schubert if (os_memcmp_const(verifier, data + 2, hash_len) != 0) {
2437c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
2438c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
2439c1d255d3SCy Schubert data + 2, hash_len);
2440c1d255d3SCy Schubert wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier",
2441c1d255d3SCy Schubert verifier, hash_len);
2442c1d255d3SCy Schubert return -1;
2443c1d255d3SCy Schubert }
2444c1d255d3SCy Schubert
2445c1d255d3SCy Schubert #ifdef CONFIG_SAE_PK
2446c1d255d3SCy Schubert if (sae_check_confirm_pk(sae, data + 2 + hash_len,
2447c1d255d3SCy Schubert len - 2 - hash_len) < 0)
2448c1d255d3SCy Schubert return -1;
2449c1d255d3SCy Schubert #endif /* CONFIG_SAE_PK */
2450c1d255d3SCy Schubert
2451*a90b9d01SCy Schubert /* 2 bytes are for send-confirm, then the hash, followed by IEs */
2452*a90b9d01SCy Schubert if (ie_offset)
2453*a90b9d01SCy Schubert *ie_offset = 2 + hash_len;
2454*a90b9d01SCy Schubert
24555b9c547cSRui Paulo return 0;
24565b9c547cSRui Paulo }
245785732ac8SCy Schubert
245885732ac8SCy Schubert
sae_state_txt(enum sae_state state)245985732ac8SCy Schubert const char * sae_state_txt(enum sae_state state)
246085732ac8SCy Schubert {
246185732ac8SCy Schubert switch (state) {
246285732ac8SCy Schubert case SAE_NOTHING:
246385732ac8SCy Schubert return "Nothing";
246485732ac8SCy Schubert case SAE_COMMITTED:
246585732ac8SCy Schubert return "Committed";
246685732ac8SCy Schubert case SAE_CONFIRMED:
246785732ac8SCy Schubert return "Confirmed";
246885732ac8SCy Schubert case SAE_ACCEPTED:
246985732ac8SCy Schubert return "Accepted";
247085732ac8SCy Schubert }
247185732ac8SCy Schubert return "?";
247285732ac8SCy Schubert }
2473