1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * Simultaneous authentication of equals 3e5b75505Sopenharmony_ci * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi> 4e5b75505Sopenharmony_ci * 5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 6e5b75505Sopenharmony_ci * See README for more details. 7e5b75505Sopenharmony_ci */ 8e5b75505Sopenharmony_ci 9e5b75505Sopenharmony_ci#include "includes.h" 10e5b75505Sopenharmony_ci 11e5b75505Sopenharmony_ci#include "common.h" 12e5b75505Sopenharmony_ci#include "utils/const_time.h" 13e5b75505Sopenharmony_ci#include "crypto/crypto.h" 14e5b75505Sopenharmony_ci#include "crypto/sha256.h" 15e5b75505Sopenharmony_ci#include "crypto/random.h" 16e5b75505Sopenharmony_ci#include "crypto/dh_groups.h" 17e5b75505Sopenharmony_ci#include "ieee802_11_defs.h" 18e5b75505Sopenharmony_ci#include "dragonfly.h" 19e5b75505Sopenharmony_ci#include "sae.h" 20e5b75505Sopenharmony_ci 21e5b75505Sopenharmony_ci 22e5b75505Sopenharmony_ciint sae_set_group(struct sae_data *sae, int group) 23e5b75505Sopenharmony_ci{ 24e5b75505Sopenharmony_ci struct sae_temporary_data *tmp; 25e5b75505Sopenharmony_ci 26e5b75505Sopenharmony_ci#ifdef CONFIG_TESTING_OPTIONS 27e5b75505Sopenharmony_ci /* Allow all groups for testing purposes in non-production builds. */ 28e5b75505Sopenharmony_ci#else /* CONFIG_TESTING_OPTIONS */ 29e5b75505Sopenharmony_ci if (!dragonfly_suitable_group(group, 0)) { 30e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group); 31e5b75505Sopenharmony_ci return -1; 32e5b75505Sopenharmony_ci } 33e5b75505Sopenharmony_ci#endif /* CONFIG_TESTING_OPTIONS */ 34e5b75505Sopenharmony_ci 35e5b75505Sopenharmony_ci sae_clear_data(sae); 36e5b75505Sopenharmony_ci tmp = sae->tmp = os_zalloc(sizeof(*tmp)); 37e5b75505Sopenharmony_ci if (tmp == NULL) 38e5b75505Sopenharmony_ci return -1; 39e5b75505Sopenharmony_ci 40e5b75505Sopenharmony_ci /* First, check if this is an ECC group */ 41e5b75505Sopenharmony_ci tmp->ec = crypto_ec_init(group); 42e5b75505Sopenharmony_ci if (tmp->ec) { 43e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", 44e5b75505Sopenharmony_ci group); 45e5b75505Sopenharmony_ci sae->group = group; 46e5b75505Sopenharmony_ci tmp->prime_len = crypto_ec_prime_len(tmp->ec); 47e5b75505Sopenharmony_ci tmp->prime = crypto_ec_get_prime(tmp->ec); 48e5b75505Sopenharmony_ci tmp->order_len = crypto_ec_order_len(tmp->ec); 49e5b75505Sopenharmony_ci tmp->order = crypto_ec_get_order(tmp->ec); 50e5b75505Sopenharmony_ci return 0; 51e5b75505Sopenharmony_ci } 52e5b75505Sopenharmony_ci 53e5b75505Sopenharmony_ci /* Not an ECC group, check FFC */ 54e5b75505Sopenharmony_ci tmp->dh = dh_groups_get(group); 55e5b75505Sopenharmony_ci if (tmp->dh) { 56e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", 57e5b75505Sopenharmony_ci group); 58e5b75505Sopenharmony_ci sae->group = group; 59e5b75505Sopenharmony_ci tmp->prime_len = tmp->dh->prime_len; 60e5b75505Sopenharmony_ci if (tmp->prime_len > SAE_MAX_PRIME_LEN) { 61e5b75505Sopenharmony_ci sae_clear_data(sae); 62e5b75505Sopenharmony_ci return -1; 63e5b75505Sopenharmony_ci } 64e5b75505Sopenharmony_ci 65e5b75505Sopenharmony_ci tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, 66e5b75505Sopenharmony_ci tmp->prime_len); 67e5b75505Sopenharmony_ci if (tmp->prime_buf == NULL) { 68e5b75505Sopenharmony_ci sae_clear_data(sae); 69e5b75505Sopenharmony_ci return -1; 70e5b75505Sopenharmony_ci } 71e5b75505Sopenharmony_ci tmp->prime = tmp->prime_buf; 72e5b75505Sopenharmony_ci 73e5b75505Sopenharmony_ci tmp->order_len = tmp->dh->order_len; 74e5b75505Sopenharmony_ci tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, 75e5b75505Sopenharmony_ci tmp->dh->order_len); 76e5b75505Sopenharmony_ci if (tmp->order_buf == NULL) { 77e5b75505Sopenharmony_ci sae_clear_data(sae); 78e5b75505Sopenharmony_ci return -1; 79e5b75505Sopenharmony_ci } 80e5b75505Sopenharmony_ci tmp->order = tmp->order_buf; 81e5b75505Sopenharmony_ci 82e5b75505Sopenharmony_ci return 0; 83e5b75505Sopenharmony_ci } 84e5b75505Sopenharmony_ci 85e5b75505Sopenharmony_ci /* Unsupported group */ 86e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 87e5b75505Sopenharmony_ci "SAE: Group %d not supported by the crypto library", group); 88e5b75505Sopenharmony_ci return -1; 89e5b75505Sopenharmony_ci} 90e5b75505Sopenharmony_ci 91e5b75505Sopenharmony_ci 92e5b75505Sopenharmony_civoid sae_clear_temp_data(struct sae_data *sae) 93e5b75505Sopenharmony_ci{ 94e5b75505Sopenharmony_ci struct sae_temporary_data *tmp; 95e5b75505Sopenharmony_ci if (sae == NULL || sae->tmp == NULL) 96e5b75505Sopenharmony_ci return; 97e5b75505Sopenharmony_ci tmp = sae->tmp; 98e5b75505Sopenharmony_ci crypto_ec_deinit(tmp->ec); 99e5b75505Sopenharmony_ci crypto_bignum_deinit(tmp->prime_buf, 0); 100e5b75505Sopenharmony_ci crypto_bignum_deinit(tmp->order_buf, 0); 101e5b75505Sopenharmony_ci crypto_bignum_deinit(tmp->sae_rand, 1); 102e5b75505Sopenharmony_ci crypto_bignum_deinit(tmp->pwe_ffc, 1); 103e5b75505Sopenharmony_ci crypto_bignum_deinit(tmp->own_commit_scalar, 0); 104e5b75505Sopenharmony_ci crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); 105e5b75505Sopenharmony_ci crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); 106e5b75505Sopenharmony_ci crypto_ec_point_deinit(tmp->pwe_ecc, 1); 107e5b75505Sopenharmony_ci crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); 108e5b75505Sopenharmony_ci crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); 109e5b75505Sopenharmony_ci wpabuf_free(tmp->anti_clogging_token); 110e5b75505Sopenharmony_ci os_free(tmp->pw_id); 111e5b75505Sopenharmony_ci bin_clear_free(tmp, sizeof(*tmp)); 112e5b75505Sopenharmony_ci sae->tmp = NULL; 113e5b75505Sopenharmony_ci} 114e5b75505Sopenharmony_ci 115e5b75505Sopenharmony_ci 116e5b75505Sopenharmony_civoid sae_clear_data(struct sae_data *sae) 117e5b75505Sopenharmony_ci{ 118e5b75505Sopenharmony_ci if (sae == NULL) 119e5b75505Sopenharmony_ci return; 120e5b75505Sopenharmony_ci sae_clear_temp_data(sae); 121e5b75505Sopenharmony_ci crypto_bignum_deinit(sae->peer_commit_scalar, 0); 122e5b75505Sopenharmony_ci os_memset(sae, 0, sizeof(*sae)); 123e5b75505Sopenharmony_ci} 124e5b75505Sopenharmony_ci 125e5b75505Sopenharmony_ci 126e5b75505Sopenharmony_cistatic void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) 127e5b75505Sopenharmony_ci{ 128e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR 129e5b75505Sopenharmony_ci " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); 130e5b75505Sopenharmony_ci if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { 131e5b75505Sopenharmony_ci os_memcpy(key, addr1, ETH_ALEN); 132e5b75505Sopenharmony_ci os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); 133e5b75505Sopenharmony_ci } else { 134e5b75505Sopenharmony_ci os_memcpy(key, addr2, ETH_ALEN); 135e5b75505Sopenharmony_ci os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); 136e5b75505Sopenharmony_ci } 137e5b75505Sopenharmony_ci} 138e5b75505Sopenharmony_ci 139e5b75505Sopenharmony_ci 140e5b75505Sopenharmony_cistatic int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, 141e5b75505Sopenharmony_ci const u8 *prime, const u8 *qr, const u8 *qnr, 142e5b75505Sopenharmony_ci u8 *pwd_value) 143e5b75505Sopenharmony_ci{ 144e5b75505Sopenharmony_ci struct crypto_bignum *y_sqr, *x_cand; 145e5b75505Sopenharmony_ci int res; 146e5b75505Sopenharmony_ci size_t bits; 147e5b75505Sopenharmony_ci int cmp_prime; 148e5b75505Sopenharmony_ci unsigned int in_range; 149e5b75505Sopenharmony_ci 150e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 151e5b75505Sopenharmony_ci 152e5b75505Sopenharmony_ci /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 153e5b75505Sopenharmony_ci bits = crypto_ec_prime_len_bits(sae->tmp->ec); 154e5b75505Sopenharmony_ci if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 155e5b75505Sopenharmony_ci prime, sae->tmp->prime_len, pwd_value, bits) < 0) 156e5b75505Sopenharmony_ci return -1; 157e5b75505Sopenharmony_ci if (bits % 8) 158e5b75505Sopenharmony_ci buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8); 159e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", 160e5b75505Sopenharmony_ci pwd_value, sae->tmp->prime_len); 161e5b75505Sopenharmony_ci 162e5b75505Sopenharmony_ci cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len); 163e5b75505Sopenharmony_ci /* Create a const_time mask for selection based on prf result 164e5b75505Sopenharmony_ci * being smaller than prime. */ 165e5b75505Sopenharmony_ci in_range = const_time_fill_msb((unsigned int) cmp_prime); 166e5b75505Sopenharmony_ci /* The algorithm description would skip the next steps if 167e5b75505Sopenharmony_ci * cmp_prime >= 0 (reutnr 0 here), but go through them regardless to 168e5b75505Sopenharmony_ci * minimize externally observable differences in behavior. */ 169e5b75505Sopenharmony_ci 170e5b75505Sopenharmony_ci x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 171e5b75505Sopenharmony_ci if (!x_cand) 172e5b75505Sopenharmony_ci return -1; 173e5b75505Sopenharmony_ci y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); 174e5b75505Sopenharmony_ci crypto_bignum_deinit(x_cand, 1); 175e5b75505Sopenharmony_ci if (!y_sqr) 176e5b75505Sopenharmony_ci return -1; 177e5b75505Sopenharmony_ci 178e5b75505Sopenharmony_ci res = dragonfly_is_quadratic_residue_blind(sae->tmp->ec, qr, qnr, 179e5b75505Sopenharmony_ci y_sqr); 180e5b75505Sopenharmony_ci crypto_bignum_deinit(y_sqr, 1); 181e5b75505Sopenharmony_ci if (res < 0) 182e5b75505Sopenharmony_ci return res; 183e5b75505Sopenharmony_ci return const_time_select_int(in_range, res, 0); 184e5b75505Sopenharmony_ci} 185e5b75505Sopenharmony_ci 186e5b75505Sopenharmony_ci 187e5b75505Sopenharmony_ci/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided 188e5b75505Sopenharmony_ci * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */ 189e5b75505Sopenharmony_cistatic int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, 190e5b75505Sopenharmony_ci struct crypto_bignum *pwe) 191e5b75505Sopenharmony_ci{ 192e5b75505Sopenharmony_ci u8 pwd_value[SAE_MAX_PRIME_LEN]; 193e5b75505Sopenharmony_ci size_t bits = sae->tmp->prime_len * 8; 194e5b75505Sopenharmony_ci u8 exp[1]; 195e5b75505Sopenharmony_ci struct crypto_bignum *a, *b = NULL; 196e5b75505Sopenharmony_ci int res, is_val; 197e5b75505Sopenharmony_ci u8 pwd_value_valid; 198e5b75505Sopenharmony_ci 199e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); 200e5b75505Sopenharmony_ci 201e5b75505Sopenharmony_ci /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ 202e5b75505Sopenharmony_ci if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", 203e5b75505Sopenharmony_ci sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, 204e5b75505Sopenharmony_ci bits) < 0) 205e5b75505Sopenharmony_ci return -1; 206e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, 207e5b75505Sopenharmony_ci sae->tmp->prime_len); 208e5b75505Sopenharmony_ci 209e5b75505Sopenharmony_ci /* Check whether pwd-value < p */ 210e5b75505Sopenharmony_ci res = const_time_memcmp(pwd_value, sae->tmp->dh->prime, 211e5b75505Sopenharmony_ci sae->tmp->prime_len); 212e5b75505Sopenharmony_ci /* pwd-value >= p is invalid, so res is < 0 for the valid cases and 213e5b75505Sopenharmony_ci * the negative sign can be used to fill the mask for constant time 214e5b75505Sopenharmony_ci * selection */ 215e5b75505Sopenharmony_ci pwd_value_valid = const_time_fill_msb(res); 216e5b75505Sopenharmony_ci 217e5b75505Sopenharmony_ci /* If pwd-value >= p, force pwd-value to be < p and perform the 218e5b75505Sopenharmony_ci * calculations anyway to hide timing difference. The derived PWE will 219e5b75505Sopenharmony_ci * be ignored in that case. */ 220e5b75505Sopenharmony_ci pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0); 221e5b75505Sopenharmony_ci 222e5b75505Sopenharmony_ci /* PWE = pwd-value^((p-1)/r) modulo p */ 223e5b75505Sopenharmony_ci 224e5b75505Sopenharmony_ci res = -1; 225e5b75505Sopenharmony_ci a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); 226e5b75505Sopenharmony_ci if (!a) 227e5b75505Sopenharmony_ci goto fail; 228e5b75505Sopenharmony_ci 229e5b75505Sopenharmony_ci /* This is an optimization based on the used group that does not depend 230e5b75505Sopenharmony_ci * on the password in any way, so it is fine to use separate branches 231e5b75505Sopenharmony_ci * for this step without constant time operations. */ 232e5b75505Sopenharmony_ci if (sae->tmp->dh->safe_prime) { 233e5b75505Sopenharmony_ci /* 234e5b75505Sopenharmony_ci * r = (p-1)/2 for the group used here, so this becomes: 235e5b75505Sopenharmony_ci * PWE = pwd-value^2 modulo p 236e5b75505Sopenharmony_ci */ 237e5b75505Sopenharmony_ci exp[0] = 2; 238e5b75505Sopenharmony_ci b = crypto_bignum_init_set(exp, sizeof(exp)); 239e5b75505Sopenharmony_ci } else { 240e5b75505Sopenharmony_ci /* Calculate exponent: (p-1)/r */ 241e5b75505Sopenharmony_ci exp[0] = 1; 242e5b75505Sopenharmony_ci b = crypto_bignum_init_set(exp, sizeof(exp)); 243e5b75505Sopenharmony_ci if (b == NULL || 244e5b75505Sopenharmony_ci crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || 245e5b75505Sopenharmony_ci crypto_bignum_div(b, sae->tmp->order, b) < 0) 246e5b75505Sopenharmony_ci goto fail; 247e5b75505Sopenharmony_ci } 248e5b75505Sopenharmony_ci 249e5b75505Sopenharmony_ci if (!b) 250e5b75505Sopenharmony_ci goto fail; 251e5b75505Sopenharmony_ci 252e5b75505Sopenharmony_ci res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); 253e5b75505Sopenharmony_ci if (res < 0) 254e5b75505Sopenharmony_ci goto fail; 255e5b75505Sopenharmony_ci 256e5b75505Sopenharmony_ci /* There were no fatal errors in calculations, so determine the return 257e5b75505Sopenharmony_ci * value using constant time operations. We get here for number of 258e5b75505Sopenharmony_ci * invalid cases which are cleared here after having performed all the 259e5b75505Sopenharmony_ci * computation. PWE is valid if pwd-value was less than prime and 260e5b75505Sopenharmony_ci * PWE > 1. Start with pwd-value check first and then use constant time 261e5b75505Sopenharmony_ci * operations to clear res to 0 if PWE is 0 or 1. 262e5b75505Sopenharmony_ci */ 263e5b75505Sopenharmony_ci res = const_time_select_u8(pwd_value_valid, 1, 0); 264e5b75505Sopenharmony_ci is_val = crypto_bignum_is_zero(pwe); 265e5b75505Sopenharmony_ci res = const_time_select_u8(const_time_is_zero(is_val), res, 0); 266e5b75505Sopenharmony_ci is_val = crypto_bignum_is_one(pwe); 267e5b75505Sopenharmony_ci res = const_time_select_u8(const_time_is_zero(is_val), res, 0); 268e5b75505Sopenharmony_ci 269e5b75505Sopenharmony_cifail: 270e5b75505Sopenharmony_ci crypto_bignum_deinit(a, 1); 271e5b75505Sopenharmony_ci crypto_bignum_deinit(b, 1); 272e5b75505Sopenharmony_ci return res; 273e5b75505Sopenharmony_ci} 274e5b75505Sopenharmony_ci 275e5b75505Sopenharmony_ci 276e5b75505Sopenharmony_cistatic int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, 277e5b75505Sopenharmony_ci const u8 *addr2, const u8 *password, 278e5b75505Sopenharmony_ci size_t password_len, const char *identifier) 279e5b75505Sopenharmony_ci{ 280e5b75505Sopenharmony_ci u8 counter, k; 281e5b75505Sopenharmony_ci u8 addrs[2 * ETH_ALEN]; 282e5b75505Sopenharmony_ci const u8 *addr[3]; 283e5b75505Sopenharmony_ci size_t len[3]; 284e5b75505Sopenharmony_ci size_t num_elem; 285e5b75505Sopenharmony_ci u8 *dummy_password, *tmp_password; 286e5b75505Sopenharmony_ci int pwd_seed_odd = 0; 287e5b75505Sopenharmony_ci u8 prime[SAE_MAX_ECC_PRIME_LEN]; 288e5b75505Sopenharmony_ci size_t prime_len; 289e5b75505Sopenharmony_ci struct crypto_bignum *x = NULL, *y = NULL, *qr = NULL, *qnr = NULL; 290e5b75505Sopenharmony_ci u8 x_bin[SAE_MAX_ECC_PRIME_LEN]; 291e5b75505Sopenharmony_ci u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; 292e5b75505Sopenharmony_ci u8 qr_bin[SAE_MAX_ECC_PRIME_LEN]; 293e5b75505Sopenharmony_ci u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN]; 294e5b75505Sopenharmony_ci u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN]; 295e5b75505Sopenharmony_ci int res = -1; 296e5b75505Sopenharmony_ci u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* 297e5b75505Sopenharmony_ci * mask */ 298e5b75505Sopenharmony_ci unsigned int is_eq; 299e5b75505Sopenharmony_ci 300e5b75505Sopenharmony_ci os_memset(x_bin, 0, sizeof(x_bin)); 301e5b75505Sopenharmony_ci 302e5b75505Sopenharmony_ci dummy_password = os_malloc(password_len); 303e5b75505Sopenharmony_ci tmp_password = os_malloc(password_len); 304e5b75505Sopenharmony_ci if (!dummy_password || !tmp_password || 305e5b75505Sopenharmony_ci random_get_bytes(dummy_password, password_len) < 0) 306e5b75505Sopenharmony_ci goto fail; 307e5b75505Sopenharmony_ci 308e5b75505Sopenharmony_ci prime_len = sae->tmp->prime_len; 309e5b75505Sopenharmony_ci if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 310e5b75505Sopenharmony_ci prime_len) < 0) 311e5b75505Sopenharmony_ci goto fail; 312e5b75505Sopenharmony_ci 313e5b75505Sopenharmony_ci /* 314e5b75505Sopenharmony_ci * Create a random quadratic residue (qr) and quadratic non-residue 315e5b75505Sopenharmony_ci * (qnr) modulo p for blinding purposes during the loop. 316e5b75505Sopenharmony_ci */ 317e5b75505Sopenharmony_ci if (dragonfly_get_random_qr_qnr(sae->tmp->prime, &qr, &qnr) < 0 || 318e5b75505Sopenharmony_ci crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 || 319e5b75505Sopenharmony_ci crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0) 320e5b75505Sopenharmony_ci goto fail; 321e5b75505Sopenharmony_ci 322e5b75505Sopenharmony_ci wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 323e5b75505Sopenharmony_ci password, password_len); 324e5b75505Sopenharmony_ci if (identifier) 325e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", 326e5b75505Sopenharmony_ci identifier); 327e5b75505Sopenharmony_ci 328e5b75505Sopenharmony_ci /* 329e5b75505Sopenharmony_ci * H(salt, ikm) = HMAC-SHA256(salt, ikm) 330e5b75505Sopenharmony_ci * base = password [|| identifier] 331e5b75505Sopenharmony_ci * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 332e5b75505Sopenharmony_ci * base || counter) 333e5b75505Sopenharmony_ci */ 334e5b75505Sopenharmony_ci sae_pwd_seed_key(addr1, addr2, addrs); 335e5b75505Sopenharmony_ci 336e5b75505Sopenharmony_ci addr[0] = tmp_password; 337e5b75505Sopenharmony_ci len[0] = password_len; 338e5b75505Sopenharmony_ci num_elem = 1; 339e5b75505Sopenharmony_ci if (identifier) { 340e5b75505Sopenharmony_ci addr[num_elem] = (const u8 *) identifier; 341e5b75505Sopenharmony_ci len[num_elem] = os_strlen(identifier); 342e5b75505Sopenharmony_ci num_elem++; 343e5b75505Sopenharmony_ci } 344e5b75505Sopenharmony_ci addr[num_elem] = &counter; 345e5b75505Sopenharmony_ci len[num_elem] = sizeof(counter); 346e5b75505Sopenharmony_ci num_elem++; 347e5b75505Sopenharmony_ci 348e5b75505Sopenharmony_ci /* 349e5b75505Sopenharmony_ci * Continue for at least k iterations to protect against side-channel 350e5b75505Sopenharmony_ci * attacks that attempt to determine the number of iterations required 351e5b75505Sopenharmony_ci * in the loop. 352e5b75505Sopenharmony_ci */ 353e5b75505Sopenharmony_ci k = dragonfly_min_pwe_loop_iter(sae->group); 354e5b75505Sopenharmony_ci 355e5b75505Sopenharmony_ci for (counter = 1; counter <= k || !found; counter++) { 356e5b75505Sopenharmony_ci u8 pwd_seed[SHA256_MAC_LEN]; 357e5b75505Sopenharmony_ci 358e5b75505Sopenharmony_ci if (counter > 200) { 359e5b75505Sopenharmony_ci /* This should not happen in practice */ 360e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 361e5b75505Sopenharmony_ci break; 362e5b75505Sopenharmony_ci } 363e5b75505Sopenharmony_ci 364e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter); 365e5b75505Sopenharmony_ci const_time_select_bin(found, dummy_password, password, 366e5b75505Sopenharmony_ci password_len, tmp_password); 367e5b75505Sopenharmony_ci if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, 368e5b75505Sopenharmony_ci addr, len, pwd_seed) < 0) 369e5b75505Sopenharmony_ci break; 370e5b75505Sopenharmony_ci 371e5b75505Sopenharmony_ci res = sae_test_pwd_seed_ecc(sae, pwd_seed, 372e5b75505Sopenharmony_ci prime, qr_bin, qnr_bin, x_cand_bin); 373e5b75505Sopenharmony_ci const_time_select_bin(found, x_bin, x_cand_bin, prime_len, 374e5b75505Sopenharmony_ci x_bin); 375e5b75505Sopenharmony_ci pwd_seed_odd = const_time_select_u8( 376e5b75505Sopenharmony_ci found, pwd_seed_odd, 377e5b75505Sopenharmony_ci pwd_seed[SHA256_MAC_LEN - 1] & 0x01); 378e5b75505Sopenharmony_ci os_memset(pwd_seed, 0, sizeof(pwd_seed)); 379e5b75505Sopenharmony_ci if (res < 0) 380e5b75505Sopenharmony_ci goto fail; 381e5b75505Sopenharmony_ci /* Need to minimize differences in handling res == 0 and 1 here 382e5b75505Sopenharmony_ci * to avoid differences in timing and instruction cache access, 383e5b75505Sopenharmony_ci * so use const_time_select_*() to make local copies of the 384e5b75505Sopenharmony_ci * values based on whether this loop iteration was the one that 385e5b75505Sopenharmony_ci * found the pwd-seed/x. */ 386e5b75505Sopenharmony_ci 387e5b75505Sopenharmony_ci /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them 388e5b75505Sopenharmony_ci * (with res converted to 0/0xff) handles this in constant time. 389e5b75505Sopenharmony_ci */ 390e5b75505Sopenharmony_ci found |= res * 0xff; 391e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x", 392e5b75505Sopenharmony_ci res, found); 393e5b75505Sopenharmony_ci } 394e5b75505Sopenharmony_ci 395e5b75505Sopenharmony_ci if (!found) { 396e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); 397e5b75505Sopenharmony_ci res = -1; 398e5b75505Sopenharmony_ci goto fail; 399e5b75505Sopenharmony_ci } 400e5b75505Sopenharmony_ci 401e5b75505Sopenharmony_ci x = crypto_bignum_init_set(x_bin, prime_len); 402e5b75505Sopenharmony_ci if (!x) { 403e5b75505Sopenharmony_ci res = -1; 404e5b75505Sopenharmony_ci goto fail; 405e5b75505Sopenharmony_ci } 406e5b75505Sopenharmony_ci 407e5b75505Sopenharmony_ci /* y = sqrt(x^3 + ax + b) mod p 408e5b75505Sopenharmony_ci * if LSB(save) == LSB(y): PWE = (x, y) 409e5b75505Sopenharmony_ci * else: PWE = (x, p - y) 410e5b75505Sopenharmony_ci * 411e5b75505Sopenharmony_ci * Calculate y and the two possible values for PWE and after that, 412e5b75505Sopenharmony_ci * use constant time selection to copy the correct alternative. 413e5b75505Sopenharmony_ci */ 414e5b75505Sopenharmony_ci y = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x); 415e5b75505Sopenharmony_ci if (!y || 416e5b75505Sopenharmony_ci dragonfly_sqrt(sae->tmp->ec, y, y) < 0 || 417e5b75505Sopenharmony_ci crypto_bignum_to_bin(y, x_y, SAE_MAX_ECC_PRIME_LEN, 418e5b75505Sopenharmony_ci prime_len) < 0 || 419e5b75505Sopenharmony_ci crypto_bignum_sub(sae->tmp->prime, y, y) < 0 || 420e5b75505Sopenharmony_ci crypto_bignum_to_bin(y, x_y + SAE_MAX_ECC_PRIME_LEN, 421e5b75505Sopenharmony_ci SAE_MAX_ECC_PRIME_LEN, prime_len) < 0) { 422e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); 423e5b75505Sopenharmony_ci goto fail; 424e5b75505Sopenharmony_ci } 425e5b75505Sopenharmony_ci 426e5b75505Sopenharmony_ci is_eq = const_time_eq(pwd_seed_odd, x_y[prime_len - 1] & 0x01); 427e5b75505Sopenharmony_ci const_time_select_bin(is_eq, x_y, x_y + SAE_MAX_ECC_PRIME_LEN, 428e5b75505Sopenharmony_ci prime_len, x_y + prime_len); 429e5b75505Sopenharmony_ci os_memcpy(x_y, x_bin, prime_len); 430e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: PWE", x_y, 2 * prime_len); 431e5b75505Sopenharmony_ci crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1); 432e5b75505Sopenharmony_ci sae->tmp->pwe_ecc = crypto_ec_point_from_bin(sae->tmp->ec, x_y); 433e5b75505Sopenharmony_ci if (!sae->tmp->pwe_ecc) { 434e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); 435e5b75505Sopenharmony_ci res = -1; 436e5b75505Sopenharmony_ci } 437e5b75505Sopenharmony_ci 438e5b75505Sopenharmony_cifail: 439e5b75505Sopenharmony_ci forced_memzero(x_y, sizeof(x_y)); 440e5b75505Sopenharmony_ci crypto_bignum_deinit(qr, 0); 441e5b75505Sopenharmony_ci crypto_bignum_deinit(qnr, 0); 442e5b75505Sopenharmony_ci crypto_bignum_deinit(y, 1); 443e5b75505Sopenharmony_ci os_free(dummy_password); 444e5b75505Sopenharmony_ci bin_clear_free(tmp_password, password_len); 445e5b75505Sopenharmony_ci crypto_bignum_deinit(x, 1); 446e5b75505Sopenharmony_ci os_memset(x_bin, 0, sizeof(x_bin)); 447e5b75505Sopenharmony_ci os_memset(x_cand_bin, 0, sizeof(x_cand_bin)); 448e5b75505Sopenharmony_ci 449e5b75505Sopenharmony_ci return res; 450e5b75505Sopenharmony_ci} 451e5b75505Sopenharmony_ci 452e5b75505Sopenharmony_ci 453e5b75505Sopenharmony_cistatic int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, 454e5b75505Sopenharmony_ci const u8 *addr2, const u8 *password, 455e5b75505Sopenharmony_ci size_t password_len, const char *identifier) 456e5b75505Sopenharmony_ci{ 457e5b75505Sopenharmony_ci u8 counter, k, sel_counter = 0; 458e5b75505Sopenharmony_ci u8 addrs[2 * ETH_ALEN]; 459e5b75505Sopenharmony_ci const u8 *addr[3]; 460e5b75505Sopenharmony_ci size_t len[3]; 461e5b75505Sopenharmony_ci size_t num_elem; 462e5b75505Sopenharmony_ci u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* 463e5b75505Sopenharmony_ci * mask */ 464e5b75505Sopenharmony_ci u8 mask; 465e5b75505Sopenharmony_ci struct crypto_bignum *pwe; 466e5b75505Sopenharmony_ci size_t prime_len = sae->tmp->prime_len * 8; 467e5b75505Sopenharmony_ci u8 *pwe_buf; 468e5b75505Sopenharmony_ci 469e5b75505Sopenharmony_ci crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); 470e5b75505Sopenharmony_ci sae->tmp->pwe_ffc = NULL; 471e5b75505Sopenharmony_ci 472e5b75505Sopenharmony_ci /* Allocate a buffer to maintain selected and candidate PWE for constant 473e5b75505Sopenharmony_ci * time selection. */ 474e5b75505Sopenharmony_ci pwe_buf = os_zalloc(prime_len * 2); 475e5b75505Sopenharmony_ci pwe = crypto_bignum_init(); 476e5b75505Sopenharmony_ci if (!pwe_buf || !pwe) 477e5b75505Sopenharmony_ci goto fail; 478e5b75505Sopenharmony_ci 479e5b75505Sopenharmony_ci wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", 480e5b75505Sopenharmony_ci password, password_len); 481e5b75505Sopenharmony_ci 482e5b75505Sopenharmony_ci /* 483e5b75505Sopenharmony_ci * H(salt, ikm) = HMAC-SHA256(salt, ikm) 484e5b75505Sopenharmony_ci * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), 485e5b75505Sopenharmony_ci * password [|| identifier] || counter) 486e5b75505Sopenharmony_ci */ 487e5b75505Sopenharmony_ci sae_pwd_seed_key(addr1, addr2, addrs); 488e5b75505Sopenharmony_ci 489e5b75505Sopenharmony_ci addr[0] = password; 490e5b75505Sopenharmony_ci len[0] = password_len; 491e5b75505Sopenharmony_ci num_elem = 1; 492e5b75505Sopenharmony_ci if (identifier) { 493e5b75505Sopenharmony_ci addr[num_elem] = (const u8 *) identifier; 494e5b75505Sopenharmony_ci len[num_elem] = os_strlen(identifier); 495e5b75505Sopenharmony_ci num_elem++; 496e5b75505Sopenharmony_ci } 497e5b75505Sopenharmony_ci addr[num_elem] = &counter; 498e5b75505Sopenharmony_ci len[num_elem] = sizeof(counter); 499e5b75505Sopenharmony_ci num_elem++; 500e5b75505Sopenharmony_ci 501e5b75505Sopenharmony_ci k = dragonfly_min_pwe_loop_iter(sae->group); 502e5b75505Sopenharmony_ci 503e5b75505Sopenharmony_ci for (counter = 1; counter <= k || !found; counter++) { 504e5b75505Sopenharmony_ci u8 pwd_seed[SHA256_MAC_LEN]; 505e5b75505Sopenharmony_ci int res; 506e5b75505Sopenharmony_ci 507e5b75505Sopenharmony_ci if (counter > 200) { 508e5b75505Sopenharmony_ci /* This should not happen in practice */ 509e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); 510e5b75505Sopenharmony_ci break; 511e5b75505Sopenharmony_ci } 512e5b75505Sopenharmony_ci 513e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter); 514e5b75505Sopenharmony_ci if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, 515e5b75505Sopenharmony_ci addr, len, pwd_seed) < 0) 516e5b75505Sopenharmony_ci break; 517e5b75505Sopenharmony_ci res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe); 518e5b75505Sopenharmony_ci /* res is -1 for fatal failure, 0 if a valid PWE was not found, 519e5b75505Sopenharmony_ci * or 1 if a valid PWE was found. */ 520e5b75505Sopenharmony_ci if (res < 0) 521e5b75505Sopenharmony_ci break; 522e5b75505Sopenharmony_ci /* Store the candidate PWE into the second half of pwe_buf and 523e5b75505Sopenharmony_ci * the selected PWE in the beginning of pwe_buf using constant 524e5b75505Sopenharmony_ci * time selection. */ 525e5b75505Sopenharmony_ci if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len, 526e5b75505Sopenharmony_ci prime_len) < 0) 527e5b75505Sopenharmony_ci break; 528e5b75505Sopenharmony_ci const_time_select_bin(found, pwe_buf, pwe_buf + prime_len, 529e5b75505Sopenharmony_ci prime_len, pwe_buf); 530e5b75505Sopenharmony_ci sel_counter = const_time_select_u8(found, sel_counter, counter); 531e5b75505Sopenharmony_ci mask = const_time_eq_u8(res, 1); 532e5b75505Sopenharmony_ci found = const_time_select_u8(found, found, mask); 533e5b75505Sopenharmony_ci } 534e5b75505Sopenharmony_ci 535e5b75505Sopenharmony_ci if (!found) 536e5b75505Sopenharmony_ci goto fail; 537e5b75505Sopenharmony_ci 538e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter); 539e5b75505Sopenharmony_ci sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len); 540e5b75505Sopenharmony_cifail: 541e5b75505Sopenharmony_ci crypto_bignum_deinit(pwe, 1); 542e5b75505Sopenharmony_ci bin_clear_free(pwe_buf, prime_len * 2); 543e5b75505Sopenharmony_ci return sae->tmp->pwe_ffc ? 0 : -1; 544e5b75505Sopenharmony_ci} 545e5b75505Sopenharmony_ci 546e5b75505Sopenharmony_ci 547e5b75505Sopenharmony_cistatic int sae_derive_commit_element_ecc(struct sae_data *sae, 548e5b75505Sopenharmony_ci struct crypto_bignum *mask) 549e5b75505Sopenharmony_ci{ 550e5b75505Sopenharmony_ci /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 551e5b75505Sopenharmony_ci if (!sae->tmp->own_commit_element_ecc) { 552e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ecc = 553e5b75505Sopenharmony_ci crypto_ec_point_init(sae->tmp->ec); 554e5b75505Sopenharmony_ci if (!sae->tmp->own_commit_element_ecc) 555e5b75505Sopenharmony_ci return -1; 556e5b75505Sopenharmony_ci } 557e5b75505Sopenharmony_ci 558e5b75505Sopenharmony_ci if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, 559e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ecc) < 0 || 560e5b75505Sopenharmony_ci crypto_ec_point_invert(sae->tmp->ec, 561e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ecc) < 0) { 562e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 563e5b75505Sopenharmony_ci return -1; 564e5b75505Sopenharmony_ci } 565e5b75505Sopenharmony_ci 566e5b75505Sopenharmony_ci return 0; 567e5b75505Sopenharmony_ci} 568e5b75505Sopenharmony_ci 569e5b75505Sopenharmony_ci 570e5b75505Sopenharmony_cistatic int sae_derive_commit_element_ffc(struct sae_data *sae, 571e5b75505Sopenharmony_ci struct crypto_bignum *mask) 572e5b75505Sopenharmony_ci{ 573e5b75505Sopenharmony_ci /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ 574e5b75505Sopenharmony_ci if (!sae->tmp->own_commit_element_ffc) { 575e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ffc = crypto_bignum_init(); 576e5b75505Sopenharmony_ci if (!sae->tmp->own_commit_element_ffc) 577e5b75505Sopenharmony_ci return -1; 578e5b75505Sopenharmony_ci } 579e5b75505Sopenharmony_ci 580e5b75505Sopenharmony_ci if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, 581e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ffc) < 0 || 582e5b75505Sopenharmony_ci crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, 583e5b75505Sopenharmony_ci sae->tmp->prime, 584e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ffc) < 0) { 585e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); 586e5b75505Sopenharmony_ci return -1; 587e5b75505Sopenharmony_ci } 588e5b75505Sopenharmony_ci 589e5b75505Sopenharmony_ci return 0; 590e5b75505Sopenharmony_ci} 591e5b75505Sopenharmony_ci 592e5b75505Sopenharmony_ci 593e5b75505Sopenharmony_cistatic int sae_derive_commit(struct sae_data *sae) 594e5b75505Sopenharmony_ci{ 595e5b75505Sopenharmony_ci struct crypto_bignum *mask; 596e5b75505Sopenharmony_ci int ret; 597e5b75505Sopenharmony_ci 598e5b75505Sopenharmony_ci mask = crypto_bignum_init(); 599e5b75505Sopenharmony_ci if (!sae->tmp->sae_rand) 600e5b75505Sopenharmony_ci sae->tmp->sae_rand = crypto_bignum_init(); 601e5b75505Sopenharmony_ci if (!sae->tmp->own_commit_scalar) 602e5b75505Sopenharmony_ci sae->tmp->own_commit_scalar = crypto_bignum_init(); 603e5b75505Sopenharmony_ci ret = !mask || !sae->tmp->sae_rand || !sae->tmp->own_commit_scalar || 604e5b75505Sopenharmony_ci dragonfly_generate_scalar(sae->tmp->order, sae->tmp->sae_rand, 605e5b75505Sopenharmony_ci mask, 606e5b75505Sopenharmony_ci sae->tmp->own_commit_scalar) < 0 || 607e5b75505Sopenharmony_ci (sae->tmp->ec && 608e5b75505Sopenharmony_ci sae_derive_commit_element_ecc(sae, mask) < 0) || 609e5b75505Sopenharmony_ci (sae->tmp->dh && 610e5b75505Sopenharmony_ci sae_derive_commit_element_ffc(sae, mask) < 0); 611e5b75505Sopenharmony_ci crypto_bignum_deinit(mask, 1); 612e5b75505Sopenharmony_ci return ret ? -1 : 0; 613e5b75505Sopenharmony_ci} 614e5b75505Sopenharmony_ci 615e5b75505Sopenharmony_ci 616e5b75505Sopenharmony_ciint sae_prepare_commit(const u8 *addr1, const u8 *addr2, 617e5b75505Sopenharmony_ci const u8 *password, size_t password_len, 618e5b75505Sopenharmony_ci const char *identifier, struct sae_data *sae) 619e5b75505Sopenharmony_ci{ 620e5b75505Sopenharmony_ci if (sae->tmp == NULL || 621e5b75505Sopenharmony_ci (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, 622e5b75505Sopenharmony_ci password_len, 623e5b75505Sopenharmony_ci identifier) < 0) || 624e5b75505Sopenharmony_ci (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, 625e5b75505Sopenharmony_ci password_len, 626e5b75505Sopenharmony_ci identifier) < 0) || 627e5b75505Sopenharmony_ci sae_derive_commit(sae) < 0) 628e5b75505Sopenharmony_ci return -1; 629e5b75505Sopenharmony_ci return 0; 630e5b75505Sopenharmony_ci} 631e5b75505Sopenharmony_ci 632e5b75505Sopenharmony_ci 633e5b75505Sopenharmony_cistatic int sae_derive_k_ecc(struct sae_data *sae, u8 *k) 634e5b75505Sopenharmony_ci{ 635e5b75505Sopenharmony_ci struct crypto_ec_point *K; 636e5b75505Sopenharmony_ci int ret = -1; 637e5b75505Sopenharmony_ci 638e5b75505Sopenharmony_ci K = crypto_ec_point_init(sae->tmp->ec); 639e5b75505Sopenharmony_ci if (K == NULL) 640e5b75505Sopenharmony_ci goto fail; 641e5b75505Sopenharmony_ci 642e5b75505Sopenharmony_ci /* 643e5b75505Sopenharmony_ci * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 644e5b75505Sopenharmony_ci * PEER-COMMIT-ELEMENT))) 645e5b75505Sopenharmony_ci * If K is identity element (point-at-infinity), reject 646e5b75505Sopenharmony_ci * k = F(K) (= x coordinate) 647e5b75505Sopenharmony_ci */ 648e5b75505Sopenharmony_ci 649e5b75505Sopenharmony_ci if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, 650e5b75505Sopenharmony_ci sae->peer_commit_scalar, K) < 0 || 651e5b75505Sopenharmony_ci crypto_ec_point_add(sae->tmp->ec, K, 652e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ecc, K) < 0 || 653e5b75505Sopenharmony_ci crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || 654e5b75505Sopenharmony_ci crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || 655e5b75505Sopenharmony_ci crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { 656e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 657e5b75505Sopenharmony_ci goto fail; 658e5b75505Sopenharmony_ci } 659e5b75505Sopenharmony_ci 660e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 661e5b75505Sopenharmony_ci 662e5b75505Sopenharmony_ci ret = 0; 663e5b75505Sopenharmony_cifail: 664e5b75505Sopenharmony_ci crypto_ec_point_deinit(K, 1); 665e5b75505Sopenharmony_ci return ret; 666e5b75505Sopenharmony_ci} 667e5b75505Sopenharmony_ci 668e5b75505Sopenharmony_ci 669e5b75505Sopenharmony_cistatic int sae_derive_k_ffc(struct sae_data *sae, u8 *k) 670e5b75505Sopenharmony_ci{ 671e5b75505Sopenharmony_ci struct crypto_bignum *K; 672e5b75505Sopenharmony_ci int ret = -1; 673e5b75505Sopenharmony_ci 674e5b75505Sopenharmony_ci K = crypto_bignum_init(); 675e5b75505Sopenharmony_ci if (K == NULL) 676e5b75505Sopenharmony_ci goto fail; 677e5b75505Sopenharmony_ci 678e5b75505Sopenharmony_ci /* 679e5b75505Sopenharmony_ci * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), 680e5b75505Sopenharmony_ci * PEER-COMMIT-ELEMENT))) 681e5b75505Sopenharmony_ci * If K is identity element (one), reject. 682e5b75505Sopenharmony_ci * k = F(K) (= x coordinate) 683e5b75505Sopenharmony_ci */ 684e5b75505Sopenharmony_ci 685e5b75505Sopenharmony_ci if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, 686e5b75505Sopenharmony_ci sae->tmp->prime, K) < 0 || 687e5b75505Sopenharmony_ci crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, 688e5b75505Sopenharmony_ci sae->tmp->prime, K) < 0 || 689e5b75505Sopenharmony_ci crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 690e5b75505Sopenharmony_ci || 691e5b75505Sopenharmony_ci crypto_bignum_is_one(K) || 692e5b75505Sopenharmony_ci crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < 693e5b75505Sopenharmony_ci 0) { 694e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); 695e5b75505Sopenharmony_ci goto fail; 696e5b75505Sopenharmony_ci } 697e5b75505Sopenharmony_ci 698e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); 699e5b75505Sopenharmony_ci 700e5b75505Sopenharmony_ci ret = 0; 701e5b75505Sopenharmony_cifail: 702e5b75505Sopenharmony_ci crypto_bignum_deinit(K, 1); 703e5b75505Sopenharmony_ci return ret; 704e5b75505Sopenharmony_ci} 705e5b75505Sopenharmony_ci 706e5b75505Sopenharmony_ci 707e5b75505Sopenharmony_cistatic int sae_derive_keys(struct sae_data *sae, const u8 *k) 708e5b75505Sopenharmony_ci{ 709e5b75505Sopenharmony_ci u8 null_key[SAE_KEYSEED_KEY_LEN], val[SAE_MAX_PRIME_LEN]; 710e5b75505Sopenharmony_ci u8 keyseed[SHA256_MAC_LEN]; 711e5b75505Sopenharmony_ci u8 keys[SAE_KCK_LEN + SAE_PMK_LEN]; 712e5b75505Sopenharmony_ci struct crypto_bignum *tmp; 713e5b75505Sopenharmony_ci int ret = -1; 714e5b75505Sopenharmony_ci 715e5b75505Sopenharmony_ci tmp = crypto_bignum_init(); 716e5b75505Sopenharmony_ci if (tmp == NULL) 717e5b75505Sopenharmony_ci goto fail; 718e5b75505Sopenharmony_ci 719e5b75505Sopenharmony_ci /* keyseed = H(<0>32, k) 720e5b75505Sopenharmony_ci * KCK || PMK = KDF-512(keyseed, "SAE KCK and PMK", 721e5b75505Sopenharmony_ci * (commit-scalar + peer-commit-scalar) modulo r) 722e5b75505Sopenharmony_ci * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) 723e5b75505Sopenharmony_ci */ 724e5b75505Sopenharmony_ci 725e5b75505Sopenharmony_ci os_memset(null_key, 0, sizeof(null_key)); 726e5b75505Sopenharmony_ci hmac_sha256(null_key, sizeof(null_key), k, sae->tmp->prime_len, 727e5b75505Sopenharmony_ci keyseed); 728e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, sizeof(keyseed)); 729e5b75505Sopenharmony_ci 730e5b75505Sopenharmony_ci crypto_bignum_add(sae->tmp->own_commit_scalar, sae->peer_commit_scalar, 731e5b75505Sopenharmony_ci tmp); 732e5b75505Sopenharmony_ci crypto_bignum_mod(tmp, sae->tmp->order, tmp); 733e5b75505Sopenharmony_ci /* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit 734e5b75505Sopenharmony_ci * string that is needed for KCK, PMK, and PMKID derivation, but it 735e5b75505Sopenharmony_ci * seems to make most sense to encode the 736e5b75505Sopenharmony_ci * (commit-scalar + peer-commit-scalar) mod r part as a bit string by 737e5b75505Sopenharmony_ci * zero padding it from left to the length of the order (in full 738e5b75505Sopenharmony_ci * octets). */ 739e5b75505Sopenharmony_ci crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len); 740e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); 741e5b75505Sopenharmony_ci if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK", 742e5b75505Sopenharmony_ci val, sae->tmp->order_len, keys, sizeof(keys)) < 0) 743e5b75505Sopenharmony_ci goto fail; 744e5b75505Sopenharmony_ci os_memset(keyseed, 0, sizeof(keyseed)); 745e5b75505Sopenharmony_ci os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN); 746e5b75505Sopenharmony_ci os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN); 747e5b75505Sopenharmony_ci os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); 748e5b75505Sopenharmony_ci os_memset(keys, 0, sizeof(keys)); 749e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN); 750e5b75505Sopenharmony_ci wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); 751e5b75505Sopenharmony_ci 752e5b75505Sopenharmony_ci ret = 0; 753e5b75505Sopenharmony_cifail: 754e5b75505Sopenharmony_ci crypto_bignum_deinit(tmp, 0); 755e5b75505Sopenharmony_ci return ret; 756e5b75505Sopenharmony_ci} 757e5b75505Sopenharmony_ci 758e5b75505Sopenharmony_ci 759e5b75505Sopenharmony_ciint sae_process_commit(struct sae_data *sae) 760e5b75505Sopenharmony_ci{ 761e5b75505Sopenharmony_ci u8 k[SAE_MAX_PRIME_LEN]; 762e5b75505Sopenharmony_ci if (sae->tmp == NULL || 763e5b75505Sopenharmony_ci (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || 764e5b75505Sopenharmony_ci (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || 765e5b75505Sopenharmony_ci sae_derive_keys(sae, k) < 0) 766e5b75505Sopenharmony_ci return -1; 767e5b75505Sopenharmony_ci return 0; 768e5b75505Sopenharmony_ci} 769e5b75505Sopenharmony_ci 770e5b75505Sopenharmony_ci 771e5b75505Sopenharmony_civoid sae_write_commit(struct sae_data *sae, struct wpabuf *buf, 772e5b75505Sopenharmony_ci const struct wpabuf *token, const char *identifier) 773e5b75505Sopenharmony_ci{ 774e5b75505Sopenharmony_ci u8 *pos; 775e5b75505Sopenharmony_ci 776e5b75505Sopenharmony_ci if (sae->tmp == NULL) 777e5b75505Sopenharmony_ci return; 778e5b75505Sopenharmony_ci 779e5b75505Sopenharmony_ci wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ 780e5b75505Sopenharmony_ci if (token) { 781e5b75505Sopenharmony_ci wpabuf_put_buf(buf, token); 782e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", 783e5b75505Sopenharmony_ci wpabuf_head(token), wpabuf_len(token)); 784e5b75505Sopenharmony_ci } 785e5b75505Sopenharmony_ci pos = wpabuf_put(buf, sae->tmp->prime_len); 786e5b75505Sopenharmony_ci crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, 787e5b75505Sopenharmony_ci sae->tmp->prime_len, sae->tmp->prime_len); 788e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", 789e5b75505Sopenharmony_ci pos, sae->tmp->prime_len); 790e5b75505Sopenharmony_ci if (sae->tmp->ec) { 791e5b75505Sopenharmony_ci pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); 792e5b75505Sopenharmony_ci crypto_ec_point_to_bin(sae->tmp->ec, 793e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ecc, 794e5b75505Sopenharmony_ci pos, pos + sae->tmp->prime_len); 795e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", 796e5b75505Sopenharmony_ci pos, sae->tmp->prime_len); 797e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", 798e5b75505Sopenharmony_ci pos + sae->tmp->prime_len, sae->tmp->prime_len); 799e5b75505Sopenharmony_ci } else { 800e5b75505Sopenharmony_ci pos = wpabuf_put(buf, sae->tmp->prime_len); 801e5b75505Sopenharmony_ci crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, 802e5b75505Sopenharmony_ci sae->tmp->prime_len, sae->tmp->prime_len); 803e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", 804e5b75505Sopenharmony_ci pos, sae->tmp->prime_len); 805e5b75505Sopenharmony_ci } 806e5b75505Sopenharmony_ci 807e5b75505Sopenharmony_ci if (identifier) { 808e5b75505Sopenharmony_ci /* Password Identifier element */ 809e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_EID_EXTENSION); 810e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 1 + os_strlen(identifier)); 811e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); 812e5b75505Sopenharmony_ci wpabuf_put_str(buf, identifier); 813e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", 814e5b75505Sopenharmony_ci identifier); 815e5b75505Sopenharmony_ci } 816e5b75505Sopenharmony_ci} 817e5b75505Sopenharmony_ci 818e5b75505Sopenharmony_ci 819e5b75505Sopenharmony_ciu16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) 820e5b75505Sopenharmony_ci{ 821e5b75505Sopenharmony_ci if (allowed_groups) { 822e5b75505Sopenharmony_ci int i; 823e5b75505Sopenharmony_ci for (i = 0; allowed_groups[i] > 0; i++) { 824e5b75505Sopenharmony_ci if (allowed_groups[i] == group) 825e5b75505Sopenharmony_ci break; 826e5b75505Sopenharmony_ci } 827e5b75505Sopenharmony_ci if (allowed_groups[i] != group) { 828e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " 829e5b75505Sopenharmony_ci "enabled in the current configuration", 830e5b75505Sopenharmony_ci group); 831e5b75505Sopenharmony_ci return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 832e5b75505Sopenharmony_ci } 833e5b75505Sopenharmony_ci } 834e5b75505Sopenharmony_ci 835e5b75505Sopenharmony_ci if (sae->state == SAE_COMMITTED && group != sae->group) { 836e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); 837e5b75505Sopenharmony_ci return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 838e5b75505Sopenharmony_ci } 839e5b75505Sopenharmony_ci 840e5b75505Sopenharmony_ci if (group != sae->group && sae_set_group(sae, group) < 0) { 841e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", 842e5b75505Sopenharmony_ci group); 843e5b75505Sopenharmony_ci return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 844e5b75505Sopenharmony_ci } 845e5b75505Sopenharmony_ci 846e5b75505Sopenharmony_ci if (sae->tmp == NULL) { 847e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); 848e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 849e5b75505Sopenharmony_ci } 850e5b75505Sopenharmony_ci 851e5b75505Sopenharmony_ci if (sae->tmp->dh && !allowed_groups) { 852e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " 853e5b75505Sopenharmony_ci "explicit configuration enabling it", group); 854e5b75505Sopenharmony_ci return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; 855e5b75505Sopenharmony_ci } 856e5b75505Sopenharmony_ci 857e5b75505Sopenharmony_ci return WLAN_STATUS_SUCCESS; 858e5b75505Sopenharmony_ci} 859e5b75505Sopenharmony_ci 860e5b75505Sopenharmony_ci 861e5b75505Sopenharmony_cistatic int sae_is_password_id_elem(const u8 *pos, const u8 *end) 862e5b75505Sopenharmony_ci{ 863e5b75505Sopenharmony_ci return end - pos >= 3 && 864e5b75505Sopenharmony_ci pos[0] == WLAN_EID_EXTENSION && 865e5b75505Sopenharmony_ci pos[1] >= 1 && 866e5b75505Sopenharmony_ci end - pos - 2 >= pos[1] && 867e5b75505Sopenharmony_ci pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; 868e5b75505Sopenharmony_ci} 869e5b75505Sopenharmony_ci 870e5b75505Sopenharmony_ci 871e5b75505Sopenharmony_cistatic void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, 872e5b75505Sopenharmony_ci const u8 *end, const u8 **token, 873e5b75505Sopenharmony_ci size_t *token_len) 874e5b75505Sopenharmony_ci{ 875e5b75505Sopenharmony_ci size_t scalar_elem_len, tlen; 876e5b75505Sopenharmony_ci const u8 *elem; 877e5b75505Sopenharmony_ci 878e5b75505Sopenharmony_ci if (token) 879e5b75505Sopenharmony_ci *token = NULL; 880e5b75505Sopenharmony_ci if (token_len) 881e5b75505Sopenharmony_ci *token_len = 0; 882e5b75505Sopenharmony_ci 883e5b75505Sopenharmony_ci scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; 884e5b75505Sopenharmony_ci if (scalar_elem_len >= (size_t) (end - *pos)) 885e5b75505Sopenharmony_ci return; /* No extra data beyond peer scalar and element */ 886e5b75505Sopenharmony_ci 887e5b75505Sopenharmony_ci /* It is a bit difficult to parse this now that there is an 888e5b75505Sopenharmony_ci * optional variable length Anti-Clogging Token field and 889e5b75505Sopenharmony_ci * optional variable length Password Identifier element in the 890e5b75505Sopenharmony_ci * frame. We are sending out fixed length Anti-Clogging Token 891e5b75505Sopenharmony_ci * fields, so use that length as a requirement for the received 892e5b75505Sopenharmony_ci * token and check for the presence of possible Password 893e5b75505Sopenharmony_ci * Identifier element based on the element header information. 894e5b75505Sopenharmony_ci */ 895e5b75505Sopenharmony_ci tlen = end - (*pos + scalar_elem_len); 896e5b75505Sopenharmony_ci 897e5b75505Sopenharmony_ci if (tlen < SHA256_MAC_LEN) { 898e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 899e5b75505Sopenharmony_ci "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", 900e5b75505Sopenharmony_ci (unsigned int) tlen); 901e5b75505Sopenharmony_ci return; 902e5b75505Sopenharmony_ci } 903e5b75505Sopenharmony_ci 904e5b75505Sopenharmony_ci elem = *pos + scalar_elem_len; 905e5b75505Sopenharmony_ci if (sae_is_password_id_elem(elem, end)) { 906e5b75505Sopenharmony_ci /* Password Identifier element takes out all available 907e5b75505Sopenharmony_ci * extra octets, so there can be no Anti-Clogging token in 908e5b75505Sopenharmony_ci * this frame. */ 909e5b75505Sopenharmony_ci return; 910e5b75505Sopenharmony_ci } 911e5b75505Sopenharmony_ci 912e5b75505Sopenharmony_ci elem += SHA256_MAC_LEN; 913e5b75505Sopenharmony_ci if (sae_is_password_id_elem(elem, end)) { 914e5b75505Sopenharmony_ci /* Password Identifier element is included in the end, so 915e5b75505Sopenharmony_ci * remove its length from the Anti-Clogging token field. */ 916e5b75505Sopenharmony_ci tlen -= 2 + elem[1]; 917e5b75505Sopenharmony_ci } 918e5b75505Sopenharmony_ci 919e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); 920e5b75505Sopenharmony_ci if (token) 921e5b75505Sopenharmony_ci *token = *pos; 922e5b75505Sopenharmony_ci if (token_len) 923e5b75505Sopenharmony_ci *token_len = tlen; 924e5b75505Sopenharmony_ci *pos += tlen; 925e5b75505Sopenharmony_ci} 926e5b75505Sopenharmony_ci 927e5b75505Sopenharmony_ci 928e5b75505Sopenharmony_cistatic u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, 929e5b75505Sopenharmony_ci const u8 *end) 930e5b75505Sopenharmony_ci{ 931e5b75505Sopenharmony_ci struct crypto_bignum *peer_scalar; 932e5b75505Sopenharmony_ci 933e5b75505Sopenharmony_ci if (sae->tmp->prime_len > end - *pos) { 934e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); 935e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 936e5b75505Sopenharmony_ci } 937e5b75505Sopenharmony_ci 938e5b75505Sopenharmony_ci peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); 939e5b75505Sopenharmony_ci if (peer_scalar == NULL) 940e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 941e5b75505Sopenharmony_ci 942e5b75505Sopenharmony_ci /* 943e5b75505Sopenharmony_ci * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for 944e5b75505Sopenharmony_ci * the peer and it is in Authenticated state, the new Commit Message 945e5b75505Sopenharmony_ci * shall be dropped if the peer-scalar is identical to the one used in 946e5b75505Sopenharmony_ci * the existing protocol instance. 947e5b75505Sopenharmony_ci */ 948e5b75505Sopenharmony_ci if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar && 949e5b75505Sopenharmony_ci crypto_bignum_cmp(sae->peer_commit_scalar, peer_scalar) == 0) { 950e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " 951e5b75505Sopenharmony_ci "peer-commit-scalar"); 952e5b75505Sopenharmony_ci crypto_bignum_deinit(peer_scalar, 0); 953e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 954e5b75505Sopenharmony_ci } 955e5b75505Sopenharmony_ci 956e5b75505Sopenharmony_ci /* 1 < scalar < r */ 957e5b75505Sopenharmony_ci if (crypto_bignum_is_zero(peer_scalar) || 958e5b75505Sopenharmony_ci crypto_bignum_is_one(peer_scalar) || 959e5b75505Sopenharmony_ci crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { 960e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); 961e5b75505Sopenharmony_ci crypto_bignum_deinit(peer_scalar, 0); 962e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 963e5b75505Sopenharmony_ci } 964e5b75505Sopenharmony_ci 965e5b75505Sopenharmony_ci 966e5b75505Sopenharmony_ci crypto_bignum_deinit(sae->peer_commit_scalar, 0); 967e5b75505Sopenharmony_ci sae->peer_commit_scalar = peer_scalar; 968e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", 969e5b75505Sopenharmony_ci *pos, sae->tmp->prime_len); 970e5b75505Sopenharmony_ci *pos += sae->tmp->prime_len; 971e5b75505Sopenharmony_ci 972e5b75505Sopenharmony_ci return WLAN_STATUS_SUCCESS; 973e5b75505Sopenharmony_ci} 974e5b75505Sopenharmony_ci 975e5b75505Sopenharmony_ci 976e5b75505Sopenharmony_cistatic u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, 977e5b75505Sopenharmony_ci const u8 *end) 978e5b75505Sopenharmony_ci{ 979e5b75505Sopenharmony_ci u8 prime[SAE_MAX_ECC_PRIME_LEN]; 980e5b75505Sopenharmony_ci 981e5b75505Sopenharmony_ci if (2 * sae->tmp->prime_len > end - *pos) { 982e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 983e5b75505Sopenharmony_ci "commit-element"); 984e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 985e5b75505Sopenharmony_ci } 986e5b75505Sopenharmony_ci 987e5b75505Sopenharmony_ci if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), 988e5b75505Sopenharmony_ci sae->tmp->prime_len) < 0) 989e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 990e5b75505Sopenharmony_ci 991e5b75505Sopenharmony_ci /* element x and y coordinates < p */ 992e5b75505Sopenharmony_ci if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || 993e5b75505Sopenharmony_ci os_memcmp(*pos + sae->tmp->prime_len, prime, 994e5b75505Sopenharmony_ci sae->tmp->prime_len) >= 0) { 995e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " 996e5b75505Sopenharmony_ci "element"); 997e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 998e5b75505Sopenharmony_ci } 999e5b75505Sopenharmony_ci 1000e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", 1001e5b75505Sopenharmony_ci *pos, sae->tmp->prime_len); 1002e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", 1003e5b75505Sopenharmony_ci *pos + sae->tmp->prime_len, sae->tmp->prime_len); 1004e5b75505Sopenharmony_ci 1005e5b75505Sopenharmony_ci crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); 1006e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ecc = 1007e5b75505Sopenharmony_ci crypto_ec_point_from_bin(sae->tmp->ec, *pos); 1008e5b75505Sopenharmony_ci if (sae->tmp->peer_commit_element_ecc == NULL) 1009e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 1010e5b75505Sopenharmony_ci 1011e5b75505Sopenharmony_ci if (!crypto_ec_point_is_on_curve(sae->tmp->ec, 1012e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ecc)) { 1013e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); 1014e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 1015e5b75505Sopenharmony_ci } 1016e5b75505Sopenharmony_ci 1017e5b75505Sopenharmony_ci *pos += 2 * sae->tmp->prime_len; 1018e5b75505Sopenharmony_ci 1019e5b75505Sopenharmony_ci return WLAN_STATUS_SUCCESS; 1020e5b75505Sopenharmony_ci} 1021e5b75505Sopenharmony_ci 1022e5b75505Sopenharmony_ci 1023e5b75505Sopenharmony_cistatic u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, 1024e5b75505Sopenharmony_ci const u8 *end) 1025e5b75505Sopenharmony_ci{ 1026e5b75505Sopenharmony_ci struct crypto_bignum *res, *one; 1027e5b75505Sopenharmony_ci const u8 one_bin[1] = { 0x01 }; 1028e5b75505Sopenharmony_ci 1029e5b75505Sopenharmony_ci if (sae->tmp->prime_len > end - *pos) { 1030e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Not enough data for " 1031e5b75505Sopenharmony_ci "commit-element"); 1032e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 1033e5b75505Sopenharmony_ci } 1034e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, 1035e5b75505Sopenharmony_ci sae->tmp->prime_len); 1036e5b75505Sopenharmony_ci 1037e5b75505Sopenharmony_ci crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); 1038e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ffc = 1039e5b75505Sopenharmony_ci crypto_bignum_init_set(*pos, sae->tmp->prime_len); 1040e5b75505Sopenharmony_ci if (sae->tmp->peer_commit_element_ffc == NULL) 1041e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 1042e5b75505Sopenharmony_ci /* 1 < element < p - 1 */ 1043e5b75505Sopenharmony_ci res = crypto_bignum_init(); 1044e5b75505Sopenharmony_ci one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); 1045e5b75505Sopenharmony_ci if (!res || !one || 1046e5b75505Sopenharmony_ci crypto_bignum_sub(sae->tmp->prime, one, res) || 1047e5b75505Sopenharmony_ci crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || 1048e5b75505Sopenharmony_ci crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || 1049e5b75505Sopenharmony_ci crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { 1050e5b75505Sopenharmony_ci crypto_bignum_deinit(res, 0); 1051e5b75505Sopenharmony_ci crypto_bignum_deinit(one, 0); 1052e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); 1053e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 1054e5b75505Sopenharmony_ci } 1055e5b75505Sopenharmony_ci crypto_bignum_deinit(one, 0); 1056e5b75505Sopenharmony_ci 1057e5b75505Sopenharmony_ci /* scalar-op(r, ELEMENT) = 1 modulo p */ 1058e5b75505Sopenharmony_ci if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, 1059e5b75505Sopenharmony_ci sae->tmp->order, sae->tmp->prime, res) < 0 || 1060e5b75505Sopenharmony_ci !crypto_bignum_is_one(res)) { 1061e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); 1062e5b75505Sopenharmony_ci crypto_bignum_deinit(res, 0); 1063e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 1064e5b75505Sopenharmony_ci } 1065e5b75505Sopenharmony_ci crypto_bignum_deinit(res, 0); 1066e5b75505Sopenharmony_ci 1067e5b75505Sopenharmony_ci *pos += sae->tmp->prime_len; 1068e5b75505Sopenharmony_ci 1069e5b75505Sopenharmony_ci return WLAN_STATUS_SUCCESS; 1070e5b75505Sopenharmony_ci} 1071e5b75505Sopenharmony_ci 1072e5b75505Sopenharmony_ci 1073e5b75505Sopenharmony_cistatic u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, 1074e5b75505Sopenharmony_ci const u8 *end) 1075e5b75505Sopenharmony_ci{ 1076e5b75505Sopenharmony_ci if (sae->tmp->dh) 1077e5b75505Sopenharmony_ci return sae_parse_commit_element_ffc(sae, pos, end); 1078e5b75505Sopenharmony_ci return sae_parse_commit_element_ecc(sae, pos, end); 1079e5b75505Sopenharmony_ci} 1080e5b75505Sopenharmony_ci 1081e5b75505Sopenharmony_ci 1082e5b75505Sopenharmony_cistatic int sae_parse_password_identifier(struct sae_data *sae, 1083e5b75505Sopenharmony_ci const u8 *pos, const u8 *end) 1084e5b75505Sopenharmony_ci{ 1085e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", 1086e5b75505Sopenharmony_ci pos, end - pos); 1087e5b75505Sopenharmony_ci if (!sae_is_password_id_elem(pos, end)) { 1088e5b75505Sopenharmony_ci if (sae->tmp->pw_id) { 1089e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1090e5b75505Sopenharmony_ci "SAE: No Password Identifier included, but expected one (%s)", 1091e5b75505Sopenharmony_ci sae->tmp->pw_id); 1092e5b75505Sopenharmony_ci return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 1093e5b75505Sopenharmony_ci } 1094e5b75505Sopenharmony_ci os_free(sae->tmp->pw_id); 1095e5b75505Sopenharmony_ci sae->tmp->pw_id = NULL; 1096e5b75505Sopenharmony_ci return WLAN_STATUS_SUCCESS; /* No Password Identifier */ 1097e5b75505Sopenharmony_ci } 1098e5b75505Sopenharmony_ci 1099e5b75505Sopenharmony_ci if (sae->tmp->pw_id && 1100e5b75505Sopenharmony_ci (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) || 1101e5b75505Sopenharmony_ci os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) { 1102e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 1103e5b75505Sopenharmony_ci "SAE: The included Password Identifier does not match the expected one (%s)", 1104e5b75505Sopenharmony_ci sae->tmp->pw_id); 1105e5b75505Sopenharmony_ci return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; 1106e5b75505Sopenharmony_ci } 1107e5b75505Sopenharmony_ci 1108e5b75505Sopenharmony_ci os_free(sae->tmp->pw_id); 1109e5b75505Sopenharmony_ci sae->tmp->pw_id = os_malloc(pos[1]); 1110e5b75505Sopenharmony_ci if (!sae->tmp->pw_id) 1111e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 1112e5b75505Sopenharmony_ci os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1); 1113e5b75505Sopenharmony_ci sae->tmp->pw_id[pos[1] - 1] = '\0'; 1114e5b75505Sopenharmony_ci wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier", 1115e5b75505Sopenharmony_ci sae->tmp->pw_id, pos[1] - 1); 1116e5b75505Sopenharmony_ci return WLAN_STATUS_SUCCESS; 1117e5b75505Sopenharmony_ci} 1118e5b75505Sopenharmony_ci 1119e5b75505Sopenharmony_ci 1120e5b75505Sopenharmony_ciu16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, 1121e5b75505Sopenharmony_ci const u8 **token, size_t *token_len, int *allowed_groups) 1122e5b75505Sopenharmony_ci{ 1123e5b75505Sopenharmony_ci const u8 *pos = data, *end = data + len; 1124e5b75505Sopenharmony_ci u16 res; 1125e5b75505Sopenharmony_ci 1126e5b75505Sopenharmony_ci /* Check Finite Cyclic Group */ 1127e5b75505Sopenharmony_ci if (end - pos < 2) 1128e5b75505Sopenharmony_ci return WLAN_STATUS_UNSPECIFIED_FAILURE; 1129e5b75505Sopenharmony_ci res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); 1130e5b75505Sopenharmony_ci if (res != WLAN_STATUS_SUCCESS) 1131e5b75505Sopenharmony_ci return res; 1132e5b75505Sopenharmony_ci pos += 2; 1133e5b75505Sopenharmony_ci 1134e5b75505Sopenharmony_ci /* Optional Anti-Clogging Token */ 1135e5b75505Sopenharmony_ci sae_parse_commit_token(sae, &pos, end, token, token_len); 1136e5b75505Sopenharmony_ci 1137e5b75505Sopenharmony_ci /* commit-scalar */ 1138e5b75505Sopenharmony_ci res = sae_parse_commit_scalar(sae, &pos, end); 1139e5b75505Sopenharmony_ci if (res != WLAN_STATUS_SUCCESS) 1140e5b75505Sopenharmony_ci return res; 1141e5b75505Sopenharmony_ci 1142e5b75505Sopenharmony_ci /* commit-element */ 1143e5b75505Sopenharmony_ci res = sae_parse_commit_element(sae, &pos, end); 1144e5b75505Sopenharmony_ci if (res != WLAN_STATUS_SUCCESS) 1145e5b75505Sopenharmony_ci return res; 1146e5b75505Sopenharmony_ci 1147e5b75505Sopenharmony_ci /* Optional Password Identifier element */ 1148e5b75505Sopenharmony_ci res = sae_parse_password_identifier(sae, pos, end); 1149e5b75505Sopenharmony_ci if (res != WLAN_STATUS_SUCCESS) 1150e5b75505Sopenharmony_ci return res; 1151e5b75505Sopenharmony_ci 1152e5b75505Sopenharmony_ci /* 1153e5b75505Sopenharmony_ci * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as 1154e5b75505Sopenharmony_ci * the values we sent which would be evidence of a reflection attack. 1155e5b75505Sopenharmony_ci */ 1156e5b75505Sopenharmony_ci if (!sae->tmp->own_commit_scalar || 1157e5b75505Sopenharmony_ci crypto_bignum_cmp(sae->tmp->own_commit_scalar, 1158e5b75505Sopenharmony_ci sae->peer_commit_scalar) != 0 || 1159e5b75505Sopenharmony_ci (sae->tmp->dh && 1160e5b75505Sopenharmony_ci (!sae->tmp->own_commit_element_ffc || 1161e5b75505Sopenharmony_ci crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, 1162e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ffc) != 0)) || 1163e5b75505Sopenharmony_ci (sae->tmp->ec && 1164e5b75505Sopenharmony_ci (!sae->tmp->own_commit_element_ecc || 1165e5b75505Sopenharmony_ci crypto_ec_point_cmp(sae->tmp->ec, 1166e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ecc, 1167e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ecc) != 0))) 1168e5b75505Sopenharmony_ci return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ 1169e5b75505Sopenharmony_ci 1170e5b75505Sopenharmony_ci /* 1171e5b75505Sopenharmony_ci * This is a reflection attack - return special value to trigger caller 1172e5b75505Sopenharmony_ci * to silently discard the frame instead of replying with a specific 1173e5b75505Sopenharmony_ci * status code. 1174e5b75505Sopenharmony_ci */ 1175e5b75505Sopenharmony_ci return SAE_SILENTLY_DISCARD; 1176e5b75505Sopenharmony_ci} 1177e5b75505Sopenharmony_ci 1178e5b75505Sopenharmony_ci 1179e5b75505Sopenharmony_cistatic void sae_cn_confirm(struct sae_data *sae, const u8 *sc, 1180e5b75505Sopenharmony_ci const struct crypto_bignum *scalar1, 1181e5b75505Sopenharmony_ci const u8 *element1, size_t element1_len, 1182e5b75505Sopenharmony_ci const struct crypto_bignum *scalar2, 1183e5b75505Sopenharmony_ci const u8 *element2, size_t element2_len, 1184e5b75505Sopenharmony_ci u8 *confirm) 1185e5b75505Sopenharmony_ci{ 1186e5b75505Sopenharmony_ci const u8 *addr[5]; 1187e5b75505Sopenharmony_ci size_t len[5]; 1188e5b75505Sopenharmony_ci u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; 1189e5b75505Sopenharmony_ci 1190e5b75505Sopenharmony_ci /* Confirm 1191e5b75505Sopenharmony_ci * CN(key, X, Y, Z, ...) = 1192e5b75505Sopenharmony_ci * HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) 1193e5b75505Sopenharmony_ci * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, 1194e5b75505Sopenharmony_ci * peer-commit-scalar, PEER-COMMIT-ELEMENT) 1195e5b75505Sopenharmony_ci * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, 1196e5b75505Sopenharmony_ci * PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) 1197e5b75505Sopenharmony_ci */ 1198e5b75505Sopenharmony_ci addr[0] = sc; 1199e5b75505Sopenharmony_ci len[0] = 2; 1200e5b75505Sopenharmony_ci crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), 1201e5b75505Sopenharmony_ci sae->tmp->prime_len); 1202e5b75505Sopenharmony_ci addr[1] = scalar_b1; 1203e5b75505Sopenharmony_ci len[1] = sae->tmp->prime_len; 1204e5b75505Sopenharmony_ci addr[2] = element1; 1205e5b75505Sopenharmony_ci len[2] = element1_len; 1206e5b75505Sopenharmony_ci crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), 1207e5b75505Sopenharmony_ci sae->tmp->prime_len); 1208e5b75505Sopenharmony_ci addr[3] = scalar_b2; 1209e5b75505Sopenharmony_ci len[3] = sae->tmp->prime_len; 1210e5b75505Sopenharmony_ci addr[4] = element2; 1211e5b75505Sopenharmony_ci len[4] = element2_len; 1212e5b75505Sopenharmony_ci hmac_sha256_vector(sae->tmp->kck, sizeof(sae->tmp->kck), 5, addr, len, 1213e5b75505Sopenharmony_ci confirm); 1214e5b75505Sopenharmony_ci} 1215e5b75505Sopenharmony_ci 1216e5b75505Sopenharmony_ci 1217e5b75505Sopenharmony_cistatic void sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, 1218e5b75505Sopenharmony_ci const struct crypto_bignum *scalar1, 1219e5b75505Sopenharmony_ci const struct crypto_ec_point *element1, 1220e5b75505Sopenharmony_ci const struct crypto_bignum *scalar2, 1221e5b75505Sopenharmony_ci const struct crypto_ec_point *element2, 1222e5b75505Sopenharmony_ci u8 *confirm) 1223e5b75505Sopenharmony_ci{ 1224e5b75505Sopenharmony_ci u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; 1225e5b75505Sopenharmony_ci u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; 1226e5b75505Sopenharmony_ci 1227e5b75505Sopenharmony_ci crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, 1228e5b75505Sopenharmony_ci element_b1 + sae->tmp->prime_len); 1229e5b75505Sopenharmony_ci crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, 1230e5b75505Sopenharmony_ci element_b2 + sae->tmp->prime_len); 1231e5b75505Sopenharmony_ci 1232e5b75505Sopenharmony_ci sae_cn_confirm(sae, sc, scalar1, element_b1, 2 * sae->tmp->prime_len, 1233e5b75505Sopenharmony_ci scalar2, element_b2, 2 * sae->tmp->prime_len, confirm); 1234e5b75505Sopenharmony_ci} 1235e5b75505Sopenharmony_ci 1236e5b75505Sopenharmony_ci 1237e5b75505Sopenharmony_cistatic void sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, 1238e5b75505Sopenharmony_ci const struct crypto_bignum *scalar1, 1239e5b75505Sopenharmony_ci const struct crypto_bignum *element1, 1240e5b75505Sopenharmony_ci const struct crypto_bignum *scalar2, 1241e5b75505Sopenharmony_ci const struct crypto_bignum *element2, 1242e5b75505Sopenharmony_ci u8 *confirm) 1243e5b75505Sopenharmony_ci{ 1244e5b75505Sopenharmony_ci u8 element_b1[SAE_MAX_PRIME_LEN]; 1245e5b75505Sopenharmony_ci u8 element_b2[SAE_MAX_PRIME_LEN]; 1246e5b75505Sopenharmony_ci 1247e5b75505Sopenharmony_ci crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), 1248e5b75505Sopenharmony_ci sae->tmp->prime_len); 1249e5b75505Sopenharmony_ci crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), 1250e5b75505Sopenharmony_ci sae->tmp->prime_len); 1251e5b75505Sopenharmony_ci 1252e5b75505Sopenharmony_ci sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, 1253e5b75505Sopenharmony_ci scalar2, element_b2, sae->tmp->prime_len, confirm); 1254e5b75505Sopenharmony_ci} 1255e5b75505Sopenharmony_ci 1256e5b75505Sopenharmony_ci 1257e5b75505Sopenharmony_civoid sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) 1258e5b75505Sopenharmony_ci{ 1259e5b75505Sopenharmony_ci const u8 *sc; 1260e5b75505Sopenharmony_ci 1261e5b75505Sopenharmony_ci if (sae->tmp == NULL) 1262e5b75505Sopenharmony_ci return; 1263e5b75505Sopenharmony_ci 1264e5b75505Sopenharmony_ci /* Send-Confirm */ 1265e5b75505Sopenharmony_ci sc = wpabuf_put(buf, 0); 1266e5b75505Sopenharmony_ci wpabuf_put_le16(buf, sae->send_confirm); 1267e5b75505Sopenharmony_ci if (sae->send_confirm < 0xffff) 1268e5b75505Sopenharmony_ci sae->send_confirm++; 1269e5b75505Sopenharmony_ci 1270e5b75505Sopenharmony_ci if (sae->tmp->ec) 1271e5b75505Sopenharmony_ci sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, 1272e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ecc, 1273e5b75505Sopenharmony_ci sae->peer_commit_scalar, 1274e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ecc, 1275e5b75505Sopenharmony_ci wpabuf_put(buf, SHA256_MAC_LEN)); 1276e5b75505Sopenharmony_ci else 1277e5b75505Sopenharmony_ci sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, 1278e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ffc, 1279e5b75505Sopenharmony_ci sae->peer_commit_scalar, 1280e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ffc, 1281e5b75505Sopenharmony_ci wpabuf_put(buf, SHA256_MAC_LEN)); 1282e5b75505Sopenharmony_ci} 1283e5b75505Sopenharmony_ci 1284e5b75505Sopenharmony_ci 1285e5b75505Sopenharmony_ciint sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) 1286e5b75505Sopenharmony_ci{ 1287e5b75505Sopenharmony_ci u8 verifier[SHA256_MAC_LEN]; 1288e5b75505Sopenharmony_ci 1289e5b75505Sopenharmony_ci if (len < 2 + SHA256_MAC_LEN) { 1290e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); 1291e5b75505Sopenharmony_ci return -1; 1292e5b75505Sopenharmony_ci } 1293e5b75505Sopenharmony_ci 1294e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); 1295e5b75505Sopenharmony_ci 1296e5b75505Sopenharmony_ci if (!sae->tmp || !sae->peer_commit_scalar || 1297e5b75505Sopenharmony_ci !sae->tmp->own_commit_scalar) { 1298e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); 1299e5b75505Sopenharmony_ci return -1; 1300e5b75505Sopenharmony_ci } 1301e5b75505Sopenharmony_ci 1302e5b75505Sopenharmony_ci if (sae->tmp->ec) { 1303e5b75505Sopenharmony_ci if (!sae->tmp->peer_commit_element_ecc || 1304e5b75505Sopenharmony_ci !sae->tmp->own_commit_element_ecc) 1305e5b75505Sopenharmony_ci return -1; 1306e5b75505Sopenharmony_ci sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, 1307e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ecc, 1308e5b75505Sopenharmony_ci sae->tmp->own_commit_scalar, 1309e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ecc, 1310e5b75505Sopenharmony_ci verifier); 1311e5b75505Sopenharmony_ci } else { 1312e5b75505Sopenharmony_ci if (!sae->tmp->peer_commit_element_ffc || 1313e5b75505Sopenharmony_ci !sae->tmp->own_commit_element_ffc) 1314e5b75505Sopenharmony_ci return -1; 1315e5b75505Sopenharmony_ci sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, 1316e5b75505Sopenharmony_ci sae->tmp->peer_commit_element_ffc, 1317e5b75505Sopenharmony_ci sae->tmp->own_commit_scalar, 1318e5b75505Sopenharmony_ci sae->tmp->own_commit_element_ffc, 1319e5b75505Sopenharmony_ci verifier); 1320e5b75505Sopenharmony_ci } 1321e5b75505Sopenharmony_ci 1322e5b75505Sopenharmony_ci if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) { 1323e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); 1324e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", 1325e5b75505Sopenharmony_ci data + 2, SHA256_MAC_LEN); 1326e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", 1327e5b75505Sopenharmony_ci verifier, SHA256_MAC_LEN); 1328e5b75505Sopenharmony_ci return -1; 1329e5b75505Sopenharmony_ci } 1330e5b75505Sopenharmony_ci 1331e5b75505Sopenharmony_ci return 0; 1332e5b75505Sopenharmony_ci} 1333e5b75505Sopenharmony_ci 1334e5b75505Sopenharmony_ci 1335e5b75505Sopenharmony_ciconst char * sae_state_txt(enum sae_state state) 1336e5b75505Sopenharmony_ci{ 1337e5b75505Sopenharmony_ci switch (state) { 1338e5b75505Sopenharmony_ci case SAE_NOTHING: 1339e5b75505Sopenharmony_ci return "Nothing"; 1340e5b75505Sopenharmony_ci case SAE_COMMITTED: 1341e5b75505Sopenharmony_ci return "Committed"; 1342e5b75505Sopenharmony_ci case SAE_CONFIRMED: 1343e5b75505Sopenharmony_ci return "Confirmed"; 1344e5b75505Sopenharmony_ci case SAE_ACCEPTED: 1345e5b75505Sopenharmony_ci return "Accepted"; 1346e5b75505Sopenharmony_ci } 1347e5b75505Sopenharmony_ci return "?"; 1348e5b75505Sopenharmony_ci} 1349