Line data Source code
1 : /*
2 : * EAP peer method: EAP-pwd (RFC 5931)
3 : * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
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 "crypto/sha256.h"
13 : #include "eap_peer/eap_i.h"
14 : #include "eap_common/eap_pwd_common.h"
15 :
16 :
17 : struct eap_pwd_data {
18 : enum {
19 : PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req,
20 : SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE
21 : } state;
22 : u8 *id_peer;
23 : size_t id_peer_len;
24 : u8 *id_server;
25 : size_t id_server_len;
26 : u8 *password;
27 : size_t password_len;
28 : u16 group_num;
29 : EAP_PWD_group *grp;
30 :
31 : struct wpabuf *inbuf;
32 : size_t in_frag_pos;
33 : struct wpabuf *outbuf;
34 : size_t out_frag_pos;
35 : size_t mtu;
36 :
37 : BIGNUM *k;
38 : BIGNUM *private_value;
39 : BIGNUM *server_scalar;
40 : BIGNUM *my_scalar;
41 : EC_POINT *my_element;
42 : EC_POINT *server_element;
43 :
44 : u8 msk[EAP_MSK_LEN];
45 : u8 emsk[EAP_EMSK_LEN];
46 : u8 session_id[1 + SHA256_MAC_LEN];
47 :
48 : BN_CTX *bnctx;
49 : };
50 :
51 :
52 : #ifndef CONFIG_NO_STDOUT_DEBUG
53 96 : static const char * eap_pwd_state_txt(int state)
54 : {
55 96 : switch (state) {
56 : case PWD_ID_Req:
57 13 : return "PWD-ID-Req";
58 : case PWD_Commit_Req:
59 24 : return "PWD-Commit-Req";
60 : case PWD_Confirm_Req:
61 24 : return "PWD-Confirm-Req";
62 : case SUCCESS_ON_FRAG_COMPLETION:
63 22 : return "SUCCESS_ON_FRAG_COMPLETION";
64 : case SUCCESS:
65 11 : return "SUCCESS";
66 : case FAILURE:
67 2 : return "FAILURE";
68 : default:
69 0 : return "PWD-UNK";
70 : }
71 : }
72 : #endif /* CONFIG_NO_STDOUT_DEBUG */
73 :
74 :
75 48 : static void eap_pwd_state(struct eap_pwd_data *data, int state)
76 : {
77 96 : wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s",
78 48 : eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
79 48 : data->state = state;
80 48 : }
81 :
82 :
83 13 : static void * eap_pwd_init(struct eap_sm *sm)
84 : {
85 : struct eap_pwd_data *data;
86 : const u8 *identity, *password;
87 : size_t identity_len, password_len;
88 : int fragment_size;
89 :
90 13 : password = eap_get_config_password(sm, &password_len);
91 13 : if (password == NULL) {
92 0 : wpa_printf(MSG_INFO, "EAP-PWD: No password configured!");
93 0 : return NULL;
94 : }
95 :
96 13 : identity = eap_get_config_identity(sm, &identity_len);
97 13 : if (identity == NULL) {
98 0 : wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!");
99 0 : return NULL;
100 : }
101 :
102 13 : if ((data = os_zalloc(sizeof(*data))) == NULL) {
103 0 : wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail");
104 0 : return NULL;
105 : }
106 :
107 13 : if ((data->bnctx = BN_CTX_new()) == NULL) {
108 0 : wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
109 0 : os_free(data);
110 0 : return NULL;
111 : }
112 :
113 13 : if ((data->id_peer = os_malloc(identity_len)) == NULL) {
114 0 : wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
115 0 : BN_CTX_free(data->bnctx);
116 0 : os_free(data);
117 0 : return NULL;
118 : }
119 :
120 13 : os_memcpy(data->id_peer, identity, identity_len);
121 13 : data->id_peer_len = identity_len;
122 :
123 13 : if ((data->password = os_malloc(password_len)) == NULL) {
124 0 : wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
125 0 : BN_CTX_free(data->bnctx);
126 0 : bin_clear_free(data->id_peer, data->id_peer_len);
127 0 : os_free(data);
128 0 : return NULL;
129 : }
130 13 : os_memcpy(data->password, password, password_len);
131 13 : data->password_len = password_len;
132 :
133 13 : data->out_frag_pos = data->in_frag_pos = 0;
134 13 : data->inbuf = data->outbuf = NULL;
135 13 : fragment_size = eap_get_config_fragment_size(sm);
136 13 : if (fragment_size <= 0)
137 0 : data->mtu = 1020; /* default from RFC 5931 */
138 : else
139 13 : data->mtu = fragment_size;
140 :
141 13 : data->state = PWD_ID_Req;
142 :
143 13 : return data;
144 : }
145 :
146 :
147 13 : static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
148 : {
149 13 : struct eap_pwd_data *data = priv;
150 :
151 13 : BN_clear_free(data->private_value);
152 13 : BN_clear_free(data->server_scalar);
153 13 : BN_clear_free(data->my_scalar);
154 13 : BN_clear_free(data->k);
155 13 : BN_CTX_free(data->bnctx);
156 13 : EC_POINT_clear_free(data->my_element);
157 13 : EC_POINT_clear_free(data->server_element);
158 13 : bin_clear_free(data->id_peer, data->id_peer_len);
159 13 : bin_clear_free(data->id_server, data->id_server_len);
160 13 : bin_clear_free(data->password, data->password_len);
161 13 : if (data->grp) {
162 13 : EC_GROUP_free(data->grp->group);
163 13 : EC_POINT_clear_free(data->grp->pwe);
164 13 : BN_clear_free(data->grp->order);
165 13 : BN_clear_free(data->grp->prime);
166 13 : os_free(data->grp);
167 : }
168 13 : wpabuf_free(data->inbuf);
169 13 : wpabuf_free(data->outbuf);
170 13 : bin_clear_free(data, sizeof(*data));
171 13 : }
172 :
173 :
174 11 : static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
175 : {
176 11 : struct eap_pwd_data *data = priv;
177 : u8 *key;
178 :
179 11 : if (data->state != SUCCESS)
180 0 : return NULL;
181 :
182 11 : key = os_malloc(EAP_MSK_LEN);
183 11 : if (key == NULL)
184 0 : return NULL;
185 :
186 11 : os_memcpy(key, data->msk, EAP_MSK_LEN);
187 11 : *len = EAP_MSK_LEN;
188 :
189 11 : return key;
190 : }
191 :
192 :
193 11 : static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
194 : {
195 11 : struct eap_pwd_data *data = priv;
196 : u8 *id;
197 :
198 11 : if (data->state != SUCCESS)
199 0 : return NULL;
200 :
201 11 : id = os_malloc(1 + SHA256_MAC_LEN);
202 11 : if (id == NULL)
203 0 : return NULL;
204 :
205 11 : os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
206 11 : *len = 1 + SHA256_MAC_LEN;
207 :
208 11 : return id;
209 : }
210 :
211 :
212 : static void
213 13 : eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
214 : struct eap_method_ret *ret,
215 : const struct wpabuf *reqData,
216 : const u8 *payload, size_t payload_len)
217 : {
218 : struct eap_pwd_id *id;
219 :
220 13 : if (data->state != PWD_ID_Req) {
221 0 : ret->ignore = TRUE;
222 0 : eap_pwd_state(data, FAILURE);
223 0 : return;
224 : }
225 :
226 13 : if (payload_len < sizeof(struct eap_pwd_id)) {
227 0 : ret->ignore = TRUE;
228 0 : eap_pwd_state(data, FAILURE);
229 0 : return;
230 : }
231 :
232 13 : id = (struct eap_pwd_id *) payload;
233 13 : data->group_num = be_to_host16(id->group_num);
234 26 : if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
235 13 : (id->prf != EAP_PWD_DEFAULT_PRF)) {
236 0 : ret->ignore = TRUE;
237 0 : eap_pwd_state(data, FAILURE);
238 0 : return;
239 : }
240 :
241 13 : wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d",
242 13 : data->group_num);
243 :
244 13 : data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
245 13 : if (data->id_server == NULL) {
246 0 : wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
247 0 : eap_pwd_state(data, FAILURE);
248 0 : return;
249 : }
250 13 : data->id_server_len = payload_len - sizeof(struct eap_pwd_id);
251 13 : os_memcpy(data->id_server, id->identity, data->id_server_len);
252 26 : wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
253 13 : data->id_server, data->id_server_len);
254 :
255 13 : data->grp = os_zalloc(sizeof(EAP_PWD_group));
256 13 : if (data->grp == NULL) {
257 0 : wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
258 : "group");
259 0 : eap_pwd_state(data, FAILURE);
260 0 : return;
261 : }
262 :
263 : /* compute PWE */
264 39 : if (compute_password_element(data->grp, data->group_num,
265 13 : data->password, data->password_len,
266 13 : data->id_server, data->id_server_len,
267 13 : data->id_peer, data->id_peer_len,
268 13 : id->token)) {
269 1 : wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
270 1 : eap_pwd_state(data, FAILURE);
271 1 : return;
272 : }
273 :
274 12 : wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
275 12 : BN_num_bits(data->grp->prime));
276 :
277 12 : data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
278 12 : data->id_peer_len);
279 12 : if (data->outbuf == NULL) {
280 0 : eap_pwd_state(data, FAILURE);
281 0 : return;
282 : }
283 12 : wpabuf_put_be16(data->outbuf, data->group_num);
284 12 : wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
285 12 : wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
286 12 : wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
287 12 : wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
288 12 : wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
289 :
290 12 : eap_pwd_state(data, PWD_Commit_Req);
291 : }
292 :
293 :
294 : static void
295 12 : eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
296 : struct eap_method_ret *ret,
297 : const struct wpabuf *reqData,
298 : const u8 *payload, size_t payload_len)
299 : {
300 12 : EC_POINT *K = NULL, *point = NULL;
301 12 : BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
302 : u16 offset;
303 12 : u8 *ptr, *scalar = NULL, *element = NULL;
304 :
305 24 : if (((data->private_value = BN_new()) == NULL) ||
306 24 : ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
307 12 : ((cofactor = BN_new()) == NULL) ||
308 24 : ((data->my_scalar = BN_new()) == NULL) ||
309 : ((mask = BN_new()) == NULL)) {
310 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
311 0 : goto fin;
312 : }
313 :
314 12 : if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
315 0 : wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
316 : "for curve");
317 0 : goto fin;
318 : }
319 :
320 24 : if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
321 24 : BN_rand_range(mask, data->grp->order) != 1 ||
322 24 : BN_add(data->my_scalar, data->private_value, mask) != 1 ||
323 12 : BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
324 : data->bnctx) != 1) {
325 0 : wpa_printf(MSG_INFO,
326 : "EAP-pwd (peer): unable to get randomness");
327 0 : goto fin;
328 : }
329 :
330 24 : if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
331 12 : data->grp->pwe, mask, data->bnctx)) {
332 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
333 : "fail");
334 0 : eap_pwd_state(data, FAILURE);
335 0 : goto fin;
336 : }
337 :
338 12 : if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
339 : {
340 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
341 0 : goto fin;
342 : }
343 12 : BN_clear_free(mask);
344 :
345 12 : if (((x = BN_new()) == NULL) ||
346 : ((y = BN_new()) == NULL)) {
347 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
348 0 : goto fin;
349 : }
350 :
351 : /* process the request */
352 24 : if (((data->server_scalar = BN_new()) == NULL) ||
353 24 : ((data->k = BN_new()) == NULL) ||
354 24 : ((K = EC_POINT_new(data->grp->group)) == NULL) ||
355 24 : ((point = EC_POINT_new(data->grp->group)) == NULL) ||
356 12 : ((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
357 : {
358 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
359 : "fail");
360 0 : goto fin;
361 : }
362 :
363 : /* element, x then y, followed by scalar */
364 12 : ptr = (u8 *) payload;
365 12 : BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
366 12 : ptr += BN_num_bytes(data->grp->prime);
367 12 : BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
368 12 : ptr += BN_num_bytes(data->grp->prime);
369 12 : BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
370 12 : if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
371 : data->server_element, x, y,
372 : data->bnctx)) {
373 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
374 : "fail");
375 0 : goto fin;
376 : }
377 :
378 : /* check to ensure server's element is not in a small sub-group */
379 12 : if (BN_cmp(cofactor, BN_value_one())) {
380 0 : if (!EC_POINT_mul(data->grp->group, point, NULL,
381 0 : data->server_element, cofactor, NULL)) {
382 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
383 : "server element by order!\n");
384 0 : goto fin;
385 : }
386 0 : if (EC_POINT_is_at_infinity(data->grp->group, point)) {
387 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
388 : "is at infinity!\n");
389 0 : goto fin;
390 : }
391 : }
392 :
393 : /* compute the shared key, k */
394 24 : if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
395 24 : data->server_scalar, data->bnctx)) ||
396 12 : (!EC_POINT_add(data->grp->group, K, K, data->server_element,
397 12 : data->bnctx)) ||
398 12 : (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
399 : data->bnctx))) {
400 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
401 : "fail");
402 0 : goto fin;
403 : }
404 :
405 : /* ensure that the shared key isn't in a small sub-group */
406 12 : if (BN_cmp(cofactor, BN_value_one())) {
407 0 : if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
408 : NULL)) {
409 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
410 : "shared key point by order");
411 0 : goto fin;
412 : }
413 : }
414 :
415 : /*
416 : * This check is strictly speaking just for the case above where
417 : * co-factor > 1 but it was suggested that even though this is probably
418 : * never going to happen it is a simple and safe check "just to be
419 : * sure" so let's be safe.
420 : */
421 12 : if (EC_POINT_is_at_infinity(data->grp->group, K)) {
422 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
423 : "infinity!\n");
424 0 : goto fin;
425 : }
426 :
427 12 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
428 : NULL, data->bnctx)) {
429 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
430 : "shared secret from point");
431 0 : goto fin;
432 : }
433 :
434 : /* now do the response */
435 24 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
436 12 : data->my_element, x, y,
437 : data->bnctx)) {
438 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
439 0 : goto fin;
440 : }
441 :
442 24 : if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
443 12 : ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
444 : NULL)) {
445 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
446 0 : goto fin;
447 : }
448 :
449 : /*
450 : * bignums occupy as little memory as possible so one that is
451 : * sufficiently smaller than the prime or order might need pre-pending
452 : * with zeros.
453 : */
454 12 : os_memset(scalar, 0, BN_num_bytes(data->grp->order));
455 12 : os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
456 24 : offset = BN_num_bytes(data->grp->order) -
457 12 : BN_num_bytes(data->my_scalar);
458 12 : BN_bn2bin(data->my_scalar, scalar + offset);
459 :
460 12 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
461 12 : BN_bn2bin(x, element + offset);
462 12 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
463 12 : BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
464 :
465 24 : data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) +
466 12 : 2 * BN_num_bytes(data->grp->prime));
467 12 : if (data->outbuf == NULL)
468 0 : goto fin;
469 :
470 : /* we send the element as (x,y) follwed by the scalar */
471 12 : wpabuf_put_data(data->outbuf, element,
472 12 : 2 * BN_num_bytes(data->grp->prime));
473 12 : wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
474 :
475 : fin:
476 12 : os_free(scalar);
477 12 : os_free(element);
478 12 : BN_clear_free(x);
479 12 : BN_clear_free(y);
480 12 : BN_clear_free(cofactor);
481 12 : EC_POINT_clear_free(K);
482 12 : EC_POINT_clear_free(point);
483 12 : if (data->outbuf == NULL)
484 0 : eap_pwd_state(data, FAILURE);
485 : else
486 12 : eap_pwd_state(data, PWD_Confirm_Req);
487 12 : }
488 :
489 :
490 : static void
491 12 : eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
492 : struct eap_method_ret *ret,
493 : const struct wpabuf *reqData,
494 : const u8 *payload, size_t payload_len)
495 : {
496 12 : BIGNUM *x = NULL, *y = NULL;
497 : struct crypto_hash *hash;
498 : u32 cs;
499 : u16 grp;
500 12 : u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
501 : int offset;
502 :
503 : /*
504 : * first build up the ciphersuite which is group | random_function |
505 : * prf
506 : */
507 12 : grp = htons(data->group_num);
508 12 : ptr = (u8 *) &cs;
509 12 : os_memcpy(ptr, &grp, sizeof(u16));
510 12 : ptr += sizeof(u16);
511 12 : *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
512 12 : ptr += sizeof(u8);
513 12 : *ptr = EAP_PWD_DEFAULT_PRF;
514 :
515 : /* each component of the cruft will be at most as big as the prime */
516 12 : if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
517 12 : ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
518 0 : wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation "
519 : "fail");
520 0 : goto fin;
521 : }
522 :
523 : /*
524 : * server's commit is H(k | server_element | server_scalar |
525 : * peer_element | peer_scalar | ciphersuite)
526 : */
527 12 : hash = eap_pwd_h_init();
528 12 : if (hash == NULL)
529 0 : goto fin;
530 :
531 : /*
532 : * zero the memory each time because this is mod prime math and some
533 : * value may start with a few zeros and the previous one did not.
534 : */
535 12 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
536 12 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
537 12 : BN_bn2bin(data->k, cruft + offset);
538 12 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
539 :
540 : /* server element: x, y */
541 24 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
542 12 : data->server_element, x, y,
543 : data->bnctx)) {
544 0 : wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
545 : "assignment fail");
546 0 : goto fin;
547 : }
548 12 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
549 12 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
550 12 : BN_bn2bin(x, cruft + offset);
551 12 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
552 12 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
553 12 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
554 12 : BN_bn2bin(y, cruft + offset);
555 12 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
556 :
557 : /* server scalar */
558 12 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
559 24 : offset = BN_num_bytes(data->grp->order) -
560 12 : BN_num_bytes(data->server_scalar);
561 12 : BN_bn2bin(data->server_scalar, cruft + offset);
562 12 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
563 :
564 : /* my element: x, y */
565 24 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
566 12 : data->my_element, x, y,
567 : data->bnctx)) {
568 0 : wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
569 : "assignment fail");
570 0 : goto fin;
571 : }
572 :
573 12 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
574 12 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
575 12 : BN_bn2bin(x, cruft + offset);
576 12 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
577 12 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
578 12 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
579 12 : BN_bn2bin(y, cruft + offset);
580 12 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
581 :
582 : /* my scalar */
583 12 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
584 24 : offset = BN_num_bytes(data->grp->order) -
585 12 : BN_num_bytes(data->my_scalar);
586 12 : BN_bn2bin(data->my_scalar, cruft + offset);
587 12 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
588 :
589 : /* the ciphersuite */
590 12 : eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
591 :
592 : /* random function fin */
593 12 : eap_pwd_h_final(hash, conf);
594 :
595 12 : ptr = (u8 *) payload;
596 12 : if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
597 1 : wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
598 1 : goto fin;
599 : }
600 :
601 11 : wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified");
602 :
603 : /*
604 : * compute confirm:
605 : * H(k | peer_element | peer_scalar | server_element | server_scalar |
606 : * ciphersuite)
607 : */
608 11 : hash = eap_pwd_h_init();
609 11 : if (hash == NULL)
610 0 : goto fin;
611 :
612 : /* k */
613 11 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
614 11 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
615 11 : BN_bn2bin(data->k, cruft + offset);
616 11 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
617 :
618 : /* my element */
619 22 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
620 11 : data->my_element, x, y,
621 : data->bnctx)) {
622 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
623 : "assignment fail");
624 0 : goto fin;
625 : }
626 11 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
627 11 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
628 11 : BN_bn2bin(x, cruft + offset);
629 11 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
630 11 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
631 11 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
632 11 : BN_bn2bin(y, cruft + offset);
633 11 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
634 :
635 : /* my scalar */
636 11 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
637 22 : offset = BN_num_bytes(data->grp->order) -
638 11 : BN_num_bytes(data->my_scalar);
639 11 : BN_bn2bin(data->my_scalar, cruft + offset);
640 11 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
641 :
642 : /* server element: x, y */
643 22 : if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
644 11 : data->server_element, x, y,
645 : data->bnctx)) {
646 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
647 : "assignment fail");
648 0 : goto fin;
649 : }
650 11 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
651 11 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
652 11 : BN_bn2bin(x, cruft + offset);
653 11 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
654 11 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
655 11 : offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
656 11 : BN_bn2bin(y, cruft + offset);
657 11 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
658 :
659 : /* server scalar */
660 11 : os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
661 22 : offset = BN_num_bytes(data->grp->order) -
662 11 : BN_num_bytes(data->server_scalar);
663 11 : BN_bn2bin(data->server_scalar, cruft + offset);
664 11 : eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
665 :
666 : /* the ciphersuite */
667 11 : eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
668 :
669 : /* all done */
670 11 : eap_pwd_h_final(hash, conf);
671 :
672 11 : if (compute_keys(data->grp, data->bnctx, data->k,
673 : data->my_scalar, data->server_scalar, conf, ptr,
674 11 : &cs, data->msk, data->emsk, data->session_id) < 0) {
675 0 : wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
676 : "EMSK");
677 0 : goto fin;
678 : }
679 :
680 11 : data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
681 11 : if (data->outbuf == NULL)
682 0 : goto fin;
683 :
684 11 : wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
685 :
686 : fin:
687 12 : bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
688 12 : BN_clear_free(x);
689 12 : BN_clear_free(y);
690 12 : if (data->outbuf == NULL) {
691 1 : ret->methodState = METHOD_DONE;
692 1 : ret->decision = DECISION_FAIL;
693 1 : eap_pwd_state(data, FAILURE);
694 : } else {
695 11 : eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
696 : }
697 12 : }
698 :
699 :
700 : static struct wpabuf *
701 49 : eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
702 : const struct wpabuf *reqData)
703 : {
704 49 : struct eap_pwd_data *data = priv;
705 49 : struct wpabuf *resp = NULL;
706 : const u8 *pos, *buf;
707 : size_t len;
708 49 : u16 tot_len = 0;
709 : u8 lm_exch;
710 :
711 49 : pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
712 49 : if ((pos == NULL) || (len < 1)) {
713 0 : wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and "
714 : "len is %d",
715 : pos == NULL ? "NULL" : "not NULL", (int) len);
716 0 : ret->ignore = TRUE;
717 0 : return NULL;
718 : }
719 :
720 49 : ret->ignore = FALSE;
721 49 : ret->methodState = METHOD_MAY_CONT;
722 49 : ret->decision = DECISION_FAIL;
723 49 : ret->allowNotifications = FALSE;
724 :
725 49 : lm_exch = *pos;
726 49 : pos++; /* skip over the bits and the exch */
727 49 : len--;
728 :
729 : /*
730 : * we're fragmenting so send out the next fragment
731 : */
732 49 : if (data->out_frag_pos) {
733 : /*
734 : * this should be an ACK
735 : */
736 10 : if (len)
737 0 : wpa_printf(MSG_INFO, "Bad Response! Fragmenting but "
738 : "not an ACK");
739 :
740 10 : wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment");
741 : /*
742 : * check if there are going to be more fragments
743 : */
744 10 : len = wpabuf_len(data->outbuf) - data->out_frag_pos;
745 10 : if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
746 5 : len = data->mtu - EAP_PWD_HDR_SIZE;
747 5 : EAP_PWD_SET_MORE_BIT(lm_exch);
748 : }
749 10 : resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
750 : EAP_PWD_HDR_SIZE + len,
751 10 : EAP_CODE_RESPONSE, eap_get_id(reqData));
752 10 : if (resp == NULL) {
753 0 : wpa_printf(MSG_INFO, "Unable to allocate memory for "
754 : "next fragment!");
755 0 : return NULL;
756 : }
757 10 : wpabuf_put_u8(resp, lm_exch);
758 10 : buf = wpabuf_head_u8(data->outbuf);
759 10 : wpabuf_put_data(resp, buf + data->out_frag_pos, len);
760 10 : data->out_frag_pos += len;
761 : /*
762 : * this is the last fragment so get rid of the out buffer
763 : */
764 10 : if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
765 5 : wpabuf_free(data->outbuf);
766 5 : data->outbuf = NULL;
767 5 : data->out_frag_pos = 0;
768 : }
769 20 : wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes",
770 10 : data->out_frag_pos == 0 ? "last" : "next",
771 : (int) len);
772 10 : if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
773 1 : ret->methodState = METHOD_DONE;
774 1 : ret->decision = DECISION_UNCOND_SUCC;
775 1 : eap_pwd_state(data, SUCCESS);
776 : }
777 10 : return resp;
778 : }
779 :
780 : /*
781 : * see if this is a fragment that needs buffering
782 : *
783 : * if it's the first fragment there'll be a length field
784 : */
785 39 : if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
786 1 : tot_len = WPA_GET_BE16(pos);
787 1 : wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose "
788 : "total length = %d", tot_len);
789 1 : if (tot_len > 15000)
790 0 : return NULL;
791 1 : data->inbuf = wpabuf_alloc(tot_len);
792 1 : if (data->inbuf == NULL) {
793 0 : wpa_printf(MSG_INFO, "Out of memory to buffer "
794 : "fragments!");
795 0 : return NULL;
796 : }
797 1 : pos += sizeof(u16);
798 1 : len -= sizeof(u16);
799 : }
800 : /*
801 : * buffer and ACK the fragment
802 : */
803 39 : if (EAP_PWD_GET_MORE_BIT(lm_exch)) {
804 2 : data->in_frag_pos += len;
805 2 : if (data->in_frag_pos > wpabuf_size(data->inbuf)) {
806 0 : wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack "
807 : "detected (%d vs. %d)!",
808 0 : (int) data->in_frag_pos,
809 0 : (int) wpabuf_len(data->inbuf));
810 0 : wpabuf_free(data->inbuf);
811 0 : data->inbuf = NULL;
812 0 : data->in_frag_pos = 0;
813 0 : return NULL;
814 : }
815 2 : wpabuf_put_data(data->inbuf, pos, len);
816 :
817 2 : resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
818 : EAP_PWD_HDR_SIZE,
819 2 : EAP_CODE_RESPONSE, eap_get_id(reqData));
820 2 : if (resp != NULL)
821 2 : wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch)));
822 2 : wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment",
823 : (int) len);
824 2 : return resp;
825 : }
826 : /*
827 : * we're buffering and this is the last fragment
828 : */
829 37 : if (data->in_frag_pos) {
830 1 : wpabuf_put_data(data->inbuf, pos, len);
831 1 : wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
832 : (int) len);
833 1 : data->in_frag_pos += len;
834 1 : pos = wpabuf_head_u8(data->inbuf);
835 1 : len = data->in_frag_pos;
836 : }
837 37 : wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d",
838 : EAP_PWD_GET_EXCHANGE(lm_exch), (int) len);
839 :
840 37 : switch (EAP_PWD_GET_EXCHANGE(lm_exch)) {
841 : case EAP_PWD_OPCODE_ID_EXCH:
842 13 : eap_pwd_perform_id_exchange(sm, data, ret, reqData,
843 : pos, len);
844 13 : break;
845 : case EAP_PWD_OPCODE_COMMIT_EXCH:
846 12 : eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
847 : pos, len);
848 12 : break;
849 : case EAP_PWD_OPCODE_CONFIRM_EXCH:
850 12 : eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
851 : pos, len);
852 12 : break;
853 : default:
854 0 : wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
855 : "opcode %d", lm_exch);
856 0 : break;
857 : }
858 : /*
859 : * if we buffered the just processed input now's the time to free it
860 : */
861 37 : if (data->in_frag_pos) {
862 1 : wpabuf_free(data->inbuf);
863 1 : data->inbuf = NULL;
864 1 : data->in_frag_pos = 0;
865 : }
866 :
867 37 : if (data->outbuf == NULL) {
868 2 : ret->methodState = METHOD_DONE;
869 2 : ret->decision = DECISION_FAIL;
870 2 : return NULL; /* generic failure */
871 : }
872 :
873 : /*
874 : * we have output! Do we need to fragment it?
875 : */
876 35 : len = wpabuf_len(data->outbuf);
877 35 : if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
878 5 : resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu,
879 5 : EAP_CODE_RESPONSE, eap_get_id(reqData));
880 : /*
881 : * if so it's the first so include a length field
882 : */
883 5 : EAP_PWD_SET_LENGTH_BIT(lm_exch);
884 5 : EAP_PWD_SET_MORE_BIT(lm_exch);
885 5 : tot_len = len;
886 : /*
887 : * keep the packet at the MTU
888 : */
889 5 : len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16);
890 5 : wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total "
891 : "length = %d", tot_len);
892 : } else {
893 30 : resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
894 : EAP_PWD_HDR_SIZE + len,
895 30 : EAP_CODE_RESPONSE, eap_get_id(reqData));
896 : }
897 35 : if (resp == NULL)
898 0 : return NULL;
899 :
900 35 : wpabuf_put_u8(resp, lm_exch);
901 35 : if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
902 5 : wpabuf_put_be16(resp, tot_len);
903 5 : data->out_frag_pos += len;
904 : }
905 35 : buf = wpabuf_head_u8(data->outbuf);
906 35 : wpabuf_put_data(resp, buf, len);
907 : /*
908 : * if we're not fragmenting then there's no need to carry this around
909 : */
910 35 : if (data->out_frag_pos == 0) {
911 30 : wpabuf_free(data->outbuf);
912 30 : data->outbuf = NULL;
913 30 : data->out_frag_pos = 0;
914 30 : if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
915 10 : ret->methodState = METHOD_DONE;
916 10 : ret->decision = DECISION_UNCOND_SUCC;
917 10 : eap_pwd_state(data, SUCCESS);
918 : }
919 : }
920 :
921 35 : return resp;
922 : }
923 :
924 :
925 49 : static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv)
926 : {
927 49 : struct eap_pwd_data *data = priv;
928 49 : return data->state == SUCCESS;
929 : }
930 :
931 :
932 1 : static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
933 : {
934 1 : struct eap_pwd_data *data = priv;
935 : u8 *key;
936 :
937 1 : if (data->state != SUCCESS)
938 0 : return NULL;
939 :
940 1 : if ((key = os_malloc(EAP_EMSK_LEN)) == NULL)
941 0 : return NULL;
942 :
943 1 : os_memcpy(key, data->emsk, EAP_EMSK_LEN);
944 1 : *len = EAP_EMSK_LEN;
945 :
946 1 : return key;
947 : }
948 :
949 :
950 30 : int eap_peer_pwd_register(void)
951 : {
952 : struct eap_method *eap;
953 : int ret;
954 :
955 30 : eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
956 : EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
957 30 : if (eap == NULL)
958 0 : return -1;
959 :
960 30 : eap->init = eap_pwd_init;
961 30 : eap->deinit = eap_pwd_deinit;
962 30 : eap->process = eap_pwd_process;
963 30 : eap->isKeyAvailable = eap_pwd_key_available;
964 30 : eap->getKey = eap_pwd_getkey;
965 30 : eap->getSessionId = eap_pwd_get_session_id;
966 30 : eap->get_emsk = eap_pwd_get_emsk;
967 :
968 30 : ret = eap_peer_method_register(eap);
969 30 : if (ret)
970 0 : eap_peer_method_free(eap);
971 30 : return ret;
972 : }
|