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