| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Simultaneous authentication of equals | 
| Dmitry Shmidt | e466304 | 2016-04-04 10:07:49 -0700 | [diff] [blame] | 3 | * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi> | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 4 | * | 
|  | 5 | * This software may be distributed under the terms of the BSD license. | 
|  | 6 | * See README for more details. | 
|  | 7 | */ | 
|  | 8 |  | 
|  | 9 | #include "includes.h" | 
|  | 10 |  | 
|  | 11 | #include "common.h" | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 12 | #include "utils/const_time.h" | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 13 | #include "crypto/crypto.h" | 
|  | 14 | #include "crypto/sha256.h" | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 15 | #include "crypto/sha384.h" | 
|  | 16 | #include "crypto/sha512.h" | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 17 | #include "crypto/random.h" | 
|  | 18 | #include "crypto/dh_groups.h" | 
|  | 19 | #include "ieee802_11_defs.h" | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 20 | #include "dragonfly.h" | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 21 | #include "sae.h" | 
|  | 22 |  | 
|  | 23 |  | 
|  | 24 | int sae_set_group(struct sae_data *sae, int group) | 
|  | 25 | { | 
|  | 26 | struct sae_temporary_data *tmp; | 
|  | 27 |  | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 28 | #ifdef CONFIG_TESTING_OPTIONS | 
|  | 29 | /* Allow all groups for testing purposes in non-production builds. */ | 
|  | 30 | #else /* CONFIG_TESTING_OPTIONS */ | 
|  | 31 | if (!dragonfly_suitable_group(group, 0)) { | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 32 | wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group); | 
|  | 33 | return -1; | 
|  | 34 | } | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 35 | #endif /* CONFIG_TESTING_OPTIONS */ | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 36 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 37 | sae_clear_data(sae); | 
|  | 38 | tmp = sae->tmp = os_zalloc(sizeof(*tmp)); | 
|  | 39 | if (tmp == NULL) | 
|  | 40 | return -1; | 
|  | 41 |  | 
|  | 42 | /* First, check if this is an ECC group */ | 
|  | 43 | tmp->ec = crypto_ec_init(group); | 
|  | 44 | if (tmp->ec) { | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 45 | wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", | 
|  | 46 | group); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 47 | sae->group = group; | 
|  | 48 | tmp->prime_len = crypto_ec_prime_len(tmp->ec); | 
|  | 49 | tmp->prime = crypto_ec_get_prime(tmp->ec); | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 50 | tmp->order_len = crypto_ec_order_len(tmp->ec); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 51 | tmp->order = crypto_ec_get_order(tmp->ec); | 
|  | 52 | return 0; | 
|  | 53 | } | 
|  | 54 |  | 
|  | 55 | /* Not an ECC group, check FFC */ | 
|  | 56 | tmp->dh = dh_groups_get(group); | 
|  | 57 | if (tmp->dh) { | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 58 | wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", | 
|  | 59 | group); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 60 | sae->group = group; | 
|  | 61 | tmp->prime_len = tmp->dh->prime_len; | 
|  | 62 | if (tmp->prime_len > SAE_MAX_PRIME_LEN) { | 
|  | 63 | sae_clear_data(sae); | 
|  | 64 | return -1; | 
|  | 65 | } | 
|  | 66 |  | 
|  | 67 | tmp->prime_buf = crypto_bignum_init_set(tmp->dh->prime, | 
|  | 68 | tmp->prime_len); | 
|  | 69 | if (tmp->prime_buf == NULL) { | 
|  | 70 | sae_clear_data(sae); | 
|  | 71 | return -1; | 
|  | 72 | } | 
|  | 73 | tmp->prime = tmp->prime_buf; | 
|  | 74 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 75 | tmp->order_len = tmp->dh->order_len; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 76 | tmp->order_buf = crypto_bignum_init_set(tmp->dh->order, | 
|  | 77 | tmp->dh->order_len); | 
|  | 78 | if (tmp->order_buf == NULL) { | 
|  | 79 | sae_clear_data(sae); | 
|  | 80 | return -1; | 
|  | 81 | } | 
|  | 82 | tmp->order = tmp->order_buf; | 
|  | 83 |  | 
|  | 84 | return 0; | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 | /* Unsupported group */ | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 88 | wpa_printf(MSG_DEBUG, | 
|  | 89 | "SAE: Group %d not supported by the crypto library", group); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 90 | return -1; | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 |  | 
|  | 94 | void sae_clear_temp_data(struct sae_data *sae) | 
|  | 95 | { | 
|  | 96 | struct sae_temporary_data *tmp; | 
|  | 97 | if (sae == NULL || sae->tmp == NULL) | 
|  | 98 | return; | 
|  | 99 | tmp = sae->tmp; | 
|  | 100 | crypto_ec_deinit(tmp->ec); | 
|  | 101 | crypto_bignum_deinit(tmp->prime_buf, 0); | 
|  | 102 | crypto_bignum_deinit(tmp->order_buf, 0); | 
|  | 103 | crypto_bignum_deinit(tmp->sae_rand, 1); | 
|  | 104 | crypto_bignum_deinit(tmp->pwe_ffc, 1); | 
|  | 105 | crypto_bignum_deinit(tmp->own_commit_scalar, 0); | 
|  | 106 | crypto_bignum_deinit(tmp->own_commit_element_ffc, 0); | 
|  | 107 | crypto_bignum_deinit(tmp->peer_commit_element_ffc, 0); | 
|  | 108 | crypto_ec_point_deinit(tmp->pwe_ecc, 1); | 
|  | 109 | crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); | 
|  | 110 | crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); | 
| Dmitry Shmidt | fb45fd5 | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 111 | wpabuf_free(tmp->anti_clogging_token); | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 112 | wpabuf_free(tmp->own_rejected_groups); | 
|  | 113 | wpabuf_free(tmp->peer_rejected_groups); | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 114 | os_free(tmp->pw_id); | 
| Dmitry Shmidt | fb45fd5 | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 115 | bin_clear_free(tmp, sizeof(*tmp)); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 116 | sae->tmp = NULL; | 
|  | 117 | } | 
|  | 118 |  | 
|  | 119 |  | 
|  | 120 | void sae_clear_data(struct sae_data *sae) | 
|  | 121 | { | 
|  | 122 | if (sae == NULL) | 
|  | 123 | return; | 
|  | 124 | sae_clear_temp_data(sae); | 
|  | 125 | crypto_bignum_deinit(sae->peer_commit_scalar, 0); | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 126 | crypto_bignum_deinit(sae->peer_commit_scalar_accepted, 0); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 127 | os_memset(sae, 0, sizeof(*sae)); | 
|  | 128 | } | 
|  | 129 |  | 
|  | 130 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 131 | static void sae_pwd_seed_key(const u8 *addr1, const u8 *addr2, u8 *key) | 
|  | 132 | { | 
|  | 133 | wpa_printf(MSG_DEBUG, "SAE: PWE derivation - addr1=" MACSTR | 
|  | 134 | " addr2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); | 
|  | 135 | if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { | 
|  | 136 | os_memcpy(key, addr1, ETH_ALEN); | 
|  | 137 | os_memcpy(key + ETH_ALEN, addr2, ETH_ALEN); | 
|  | 138 | } else { | 
|  | 139 | os_memcpy(key, addr2, ETH_ALEN); | 
|  | 140 | os_memcpy(key + ETH_ALEN, addr1, ETH_ALEN); | 
|  | 141 | } | 
|  | 142 | } | 
|  | 143 |  | 
|  | 144 |  | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 145 | static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed, | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 146 | const u8 *prime, const u8 *qr, const u8 *qnr, | 
|  | 147 | u8 *pwd_value) | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 148 | { | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 149 | struct crypto_bignum *y_sqr, *x_cand; | 
|  | 150 | int res; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 151 | size_t bits; | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 152 | int cmp_prime; | 
|  | 153 | unsigned int in_range; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 154 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 155 | wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); | 
|  | 156 |  | 
|  | 157 | /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ | 
|  | 158 | bits = crypto_ec_prime_len_bits(sae->tmp->ec); | 
| Dmitry Shmidt | e466304 | 2016-04-04 10:07:49 -0700 | [diff] [blame] | 159 | if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", | 
|  | 160 | prime, sae->tmp->prime_len, pwd_value, bits) < 0) | 
|  | 161 | return -1; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 162 | if (bits % 8) | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 163 | buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 164 | wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", | 
|  | 165 | pwd_value, sae->tmp->prime_len); | 
|  | 166 |  | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 167 | cmp_prime = const_time_memcmp(pwd_value, prime, sae->tmp->prime_len); | 
|  | 168 | /* Create a const_time mask for selection based on prf result | 
|  | 169 | * being smaller than prime. */ | 
|  | 170 | in_range = const_time_fill_msb((unsigned int) cmp_prime); | 
|  | 171 | /* The algorithm description would skip the next steps if | 
| Hai Shalom | 4fbc08f | 2020-05-18 12:37:00 -0700 | [diff] [blame] | 172 | * cmp_prime >= 0 (return 0 here), but go through them regardless to | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 173 | * minimize externally observable differences in behavior. */ | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 174 |  | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 175 | x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); | 
|  | 176 | if (!x_cand) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 177 | return -1; | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 178 | y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand); | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 179 | crypto_bignum_deinit(x_cand, 1); | 
|  | 180 | if (!y_sqr) | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 181 | return -1; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 182 |  | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 183 | res = dragonfly_is_quadratic_residue_blind(sae->tmp->ec, qr, qnr, | 
|  | 184 | y_sqr); | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 185 | crypto_bignum_deinit(y_sqr, 1); | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 186 | if (res < 0) | 
|  | 187 | return res; | 
|  | 188 | return const_time_select_int(in_range, res, 0); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 189 | } | 
|  | 190 |  | 
|  | 191 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 192 | /* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided | 
|  | 193 | * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */ | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 194 | static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed, | 
|  | 195 | struct crypto_bignum *pwe) | 
|  | 196 | { | 
|  | 197 | u8 pwd_value[SAE_MAX_PRIME_LEN]; | 
|  | 198 | size_t bits = sae->tmp->prime_len * 8; | 
|  | 199 | u8 exp[1]; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 200 | struct crypto_bignum *a, *b = NULL; | 
|  | 201 | int res, is_val; | 
|  | 202 | u8 pwd_value_valid; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 203 |  | 
|  | 204 | wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN); | 
|  | 205 |  | 
|  | 206 | /* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */ | 
| Dmitry Shmidt | e466304 | 2016-04-04 10:07:49 -0700 | [diff] [blame] | 207 | if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking", | 
|  | 208 | sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value, | 
|  | 209 | bits) < 0) | 
|  | 210 | return -1; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 211 | wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value, | 
|  | 212 | sae->tmp->prime_len); | 
|  | 213 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 214 | /* Check whether pwd-value < p */ | 
|  | 215 | res = const_time_memcmp(pwd_value, sae->tmp->dh->prime, | 
|  | 216 | sae->tmp->prime_len); | 
|  | 217 | /* pwd-value >= p is invalid, so res is < 0 for the valid cases and | 
|  | 218 | * the negative sign can be used to fill the mask for constant time | 
|  | 219 | * selection */ | 
|  | 220 | pwd_value_valid = const_time_fill_msb(res); | 
|  | 221 |  | 
|  | 222 | /* If pwd-value >= p, force pwd-value to be < p and perform the | 
|  | 223 | * calculations anyway to hide timing difference. The derived PWE will | 
|  | 224 | * be ignored in that case. */ | 
|  | 225 | pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 226 |  | 
|  | 227 | /* PWE = pwd-value^((p-1)/r) modulo p */ | 
|  | 228 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 229 | res = -1; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 230 | a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len); | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 231 | if (!a) | 
|  | 232 | goto fail; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 233 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 234 | /* This is an optimization based on the used group that does not depend | 
|  | 235 | * on the password in any way, so it is fine to use separate branches | 
|  | 236 | * for this step without constant time operations. */ | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 237 | if (sae->tmp->dh->safe_prime) { | 
|  | 238 | /* | 
|  | 239 | * r = (p-1)/2 for the group used here, so this becomes: | 
|  | 240 | * PWE = pwd-value^2 modulo p | 
|  | 241 | */ | 
|  | 242 | exp[0] = 2; | 
|  | 243 | b = crypto_bignum_init_set(exp, sizeof(exp)); | 
|  | 244 | } else { | 
|  | 245 | /* Calculate exponent: (p-1)/r */ | 
|  | 246 | exp[0] = 1; | 
|  | 247 | b = crypto_bignum_init_set(exp, sizeof(exp)); | 
|  | 248 | if (b == NULL || | 
|  | 249 | crypto_bignum_sub(sae->tmp->prime, b, b) < 0 || | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 250 | crypto_bignum_div(b, sae->tmp->order, b) < 0) | 
|  | 251 | goto fail; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 252 | } | 
|  | 253 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 254 | if (!b) | 
|  | 255 | goto fail; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 256 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 257 | res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe); | 
|  | 258 | if (res < 0) | 
|  | 259 | goto fail; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 260 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 261 | /* There were no fatal errors in calculations, so determine the return | 
|  | 262 | * value using constant time operations. We get here for number of | 
|  | 263 | * invalid cases which are cleared here after having performed all the | 
|  | 264 | * computation. PWE is valid if pwd-value was less than prime and | 
|  | 265 | * PWE > 1. Start with pwd-value check first and then use constant time | 
|  | 266 | * operations to clear res to 0 if PWE is 0 or 1. | 
|  | 267 | */ | 
|  | 268 | res = const_time_select_u8(pwd_value_valid, 1, 0); | 
|  | 269 | is_val = crypto_bignum_is_zero(pwe); | 
|  | 270 | res = const_time_select_u8(const_time_is_zero(is_val), res, 0); | 
|  | 271 | is_val = crypto_bignum_is_one(pwe); | 
|  | 272 | res = const_time_select_u8(const_time_is_zero(is_val), res, 0); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 273 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 274 | fail: | 
|  | 275 | crypto_bignum_deinit(a, 1); | 
|  | 276 | crypto_bignum_deinit(b, 1); | 
|  | 277 | return res; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 278 | } | 
|  | 279 |  | 
|  | 280 |  | 
|  | 281 | static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, | 
|  | 282 | const u8 *addr2, const u8 *password, | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 283 | size_t password_len, const char *identifier) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 284 | { | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 285 | u8 counter, k; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 286 | u8 addrs[2 * ETH_ALEN]; | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 287 | const u8 *addr[3]; | 
|  | 288 | size_t len[3]; | 
|  | 289 | size_t num_elem; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 290 | u8 *dummy_password, *tmp_password; | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 291 | int pwd_seed_odd = 0; | 
|  | 292 | u8 prime[SAE_MAX_ECC_PRIME_LEN]; | 
|  | 293 | size_t prime_len; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 294 | struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL; | 
|  | 295 | u8 x_bin[SAE_MAX_ECC_PRIME_LEN]; | 
|  | 296 | u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN]; | 
|  | 297 | u8 qr_bin[SAE_MAX_ECC_PRIME_LEN]; | 
|  | 298 | u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN]; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 299 | int res = -1; | 
|  | 300 | u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* | 
|  | 301 | * mask */ | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 302 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 303 | os_memset(x_bin, 0, sizeof(x_bin)); | 
|  | 304 |  | 
|  | 305 | dummy_password = os_malloc(password_len); | 
|  | 306 | tmp_password = os_malloc(password_len); | 
|  | 307 | if (!dummy_password || !tmp_password || | 
|  | 308 | random_get_bytes(dummy_password, password_len) < 0) | 
|  | 309 | goto fail; | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 310 |  | 
|  | 311 | prime_len = sae->tmp->prime_len; | 
|  | 312 | if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), | 
|  | 313 | prime_len) < 0) | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 314 | goto fail; | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 315 |  | 
|  | 316 | /* | 
|  | 317 | * Create a random quadratic residue (qr) and quadratic non-residue | 
|  | 318 | * (qnr) modulo p for blinding purposes during the loop. | 
|  | 319 | */ | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 320 | if (dragonfly_get_random_qr_qnr(sae->tmp->prime, &qr, &qnr) < 0 || | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 321 | crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 || | 
|  | 322 | crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0) | 
|  | 323 | goto fail; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 324 |  | 
|  | 325 | wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", | 
|  | 326 | password, password_len); | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 327 | if (identifier) | 
|  | 328 | wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", | 
|  | 329 | identifier); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 330 |  | 
|  | 331 | /* | 
|  | 332 | * H(salt, ikm) = HMAC-SHA256(salt, ikm) | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 333 | * base = password [|| identifier] | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 334 | * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 335 | *              base || counter) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 336 | */ | 
|  | 337 | sae_pwd_seed_key(addr1, addr2, addrs); | 
|  | 338 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 339 | addr[0] = tmp_password; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 340 | len[0] = password_len; | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 341 | num_elem = 1; | 
|  | 342 | if (identifier) { | 
|  | 343 | addr[num_elem] = (const u8 *) identifier; | 
|  | 344 | len[num_elem] = os_strlen(identifier); | 
|  | 345 | num_elem++; | 
|  | 346 | } | 
|  | 347 | addr[num_elem] = &counter; | 
|  | 348 | len[num_elem] = sizeof(counter); | 
|  | 349 | num_elem++; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 350 |  | 
|  | 351 | /* | 
|  | 352 | * Continue for at least k iterations to protect against side-channel | 
|  | 353 | * attacks that attempt to determine the number of iterations required | 
|  | 354 | * in the loop. | 
|  | 355 | */ | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 356 | k = dragonfly_min_pwe_loop_iter(sae->group); | 
|  | 357 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 358 | for (counter = 1; counter <= k || !found; counter++) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 359 | u8 pwd_seed[SHA256_MAC_LEN]; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 360 |  | 
|  | 361 | if (counter > 200) { | 
|  | 362 | /* This should not happen in practice */ | 
|  | 363 | wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); | 
|  | 364 | break; | 
|  | 365 | } | 
|  | 366 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 367 | wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter); | 
|  | 368 | const_time_select_bin(found, dummy_password, password, | 
|  | 369 | password_len, tmp_password); | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 370 | if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, | 
|  | 371 | addr, len, pwd_seed) < 0) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 372 | break; | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 373 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 374 | res = sae_test_pwd_seed_ecc(sae, pwd_seed, | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 375 | prime, qr_bin, qnr_bin, x_cand_bin); | 
|  | 376 | const_time_select_bin(found, x_bin, x_cand_bin, prime_len, | 
|  | 377 | x_bin); | 
|  | 378 | pwd_seed_odd = const_time_select_u8( | 
|  | 379 | found, pwd_seed_odd, | 
|  | 380 | pwd_seed[SHA256_MAC_LEN - 1] & 0x01); | 
|  | 381 | os_memset(pwd_seed, 0, sizeof(pwd_seed)); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 382 | if (res < 0) | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 383 | goto fail; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 384 | /* Need to minimize differences in handling res == 0 and 1 here | 
|  | 385 | * to avoid differences in timing and instruction cache access, | 
|  | 386 | * so use const_time_select_*() to make local copies of the | 
|  | 387 | * values based on whether this loop iteration was the one that | 
|  | 388 | * found the pwd-seed/x. */ | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 389 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 390 | /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them | 
|  | 391 | * (with res converted to 0/0xff) handles this in constant time. | 
|  | 392 | */ | 
|  | 393 | found |= res * 0xff; | 
|  | 394 | wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x", | 
|  | 395 | res, found); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 396 | } | 
|  | 397 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 398 | if (!found) { | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 399 | wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE"); | 
|  | 400 | res = -1; | 
|  | 401 | goto fail; | 
|  | 402 | } | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 403 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 404 | x = crypto_bignum_init_set(x_bin, prime_len); | 
|  | 405 | if (!x) { | 
|  | 406 | res = -1; | 
|  | 407 | goto fail; | 
|  | 408 | } | 
|  | 409 |  | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 410 | if (!sae->tmp->pwe_ecc) | 
|  | 411 | sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec); | 
|  | 412 | if (!sae->tmp->pwe_ecc) | 
|  | 413 | res = -1; | 
|  | 414 | else | 
|  | 415 | res = crypto_ec_point_solve_y_coord(sae->tmp->ec, | 
|  | 416 | sae->tmp->pwe_ecc, x, | 
|  | 417 | pwd_seed_odd); | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 418 | if (res < 0) { | 
|  | 419 | /* | 
|  | 420 | * This should not happen since we already checked that there | 
|  | 421 | * is a result. | 
|  | 422 | */ | 
|  | 423 | wpa_printf(MSG_DEBUG, "SAE: Could not solve y"); | 
|  | 424 | } | 
|  | 425 |  | 
|  | 426 | fail: | 
|  | 427 | crypto_bignum_deinit(qr, 0); | 
|  | 428 | crypto_bignum_deinit(qnr, 0); | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 429 | os_free(dummy_password); | 
|  | 430 | bin_clear_free(tmp_password, password_len); | 
|  | 431 | crypto_bignum_deinit(x, 1); | 
|  | 432 | os_memset(x_bin, 0, sizeof(x_bin)); | 
|  | 433 | os_memset(x_cand_bin, 0, sizeof(x_cand_bin)); | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 434 |  | 
|  | 435 | return res; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 436 | } | 
|  | 437 |  | 
|  | 438 |  | 
|  | 439 | static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, | 
|  | 440 | const u8 *addr2, const u8 *password, | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 441 | size_t password_len, const char *identifier) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 442 | { | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 443 | u8 counter, k, sel_counter = 0; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 444 | u8 addrs[2 * ETH_ALEN]; | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 445 | const u8 *addr[3]; | 
|  | 446 | size_t len[3]; | 
|  | 447 | size_t num_elem; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 448 | u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_* | 
|  | 449 | * mask */ | 
|  | 450 | u8 mask; | 
|  | 451 | struct crypto_bignum *pwe; | 
|  | 452 | size_t prime_len = sae->tmp->prime_len * 8; | 
|  | 453 | u8 *pwe_buf; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 454 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 455 | crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); | 
|  | 456 | sae->tmp->pwe_ffc = NULL; | 
|  | 457 |  | 
|  | 458 | /* Allocate a buffer to maintain selected and candidate PWE for constant | 
|  | 459 | * time selection. */ | 
|  | 460 | pwe_buf = os_zalloc(prime_len * 2); | 
|  | 461 | pwe = crypto_bignum_init(); | 
|  | 462 | if (!pwe_buf || !pwe) | 
|  | 463 | goto fail; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 464 |  | 
|  | 465 | wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", | 
|  | 466 | password, password_len); | 
|  | 467 |  | 
|  | 468 | /* | 
|  | 469 | * H(salt, ikm) = HMAC-SHA256(salt, ikm) | 
|  | 470 | * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 471 | *              password [|| identifier] || counter) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 472 | */ | 
|  | 473 | sae_pwd_seed_key(addr1, addr2, addrs); | 
|  | 474 |  | 
|  | 475 | addr[0] = password; | 
|  | 476 | len[0] = password_len; | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 477 | num_elem = 1; | 
|  | 478 | if (identifier) { | 
|  | 479 | addr[num_elem] = (const u8 *) identifier; | 
|  | 480 | len[num_elem] = os_strlen(identifier); | 
|  | 481 | num_elem++; | 
|  | 482 | } | 
|  | 483 | addr[num_elem] = &counter; | 
|  | 484 | len[num_elem] = sizeof(counter); | 
|  | 485 | num_elem++; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 486 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 487 | k = dragonfly_min_pwe_loop_iter(sae->group); | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 488 |  | 
|  | 489 | for (counter = 1; counter <= k || !found; counter++) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 490 | u8 pwd_seed[SHA256_MAC_LEN]; | 
|  | 491 | int res; | 
|  | 492 |  | 
|  | 493 | if (counter > 200) { | 
|  | 494 | /* This should not happen in practice */ | 
|  | 495 | wpa_printf(MSG_DEBUG, "SAE: Failed to derive PWE"); | 
|  | 496 | break; | 
|  | 497 | } | 
|  | 498 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 499 | wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter); | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 500 | if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, | 
|  | 501 | addr, len, pwd_seed) < 0) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 502 | break; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 503 | res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe); | 
|  | 504 | /* res is -1 for fatal failure, 0 if a valid PWE was not found, | 
|  | 505 | * or 1 if a valid PWE was found. */ | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 506 | if (res < 0) | 
|  | 507 | break; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 508 | /* Store the candidate PWE into the second half of pwe_buf and | 
|  | 509 | * the selected PWE in the beginning of pwe_buf using constant | 
|  | 510 | * time selection. */ | 
|  | 511 | if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len, | 
|  | 512 | prime_len) < 0) | 
|  | 513 | break; | 
|  | 514 | const_time_select_bin(found, pwe_buf, pwe_buf + prime_len, | 
|  | 515 | prime_len, pwe_buf); | 
|  | 516 | sel_counter = const_time_select_u8(found, sel_counter, counter); | 
|  | 517 | mask = const_time_eq_u8(res, 1); | 
|  | 518 | found = const_time_select_u8(found, found, mask); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 519 | } | 
|  | 520 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 521 | if (!found) | 
|  | 522 | goto fail; | 
|  | 523 |  | 
|  | 524 | wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter); | 
|  | 525 | sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len); | 
|  | 526 | fail: | 
|  | 527 | crypto_bignum_deinit(pwe, 1); | 
|  | 528 | bin_clear_free(pwe_buf, prime_len * 2); | 
|  | 529 | return sae->tmp->pwe_ffc ? 0 : -1; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 530 | } | 
|  | 531 |  | 
|  | 532 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 533 | static int hkdf_extract(size_t hash_len, const u8 *salt, size_t salt_len, | 
|  | 534 | size_t num_elem, const u8 *addr[], const size_t len[], | 
|  | 535 | u8 *prk) | 
|  | 536 | { | 
|  | 537 | if (hash_len == 32) | 
|  | 538 | return hmac_sha256_vector(salt, salt_len, num_elem, addr, len, | 
|  | 539 | prk); | 
|  | 540 | #ifdef CONFIG_SHA384 | 
|  | 541 | if (hash_len == 48) | 
|  | 542 | return hmac_sha384_vector(salt, salt_len, num_elem, addr, len, | 
|  | 543 | prk); | 
|  | 544 | #endif /* CONFIG_SHA384 */ | 
|  | 545 | #ifdef CONFIG_SHA512 | 
|  | 546 | if (hash_len == 64) | 
|  | 547 | return hmac_sha512_vector(salt, salt_len, num_elem, addr, len, | 
|  | 548 | prk); | 
|  | 549 | #endif /* CONFIG_SHA512 */ | 
|  | 550 | return -1; | 
|  | 551 | } | 
|  | 552 |  | 
|  | 553 |  | 
|  | 554 | static int hkdf_expand(size_t hash_len, const u8 *prk, size_t prk_len, | 
|  | 555 | const char *info, u8 *okm, size_t okm_len) | 
|  | 556 | { | 
|  | 557 | size_t info_len = os_strlen(info); | 
|  | 558 |  | 
|  | 559 | if (hash_len == 32) | 
|  | 560 | return hmac_sha256_kdf(prk, prk_len, NULL, | 
|  | 561 | (const u8 *) info, info_len, | 
|  | 562 | okm, okm_len); | 
|  | 563 | #ifdef CONFIG_SHA384 | 
|  | 564 | if (hash_len == 48) | 
|  | 565 | return hmac_sha384_kdf(prk, prk_len, NULL, | 
|  | 566 | (const u8 *) info, info_len, | 
|  | 567 | okm, okm_len); | 
|  | 568 | #endif /* CONFIG_SHA384 */ | 
|  | 569 | #ifdef CONFIG_SHA512 | 
|  | 570 | if (hash_len == 64) | 
|  | 571 | return hmac_sha512_kdf(prk, prk_len, NULL, | 
|  | 572 | (const u8 *) info, info_len, | 
|  | 573 | okm, okm_len); | 
|  | 574 | #endif /* CONFIG_SHA512 */ | 
|  | 575 | return -1; | 
|  | 576 | } | 
|  | 577 |  | 
|  | 578 |  | 
|  | 579 | static int sswu_curve_param(int group, int *z) | 
|  | 580 | { | 
|  | 581 | switch (group) { | 
|  | 582 | case 19: | 
| Ahmed ElArabawy | 0ff61c5 | 2019-12-26 12:38:39 -0800 | [diff] [blame] | 583 | *z = -10; | 
|  | 584 | return 0; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 585 | case 20: | 
| Ahmed ElArabawy | 0ff61c5 | 2019-12-26 12:38:39 -0800 | [diff] [blame] | 586 | *z = -12; | 
|  | 587 | return 0; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 588 | case 21: | 
| Ahmed ElArabawy | 0ff61c5 | 2019-12-26 12:38:39 -0800 | [diff] [blame] | 589 | *z = -4; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 590 | return 0; | 
|  | 591 | case 25: | 
|  | 592 | case 29: | 
|  | 593 | *z = -5; | 
|  | 594 | return 0; | 
|  | 595 | case 26: | 
| Ahmed ElArabawy | 0ff61c5 | 2019-12-26 12:38:39 -0800 | [diff] [blame] | 596 | *z = 31; | 
|  | 597 | return 0; | 
|  | 598 | case 28: | 
|  | 599 | *z = -2; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 600 | return 0; | 
|  | 601 | case 30: | 
| Ahmed ElArabawy | 0ff61c5 | 2019-12-26 12:38:39 -0800 | [diff] [blame] | 602 | *z = 7; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 603 | return 0; | 
|  | 604 | } | 
|  | 605 |  | 
|  | 606 | return -1; | 
|  | 607 | } | 
|  | 608 |  | 
|  | 609 |  | 
|  | 610 | static void debug_print_bignum(const char *title, const struct crypto_bignum *a, | 
|  | 611 | size_t prime_len) | 
|  | 612 | { | 
|  | 613 | u8 *bin; | 
|  | 614 |  | 
|  | 615 | bin = os_malloc(prime_len); | 
|  | 616 | if (bin && crypto_bignum_to_bin(a, bin, prime_len, prime_len) >= 0) | 
|  | 617 | wpa_hexdump_key(MSG_DEBUG, title, bin, prime_len); | 
|  | 618 | else | 
|  | 619 | wpa_printf(MSG_DEBUG, "Could not print bignum (%s)", title); | 
|  | 620 | bin_clear_free(bin, prime_len); | 
|  | 621 | } | 
|  | 622 |  | 
|  | 623 |  | 
|  | 624 | static struct crypto_ec_point * sswu(struct crypto_ec *ec, int group, | 
|  | 625 | const struct crypto_bignum *u) | 
|  | 626 | { | 
|  | 627 | int z_int; | 
|  | 628 | const struct crypto_bignum *a, *b, *prime; | 
|  | 629 | struct crypto_bignum *u2, *t1, *t2, *z, *t, *zero, *one, *two, *three, | 
|  | 630 | *x1a, *x1b, *y = NULL; | 
|  | 631 | struct crypto_bignum *x1 = NULL, *x2, *gx1, *gx2, *v = NULL; | 
|  | 632 | unsigned int m_is_zero, is_qr, is_eq; | 
|  | 633 | size_t prime_len; | 
|  | 634 | u8 bin[SAE_MAX_ECC_PRIME_LEN]; | 
|  | 635 | u8 bin1[SAE_MAX_ECC_PRIME_LEN]; | 
|  | 636 | u8 bin2[SAE_MAX_ECC_PRIME_LEN]; | 
|  | 637 | u8 x_y[2 * SAE_MAX_ECC_PRIME_LEN]; | 
|  | 638 | struct crypto_ec_point *p = NULL; | 
|  | 639 |  | 
|  | 640 | if (sswu_curve_param(group, &z_int) < 0) | 
|  | 641 | return NULL; | 
|  | 642 |  | 
|  | 643 | prime = crypto_ec_get_prime(ec); | 
|  | 644 | prime_len = crypto_ec_prime_len(ec); | 
|  | 645 | a = crypto_ec_get_a(ec); | 
|  | 646 | b = crypto_ec_get_b(ec); | 
|  | 647 |  | 
|  | 648 | u2 = crypto_bignum_init(); | 
|  | 649 | t1 = crypto_bignum_init(); | 
|  | 650 | t2 = crypto_bignum_init(); | 
|  | 651 | z = crypto_bignum_init_uint(abs(z_int)); | 
|  | 652 | t = crypto_bignum_init(); | 
|  | 653 | zero = crypto_bignum_init_uint(0); | 
|  | 654 | one = crypto_bignum_init_uint(1); | 
|  | 655 | two = crypto_bignum_init_uint(2); | 
|  | 656 | three = crypto_bignum_init_uint(3); | 
|  | 657 | x1a = crypto_bignum_init(); | 
|  | 658 | x1b = crypto_bignum_init(); | 
|  | 659 | x2 = crypto_bignum_init(); | 
|  | 660 | gx1 = crypto_bignum_init(); | 
|  | 661 | gx2 = crypto_bignum_init(); | 
|  | 662 | if (!u2 || !t1 || !t2 || !z || !t || !zero || !one || !two || !three || | 
|  | 663 | !x1a || !x1b || !x2 || !gx1 || !gx2) | 
|  | 664 | goto fail; | 
|  | 665 |  | 
|  | 666 | if (z_int < 0 && crypto_bignum_sub(prime, z, z) < 0) | 
|  | 667 | goto fail; | 
|  | 668 |  | 
|  | 669 | /* m = z^2 * u^4 + z * u^2 */ | 
|  | 670 | /* --> tmp = z * u^2, m = tmp^2 + tmp */ | 
|  | 671 |  | 
|  | 672 | /* u2 = u^2 | 
|  | 673 | * t1 = z * u2 | 
|  | 674 | * t2 = t1^2 | 
|  | 675 | * m = t1 = t1 + t2 */ | 
|  | 676 | if (crypto_bignum_sqrmod(u, prime, u2) < 0 || | 
|  | 677 | crypto_bignum_mulmod(z, u2, prime, t1) < 0 || | 
|  | 678 | crypto_bignum_sqrmod(t1, prime, t2) < 0 || | 
|  | 679 | crypto_bignum_addmod(t1, t2, prime, t1) < 0) | 
|  | 680 | goto fail; | 
|  | 681 | debug_print_bignum("SSWU: m", t1, prime_len); | 
|  | 682 |  | 
|  | 683 | /* l = CEQ(m, 0) | 
|  | 684 | * t = CSEL(l, 0, inverse(m); where inverse(x) is calculated as | 
|  | 685 | * x^(p-2) modulo p which will handle m == 0 case correctly */ | 
|  | 686 | /* TODO: Make sure crypto_bignum_is_zero() is constant time */ | 
|  | 687 | m_is_zero = const_time_eq(crypto_bignum_is_zero(t1), 1); | 
|  | 688 | /* t = m^(p-2) modulo p */ | 
|  | 689 | if (crypto_bignum_sub(prime, two, t2) < 0 || | 
|  | 690 | crypto_bignum_exptmod(t1, t2, prime, t) < 0) | 
|  | 691 | goto fail; | 
|  | 692 | debug_print_bignum("SSWU: t", t, prime_len); | 
|  | 693 |  | 
|  | 694 | /* b / (z * a) */ | 
|  | 695 | if (crypto_bignum_mulmod(z, a, prime, t1) < 0 || | 
|  | 696 | crypto_bignum_inverse(t1, prime, t1) < 0 || | 
|  | 697 | crypto_bignum_mulmod(b, t1, prime, x1a) < 0) | 
|  | 698 | goto fail; | 
|  | 699 | debug_print_bignum("SSWU: x1a = b / (z * a)", x1a, prime_len); | 
|  | 700 |  | 
|  | 701 | /* (-b/a) * (1 + t) */ | 
|  | 702 | if (crypto_bignum_sub(prime, b, t1) < 0 || | 
|  | 703 | crypto_bignum_inverse(a, prime, t2) < 0 || | 
|  | 704 | crypto_bignum_mulmod(t1, t2, prime, t1) < 0 || | 
|  | 705 | crypto_bignum_addmod(one, t, prime, t2) < 0 || | 
|  | 706 | crypto_bignum_mulmod(t1, t2, prime, x1b) < 0) | 
|  | 707 | goto fail; | 
|  | 708 | debug_print_bignum("SSWU: x1b = (-b/a) * (1 + t)", x1b, prime_len); | 
|  | 709 |  | 
|  | 710 | /* x1 = CSEL(CEQ(m, 0), x1a, x1b) */ | 
|  | 711 | if (crypto_bignum_to_bin(x1a, bin1, sizeof(bin1), prime_len) < 0 || | 
|  | 712 | crypto_bignum_to_bin(x1b, bin2, sizeof(bin2), prime_len) < 0) | 
|  | 713 | goto fail; | 
|  | 714 | const_time_select_bin(m_is_zero, bin1, bin2, prime_len, bin); | 
|  | 715 | x1 = crypto_bignum_init_set(bin, prime_len); | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 716 | if (!x1) | 
|  | 717 | goto fail; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 718 | debug_print_bignum("SSWU: x1 = CSEL(l, x1a, x1b)", x1, prime_len); | 
|  | 719 |  | 
|  | 720 | /* gx1 = x1^3 + a * x1 + b */ | 
|  | 721 | if (crypto_bignum_exptmod(x1, three, prime, t1) < 0 || | 
|  | 722 | crypto_bignum_mulmod(a, x1, prime, t2) < 0 || | 
|  | 723 | crypto_bignum_addmod(t1, t2, prime, t1) < 0 || | 
|  | 724 | crypto_bignum_addmod(t1, b, prime, gx1) < 0) | 
|  | 725 | goto fail; | 
|  | 726 | debug_print_bignum("SSWU: gx1 = x1^3 + a * x1 + b", gx1, prime_len); | 
|  | 727 |  | 
|  | 728 | /* x2 = z * u^2 * x1 */ | 
|  | 729 | if (crypto_bignum_mulmod(z, u2, prime, t1) < 0 || | 
|  | 730 | crypto_bignum_mulmod(t1, x1, prime, x2) < 0) | 
|  | 731 | goto fail; | 
|  | 732 | debug_print_bignum("SSWU: x2 = z * u^2 * x1", x2, prime_len); | 
|  | 733 |  | 
|  | 734 | /* gx2 = x2^3 + a * x2 + b */ | 
|  | 735 | if (crypto_bignum_exptmod(x2, three, prime, t1) < 0 || | 
|  | 736 | crypto_bignum_mulmod(a, x2, prime, t2) < 0 || | 
|  | 737 | crypto_bignum_addmod(t1, t2, prime, t1) < 0 || | 
|  | 738 | crypto_bignum_addmod(t1, b, prime, gx2) < 0) | 
|  | 739 | goto fail; | 
|  | 740 | debug_print_bignum("SSWU: gx2 = x2^3 + a * x2 + b", gx2, prime_len); | 
|  | 741 |  | 
|  | 742 | /* l = gx1 is a quadratic residue modulo p | 
|  | 743 | * --> gx1^((p-1)/2) modulo p is zero or one */ | 
|  | 744 | if (crypto_bignum_sub(prime, one, t1) < 0 || | 
|  | 745 | crypto_bignum_rshift(t1, 1, t1) < 0 || | 
|  | 746 | crypto_bignum_exptmod(gx1, t1, prime, t1) < 0) | 
|  | 747 | goto fail; | 
|  | 748 | debug_print_bignum("SSWU: gx1^((p-1)/2) modulo p", t1, prime_len); | 
|  | 749 | is_qr = const_time_eq(crypto_bignum_is_zero(t1) | | 
|  | 750 | crypto_bignum_is_one(t1), 1); | 
|  | 751 |  | 
|  | 752 | /* v = CSEL(l, gx1, gx2) */ | 
|  | 753 | if (crypto_bignum_to_bin(gx1, bin1, sizeof(bin1), prime_len) < 0 || | 
|  | 754 | crypto_bignum_to_bin(gx2, bin2, sizeof(bin2), prime_len) < 0) | 
|  | 755 | goto fail; | 
|  | 756 | const_time_select_bin(is_qr, bin1, bin2, prime_len, bin); | 
|  | 757 | v = crypto_bignum_init_set(bin, prime_len); | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 758 | if (!v) | 
|  | 759 | goto fail; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 760 | debug_print_bignum("SSWU: v = CSEL(l, gx1, gx2)", v, prime_len); | 
|  | 761 |  | 
|  | 762 | /* x = CSEL(l, x1, x2) */ | 
|  | 763 | if (crypto_bignum_to_bin(x1, bin1, sizeof(bin1), prime_len) < 0 || | 
|  | 764 | crypto_bignum_to_bin(x2, bin2, sizeof(bin2), prime_len) < 0) | 
|  | 765 | goto fail; | 
|  | 766 | const_time_select_bin(is_qr, bin1, bin2, prime_len, x_y); | 
|  | 767 | wpa_hexdump_key(MSG_DEBUG, "SSWU: x = CSEL(l, x1, x2)", x_y, prime_len); | 
|  | 768 |  | 
|  | 769 | /* y = sqrt(v) | 
|  | 770 | * For prime p such that p = 3 mod 4 --> v^((p+1)/4) */ | 
|  | 771 | if (crypto_bignum_to_bin(prime, bin1, sizeof(bin1), prime_len) < 0) | 
|  | 772 | goto fail; | 
|  | 773 | if ((bin1[prime_len - 1] & 0x03) != 3) { | 
|  | 774 | wpa_printf(MSG_DEBUG, "SSWU: prime does not have p = 3 mod 4"); | 
|  | 775 | goto fail; | 
|  | 776 | } | 
|  | 777 | y = crypto_bignum_init(); | 
|  | 778 | if (!y || | 
|  | 779 | crypto_bignum_add(prime, one, t1) < 0 || | 
|  | 780 | crypto_bignum_rshift(t1, 2, t1) < 0 || | 
|  | 781 | crypto_bignum_exptmod(v, t1, prime, y) < 0) | 
|  | 782 | goto fail; | 
|  | 783 | debug_print_bignum("SSWU: y = sqrt(v)", y, prime_len); | 
|  | 784 |  | 
|  | 785 | /* l = CEQ(LSB(u), LSB(y)) */ | 
|  | 786 | if (crypto_bignum_to_bin(u, bin1, sizeof(bin1), prime_len) < 0 || | 
|  | 787 | crypto_bignum_to_bin(y, bin2, sizeof(bin2), prime_len) < 0) | 
|  | 788 | goto fail; | 
|  | 789 | is_eq = const_time_eq(bin1[prime_len - 1] & 0x01, | 
|  | 790 | bin2[prime_len - 1] & 0x01); | 
|  | 791 |  | 
|  | 792 | /* P = CSEL(l, (x,y), (x, p-y)) */ | 
|  | 793 | if (crypto_bignum_sub(prime, y, t1) < 0) | 
|  | 794 | goto fail; | 
|  | 795 | debug_print_bignum("SSWU: p - y", t1, prime_len); | 
|  | 796 | if (crypto_bignum_to_bin(y, bin1, sizeof(bin1), prime_len) < 0 || | 
|  | 797 | crypto_bignum_to_bin(t1, bin2, sizeof(bin2), prime_len) < 0) | 
|  | 798 | goto fail; | 
|  | 799 | const_time_select_bin(is_eq, bin1, bin2, prime_len, &x_y[prime_len]); | 
|  | 800 |  | 
|  | 801 | /* output P */ | 
|  | 802 | wpa_hexdump_key(MSG_DEBUG, "SSWU: P.x", x_y, prime_len); | 
|  | 803 | wpa_hexdump_key(MSG_DEBUG, "SSWU: P.y", &x_y[prime_len], prime_len); | 
|  | 804 | p = crypto_ec_point_from_bin(ec, x_y); | 
|  | 805 |  | 
|  | 806 | fail: | 
|  | 807 | crypto_bignum_deinit(u2, 1); | 
|  | 808 | crypto_bignum_deinit(t1, 1); | 
|  | 809 | crypto_bignum_deinit(t2, 1); | 
|  | 810 | crypto_bignum_deinit(z, 0); | 
|  | 811 | crypto_bignum_deinit(t, 1); | 
|  | 812 | crypto_bignum_deinit(x1a, 1); | 
|  | 813 | crypto_bignum_deinit(x1b, 1); | 
|  | 814 | crypto_bignum_deinit(x1, 1); | 
|  | 815 | crypto_bignum_deinit(x2, 1); | 
|  | 816 | crypto_bignum_deinit(gx1, 1); | 
|  | 817 | crypto_bignum_deinit(gx2, 1); | 
|  | 818 | crypto_bignum_deinit(y, 1); | 
|  | 819 | crypto_bignum_deinit(v, 1); | 
|  | 820 | crypto_bignum_deinit(zero, 0); | 
|  | 821 | crypto_bignum_deinit(one, 0); | 
|  | 822 | crypto_bignum_deinit(two, 0); | 
|  | 823 | crypto_bignum_deinit(three, 0); | 
|  | 824 | forced_memzero(bin, sizeof(bin)); | 
|  | 825 | forced_memzero(bin1, sizeof(bin1)); | 
|  | 826 | forced_memzero(bin2, sizeof(bin2)); | 
|  | 827 | forced_memzero(x_y, sizeof(x_y)); | 
|  | 828 | return p; | 
|  | 829 | } | 
|  | 830 |  | 
|  | 831 |  | 
|  | 832 | static int sae_pwd_seed(size_t hash_len, const u8 *ssid, size_t ssid_len, | 
|  | 833 | const u8 *password, size_t password_len, | 
|  | 834 | const char *identifier, u8 *pwd_seed) | 
|  | 835 | { | 
|  | 836 | const u8 *addr[2]; | 
|  | 837 | size_t len[2]; | 
|  | 838 | size_t num_elem; | 
|  | 839 |  | 
|  | 840 | /* pwd-seed = HKDF-Extract(ssid, password [ || identifier ]) */ | 
|  | 841 | addr[0] = password; | 
|  | 842 | len[0] = password_len; | 
|  | 843 | num_elem = 1; | 
|  | 844 | wpa_hexdump_ascii(MSG_DEBUG, "SAE: SSID", ssid, ssid_len); | 
|  | 845 | wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", | 
|  | 846 | password, password_len); | 
|  | 847 | if (identifier) { | 
|  | 848 | wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", | 
|  | 849 | identifier); | 
|  | 850 | addr[num_elem] = (const u8 *) identifier; | 
|  | 851 | len[num_elem] = os_strlen(identifier); | 
|  | 852 | num_elem++; | 
|  | 853 | } | 
|  | 854 | if (hkdf_extract(hash_len, ssid, ssid_len, num_elem, addr, len, | 
|  | 855 | pwd_seed) < 0) | 
|  | 856 | return -1; | 
|  | 857 | wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, hash_len); | 
|  | 858 | return 0; | 
|  | 859 | } | 
|  | 860 |  | 
|  | 861 |  | 
|  | 862 | size_t sae_ecc_prime_len_2_hash_len(size_t prime_len) | 
|  | 863 | { | 
|  | 864 | if (prime_len <= 256 / 8) | 
|  | 865 | return 32; | 
|  | 866 | if (prime_len <= 384 / 8) | 
|  | 867 | return 48; | 
|  | 868 | return 64; | 
|  | 869 | } | 
|  | 870 |  | 
|  | 871 |  | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 872 | static struct crypto_ec_point * | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 873 | sae_derive_pt_ecc(struct crypto_ec *ec, int group, | 
|  | 874 | const u8 *ssid, size_t ssid_len, | 
|  | 875 | const u8 *password, size_t password_len, | 
|  | 876 | const char *identifier) | 
|  | 877 | { | 
|  | 878 | u8 pwd_seed[64]; | 
|  | 879 | u8 pwd_value[SAE_MAX_ECC_PRIME_LEN * 2]; | 
|  | 880 | size_t pwd_value_len, hash_len, prime_len; | 
|  | 881 | const struct crypto_bignum *prime; | 
|  | 882 | struct crypto_bignum *bn = NULL; | 
|  | 883 | struct crypto_ec_point *p1 = NULL, *p2 = NULL, *pt = NULL; | 
|  | 884 |  | 
|  | 885 | prime = crypto_ec_get_prime(ec); | 
|  | 886 | prime_len = crypto_ec_prime_len(ec); | 
|  | 887 | if (prime_len > SAE_MAX_ECC_PRIME_LEN) | 
|  | 888 | goto fail; | 
|  | 889 | hash_len = sae_ecc_prime_len_2_hash_len(prime_len); | 
|  | 890 |  | 
|  | 891 | /* len = olen(p) + ceil(olen(p)/2) */ | 
|  | 892 | pwd_value_len = prime_len + (prime_len + 1) / 2; | 
|  | 893 |  | 
|  | 894 | if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, | 
|  | 895 | identifier, pwd_seed) < 0) | 
|  | 896 | goto fail; | 
|  | 897 |  | 
|  | 898 | /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u1 P1", len) | 
|  | 899 | */ | 
|  | 900 | if (hkdf_expand(hash_len, pwd_seed, hash_len, | 
|  | 901 | "SAE Hash to Element u1 P1", pwd_value, pwd_value_len) < | 
|  | 902 | 0) | 
|  | 903 | goto fail; | 
|  | 904 | wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u1 P1)", | 
|  | 905 | pwd_value, pwd_value_len); | 
|  | 906 |  | 
|  | 907 | /* u1 = pwd-value modulo p */ | 
|  | 908 | bn = crypto_bignum_init_set(pwd_value, pwd_value_len); | 
|  | 909 | if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 || | 
|  | 910 | crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), | 
|  | 911 | prime_len) < 0) | 
|  | 912 | goto fail; | 
|  | 913 | wpa_hexdump_key(MSG_DEBUG, "SAE: u1", pwd_value, prime_len); | 
|  | 914 |  | 
|  | 915 | /* P1 = SSWU(u1) */ | 
|  | 916 | p1 = sswu(ec, group, bn); | 
|  | 917 | if (!p1) | 
|  | 918 | goto fail; | 
|  | 919 |  | 
|  | 920 | /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element u2 P2", len) | 
|  | 921 | */ | 
|  | 922 | if (hkdf_expand(hash_len, pwd_seed, hash_len, | 
|  | 923 | "SAE Hash to Element u2 P2", pwd_value, | 
|  | 924 | pwd_value_len) < 0) | 
|  | 925 | goto fail; | 
|  | 926 | wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value (u2 P2)", | 
|  | 927 | pwd_value, pwd_value_len); | 
|  | 928 |  | 
|  | 929 | /* u2 = pwd-value modulo p */ | 
|  | 930 | crypto_bignum_deinit(bn, 1); | 
|  | 931 | bn = crypto_bignum_init_set(pwd_value, pwd_value_len); | 
|  | 932 | if (!bn || crypto_bignum_mod(bn, prime, bn) < 0 || | 
|  | 933 | crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), | 
|  | 934 | prime_len) < 0) | 
|  | 935 | goto fail; | 
|  | 936 | wpa_hexdump_key(MSG_DEBUG, "SAE: u2", pwd_value, prime_len); | 
|  | 937 |  | 
|  | 938 | /* P2 = SSWU(u2) */ | 
|  | 939 | p2 = sswu(ec, group, bn); | 
|  | 940 | if (!p2) | 
|  | 941 | goto fail; | 
|  | 942 |  | 
|  | 943 | /* PT = elem-op(P1, P2) */ | 
|  | 944 | pt = crypto_ec_point_init(ec); | 
|  | 945 | if (!pt) | 
|  | 946 | goto fail; | 
|  | 947 | if (crypto_ec_point_add(ec, p1, p2, pt) < 0) { | 
|  | 948 | crypto_ec_point_deinit(pt, 1); | 
|  | 949 | pt = NULL; | 
|  | 950 | } | 
|  | 951 |  | 
|  | 952 | fail: | 
|  | 953 | forced_memzero(pwd_seed, sizeof(pwd_seed)); | 
|  | 954 | forced_memzero(pwd_value, sizeof(pwd_value)); | 
|  | 955 | crypto_bignum_deinit(bn, 1); | 
|  | 956 | crypto_ec_point_deinit(p1, 1); | 
|  | 957 | crypto_ec_point_deinit(p2, 1); | 
|  | 958 | return pt; | 
|  | 959 | } | 
|  | 960 |  | 
|  | 961 |  | 
|  | 962 | size_t sae_ffc_prime_len_2_hash_len(size_t prime_len) | 
|  | 963 | { | 
|  | 964 | if (prime_len <= 2048 / 8) | 
|  | 965 | return 32; | 
|  | 966 | if (prime_len <= 3072 / 8) | 
|  | 967 | return 48; | 
|  | 968 | return 64; | 
|  | 969 | } | 
|  | 970 |  | 
|  | 971 |  | 
|  | 972 | static struct crypto_bignum * | 
|  | 973 | sae_derive_pt_ffc(const struct dh_group *dh, int group, | 
|  | 974 | const u8 *ssid, size_t ssid_len, | 
|  | 975 | const u8 *password, size_t password_len, | 
|  | 976 | const char *identifier) | 
|  | 977 | { | 
|  | 978 | size_t hash_len, prime_len, pwd_value_len; | 
|  | 979 | struct crypto_bignum *prime, *order; | 
|  | 980 | struct crypto_bignum *one = NULL, *two = NULL, *bn = NULL, *tmp = NULL, | 
|  | 981 | *pt = NULL; | 
|  | 982 | u8 pwd_seed[64]; | 
|  | 983 | u8 pwd_value[SAE_MAX_PRIME_LEN + SAE_MAX_PRIME_LEN / 2]; | 
|  | 984 |  | 
|  | 985 | prime = crypto_bignum_init_set(dh->prime, dh->prime_len); | 
|  | 986 | order = crypto_bignum_init_set(dh->order, dh->order_len); | 
|  | 987 | if (!prime || !order) | 
|  | 988 | goto fail; | 
|  | 989 | prime_len = dh->prime_len; | 
|  | 990 | if (prime_len > SAE_MAX_PRIME_LEN) | 
|  | 991 | goto fail; | 
|  | 992 | hash_len = sae_ffc_prime_len_2_hash_len(prime_len); | 
|  | 993 |  | 
|  | 994 | /* len = olen(p) + ceil(olen(p)/2) */ | 
|  | 995 | pwd_value_len = prime_len + (prime_len + 1) / 2; | 
|  | 996 | if (pwd_value_len > sizeof(pwd_value)) | 
|  | 997 | goto fail; | 
|  | 998 |  | 
|  | 999 | if (sae_pwd_seed(hash_len, ssid, ssid_len, password, password_len, | 
|  | 1000 | identifier, pwd_seed) < 0) | 
|  | 1001 | goto fail; | 
|  | 1002 |  | 
|  | 1003 | /* pwd-value = HKDF-Expand(pwd-seed, "SAE Hash to Element", len) */ | 
|  | 1004 | if (hkdf_expand(hash_len, pwd_seed, hash_len, | 
|  | 1005 | "SAE Hash to Element", pwd_value, pwd_value_len) < 0) | 
|  | 1006 | goto fail; | 
|  | 1007 | wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", | 
|  | 1008 | pwd_value, pwd_value_len); | 
|  | 1009 |  | 
|  | 1010 | /* pwd-value = (pwd-value modulo (p-2)) + 2 */ | 
|  | 1011 | bn = crypto_bignum_init_set(pwd_value, pwd_value_len); | 
|  | 1012 | one = crypto_bignum_init_uint(1); | 
|  | 1013 | two = crypto_bignum_init_uint(2); | 
|  | 1014 | tmp = crypto_bignum_init(); | 
|  | 1015 | if (!bn || !one || !two || !tmp || | 
|  | 1016 | crypto_bignum_sub(prime, two, tmp) < 0 || | 
|  | 1017 | crypto_bignum_mod(bn, tmp, bn) < 0 || | 
|  | 1018 | crypto_bignum_add(bn, two, bn) < 0 || | 
|  | 1019 | crypto_bignum_to_bin(bn, pwd_value, sizeof(pwd_value), | 
|  | 1020 | prime_len) < 0) | 
|  | 1021 | goto fail; | 
|  | 1022 | wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value(reduced)", | 
|  | 1023 | pwd_value, prime_len); | 
|  | 1024 |  | 
|  | 1025 | /* PT = pwd-value^((p-1)/q) modulo p */ | 
|  | 1026 | pt = crypto_bignum_init(); | 
|  | 1027 | if (!pt || | 
|  | 1028 | crypto_bignum_sub(prime, one, tmp) < 0 || | 
|  | 1029 | crypto_bignum_div(tmp, order, tmp) < 0 || | 
|  | 1030 | crypto_bignum_exptmod(bn, tmp, prime, pt) < 0) { | 
|  | 1031 | crypto_bignum_deinit(pt, 1); | 
|  | 1032 | pt = NULL; | 
|  | 1033 | goto fail; | 
|  | 1034 | } | 
|  | 1035 | debug_print_bignum("SAE: PT", pt, prime_len); | 
|  | 1036 |  | 
|  | 1037 | fail: | 
|  | 1038 | forced_memzero(pwd_seed, sizeof(pwd_seed)); | 
|  | 1039 | forced_memzero(pwd_value, sizeof(pwd_value)); | 
|  | 1040 | crypto_bignum_deinit(bn, 1); | 
|  | 1041 | crypto_bignum_deinit(tmp, 1); | 
|  | 1042 | crypto_bignum_deinit(one, 0); | 
|  | 1043 | crypto_bignum_deinit(two, 0); | 
|  | 1044 | crypto_bignum_deinit(prime, 0); | 
|  | 1045 | crypto_bignum_deinit(order, 0); | 
|  | 1046 | return pt; | 
|  | 1047 | } | 
|  | 1048 |  | 
|  | 1049 |  | 
|  | 1050 | static struct sae_pt * | 
|  | 1051 | sae_derive_pt_group(int group, const u8 *ssid, size_t ssid_len, | 
|  | 1052 | const u8 *password, size_t password_len, | 
|  | 1053 | const char *identifier) | 
|  | 1054 | { | 
|  | 1055 | struct sae_pt *pt; | 
|  | 1056 |  | 
|  | 1057 | wpa_printf(MSG_DEBUG, "SAE: Derive PT - group %d", group); | 
|  | 1058 |  | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1059 | if (ssid_len > 32) | 
|  | 1060 | return NULL; | 
|  | 1061 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1062 | pt = os_zalloc(sizeof(*pt)); | 
|  | 1063 | if (!pt) | 
|  | 1064 | return NULL; | 
|  | 1065 |  | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1066 | #ifdef CONFIG_SAE_PK | 
|  | 1067 | os_memcpy(pt->ssid, ssid, ssid_len); | 
|  | 1068 | pt->ssid_len = ssid_len; | 
|  | 1069 | #endif /* CONFIG_SAE_PK */ | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1070 | pt->group = group; | 
|  | 1071 | pt->ec = crypto_ec_init(group); | 
|  | 1072 | if (pt->ec) { | 
|  | 1073 | pt->ecc_pt = sae_derive_pt_ecc(pt->ec, group, ssid, ssid_len, | 
|  | 1074 | password, password_len, | 
|  | 1075 | identifier); | 
|  | 1076 | if (!pt->ecc_pt) { | 
|  | 1077 | wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); | 
|  | 1078 | goto fail; | 
|  | 1079 | } | 
|  | 1080 |  | 
|  | 1081 | return pt; | 
|  | 1082 | } | 
|  | 1083 |  | 
|  | 1084 | pt->dh = dh_groups_get(group); | 
|  | 1085 | if (!pt->dh) { | 
|  | 1086 | wpa_printf(MSG_DEBUG, "SAE: Unsupported group %d", group); | 
|  | 1087 | goto fail; | 
|  | 1088 | } | 
|  | 1089 |  | 
|  | 1090 | pt->ffc_pt = sae_derive_pt_ffc(pt->dh, group, ssid, ssid_len, | 
|  | 1091 | password, password_len, identifier); | 
|  | 1092 | if (!pt->ffc_pt) { | 
|  | 1093 | wpa_printf(MSG_DEBUG, "SAE: Failed to derive PT"); | 
|  | 1094 | goto fail; | 
|  | 1095 | } | 
|  | 1096 |  | 
|  | 1097 | return pt; | 
|  | 1098 | fail: | 
|  | 1099 | sae_deinit_pt(pt); | 
|  | 1100 | return NULL; | 
|  | 1101 | } | 
|  | 1102 |  | 
|  | 1103 |  | 
|  | 1104 | struct sae_pt * sae_derive_pt(int *groups, const u8 *ssid, size_t ssid_len, | 
|  | 1105 | const u8 *password, size_t password_len, | 
|  | 1106 | const char *identifier) | 
|  | 1107 | { | 
|  | 1108 | struct sae_pt *pt = NULL, *last = NULL, *tmp; | 
|  | 1109 | int default_groups[] = { 19, 0 }; | 
|  | 1110 | int i; | 
|  | 1111 |  | 
|  | 1112 | if (!groups) | 
|  | 1113 | groups = default_groups; | 
|  | 1114 | for (i = 0; groups[i] > 0; i++) { | 
|  | 1115 | tmp = sae_derive_pt_group(groups[i], ssid, ssid_len, password, | 
|  | 1116 | password_len, identifier); | 
|  | 1117 | if (!tmp) | 
|  | 1118 | continue; | 
|  | 1119 |  | 
|  | 1120 | if (last) | 
|  | 1121 | last->next = tmp; | 
|  | 1122 | else | 
|  | 1123 | pt = tmp; | 
|  | 1124 | last = tmp; | 
|  | 1125 | } | 
|  | 1126 |  | 
|  | 1127 | return pt; | 
|  | 1128 | } | 
|  | 1129 |  | 
|  | 1130 |  | 
|  | 1131 | static void sae_max_min_addr(const u8 *addr[], size_t len[], | 
|  | 1132 | const u8 *addr1, const u8 *addr2) | 
|  | 1133 | { | 
|  | 1134 | len[0] = ETH_ALEN; | 
|  | 1135 | len[1] = ETH_ALEN; | 
|  | 1136 | if (os_memcmp(addr1, addr2, ETH_ALEN) > 0) { | 
|  | 1137 | addr[0] = addr1; | 
|  | 1138 | addr[1] = addr2; | 
|  | 1139 | } else { | 
|  | 1140 | addr[0] = addr2; | 
|  | 1141 | addr[1] = addr1; | 
|  | 1142 | } | 
|  | 1143 | } | 
|  | 1144 |  | 
|  | 1145 |  | 
|  | 1146 | struct crypto_ec_point * | 
|  | 1147 | sae_derive_pwe_from_pt_ecc(const struct sae_pt *pt, | 
|  | 1148 | const u8 *addr1, const u8 *addr2) | 
|  | 1149 | { | 
|  | 1150 | u8 bin[SAE_MAX_ECC_PRIME_LEN * 2]; | 
|  | 1151 | size_t prime_len; | 
|  | 1152 | const u8 *addr[2]; | 
|  | 1153 | size_t len[2]; | 
|  | 1154 | u8 salt[64], hash[64]; | 
|  | 1155 | size_t hash_len; | 
|  | 1156 | const struct crypto_bignum *order; | 
|  | 1157 | struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL; | 
|  | 1158 | struct crypto_ec_point *pwe = NULL; | 
|  | 1159 |  | 
|  | 1160 | wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT"); | 
|  | 1161 | prime_len = crypto_ec_prime_len(pt->ec); | 
|  | 1162 | if (crypto_ec_point_to_bin(pt->ec, pt->ecc_pt, | 
|  | 1163 | bin, bin + prime_len) < 0) | 
|  | 1164 | return NULL; | 
|  | 1165 | wpa_hexdump_key(MSG_DEBUG, "SAE: PT.x", bin, prime_len); | 
|  | 1166 | wpa_hexdump_key(MSG_DEBUG, "SAE: PT.y", bin + prime_len, prime_len); | 
|  | 1167 |  | 
|  | 1168 | sae_max_min_addr(addr, len, addr1, addr2); | 
|  | 1169 |  | 
|  | 1170 | /* val = H(0^n, | 
|  | 1171 | *         MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */ | 
|  | 1172 | wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))"); | 
|  | 1173 | hash_len = sae_ecc_prime_len_2_hash_len(prime_len); | 
|  | 1174 | os_memset(salt, 0, hash_len); | 
|  | 1175 | if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0) | 
|  | 1176 | goto fail; | 
|  | 1177 | wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len); | 
|  | 1178 |  | 
|  | 1179 | /* val = val modulo (q - 1) + 1 */ | 
|  | 1180 | order = crypto_ec_get_order(pt->ec); | 
|  | 1181 | tmp = crypto_bignum_init(); | 
|  | 1182 | val = crypto_bignum_init_set(hash, hash_len); | 
|  | 1183 | one = crypto_bignum_init_uint(1); | 
|  | 1184 | if (!tmp || !val || !one || | 
|  | 1185 | crypto_bignum_sub(order, one, tmp) < 0 || | 
|  | 1186 | crypto_bignum_mod(val, tmp, val) < 0 || | 
|  | 1187 | crypto_bignum_add(val, one, val) < 0) | 
|  | 1188 | goto fail; | 
|  | 1189 | debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len); | 
|  | 1190 |  | 
|  | 1191 | /* PWE = scalar-op(val, PT) */ | 
|  | 1192 | pwe = crypto_ec_point_init(pt->ec); | 
|  | 1193 | if (!pwe || | 
|  | 1194 | crypto_ec_point_mul(pt->ec, pt->ecc_pt, val, pwe) < 0 || | 
|  | 1195 | crypto_ec_point_to_bin(pt->ec, pwe, bin, bin + prime_len) < 0) { | 
|  | 1196 | crypto_ec_point_deinit(pwe, 1); | 
|  | 1197 | pwe = NULL; | 
|  | 1198 | goto fail; | 
|  | 1199 | } | 
|  | 1200 | wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.x", bin, prime_len); | 
|  | 1201 | wpa_hexdump_key(MSG_DEBUG, "SAE: PWE.y", bin + prime_len, prime_len); | 
|  | 1202 |  | 
|  | 1203 | fail: | 
|  | 1204 | crypto_bignum_deinit(tmp, 1); | 
|  | 1205 | crypto_bignum_deinit(val, 1); | 
|  | 1206 | crypto_bignum_deinit(one, 0); | 
|  | 1207 | return pwe; | 
|  | 1208 | } | 
|  | 1209 |  | 
|  | 1210 |  | 
|  | 1211 | struct crypto_bignum * | 
|  | 1212 | sae_derive_pwe_from_pt_ffc(const struct sae_pt *pt, | 
|  | 1213 | const u8 *addr1, const u8 *addr2) | 
|  | 1214 | { | 
|  | 1215 | size_t prime_len; | 
|  | 1216 | const u8 *addr[2]; | 
|  | 1217 | size_t len[2]; | 
|  | 1218 | u8 salt[64], hash[64]; | 
|  | 1219 | size_t hash_len; | 
|  | 1220 | struct crypto_bignum *tmp = NULL, *val = NULL, *one = NULL; | 
|  | 1221 | struct crypto_bignum *pwe = NULL, *order = NULL, *prime = NULL; | 
|  | 1222 |  | 
|  | 1223 | wpa_printf(MSG_DEBUG, "SAE: Derive PWE from PT"); | 
|  | 1224 | prime = crypto_bignum_init_set(pt->dh->prime, pt->dh->prime_len); | 
|  | 1225 | order = crypto_bignum_init_set(pt->dh->order, pt->dh->order_len); | 
|  | 1226 | if (!prime || !order) | 
|  | 1227 | goto fail; | 
|  | 1228 | prime_len = pt->dh->prime_len; | 
|  | 1229 |  | 
|  | 1230 | sae_max_min_addr(addr, len, addr1, addr2); | 
|  | 1231 |  | 
|  | 1232 | /* val = H(0^n, | 
|  | 1233 | *         MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC)) */ | 
|  | 1234 | wpa_printf(MSG_DEBUG, "SAE: val = H(0^n, MAX(addrs) || MIN(addrs))"); | 
|  | 1235 | hash_len = sae_ffc_prime_len_2_hash_len(prime_len); | 
|  | 1236 | os_memset(salt, 0, hash_len); | 
|  | 1237 | if (hkdf_extract(hash_len, salt, hash_len, 2, addr, len, hash) < 0) | 
|  | 1238 | goto fail; | 
|  | 1239 | wpa_hexdump(MSG_DEBUG, "SAE: val", hash, hash_len); | 
|  | 1240 |  | 
|  | 1241 | /* val = val modulo (q - 1) + 1 */ | 
|  | 1242 | tmp = crypto_bignum_init(); | 
|  | 1243 | val = crypto_bignum_init_set(hash, hash_len); | 
|  | 1244 | one = crypto_bignum_init_uint(1); | 
|  | 1245 | if (!tmp || !val || !one || | 
|  | 1246 | crypto_bignum_sub(order, one, tmp) < 0 || | 
|  | 1247 | crypto_bignum_mod(val, tmp, val) < 0 || | 
|  | 1248 | crypto_bignum_add(val, one, val) < 0) | 
|  | 1249 | goto fail; | 
|  | 1250 | debug_print_bignum("SAE: val(reduced to 1..q-1)", val, prime_len); | 
|  | 1251 |  | 
|  | 1252 | /* PWE = scalar-op(val, PT) */ | 
|  | 1253 | pwe = crypto_bignum_init(); | 
|  | 1254 | if (!pwe || crypto_bignum_exptmod(pt->ffc_pt, val, prime, pwe) < 0) { | 
|  | 1255 | crypto_bignum_deinit(pwe, 1); | 
|  | 1256 | pwe = NULL; | 
|  | 1257 | goto fail; | 
|  | 1258 | } | 
|  | 1259 | debug_print_bignum("SAE: PWE", pwe, prime_len); | 
|  | 1260 |  | 
|  | 1261 | fail: | 
|  | 1262 | crypto_bignum_deinit(tmp, 1); | 
|  | 1263 | crypto_bignum_deinit(val, 1); | 
|  | 1264 | crypto_bignum_deinit(one, 0); | 
|  | 1265 | crypto_bignum_deinit(prime, 0); | 
|  | 1266 | crypto_bignum_deinit(order, 0); | 
|  | 1267 | return pwe; | 
|  | 1268 | } | 
|  | 1269 |  | 
|  | 1270 |  | 
|  | 1271 | void sae_deinit_pt(struct sae_pt *pt) | 
|  | 1272 | { | 
|  | 1273 | struct sae_pt *prev; | 
|  | 1274 |  | 
|  | 1275 | while (pt) { | 
|  | 1276 | crypto_ec_point_deinit(pt->ecc_pt, 1); | 
|  | 1277 | crypto_bignum_deinit(pt->ffc_pt, 1); | 
|  | 1278 | crypto_ec_deinit(pt->ec); | 
|  | 1279 | prev = pt; | 
|  | 1280 | pt = pt->next; | 
|  | 1281 | os_free(prev); | 
|  | 1282 | } | 
|  | 1283 | } | 
|  | 1284 |  | 
|  | 1285 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1286 | static int sae_derive_commit_element_ecc(struct sae_data *sae, | 
|  | 1287 | struct crypto_bignum *mask) | 
|  | 1288 | { | 
|  | 1289 | /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ | 
|  | 1290 | if (!sae->tmp->own_commit_element_ecc) { | 
|  | 1291 | sae->tmp->own_commit_element_ecc = | 
|  | 1292 | crypto_ec_point_init(sae->tmp->ec); | 
|  | 1293 | if (!sae->tmp->own_commit_element_ecc) | 
|  | 1294 | return -1; | 
|  | 1295 | } | 
|  | 1296 |  | 
|  | 1297 | if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, mask, | 
|  | 1298 | sae->tmp->own_commit_element_ecc) < 0 || | 
|  | 1299 | crypto_ec_point_invert(sae->tmp->ec, | 
|  | 1300 | sae->tmp->own_commit_element_ecc) < 0) { | 
|  | 1301 | wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); | 
|  | 1302 | return -1; | 
|  | 1303 | } | 
|  | 1304 |  | 
|  | 1305 | return 0; | 
|  | 1306 | } | 
|  | 1307 |  | 
|  | 1308 |  | 
|  | 1309 | static int sae_derive_commit_element_ffc(struct sae_data *sae, | 
|  | 1310 | struct crypto_bignum *mask) | 
|  | 1311 | { | 
|  | 1312 | /* COMMIT-ELEMENT = inverse(scalar-op(mask, PWE)) */ | 
|  | 1313 | if (!sae->tmp->own_commit_element_ffc) { | 
|  | 1314 | sae->tmp->own_commit_element_ffc = crypto_bignum_init(); | 
|  | 1315 | if (!sae->tmp->own_commit_element_ffc) | 
|  | 1316 | return -1; | 
|  | 1317 | } | 
|  | 1318 |  | 
|  | 1319 | if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, mask, sae->tmp->prime, | 
|  | 1320 | sae->tmp->own_commit_element_ffc) < 0 || | 
|  | 1321 | crypto_bignum_inverse(sae->tmp->own_commit_element_ffc, | 
|  | 1322 | sae->tmp->prime, | 
|  | 1323 | sae->tmp->own_commit_element_ffc) < 0) { | 
|  | 1324 | wpa_printf(MSG_DEBUG, "SAE: Could not compute commit-element"); | 
|  | 1325 | return -1; | 
|  | 1326 | } | 
|  | 1327 |  | 
|  | 1328 | return 0; | 
|  | 1329 | } | 
|  | 1330 |  | 
|  | 1331 |  | 
|  | 1332 | static int sae_derive_commit(struct sae_data *sae) | 
|  | 1333 | { | 
|  | 1334 | struct crypto_bignum *mask; | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 1335 | int ret; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1336 |  | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 1337 | mask = crypto_bignum_init(); | 
|  | 1338 | if (!sae->tmp->sae_rand) | 
|  | 1339 | sae->tmp->sae_rand = crypto_bignum_init(); | 
|  | 1340 | if (!sae->tmp->own_commit_scalar) | 
|  | 1341 | sae->tmp->own_commit_scalar = crypto_bignum_init(); | 
|  | 1342 | ret = !mask || !sae->tmp->sae_rand || !sae->tmp->own_commit_scalar || | 
|  | 1343 | dragonfly_generate_scalar(sae->tmp->order, sae->tmp->sae_rand, | 
|  | 1344 | mask, | 
|  | 1345 | sae->tmp->own_commit_scalar) < 0 || | 
|  | 1346 | (sae->tmp->ec && | 
|  | 1347 | sae_derive_commit_element_ecc(sae, mask) < 0) || | 
|  | 1348 | (sae->tmp->dh && | 
|  | 1349 | sae_derive_commit_element_ffc(sae, mask) < 0); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1350 | crypto_bignum_deinit(mask, 1); | 
| Hai Shalom | 81f62d8 | 2019-07-22 12:10:00 -0700 | [diff] [blame] | 1351 | return ret ? -1 : 0; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1352 | } | 
|  | 1353 |  | 
|  | 1354 |  | 
|  | 1355 | int sae_prepare_commit(const u8 *addr1, const u8 *addr2, | 
|  | 1356 | const u8 *password, size_t password_len, | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1357 | const char *identifier, struct sae_data *sae) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1358 | { | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 1359 | if (sae->tmp == NULL || | 
|  | 1360 | (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1361 | password_len, | 
|  | 1362 | identifier) < 0) || | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 1363 | (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1364 | password_len, | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1365 | identifier) < 0)) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1366 | return -1; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1367 |  | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1368 | sae->h2e = 0; | 
|  | 1369 | sae->pk = 0; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1370 | return sae_derive_commit(sae); | 
|  | 1371 | } | 
|  | 1372 |  | 
|  | 1373 |  | 
|  | 1374 | int sae_prepare_commit_pt(struct sae_data *sae, const struct sae_pt *pt, | 
|  | 1375 | const u8 *addr1, const u8 *addr2, | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1376 | int *rejected_groups, const struct sae_pk *pk) | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1377 | { | 
|  | 1378 | if (!sae->tmp) | 
|  | 1379 | return -1; | 
|  | 1380 |  | 
|  | 1381 | while (pt) { | 
|  | 1382 | if (pt->group == sae->group) | 
|  | 1383 | break; | 
|  | 1384 | pt = pt->next; | 
|  | 1385 | } | 
|  | 1386 | if (!pt) { | 
|  | 1387 | wpa_printf(MSG_INFO, "SAE: Could not find PT for group %u", | 
|  | 1388 | sae->group); | 
|  | 1389 | return -1; | 
|  | 1390 | } | 
|  | 1391 |  | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1392 | #ifdef CONFIG_SAE_PK | 
|  | 1393 | os_memcpy(sae->tmp->ssid, pt->ssid, pt->ssid_len); | 
|  | 1394 | sae->tmp->ssid_len = pt->ssid_len; | 
|  | 1395 | sae->tmp->ap_pk = pk; | 
|  | 1396 | #endif /* CONFIG_SAE_PK */ | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1397 | sae->tmp->own_addr_higher = os_memcmp(addr1, addr2, ETH_ALEN) > 0; | 
|  | 1398 | wpabuf_free(sae->tmp->own_rejected_groups); | 
|  | 1399 | sae->tmp->own_rejected_groups = NULL; | 
|  | 1400 | if (rejected_groups) { | 
|  | 1401 | int count, i; | 
|  | 1402 | struct wpabuf *groups; | 
|  | 1403 |  | 
|  | 1404 | count = int_array_len(rejected_groups); | 
|  | 1405 | groups = wpabuf_alloc(count * 2); | 
|  | 1406 | if (!groups) | 
|  | 1407 | return -1; | 
|  | 1408 | for (i = 0; i < count; i++) | 
|  | 1409 | wpabuf_put_le16(groups, rejected_groups[i]); | 
|  | 1410 | sae->tmp->own_rejected_groups = groups; | 
|  | 1411 | } | 
|  | 1412 |  | 
|  | 1413 | if (pt->ec) { | 
|  | 1414 | crypto_ec_point_deinit(sae->tmp->pwe_ecc, 1); | 
|  | 1415 | sae->tmp->pwe_ecc = sae_derive_pwe_from_pt_ecc(pt, addr1, | 
|  | 1416 | addr2); | 
|  | 1417 | if (!sae->tmp->pwe_ecc) | 
|  | 1418 | return -1; | 
|  | 1419 | } | 
|  | 1420 |  | 
|  | 1421 | if (pt->dh) { | 
|  | 1422 | crypto_bignum_deinit(sae->tmp->pwe_ffc, 1); | 
|  | 1423 | sae->tmp->pwe_ffc = sae_derive_pwe_from_pt_ffc(pt, addr1, | 
|  | 1424 | addr2); | 
|  | 1425 | if (!sae->tmp->pwe_ffc) | 
|  | 1426 | return -1; | 
|  | 1427 | } | 
|  | 1428 |  | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1429 | sae->h2e = 1; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1430 | return sae_derive_commit(sae); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1431 | } | 
|  | 1432 |  | 
|  | 1433 |  | 
|  | 1434 | static int sae_derive_k_ecc(struct sae_data *sae, u8 *k) | 
|  | 1435 | { | 
|  | 1436 | struct crypto_ec_point *K; | 
|  | 1437 | int ret = -1; | 
|  | 1438 |  | 
|  | 1439 | K = crypto_ec_point_init(sae->tmp->ec); | 
|  | 1440 | if (K == NULL) | 
|  | 1441 | goto fail; | 
|  | 1442 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1443 | /* | 
|  | 1444 | * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), | 
|  | 1445 | *                                        PEER-COMMIT-ELEMENT))) | 
|  | 1446 | * If K is identity element (point-at-infinity), reject | 
|  | 1447 | * k = F(K) (= x coordinate) | 
|  | 1448 | */ | 
|  | 1449 |  | 
|  | 1450 | if (crypto_ec_point_mul(sae->tmp->ec, sae->tmp->pwe_ecc, | 
|  | 1451 | sae->peer_commit_scalar, K) < 0 || | 
|  | 1452 | crypto_ec_point_add(sae->tmp->ec, K, | 
|  | 1453 | sae->tmp->peer_commit_element_ecc, K) < 0 || | 
|  | 1454 | crypto_ec_point_mul(sae->tmp->ec, K, sae->tmp->sae_rand, K) < 0 || | 
|  | 1455 | crypto_ec_point_is_at_infinity(sae->tmp->ec, K) || | 
|  | 1456 | crypto_ec_point_to_bin(sae->tmp->ec, K, k, NULL) < 0) { | 
|  | 1457 | wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); | 
|  | 1458 | goto fail; | 
|  | 1459 | } | 
|  | 1460 |  | 
|  | 1461 | wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); | 
|  | 1462 |  | 
|  | 1463 | ret = 0; | 
|  | 1464 | fail: | 
|  | 1465 | crypto_ec_point_deinit(K, 1); | 
|  | 1466 | return ret; | 
|  | 1467 | } | 
|  | 1468 |  | 
|  | 1469 |  | 
|  | 1470 | static int sae_derive_k_ffc(struct sae_data *sae, u8 *k) | 
|  | 1471 | { | 
|  | 1472 | struct crypto_bignum *K; | 
|  | 1473 | int ret = -1; | 
|  | 1474 |  | 
|  | 1475 | K = crypto_bignum_init(); | 
|  | 1476 | if (K == NULL) | 
|  | 1477 | goto fail; | 
|  | 1478 |  | 
|  | 1479 | /* | 
|  | 1480 | * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE), | 
|  | 1481 | *                                        PEER-COMMIT-ELEMENT))) | 
|  | 1482 | * If K is identity element (one), reject. | 
|  | 1483 | * k = F(K) (= x coordinate) | 
|  | 1484 | */ | 
|  | 1485 |  | 
|  | 1486 | if (crypto_bignum_exptmod(sae->tmp->pwe_ffc, sae->peer_commit_scalar, | 
|  | 1487 | sae->tmp->prime, K) < 0 || | 
|  | 1488 | crypto_bignum_mulmod(K, sae->tmp->peer_commit_element_ffc, | 
|  | 1489 | sae->tmp->prime, K) < 0 || | 
|  | 1490 | crypto_bignum_exptmod(K, sae->tmp->sae_rand, sae->tmp->prime, K) < 0 | 
|  | 1491 | || | 
|  | 1492 | crypto_bignum_is_one(K) || | 
|  | 1493 | crypto_bignum_to_bin(K, k, SAE_MAX_PRIME_LEN, sae->tmp->prime_len) < | 
|  | 1494 | 0) { | 
|  | 1495 | wpa_printf(MSG_DEBUG, "SAE: Failed to calculate K and k"); | 
|  | 1496 | goto fail; | 
|  | 1497 | } | 
|  | 1498 |  | 
|  | 1499 | wpa_hexdump_key(MSG_DEBUG, "SAE: k", k, sae->tmp->prime_len); | 
|  | 1500 |  | 
|  | 1501 | ret = 0; | 
|  | 1502 | fail: | 
|  | 1503 | crypto_bignum_deinit(K, 1); | 
|  | 1504 | return ret; | 
|  | 1505 | } | 
|  | 1506 |  | 
|  | 1507 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1508 | static int sae_kdf_hash(size_t hash_len, const u8 *k, const char *label, | 
|  | 1509 | const u8 *context, size_t context_len, | 
|  | 1510 | u8 *out, size_t out_len) | 
|  | 1511 | { | 
|  | 1512 | if (hash_len == 32) | 
|  | 1513 | return sha256_prf(k, hash_len, label, | 
|  | 1514 | context, context_len, out, out_len); | 
|  | 1515 | #ifdef CONFIG_SHA384 | 
|  | 1516 | if (hash_len == 48) | 
|  | 1517 | return sha384_prf(k, hash_len, label, | 
|  | 1518 | context, context_len, out, out_len); | 
|  | 1519 | #endif /* CONFIG_SHA384 */ | 
|  | 1520 | #ifdef CONFIG_SHA512 | 
|  | 1521 | if (hash_len == 64) | 
|  | 1522 | return sha512_prf(k, hash_len, label, | 
|  | 1523 | context, context_len, out, out_len); | 
|  | 1524 | #endif /* CONFIG_SHA512 */ | 
|  | 1525 | return -1; | 
|  | 1526 | } | 
|  | 1527 |  | 
|  | 1528 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1529 | static int sae_derive_keys(struct sae_data *sae, const u8 *k) | 
|  | 1530 | { | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1531 | u8 zero[SAE_MAX_HASH_LEN], val[SAE_MAX_PRIME_LEN]; | 
|  | 1532 | const u8 *salt; | 
|  | 1533 | struct wpabuf *rejected_groups = NULL; | 
|  | 1534 | u8 keyseed[SAE_MAX_HASH_LEN]; | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1535 | u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN]; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1536 | struct crypto_bignum *tmp; | 
|  | 1537 | int ret = -1; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1538 | size_t hash_len, salt_len, prime_len = sae->tmp->prime_len; | 
|  | 1539 | const u8 *addr[1]; | 
|  | 1540 | size_t len[1]; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1541 |  | 
|  | 1542 | tmp = crypto_bignum_init(); | 
|  | 1543 | if (tmp == NULL) | 
|  | 1544 | goto fail; | 
|  | 1545 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1546 | /* keyseed = H(salt, k) | 
|  | 1547 | * KCK || PMK = KDF-Hash-Length(keyseed, "SAE KCK and PMK", | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1548 | *                      (commit-scalar + peer-commit-scalar) modulo r) | 
|  | 1549 | * PMKID = L((commit-scalar + peer-commit-scalar) modulo r, 0, 128) | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1550 | * | 
|  | 1551 | * When SAE-PK is used, | 
|  | 1552 | * KCK || PMK || KEK = KDF-Hash-Length(keyseed, "SAE-PK keys", context) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1553 | */ | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1554 | if (!sae->h2e) | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1555 | hash_len = SHA256_MAC_LEN; | 
|  | 1556 | else if (sae->tmp->dh) | 
|  | 1557 | hash_len = sae_ffc_prime_len_2_hash_len(prime_len); | 
|  | 1558 | else | 
|  | 1559 | hash_len = sae_ecc_prime_len_2_hash_len(prime_len); | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1560 | if (sae->h2e && (sae->tmp->own_rejected_groups || | 
|  | 1561 | sae->tmp->peer_rejected_groups)) { | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1562 | struct wpabuf *own, *peer; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1563 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1564 | own = sae->tmp->own_rejected_groups; | 
|  | 1565 | peer = sae->tmp->peer_rejected_groups; | 
|  | 1566 | salt_len = 0; | 
|  | 1567 | if (own) | 
|  | 1568 | salt_len += wpabuf_len(own); | 
|  | 1569 | if (peer) | 
|  | 1570 | salt_len += wpabuf_len(peer); | 
|  | 1571 | rejected_groups = wpabuf_alloc(salt_len); | 
|  | 1572 | if (!rejected_groups) | 
|  | 1573 | goto fail; | 
|  | 1574 | if (sae->tmp->own_addr_higher) { | 
|  | 1575 | if (own) | 
|  | 1576 | wpabuf_put_buf(rejected_groups, own); | 
|  | 1577 | if (peer) | 
|  | 1578 | wpabuf_put_buf(rejected_groups, peer); | 
|  | 1579 | } else { | 
|  | 1580 | if (peer) | 
|  | 1581 | wpabuf_put_buf(rejected_groups, peer); | 
|  | 1582 | if (own) | 
|  | 1583 | wpabuf_put_buf(rejected_groups, own); | 
|  | 1584 | } | 
|  | 1585 | salt = wpabuf_head(rejected_groups); | 
|  | 1586 | salt_len = wpabuf_len(rejected_groups); | 
|  | 1587 | } else { | 
|  | 1588 | os_memset(zero, 0, hash_len); | 
|  | 1589 | salt = zero; | 
|  | 1590 | salt_len = hash_len; | 
|  | 1591 | } | 
|  | 1592 | wpa_hexdump(MSG_DEBUG, "SAE: salt for keyseed derivation", | 
|  | 1593 | salt, salt_len); | 
|  | 1594 | addr[0] = k; | 
|  | 1595 | len[0] = prime_len; | 
|  | 1596 | if (hkdf_extract(hash_len, salt, salt_len, 1, addr, len, keyseed) < 0) | 
| Dmitry Shmidt | e466304 | 2016-04-04 10:07:49 -0700 | [diff] [blame] | 1597 | goto fail; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1598 | wpa_hexdump_key(MSG_DEBUG, "SAE: keyseed", keyseed, hash_len); | 
|  | 1599 |  | 
|  | 1600 | if (crypto_bignum_add(sae->tmp->own_commit_scalar, | 
|  | 1601 | sae->peer_commit_scalar, tmp) < 0 || | 
|  | 1602 | crypto_bignum_mod(tmp, sae->tmp->order, tmp) < 0) | 
|  | 1603 | goto fail; | 
|  | 1604 | /* IEEE Std 802.11-2016 is not exactly clear on the encoding of the bit | 
|  | 1605 | * string that is needed for KCK, PMK, and PMKID derivation, but it | 
|  | 1606 | * seems to make most sense to encode the | 
|  | 1607 | * (commit-scalar + peer-commit-scalar) mod r part as a bit string by | 
|  | 1608 | * zero padding it from left to the length of the order (in full | 
|  | 1609 | * octets). */ | 
|  | 1610 | crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->order_len); | 
|  | 1611 | wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN); | 
| Hai Shalom | 6084025 | 2021-02-19 19:02:11 -0800 | [diff] [blame] | 1612 |  | 
|  | 1613 | #ifdef CONFIG_SAE_PK | 
|  | 1614 | if (sae->pk) { | 
|  | 1615 | if (sae_kdf_hash(hash_len, keyseed, "SAE-PK keys", | 
|  | 1616 | val, sae->tmp->order_len, | 
|  | 1617 | keys, 2 * hash_len + SAE_PMK_LEN) < 0) | 
|  | 1618 | goto fail; | 
|  | 1619 | } else { | 
|  | 1620 | if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK", | 
|  | 1621 | val, sae->tmp->order_len, | 
|  | 1622 | keys, hash_len + SAE_PMK_LEN) < 0) | 
|  | 1623 | goto fail; | 
|  | 1624 | } | 
|  | 1625 | #else /* CONFIG_SAE_PK */ | 
|  | 1626 | if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK", | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1627 | val, sae->tmp->order_len, | 
|  | 1628 | keys, hash_len + SAE_PMK_LEN) < 0) | 
|  | 1629 | goto fail; | 
| Hai Shalom | 6084025 | 2021-02-19 19:02:11 -0800 | [diff] [blame] | 1630 | #endif /* !CONFIG_SAE_PK */ | 
|  | 1631 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1632 | forced_memzero(keyseed, sizeof(keyseed)); | 
|  | 1633 | os_memcpy(sae->tmp->kck, keys, hash_len); | 
|  | 1634 | sae->tmp->kck_len = hash_len; | 
|  | 1635 | os_memcpy(sae->pmk, keys + hash_len, SAE_PMK_LEN); | 
| Dmitry Shmidt | d97138d | 2015-12-28 13:27:49 -0800 | [diff] [blame] | 1636 | os_memcpy(sae->pmkid, val, SAE_PMKID_LEN); | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1637 | #ifdef CONFIG_SAE_PK | 
|  | 1638 | if (sae->pk) { | 
|  | 1639 | os_memcpy(sae->tmp->kek, keys + hash_len + SAE_PMK_LEN, | 
|  | 1640 | hash_len); | 
|  | 1641 | sae->tmp->kek_len = hash_len; | 
|  | 1642 | wpa_hexdump_key(MSG_DEBUG, "SAE: KEK for SAE-PK", | 
|  | 1643 | sae->tmp->kek, sae->tmp->kek_len); | 
|  | 1644 | } | 
|  | 1645 | #endif /* CONFIG_SAE_PK */ | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1646 | forced_memzero(keys, sizeof(keys)); | 
|  | 1647 | wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", | 
|  | 1648 | sae->tmp->kck, sae->tmp->kck_len); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1649 | wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN); | 
|  | 1650 |  | 
|  | 1651 | ret = 0; | 
|  | 1652 | fail: | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1653 | wpabuf_free(rejected_groups); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1654 | crypto_bignum_deinit(tmp, 0); | 
|  | 1655 | return ret; | 
|  | 1656 | } | 
|  | 1657 |  | 
|  | 1658 |  | 
|  | 1659 | int sae_process_commit(struct sae_data *sae) | 
|  | 1660 | { | 
|  | 1661 | u8 k[SAE_MAX_PRIME_LEN]; | 
| Dmitry Shmidt | 96be622 | 2014-02-13 10:16:51 -0800 | [diff] [blame] | 1662 | if (sae->tmp == NULL || | 
|  | 1663 | (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) || | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1664 | (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) || | 
|  | 1665 | sae_derive_keys(sae, k) < 0) | 
|  | 1666 | return -1; | 
|  | 1667 | return 0; | 
|  | 1668 | } | 
|  | 1669 |  | 
|  | 1670 |  | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1671 | int sae_write_commit(struct sae_data *sae, struct wpabuf *buf, | 
|  | 1672 | const struct wpabuf *token, const char *identifier) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1673 | { | 
|  | 1674 | u8 *pos; | 
| Dmitry Shmidt | 96be622 | 2014-02-13 10:16:51 -0800 | [diff] [blame] | 1675 |  | 
|  | 1676 | if (sae->tmp == NULL) | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1677 | return -1; | 
| Dmitry Shmidt | 96be622 | 2014-02-13 10:16:51 -0800 | [diff] [blame] | 1678 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1679 | wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */ | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1680 | if (!sae->h2e && token) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1681 | wpabuf_put_buf(buf, token); | 
| Dmitry Shmidt | fb45fd5 | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 1682 | wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token", | 
|  | 1683 | wpabuf_head(token), wpabuf_len(token)); | 
|  | 1684 | } | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1685 | pos = wpabuf_put(buf, sae->tmp->prime_len); | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1686 | if (crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos, | 
|  | 1687 | sae->tmp->prime_len, sae->tmp->prime_len) < 0) | 
|  | 1688 | return -1; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1689 | wpa_hexdump(MSG_DEBUG, "SAE: own commit-scalar", | 
|  | 1690 | pos, sae->tmp->prime_len); | 
|  | 1691 | if (sae->tmp->ec) { | 
|  | 1692 | pos = wpabuf_put(buf, 2 * sae->tmp->prime_len); | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1693 | if (crypto_ec_point_to_bin(sae->tmp->ec, | 
|  | 1694 | sae->tmp->own_commit_element_ecc, | 
|  | 1695 | pos, pos + sae->tmp->prime_len) < 0) | 
|  | 1696 | return -1; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1697 | wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(x)", | 
|  | 1698 | pos, sae->tmp->prime_len); | 
|  | 1699 | wpa_hexdump(MSG_DEBUG, "SAE: own commit-element(y)", | 
|  | 1700 | pos + sae->tmp->prime_len, sae->tmp->prime_len); | 
|  | 1701 | } else { | 
|  | 1702 | pos = wpabuf_put(buf, sae->tmp->prime_len); | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1703 | if (crypto_bignum_to_bin(sae->tmp->own_commit_element_ffc, pos, | 
|  | 1704 | sae->tmp->prime_len, | 
|  | 1705 | sae->tmp->prime_len) < 0) | 
|  | 1706 | return -1; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1707 | wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", | 
|  | 1708 | pos, sae->tmp->prime_len); | 
|  | 1709 | } | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1710 |  | 
|  | 1711 | if (identifier) { | 
|  | 1712 | /* Password Identifier element */ | 
|  | 1713 | wpabuf_put_u8(buf, WLAN_EID_EXTENSION); | 
|  | 1714 | wpabuf_put_u8(buf, 1 + os_strlen(identifier)); | 
|  | 1715 | wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); | 
|  | 1716 | wpabuf_put_str(buf, identifier); | 
|  | 1717 | wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", | 
|  | 1718 | identifier); | 
|  | 1719 | } | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1720 |  | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1721 | if (sae->h2e && sae->tmp->own_rejected_groups) { | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1722 | wpa_hexdump_buf(MSG_DEBUG, "SAE: own Rejected Groups", | 
|  | 1723 | sae->tmp->own_rejected_groups); | 
|  | 1724 | wpabuf_put_u8(buf, WLAN_EID_EXTENSION); | 
|  | 1725 | wpabuf_put_u8(buf, | 
|  | 1726 | 1 + wpabuf_len(sae->tmp->own_rejected_groups)); | 
|  | 1727 | wpabuf_put_u8(buf, WLAN_EID_EXT_REJECTED_GROUPS); | 
|  | 1728 | wpabuf_put_buf(buf, sae->tmp->own_rejected_groups); | 
|  | 1729 | } | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1730 |  | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 1731 | if (sae->h2e && token) { | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1732 | wpabuf_put_u8(buf, WLAN_EID_EXTENSION); | 
|  | 1733 | wpabuf_put_u8(buf, 1 + wpabuf_len(token)); | 
|  | 1734 | wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN); | 
|  | 1735 | wpabuf_put_buf(buf, token); | 
|  | 1736 | wpa_hexdump_buf(MSG_DEBUG, | 
|  | 1737 | "SAE: Anti-clogging token (in container)", | 
|  | 1738 | token); | 
|  | 1739 | } | 
|  | 1740 |  | 
|  | 1741 | return 0; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1742 | } | 
|  | 1743 |  | 
|  | 1744 |  | 
| Dmitry Shmidt | fb45fd5 | 2015-01-05 13:08:17 -0800 | [diff] [blame] | 1745 | u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1746 | { | 
|  | 1747 | if (allowed_groups) { | 
|  | 1748 | int i; | 
| Dmitry Shmidt | cce0666 | 2013-11-04 18:44:24 -0800 | [diff] [blame] | 1749 | for (i = 0; allowed_groups[i] > 0; i++) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1750 | if (allowed_groups[i] == group) | 
|  | 1751 | break; | 
|  | 1752 | } | 
|  | 1753 | if (allowed_groups[i] != group) { | 
|  | 1754 | wpa_printf(MSG_DEBUG, "SAE: Proposed group %u not " | 
|  | 1755 | "enabled in the current configuration", | 
|  | 1756 | group); | 
|  | 1757 | return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; | 
|  | 1758 | } | 
|  | 1759 | } | 
|  | 1760 |  | 
|  | 1761 | if (sae->state == SAE_COMMITTED && group != sae->group) { | 
|  | 1762 | wpa_printf(MSG_DEBUG, "SAE: Do not allow group to be changed"); | 
|  | 1763 | return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; | 
|  | 1764 | } | 
|  | 1765 |  | 
|  | 1766 | if (group != sae->group && sae_set_group(sae, group) < 0) { | 
|  | 1767 | wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", | 
|  | 1768 | group); | 
|  | 1769 | return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; | 
|  | 1770 | } | 
|  | 1771 |  | 
| Dmitry Shmidt | fb79edc | 2014-01-10 10:45:54 -0800 | [diff] [blame] | 1772 | if (sae->tmp == NULL) { | 
|  | 1773 | wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized"); | 
|  | 1774 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1775 | } | 
|  | 1776 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1777 | if (sae->tmp->dh && !allowed_groups) { | 
|  | 1778 | wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without " | 
|  | 1779 | "explicit configuration enabling it", group); | 
|  | 1780 | return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; | 
|  | 1781 | } | 
|  | 1782 |  | 
|  | 1783 | return WLAN_STATUS_SUCCESS; | 
|  | 1784 | } | 
|  | 1785 |  | 
|  | 1786 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1787 | static int sae_is_password_id_elem(const u8 *pos, const u8 *end) | 
|  | 1788 | { | 
|  | 1789 | return end - pos >= 3 && | 
|  | 1790 | pos[0] == WLAN_EID_EXTENSION && | 
|  | 1791 | pos[1] >= 1 && | 
|  | 1792 | end - pos - 2 >= pos[1] && | 
|  | 1793 | pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; | 
|  | 1794 | } | 
|  | 1795 |  | 
|  | 1796 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1797 | static int sae_is_rejected_groups_elem(const u8 *pos, const u8 *end) | 
|  | 1798 | { | 
|  | 1799 | return end - pos >= 3 && | 
|  | 1800 | pos[0] == WLAN_EID_EXTENSION && | 
|  | 1801 | pos[1] >= 2 && | 
|  | 1802 | end - pos - 2 >= pos[1] && | 
|  | 1803 | pos[2] == WLAN_EID_EXT_REJECTED_GROUPS; | 
|  | 1804 | } | 
|  | 1805 |  | 
|  | 1806 |  | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1807 | static int sae_is_token_container_elem(const u8 *pos, const u8 *end) | 
|  | 1808 | { | 
|  | 1809 | return end - pos >= 3 && | 
|  | 1810 | pos[0] == WLAN_EID_EXTENSION && | 
|  | 1811 | pos[1] >= 1 && | 
|  | 1812 | end - pos - 2 >= pos[1] && | 
|  | 1813 | pos[2] == WLAN_EID_EXT_ANTI_CLOGGING_TOKEN; | 
|  | 1814 | } | 
|  | 1815 |  | 
|  | 1816 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1817 | static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, | 
|  | 1818 | const u8 *end, const u8 **token, | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 1819 | size_t *token_len, int h2e) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1820 | { | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1821 | size_t scalar_elem_len, tlen; | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1822 |  | 
|  | 1823 | if (token) | 
|  | 1824 | *token = NULL; | 
|  | 1825 | if (token_len) | 
|  | 1826 | *token_len = 0; | 
|  | 1827 |  | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1828 | if (h2e) | 
|  | 1829 | return; /* No Anti-Clogging Token field outside container IE */ | 
|  | 1830 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1831 | scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; | 
|  | 1832 | if (scalar_elem_len >= (size_t) (end - *pos)) | 
|  | 1833 | return; /* No extra data beyond peer scalar and element */ | 
|  | 1834 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1835 | tlen = end - (*pos + scalar_elem_len); | 
|  | 1836 |  | 
|  | 1837 | if (tlen < SHA256_MAC_LEN) { | 
|  | 1838 | wpa_printf(MSG_DEBUG, | 
|  | 1839 | "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", | 
|  | 1840 | (unsigned int) tlen); | 
|  | 1841 | return; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1842 | } | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1843 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1844 | wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); | 
|  | 1845 | if (token) | 
|  | 1846 | *token = *pos; | 
|  | 1847 | if (token_len) | 
|  | 1848 | *token_len = tlen; | 
|  | 1849 | *pos += tlen; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1850 | } | 
|  | 1851 |  | 
|  | 1852 |  | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1853 | static void sae_parse_token_container(struct sae_data *sae, | 
|  | 1854 | const u8 *pos, const u8 *end, | 
|  | 1855 | const u8 **token, size_t *token_len) | 
|  | 1856 | { | 
|  | 1857 | wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", | 
|  | 1858 | pos, end - pos); | 
|  | 1859 | if (!sae_is_token_container_elem(pos, end)) | 
|  | 1860 | return; | 
|  | 1861 | *token = pos + 3; | 
|  | 1862 | *token_len = pos[1] - 1; | 
|  | 1863 | wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token (in container)", | 
|  | 1864 | *token, *token_len); | 
|  | 1865 | } | 
|  | 1866 |  | 
|  | 1867 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1868 | static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, | 
|  | 1869 | const u8 *end) | 
|  | 1870 | { | 
|  | 1871 | struct crypto_bignum *peer_scalar; | 
|  | 1872 |  | 
| Dmitry Shmidt | d80a401 | 2015-11-05 16:35:40 -0800 | [diff] [blame] | 1873 | if (sae->tmp->prime_len > end - *pos) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1874 | wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar"); | 
|  | 1875 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1876 | } | 
|  | 1877 |  | 
|  | 1878 | peer_scalar = crypto_bignum_init_set(*pos, sae->tmp->prime_len); | 
|  | 1879 | if (peer_scalar == NULL) | 
|  | 1880 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1881 |  | 
|  | 1882 | /* | 
|  | 1883 | * IEEE Std 802.11-2012, 11.3.8.6.1: If there is a protocol instance for | 
|  | 1884 | * the peer and it is in Authenticated state, the new Commit Message | 
|  | 1885 | * shall be dropped if the peer-scalar is identical to the one used in | 
|  | 1886 | * the existing protocol instance. | 
|  | 1887 | */ | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 1888 | if (sae->state == SAE_ACCEPTED && sae->peer_commit_scalar_accepted && | 
|  | 1889 | crypto_bignum_cmp(sae->peer_commit_scalar_accepted, | 
|  | 1890 | peer_scalar) == 0) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1891 | wpa_printf(MSG_DEBUG, "SAE: Do not accept re-use of previous " | 
|  | 1892 | "peer-commit-scalar"); | 
|  | 1893 | crypto_bignum_deinit(peer_scalar, 0); | 
|  | 1894 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1895 | } | 
|  | 1896 |  | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 1897 | /* 1 < scalar < r */ | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1898 | if (crypto_bignum_is_zero(peer_scalar) || | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 1899 | crypto_bignum_is_one(peer_scalar) || | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1900 | crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) { | 
|  | 1901 | wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar"); | 
|  | 1902 | crypto_bignum_deinit(peer_scalar, 0); | 
|  | 1903 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1904 | } | 
|  | 1905 |  | 
|  | 1906 |  | 
|  | 1907 | crypto_bignum_deinit(sae->peer_commit_scalar, 0); | 
|  | 1908 | sae->peer_commit_scalar = peer_scalar; | 
|  | 1909 | wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-scalar", | 
|  | 1910 | *pos, sae->tmp->prime_len); | 
|  | 1911 | *pos += sae->tmp->prime_len; | 
|  | 1912 |  | 
|  | 1913 | return WLAN_STATUS_SUCCESS; | 
|  | 1914 | } | 
|  | 1915 |  | 
|  | 1916 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1917 | static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1918 | const u8 *end) | 
|  | 1919 | { | 
|  | 1920 | u8 prime[SAE_MAX_ECC_PRIME_LEN]; | 
|  | 1921 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1922 | if (2 * sae->tmp->prime_len > end - *pos) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1923 | wpa_printf(MSG_DEBUG, "SAE: Not enough data for " | 
|  | 1924 | "commit-element"); | 
|  | 1925 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1926 | } | 
|  | 1927 |  | 
|  | 1928 | if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime), | 
|  | 1929 | sae->tmp->prime_len) < 0) | 
|  | 1930 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1931 |  | 
|  | 1932 | /* element x and y coordinates < p */ | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1933 | if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || | 
|  | 1934 | os_memcmp(*pos + sae->tmp->prime_len, prime, | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1935 | sae->tmp->prime_len) >= 0) { | 
|  | 1936 | wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " | 
|  | 1937 | "element"); | 
|  | 1938 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1939 | } | 
|  | 1940 |  | 
|  | 1941 | wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1942 | *pos, sae->tmp->prime_len); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1943 | wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1944 | *pos + sae->tmp->prime_len, sae->tmp->prime_len); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1945 |  | 
|  | 1946 | crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); | 
|  | 1947 | sae->tmp->peer_commit_element_ecc = | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1948 | crypto_ec_point_from_bin(sae->tmp->ec, *pos); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1949 | if (sae->tmp->peer_commit_element_ecc == NULL) | 
|  | 1950 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1951 |  | 
| Dmitry Shmidt | 2f02319 | 2013-03-12 12:44:17 -0700 | [diff] [blame] | 1952 | if (!crypto_ec_point_is_on_curve(sae->tmp->ec, | 
|  | 1953 | sae->tmp->peer_commit_element_ecc)) { | 
|  | 1954 | wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve"); | 
|  | 1955 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1956 | } | 
|  | 1957 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1958 | *pos += 2 * sae->tmp->prime_len; | 
|  | 1959 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1960 | return WLAN_STATUS_SUCCESS; | 
|  | 1961 | } | 
|  | 1962 |  | 
|  | 1963 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1964 | static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1965 | const u8 *end) | 
|  | 1966 | { | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 1967 | struct crypto_bignum *res, *one; | 
|  | 1968 | const u8 one_bin[1] = { 0x01 }; | 
| Dmitry Shmidt | 2f02319 | 2013-03-12 12:44:17 -0700 | [diff] [blame] | 1969 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1970 | if (sae->tmp->prime_len > end - *pos) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1971 | wpa_printf(MSG_DEBUG, "SAE: Not enough data for " | 
|  | 1972 | "commit-element"); | 
|  | 1973 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1974 | } | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1975 | wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1976 | sae->tmp->prime_len); | 
|  | 1977 |  | 
|  | 1978 | crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); | 
|  | 1979 | sae->tmp->peer_commit_element_ffc = | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 1980 | crypto_bignum_init_set(*pos, sae->tmp->prime_len); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1981 | if (sae->tmp->peer_commit_element_ffc == NULL) | 
|  | 1982 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 1983 | /* 1 < element < p - 1 */ | 
|  | 1984 | res = crypto_bignum_init(); | 
|  | 1985 | one = crypto_bignum_init_set(one_bin, sizeof(one_bin)); | 
|  | 1986 | if (!res || !one || | 
|  | 1987 | crypto_bignum_sub(sae->tmp->prime, one, res) || | 
|  | 1988 | crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) || | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1989 | crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) || | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 1990 | crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) { | 
|  | 1991 | crypto_bignum_deinit(res, 0); | 
|  | 1992 | crypto_bignum_deinit(one, 0); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1993 | wpa_printf(MSG_DEBUG, "SAE: Invalid peer element"); | 
|  | 1994 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 1995 | } | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 1996 | crypto_bignum_deinit(one, 0); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 1997 |  | 
| Dmitry Shmidt | 2f02319 | 2013-03-12 12:44:17 -0700 | [diff] [blame] | 1998 | /* scalar-op(r, ELEMENT) = 1 modulo p */ | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 1999 | if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc, | 
| Dmitry Shmidt | 2f02319 | 2013-03-12 12:44:17 -0700 | [diff] [blame] | 2000 | sae->tmp->order, sae->tmp->prime, res) < 0 || | 
|  | 2001 | !crypto_bignum_is_one(res)) { | 
|  | 2002 | wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)"); | 
|  | 2003 | crypto_bignum_deinit(res, 0); | 
|  | 2004 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 2005 | } | 
|  | 2006 | crypto_bignum_deinit(res, 0); | 
|  | 2007 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2008 | *pos += sae->tmp->prime_len; | 
|  | 2009 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2010 | return WLAN_STATUS_SUCCESS; | 
|  | 2011 | } | 
|  | 2012 |  | 
|  | 2013 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2014 | static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2015 | const u8 *end) | 
|  | 2016 | { | 
|  | 2017 | if (sae->tmp->dh) | 
|  | 2018 | return sae_parse_commit_element_ffc(sae, pos, end); | 
|  | 2019 | return sae_parse_commit_element_ecc(sae, pos, end); | 
|  | 2020 | } | 
|  | 2021 |  | 
|  | 2022 |  | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2023 | static int sae_parse_password_identifier(struct sae_data *sae, | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2024 | const u8 **pos, const u8 *end) | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2025 | { | 
|  | 2026 | wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2027 | *pos, end - *pos); | 
|  | 2028 | if (!sae_is_password_id_elem(*pos, end)) { | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2029 | if (sae->tmp->pw_id) { | 
|  | 2030 | wpa_printf(MSG_DEBUG, | 
|  | 2031 | "SAE: No Password Identifier included, but expected one (%s)", | 
|  | 2032 | sae->tmp->pw_id); | 
|  | 2033 | return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; | 
|  | 2034 | } | 
|  | 2035 | os_free(sae->tmp->pw_id); | 
|  | 2036 | sae->tmp->pw_id = NULL; | 
|  | 2037 | return WLAN_STATUS_SUCCESS; /* No Password Identifier */ | 
|  | 2038 | } | 
|  | 2039 |  | 
|  | 2040 | if (sae->tmp->pw_id && | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2041 | ((*pos)[1] - 1 != (int) os_strlen(sae->tmp->pw_id) || | 
|  | 2042 | os_memcmp(sae->tmp->pw_id, (*pos) + 3, (*pos)[1] - 1) != 0)) { | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2043 | wpa_printf(MSG_DEBUG, | 
|  | 2044 | "SAE: The included Password Identifier does not match the expected one (%s)", | 
|  | 2045 | sae->tmp->pw_id); | 
|  | 2046 | return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; | 
|  | 2047 | } | 
|  | 2048 |  | 
|  | 2049 | os_free(sae->tmp->pw_id); | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2050 | sae->tmp->pw_id = os_malloc((*pos)[1]); | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2051 | if (!sae->tmp->pw_id) | 
|  | 2052 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2053 | os_memcpy(sae->tmp->pw_id, (*pos) + 3, (*pos)[1] - 1); | 
|  | 2054 | sae->tmp->pw_id[(*pos)[1] - 1] = '\0'; | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2055 | wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier", | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2056 | sae->tmp->pw_id, (*pos)[1] -  1); | 
|  | 2057 | *pos = *pos + 2 + (*pos)[1]; | 
|  | 2058 | return WLAN_STATUS_SUCCESS; | 
|  | 2059 | } | 
|  | 2060 |  | 
|  | 2061 |  | 
|  | 2062 | static int sae_parse_rejected_groups(struct sae_data *sae, | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 2063 | const u8 **pos, const u8 *end) | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2064 | { | 
|  | 2065 | wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 2066 | *pos, end - *pos); | 
|  | 2067 | if (!sae_is_rejected_groups_elem(*pos, end)) | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2068 | return WLAN_STATUS_SUCCESS; | 
|  | 2069 | wpabuf_free(sae->tmp->peer_rejected_groups); | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 2070 | sae->tmp->peer_rejected_groups = wpabuf_alloc((*pos)[1] - 1); | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2071 | if (!sae->tmp->peer_rejected_groups) | 
|  | 2072 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 2073 | wpabuf_put_data(sae->tmp->peer_rejected_groups, (*pos) + 3, | 
|  | 2074 | (*pos)[1] - 1); | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2075 | wpa_hexdump_buf(MSG_DEBUG, "SAE: Received Rejected Groups list", | 
|  | 2076 | sae->tmp->peer_rejected_groups); | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 2077 | *pos = *pos + 2 + (*pos)[1]; | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2078 | return WLAN_STATUS_SUCCESS; | 
|  | 2079 | } | 
|  | 2080 |  | 
|  | 2081 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2082 | u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2083 | const u8 **token, size_t *token_len, int *allowed_groups, | 
|  | 2084 | int h2e) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2085 | { | 
|  | 2086 | const u8 *pos = data, *end = data + len; | 
|  | 2087 | u16 res; | 
|  | 2088 |  | 
|  | 2089 | /* Check Finite Cyclic Group */ | 
| Dmitry Shmidt | d80a401 | 2015-11-05 16:35:40 -0800 | [diff] [blame] | 2090 | if (end - pos < 2) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2091 | return WLAN_STATUS_UNSPECIFIED_FAILURE; | 
|  | 2092 | res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos)); | 
|  | 2093 | if (res != WLAN_STATUS_SUCCESS) | 
|  | 2094 | return res; | 
|  | 2095 | pos += 2; | 
|  | 2096 |  | 
|  | 2097 | /* Optional Anti-Clogging Token */ | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2098 | sae_parse_commit_token(sae, &pos, end, token, token_len, h2e); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2099 |  | 
|  | 2100 | /* commit-scalar */ | 
|  | 2101 | res = sae_parse_commit_scalar(sae, &pos, end); | 
|  | 2102 | if (res != WLAN_STATUS_SUCCESS) | 
|  | 2103 | return res; | 
|  | 2104 |  | 
|  | 2105 | /* commit-element */ | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2106 | res = sae_parse_commit_element(sae, &pos, end); | 
|  | 2107 | if (res != WLAN_STATUS_SUCCESS) | 
|  | 2108 | return res; | 
|  | 2109 |  | 
|  | 2110 | /* Optional Password Identifier element */ | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2111 | res = sae_parse_password_identifier(sae, &pos, end); | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 2112 | if (res != WLAN_STATUS_SUCCESS) | 
|  | 2113 | return res; | 
|  | 2114 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2115 | /* Conditional Rejected Groups element */ | 
|  | 2116 | if (h2e) { | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 2117 | res = sae_parse_rejected_groups(sae, &pos, end); | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2118 | if (res != WLAN_STATUS_SUCCESS) | 
|  | 2119 | return res; | 
|  | 2120 | } | 
|  | 2121 |  | 
| Hai Shalom | fdcde76 | 2020-04-02 11:19:20 -0700 | [diff] [blame] | 2122 | /* Optional Anti-Clogging Token Container element */ | 
|  | 2123 | if (h2e) | 
|  | 2124 | sae_parse_token_container(sae, pos, end, token, token_len); | 
|  | 2125 |  | 
| Dmitry Shmidt | 4171258 | 2015-06-29 11:02:15 -0700 | [diff] [blame] | 2126 | /* | 
|  | 2127 | * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as | 
|  | 2128 | * the values we sent which would be evidence of a reflection attack. | 
|  | 2129 | */ | 
|  | 2130 | if (!sae->tmp->own_commit_scalar || | 
|  | 2131 | crypto_bignum_cmp(sae->tmp->own_commit_scalar, | 
|  | 2132 | sae->peer_commit_scalar) != 0 || | 
|  | 2133 | (sae->tmp->dh && | 
|  | 2134 | (!sae->tmp->own_commit_element_ffc || | 
|  | 2135 | crypto_bignum_cmp(sae->tmp->own_commit_element_ffc, | 
|  | 2136 | sae->tmp->peer_commit_element_ffc) != 0)) || | 
|  | 2137 | (sae->tmp->ec && | 
|  | 2138 | (!sae->tmp->own_commit_element_ecc || | 
|  | 2139 | crypto_ec_point_cmp(sae->tmp->ec, | 
|  | 2140 | sae->tmp->own_commit_element_ecc, | 
|  | 2141 | sae->tmp->peer_commit_element_ecc) != 0))) | 
|  | 2142 | return WLAN_STATUS_SUCCESS; /* scalars/elements are different */ | 
|  | 2143 |  | 
|  | 2144 | /* | 
|  | 2145 | * This is a reflection attack - return special value to trigger caller | 
|  | 2146 | * to silently discard the frame instead of replying with a specific | 
|  | 2147 | * status code. | 
|  | 2148 | */ | 
|  | 2149 | return SAE_SILENTLY_DISCARD; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2150 | } | 
|  | 2151 |  | 
|  | 2152 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2153 | static int sae_cn_confirm(struct sae_data *sae, const u8 *sc, | 
|  | 2154 | const struct crypto_bignum *scalar1, | 
|  | 2155 | const u8 *element1, size_t element1_len, | 
|  | 2156 | const struct crypto_bignum *scalar2, | 
|  | 2157 | const u8 *element2, size_t element2_len, | 
|  | 2158 | u8 *confirm) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2159 | { | 
|  | 2160 | const u8 *addr[5]; | 
|  | 2161 | size_t len[5]; | 
|  | 2162 | u8 scalar_b1[SAE_MAX_PRIME_LEN], scalar_b2[SAE_MAX_PRIME_LEN]; | 
|  | 2163 |  | 
|  | 2164 | /* Confirm | 
|  | 2165 | * CN(key, X, Y, Z, ...) = | 
|  | 2166 | *    HMAC-SHA256(key, D2OS(X) || D2OS(Y) || D2OS(Z) | ...) | 
|  | 2167 | * confirm = CN(KCK, send-confirm, commit-scalar, COMMIT-ELEMENT, | 
|  | 2168 | *              peer-commit-scalar, PEER-COMMIT-ELEMENT) | 
|  | 2169 | * verifier = CN(KCK, peer-send-confirm, peer-commit-scalar, | 
|  | 2170 | *               PEER-COMMIT-ELEMENT, commit-scalar, COMMIT-ELEMENT) | 
|  | 2171 | */ | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2172 | if (crypto_bignum_to_bin(scalar1, scalar_b1, sizeof(scalar_b1), | 
|  | 2173 | sae->tmp->prime_len) < 0 || | 
|  | 2174 | crypto_bignum_to_bin(scalar2, scalar_b2, sizeof(scalar_b2), | 
|  | 2175 | sae->tmp->prime_len) < 0) | 
|  | 2176 | return -1; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2177 | addr[0] = sc; | 
|  | 2178 | len[0] = 2; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2179 | addr[1] = scalar_b1; | 
|  | 2180 | len[1] = sae->tmp->prime_len; | 
|  | 2181 | addr[2] = element1; | 
|  | 2182 | len[2] = element1_len; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2183 | addr[3] = scalar_b2; | 
|  | 2184 | len[3] = sae->tmp->prime_len; | 
|  | 2185 | addr[4] = element2; | 
|  | 2186 | len[4] = element2_len; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2187 | return hkdf_extract(sae->tmp->kck_len, sae->tmp->kck, sae->tmp->kck_len, | 
|  | 2188 | 5, addr, len, confirm); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2189 | } | 
|  | 2190 |  | 
|  | 2191 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2192 | static int sae_cn_confirm_ecc(struct sae_data *sae, const u8 *sc, | 
|  | 2193 | const struct crypto_bignum *scalar1, | 
|  | 2194 | const struct crypto_ec_point *element1, | 
|  | 2195 | const struct crypto_bignum *scalar2, | 
|  | 2196 | const struct crypto_ec_point *element2, | 
|  | 2197 | u8 *confirm) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2198 | { | 
|  | 2199 | u8 element_b1[2 * SAE_MAX_ECC_PRIME_LEN]; | 
|  | 2200 | u8 element_b2[2 * SAE_MAX_ECC_PRIME_LEN]; | 
|  | 2201 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2202 | if (crypto_ec_point_to_bin(sae->tmp->ec, element1, element_b1, | 
|  | 2203 | element_b1 + sae->tmp->prime_len) < 0 || | 
|  | 2204 | crypto_ec_point_to_bin(sae->tmp->ec, element2, element_b2, | 
|  | 2205 | element_b2 + sae->tmp->prime_len) < 0 || | 
|  | 2206 | sae_cn_confirm(sae, sc, scalar1, element_b1, | 
|  | 2207 | 2 * sae->tmp->prime_len, | 
|  | 2208 | scalar2, element_b2, 2 * sae->tmp->prime_len, | 
|  | 2209 | confirm) < 0) | 
|  | 2210 | return -1; | 
|  | 2211 | return 0; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2212 | } | 
|  | 2213 |  | 
|  | 2214 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2215 | static int sae_cn_confirm_ffc(struct sae_data *sae, const u8 *sc, | 
|  | 2216 | const struct crypto_bignum *scalar1, | 
|  | 2217 | const struct crypto_bignum *element1, | 
|  | 2218 | const struct crypto_bignum *scalar2, | 
|  | 2219 | const struct crypto_bignum *element2, | 
|  | 2220 | u8 *confirm) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2221 | { | 
|  | 2222 | u8 element_b1[SAE_MAX_PRIME_LEN]; | 
|  | 2223 | u8 element_b2[SAE_MAX_PRIME_LEN]; | 
|  | 2224 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2225 | if (crypto_bignum_to_bin(element1, element_b1, sizeof(element_b1), | 
|  | 2226 | sae->tmp->prime_len) < 0 || | 
|  | 2227 | crypto_bignum_to_bin(element2, element_b2, sizeof(element_b2), | 
|  | 2228 | sae->tmp->prime_len) < 0 || | 
|  | 2229 | sae_cn_confirm(sae, sc, scalar1, element_b1, sae->tmp->prime_len, | 
|  | 2230 | scalar2, element_b2, sae->tmp->prime_len, | 
|  | 2231 | confirm) < 0) | 
|  | 2232 | return -1; | 
|  | 2233 | return 0; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2234 | } | 
|  | 2235 |  | 
|  | 2236 |  | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 2237 | int sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2238 | { | 
|  | 2239 | const u8 *sc; | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2240 | size_t hash_len; | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 2241 | int res; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2242 |  | 
| Dmitry Shmidt | 96be622 | 2014-02-13 10:16:51 -0800 | [diff] [blame] | 2243 | if (sae->tmp == NULL) | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 2244 | return -1; | 
| Dmitry Shmidt | 96be622 | 2014-02-13 10:16:51 -0800 | [diff] [blame] | 2245 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2246 | hash_len = sae->tmp->kck_len; | 
|  | 2247 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2248 | /* Send-Confirm */ | 
|  | 2249 | sc = wpabuf_put(buf, 0); | 
|  | 2250 | wpabuf_put_le16(buf, sae->send_confirm); | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2251 | if (sae->send_confirm < 0xffff) | 
|  | 2252 | sae->send_confirm++; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2253 |  | 
|  | 2254 | if (sae->tmp->ec) | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 2255 | res = sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, | 
|  | 2256 | sae->tmp->own_commit_element_ecc, | 
|  | 2257 | sae->peer_commit_scalar, | 
|  | 2258 | sae->tmp->peer_commit_element_ecc, | 
|  | 2259 | wpabuf_put(buf, hash_len)); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2260 | else | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 2261 | res = sae_cn_confirm_ffc(sae, sc, sae->tmp->own_commit_scalar, | 
|  | 2262 | sae->tmp->own_commit_element_ffc, | 
|  | 2263 | sae->peer_commit_scalar, | 
|  | 2264 | sae->tmp->peer_commit_element_ffc, | 
|  | 2265 | wpabuf_put(buf, hash_len)); | 
|  | 2266 | if (res) | 
|  | 2267 | return res; | 
|  | 2268 |  | 
|  | 2269 | #ifdef CONFIG_SAE_PK | 
|  | 2270 | if (sae_write_confirm_pk(sae, buf) < 0) | 
|  | 2271 | return -1; | 
|  | 2272 | #endif /* CONFIG_SAE_PK */ | 
|  | 2273 |  | 
|  | 2274 | return 0; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2275 | } | 
|  | 2276 |  | 
|  | 2277 |  | 
|  | 2278 | int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) | 
|  | 2279 | { | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2280 | u8 verifier[SAE_MAX_HASH_LEN]; | 
|  | 2281 | size_t hash_len; | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2282 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2283 | if (!sae->tmp) | 
|  | 2284 | return -1; | 
|  | 2285 |  | 
|  | 2286 | hash_len = sae->tmp->kck_len; | 
|  | 2287 | if (len < 2 + hash_len) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2288 | wpa_printf(MSG_DEBUG, "SAE: Too short confirm message"); | 
|  | 2289 | return -1; | 
|  | 2290 | } | 
|  | 2291 |  | 
|  | 2292 | wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data)); | 
|  | 2293 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2294 | if (!sae->peer_commit_scalar || !sae->tmp->own_commit_scalar) { | 
| Dmitry Shmidt | 96be622 | 2014-02-13 10:16:51 -0800 | [diff] [blame] | 2295 | wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available"); | 
|  | 2296 | return -1; | 
|  | 2297 | } | 
|  | 2298 |  | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 2299 | if (sae->tmp->ec) { | 
|  | 2300 | if (!sae->tmp->peer_commit_element_ecc || | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2301 | !sae->tmp->own_commit_element_ecc || | 
|  | 2302 | sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar, | 
|  | 2303 | sae->tmp->peer_commit_element_ecc, | 
|  | 2304 | sae->tmp->own_commit_scalar, | 
|  | 2305 | sae->tmp->own_commit_element_ecc, | 
|  | 2306 | verifier) < 0) | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 2307 | return -1; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 2308 | } else { | 
|  | 2309 | if (!sae->tmp->peer_commit_element_ffc || | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2310 | !sae->tmp->own_commit_element_ffc || | 
|  | 2311 | sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar, | 
|  | 2312 | sae->tmp->peer_commit_element_ffc, | 
|  | 2313 | sae->tmp->own_commit_scalar, | 
|  | 2314 | sae->tmp->own_commit_element_ffc, | 
|  | 2315 | verifier) < 0) | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 2316 | return -1; | 
| Hai Shalom | 021b0b5 | 2019-04-10 11:17:58 -0700 | [diff] [blame] | 2317 | } | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2318 |  | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2319 | if (os_memcmp_const(verifier, data + 2, hash_len) != 0) { | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2320 | wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch"); | 
|  | 2321 | wpa_hexdump(MSG_DEBUG, "SAE: Received confirm", | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2322 | data + 2, hash_len); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2323 | wpa_hexdump(MSG_DEBUG, "SAE: Calculated verifier", | 
| Hai Shalom | c356592 | 2019-10-28 11:58:20 -0700 | [diff] [blame] | 2324 | verifier, hash_len); | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2325 | return -1; | 
|  | 2326 | } | 
|  | 2327 |  | 
| Hai Shalom | 899fcc7 | 2020-10-19 14:38:18 -0700 | [diff] [blame] | 2328 | #ifdef CONFIG_SAE_PK | 
|  | 2329 | if (sae_check_confirm_pk(sae, data + 2 + hash_len, | 
|  | 2330 | len - 2 - hash_len) < 0) | 
|  | 2331 | return -1; | 
|  | 2332 | #endif /* CONFIG_SAE_PK */ | 
|  | 2333 |  | 
| Dmitry Shmidt | a54fa5f | 2013-01-15 13:53:35 -0800 | [diff] [blame] | 2334 | return 0; | 
|  | 2335 | } | 
| Roshan Pius | 3a1667e | 2018-07-03 15:17:14 -0700 | [diff] [blame] | 2336 |  | 
|  | 2337 |  | 
|  | 2338 | const char * sae_state_txt(enum sae_state state) | 
|  | 2339 | { | 
|  | 2340 | switch (state) { | 
|  | 2341 | case SAE_NOTHING: | 
|  | 2342 | return "Nothing"; | 
|  | 2343 | case SAE_COMMITTED: | 
|  | 2344 | return "Committed"; | 
|  | 2345 | case SAE_CONFIRMED: | 
|  | 2346 | return "Confirmed"; | 
|  | 2347 | case SAE_ACCEPTED: | 
|  | 2348 | return "Accepted"; | 
|  | 2349 | } | 
|  | 2350 | return "?"; | 
|  | 2351 | } |