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