EAP-pwd: Add support for EAP-pwd server and peer functionality
[libeap.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 program is free software; you can redistribute it and/or modify
6  * it under the terms of the BSD license.
7  *
8  * Alternatively, this software may be distributed under the terms of the
9  * GNU General Public License version 2 as published by the Free Software
10  * Foundation.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "eap_server/eap_i.h"
19 #include "eap_common/eap_pwd_common.h"
20
21
22 struct eap_pwd_data {
23         enum {
24                 PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
25         } state;
26         u8 *id_peer;
27         size_t id_peer_len;
28         u8 *id_server;
29         size_t id_server_len;
30         u8 *password;
31         size_t password_len;
32         u32 token;
33         u16 group_num;
34         EAP_PWD_group *grp;
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 msk[EAP_MSK_LEN];
44         u8 emsk[EAP_EMSK_LEN];
45 };
46
47 static BN_CTX *bnctx;
48
49
50 static const char * eap_pwd_state_txt(int state)
51 {
52         switch (state) {
53         case PWD_ID_Req:
54                 return "PWD-ID-Req";
55         case PWD_Commit_Req:
56                 return "PWD-Commit-Req";
57         case PWD_Confirm_Req:
58                 return "PWD-Confirm-Req";
59         case SUCCESS:
60                 return "SUCCESS";
61         case FAILURE:
62                 return "FAILURE";
63         default:
64                 return "PWD-Unk";
65         }
66 }
67
68
69 static void eap_pwd_state(struct eap_pwd_data *data, int state)
70 {
71         wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s",
72                    eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
73         data->state = state;
74 }
75
76
77 static void * eap_pwd_init(struct eap_sm *sm)
78 {
79         struct eap_pwd_data *data;
80
81         if (sm->user == NULL || sm->user->password == NULL ||
82             sm->user->password_len == 0) {
83                 wpa_printf(MSG_INFO, "EAP-PWD (server): Password is not "
84                            "configured");
85                 return NULL;
86         }
87
88         data = os_zalloc(sizeof(*data));
89         if (data == NULL)
90                 return NULL;
91
92         data->group_num = sm->pwd_group;
93         wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d",
94                    data->group_num);
95         data->state = PWD_ID_Req;
96
97         data->id_server = (u8 *) os_strdup("server");
98         if (data->id_server)
99                 data->id_server_len = os_strlen((char *)data->id_server);
100
101         data->password = os_malloc(sm->user->password_len);
102         if (data->password == NULL) {
103                 wpa_printf(MSG_INFO, "EAP-PWD: Mmemory allocation password "
104                            "fail");
105                 return NULL;
106         }
107         data->password_len = sm->user->password_len;
108         os_memcpy(data->password, sm->user->password, data->password_len);
109
110         bnctx = BN_CTX_new();
111         if (bnctx == NULL) {
112                 wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
113                 return NULL;
114         }
115
116         return data;
117 }
118
119
120 static void eap_pwd_reset(struct eap_sm *sm, void *priv)
121 {
122         struct eap_pwd_data *data = priv;
123
124         BN_free(data->private_value);
125         BN_free(data->peer_scalar);
126         BN_free(data->my_scalar);
127         BN_free(data->k);
128         BN_CTX_free(bnctx);
129         EC_POINT_free(data->my_element);
130         EC_POINT_free(data->peer_element);
131         os_free(data->id_peer);
132         os_free(data->id_server);
133         os_free(data->grp);
134         os_free(data);
135 }
136
137
138 static struct wpabuf *
139 eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
140 {
141         struct wpabuf *req;
142
143         wpa_printf(MSG_DEBUG, "EAP-pwd: ID/Request");
144         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
145                             sizeof(struct eap_pwd_hdr) +
146                             sizeof(struct eap_pwd_id) + data->id_server_len,
147                             EAP_CODE_REQUEST, id);
148         if (req == NULL) {
149                 eap_pwd_state(data, FAILURE);
150                 return NULL;
151         }
152
153         /* an lfsr is good enough to generate unpredictable tokens */
154         data->token = os_random();
155         wpabuf_put_u8(req, EAP_PWD_OPCODE_ID_EXCH);
156         wpabuf_put_be16(req, data->group_num);
157         wpabuf_put_u8(req, EAP_PWD_DEFAULT_RAND_FUNC);
158         wpabuf_put_u8(req, EAP_PWD_DEFAULT_PRF);
159         wpabuf_put_data(req, &data->token, sizeof(data->token));
160         wpabuf_put_u8(req, EAP_PWD_PREP_NONE);
161         wpabuf_put_data(req, data->id_server, data->id_server_len);
162
163         return req;
164 }
165
166
167 static struct wpabuf *
168 eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
169 {
170         struct wpabuf *req = NULL;
171         BIGNUM *mask = NULL, *x = NULL, *y = NULL;
172         u8 *scalar = NULL, *element = NULL;
173         u16 offset;
174
175         wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
176
177         if (((data->private_value = BN_new()) == NULL) ||
178             ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
179             ((data->my_scalar = BN_new()) == NULL) ||
180             ((mask = BN_new()) == NULL)) {
181                 wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation "
182                            "fail");
183                 goto fin;
184         }
185
186         BN_rand_range(data->private_value, data->grp->order);
187         BN_rand_range(mask, data->grp->order);
188         BN_add(data->my_scalar, data->private_value, mask);
189         BN_mod(data->my_scalar, data->my_scalar, data->grp->order, bnctx);
190
191         if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
192                           data->grp->pwe, mask, bnctx)) {
193                 wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation "
194                            "fail");
195                 eap_pwd_state(data, FAILURE);
196                 goto fin;
197         }
198
199         if (!EC_POINT_invert(data->grp->group, data->my_element, bnctx)) {
200                 wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
201                            "fail");
202                 goto fin;
203         }
204         BN_free(mask);
205
206         if (((x = BN_new()) == NULL) ||
207             ((y = BN_new()) == NULL)) {
208                 wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation "
209                            "fail");
210                 goto fin;
211         }
212         if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
213                                                  data->my_element, x, y,
214                                                  bnctx)) {
215                 wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
216                            "fail");
217                 goto fin;
218         }
219
220         if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
221             ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
222              NULL)) {
223                 wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
224                 goto fin;
225         }
226
227         /*
228          * bignums occupy as little memory as possible so one that is
229          * sufficiently smaller than the prime or order might need pre-pending
230          * with zeros.
231          */
232         os_memset(scalar, 0, BN_num_bytes(data->grp->order));
233         os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
234         offset = BN_num_bytes(data->grp->order) -
235                 BN_num_bytes(data->my_scalar);
236         BN_bn2bin(data->my_scalar, scalar + offset);
237
238         offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
239         BN_bn2bin(x, element + offset);
240         offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
241         BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
242
243         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
244                             sizeof(struct eap_pwd_hdr) +
245                             (2 * BN_num_bytes(data->grp->prime)) +
246                             BN_num_bytes(data->grp->order),
247                             EAP_CODE_REQUEST, id);
248         if (req == NULL)
249                 goto fin;
250         wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH);
251
252         /* We send the element as (x,y) followed by the scalar */
253         wpabuf_put_data(req, element, (2 * BN_num_bytes(data->grp->prime)));
254         wpabuf_put_data(req, scalar, BN_num_bytes(data->grp->order));
255
256 fin:
257         os_free(scalar);
258         os_free(element);
259         BN_free(x);
260         BN_free(y);
261         if (req == NULL)
262                 eap_pwd_state(data, FAILURE);
263
264         return req;
265 }
266
267
268 static struct wpabuf *
269 eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
270 {
271         struct wpabuf *req = NULL;
272         BIGNUM *x = NULL, *y = NULL;
273         HMAC_CTX ctx;
274         u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
275
276         wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
277
278         /* Each component of the cruft will be at most as big as the prime */
279         if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
280             ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
281                 wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation "
282                            "fail");
283                 goto fin;
284         }
285
286         /*
287          * commit is H(k | server_element | server_scalar | peer_element |
288          *             peer_scalar | ciphersuite)
289          */
290         H_Init(&ctx);
291
292         /*
293          * Zero the memory each time because this is mod prime math and some
294          * value may start with a few zeros and the previous one did not.
295          *
296          * First is k
297          */
298         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
299         BN_bn2bin(data->k, cruft);
300         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
301
302         /* server element: x, y */
303         if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
304                                                  data->my_element, x, y,
305                                                  bnctx)) {
306                 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
307                            "assignment fail");
308                 goto fin;
309         }
310
311         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
312         BN_bn2bin(x, cruft);
313         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
314         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
315         BN_bn2bin(y, cruft);
316         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
317
318         /* server scalar */
319         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
320         BN_bn2bin(data->my_scalar, cruft);
321         H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
322
323         /* peer element: x, y */
324         if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
325                                                  data->peer_element, x, y,
326                                                  bnctx)) {
327                 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
328                            "assignment fail");
329                 goto fin;
330         }
331
332         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
333         BN_bn2bin(x, cruft);
334         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
335         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
336         BN_bn2bin(y, cruft);
337         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
338
339         /* peer scalar */
340         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
341         BN_bn2bin(data->peer_scalar, cruft);
342         H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
343
344         /* ciphersuite */
345         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
346         ptr = cruft;
347         os_memcpy(ptr, &data->group_num, sizeof(u16));
348         ptr += sizeof(u16);
349         *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
350         ptr += sizeof(u8);
351         *ptr = EAP_PWD_DEFAULT_PRF;
352         ptr += sizeof(u8);
353         H_Update(&ctx, cruft, ptr-cruft);
354
355         /* all done with the random function */
356         H_Final(&ctx, conf);
357
358         req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
359                             sizeof(struct eap_pwd_hdr) + SHA256_DIGEST_LENGTH,
360                             EAP_CODE_REQUEST, id);
361         if (req == NULL)
362                 goto fin;
363
364         wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH);
365         wpabuf_put_data(req, conf, SHA256_DIGEST_LENGTH);
366
367 fin:
368         os_free(cruft);
369         BN_free(x);
370         BN_free(y);
371         if (req == NULL)
372                 eap_pwd_state(data, FAILURE);
373
374         return req;
375 }
376
377
378 static struct wpabuf *
379 eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
380 {
381         struct eap_pwd_data *data = priv;
382
383         switch (data->state) {
384         case PWD_ID_Req:
385                 return eap_pwd_build_id_req(sm, data, id);
386         case PWD_Commit_Req:
387                 return eap_pwd_build_commit_req(sm, data, id);
388         case PWD_Confirm_Req:
389                 return eap_pwd_build_confirm_req(sm, data, id);
390         default:
391                 wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req",
392                            data->state);
393                 break;
394         }
395
396         return NULL;
397 }
398
399
400 static Boolean eap_pwd_check(struct eap_sm *sm, void *priv,
401                              struct wpabuf *respData)
402 {
403         struct eap_pwd_data *data = priv;
404         const u8 *pos;
405         size_t len;
406
407         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
408         if (pos == NULL || len < 1) {
409                 wpa_printf(MSG_INFO, "EAP-pwd: Invalid frame");
410                 return TRUE;
411         }
412
413         wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: opcode=%d", *pos);
414
415         if (data->state == PWD_ID_Req && *pos == EAP_PWD_OPCODE_ID_EXCH)
416                 return FALSE;
417
418         if (data->state == PWD_Commit_Req &&
419             *pos == EAP_PWD_OPCODE_COMMIT_EXCH)
420                 return FALSE;
421
422         if (data->state == PWD_Confirm_Req &&
423             *pos == EAP_PWD_OPCODE_CONFIRM_EXCH)
424                 return FALSE;
425
426         wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d",
427                    *pos, data->state);
428
429         return TRUE;
430 }
431
432
433 static void eap_pwd_process_id_resp(struct eap_sm *sm,
434                                     struct eap_pwd_data *data,
435                                     const u8 *payload, size_t payload_len)
436 {
437         struct eap_pwd_id *id;
438
439         if (payload_len < sizeof(struct eap_pwd_id)) {
440                 wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response");
441                 return;
442         }
443
444         id = (struct eap_pwd_id *) payload;
445         if ((data->group_num != be_to_host16(id->group_num)) ||
446             (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
447             (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
448             (id->prf != EAP_PWD_DEFAULT_PRF)) {
449                 wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
450                 eap_pwd_state(data, FAILURE);
451                 return;
452         }
453         data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id));
454         if (data->id_peer == NULL) {
455                 wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
456                 return;
457         }
458         data->id_peer_len = payload_len - sizeof(struct eap_pwd_id);
459         os_memcpy(data->id_peer, id->identity, data->id_peer_len);
460         wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
461                           data->id_peer, data->id_peer_len);
462
463         if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) {
464                 wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
465                            "group");
466                 return;
467         }
468         if (compute_password_element(data->grp, data->group_num,
469                                      data->password, data->password_len,
470                                      data->id_server, data->id_server_len,
471                                      data->id_peer, data->id_peer_len,
472                                      (u8 *) &data->token)) {
473                 wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute "
474                            "PWE");
475                 return;
476         }
477         wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
478                    BN_num_bits(data->grp->prime));
479
480         eap_pwd_state(data, PWD_Commit_Req);
481 }
482
483
484 static void
485 eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
486                             const u8 *payload, size_t payload_len)
487 {
488         u8 *ptr;
489         BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
490         EC_POINT *K = NULL, *point = NULL;
491         int res = 0;
492
493         wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
494
495         if (((data->peer_scalar = BN_new()) == NULL) ||
496             ((data->k = BN_new()) == NULL) ||
497             ((cofactor = BN_new()) == NULL) ||
498             ((x = BN_new()) == NULL) ||
499             ((y = BN_new()) == NULL) ||
500             ((point = EC_POINT_new(data->grp->group)) == NULL) ||
501             ((K = EC_POINT_new(data->grp->group)) == NULL) ||
502             ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) {
503                 wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
504                            "fail");
505                 goto fin;
506         }
507
508         if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
509                 wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
510                            "cofactor for curve");
511                 goto fin;
512         }
513
514         /* element, x then y, followed by scalar */
515         ptr = (u8 *) payload;
516         BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
517         ptr += BN_num_bytes(data->grp->prime);
518         BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
519         ptr += BN_num_bytes(data->grp->prime);
520         BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar);
521         if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
522                                                  data->peer_element, x, y,
523                                                  bnctx)) {
524                 wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
525                            "fail");
526                 goto fin;
527         }
528
529         /* check to ensure peer's element is not in a small sub-group */
530         if (BN_cmp(cofactor, BN_value_one())) {
531                 if (!EC_POINT_mul(data->grp->group, point, NULL,
532                                   data->peer_element, cofactor, NULL)) {
533                         wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
534                                    "multiply peer element by order");
535                         goto fin;
536                 }
537                 if (EC_POINT_is_at_infinity(data->grp->group, point)) {
538                         wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
539                                    "is at infinity!\n");
540                         goto fin;
541                 }
542         }
543
544         /* compute the shared key, k */
545         if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
546                            data->peer_scalar, bnctx)) ||
547             (!EC_POINT_add(data->grp->group, K, K, data->peer_element,
548                            bnctx)) ||
549             (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
550                            bnctx))) {
551                 wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
552                            "fail");
553                 goto fin;
554         }
555
556         /* ensure that the shared key isn't in a small sub-group */
557         if (BN_cmp(cofactor, BN_value_one())) {
558                 if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
559                                   NULL)) {
560                         wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
561                                    "multiply shared key point by order!\n");
562                         goto fin;
563                 }
564         }
565
566         /*
567          * This check is strictly speaking just for the case above where
568          * co-factor > 1 but it was suggested that even though this is probably
569          * never going to happen it is a simple and safe check "just to be
570          * sure" so let's be safe.
571          */
572         if (EC_POINT_is_at_infinity(data->grp->group, K)) {
573                 wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
574                            "at infinity");
575                 goto fin;
576         }
577         if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
578                                                  NULL, bnctx)) {
579                 wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
580                            "shared secret from secret point");
581                 goto fin;
582         }
583         res = 1;
584
585 fin:
586         EC_POINT_free(K);
587         EC_POINT_free(point);
588         BN_free(cofactor);
589         BN_free(x);
590         BN_free(y);
591
592         if (res)
593                 eap_pwd_state(data, PWD_Confirm_Req);
594         else
595                 eap_pwd_state(data, FAILURE);
596 }
597
598
599 static void
600 eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
601                              const u8 *payload, size_t payload_len)
602 {
603         BIGNUM *x = NULL, *y = NULL;
604         HMAC_CTX ctx;
605         u32 cs;
606         u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
607
608         /* build up the ciphersuite: group | random_function | prf */
609         ptr = (u8 *) &cs;
610         os_memcpy(ptr, &data->group_num, sizeof(u16));
611         ptr += sizeof(u16);
612         *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
613         ptr += sizeof(u8);
614         *ptr = EAP_PWD_DEFAULT_PRF;
615
616         /* each component of the cruft will be at most as big as the prime */
617         if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
618             ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
619                 wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail");
620                 goto fin;
621         }
622
623         /*
624          * commit is H(k | peer_element | peer_scalar | server_element |
625          *             server_scalar | ciphersuite)
626          */
627         H_Init(&ctx);
628
629         /* k */
630         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
631         BN_bn2bin(data->k, cruft);
632         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
633
634         /* peer element: x, y */
635         if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
636                                                  data->peer_element, x, y,
637                                                  bnctx)) {
638                 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
639                            "assignment fail");
640                 goto fin;
641         }
642         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
643         BN_bn2bin(x, cruft);
644         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
645         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
646         BN_bn2bin(y, cruft);
647         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
648
649         /* peer scalar */
650         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
651         BN_bn2bin(data->peer_scalar, cruft);
652         H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
653
654         /* server element: x, y */
655         if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
656                                                  data->my_element, x, y,
657                                                  bnctx)) {
658                 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
659                            "assignment fail");
660                 goto fin;
661         }
662
663         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
664         BN_bn2bin(x, cruft);
665         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
666         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
667         BN_bn2bin(y, cruft);
668         H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
669
670         /* server scalar */
671         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
672         BN_bn2bin(data->my_scalar, cruft);
673         H_Update(&ctx, cruft, BN_num_bytes(data->grp->order));
674
675         /* ciphersuite */
676         os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
677         H_Update(&ctx, (u8 *)&cs, sizeof(u32));
678
679         /* all done */
680         H_Final(&ctx, conf);
681
682         ptr = (u8 *) payload;
683         if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
684                 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
685                            "verify");
686                 goto fin;
687         }
688
689         wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified");
690         if (compute_keys(data->grp, bnctx, data->k, data->my_element,
691                          data->peer_element, data->my_scalar,
692                          data->peer_scalar, &cs, data->msk, data->emsk) < 0)
693                 eap_pwd_state(data, FAILURE);
694         else
695                 eap_pwd_state(data, SUCCESS);
696
697 fin:
698         os_free(cruft);
699         BN_free(x);
700         BN_free(y);
701 }
702
703
704 static void eap_pwd_process(struct eap_sm *sm, void *priv,
705                             struct wpabuf *respData)
706 {
707         struct eap_pwd_data *data = priv;
708         const u8 *pos;
709         size_t len;
710         u8 exch;
711
712         pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, respData, &len);
713         if ((pos == NULL) || (len < 1)) {
714                 wpa_printf(MSG_INFO, "Bad EAP header! pos %s and len = %d",
715                            (pos == NULL) ? "is NULL" : "is not NULL",
716                            (int) len);
717                 return;
718         }
719
720         exch = *pos & 0x3f;
721         switch (exch) {
722         case EAP_PWD_OPCODE_ID_EXCH:
723                 eap_pwd_process_id_resp(sm, data, pos + 1, len - 1);
724                 break;
725         case EAP_PWD_OPCODE_COMMIT_EXCH:
726                 eap_pwd_process_commit_resp(sm, data, pos + 1, len - 1);
727                 break;
728         case EAP_PWD_OPCODE_CONFIRM_EXCH:
729                 eap_pwd_process_confirm_resp(sm, data, pos + 1, len - 1);
730                 break;
731         }
732 }
733
734
735 static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
736 {
737         struct eap_pwd_data *data = priv;
738         u8 *key;
739
740         if (data->state != SUCCESS)
741                 return NULL;
742
743         key = os_malloc(EAP_MSK_LEN);
744         if (key == NULL)
745                 return NULL;
746
747         os_memcpy(key, data->msk, EAP_MSK_LEN);
748         *len = EAP_MSK_LEN;
749
750         return key;
751 }
752
753
754 static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
755 {
756         struct eap_pwd_data *data = priv;
757         u8 *key;
758
759         if (data->state != SUCCESS)
760                 return NULL;
761
762         key = os_malloc(EAP_EMSK_LEN);
763         if (key == NULL)
764                 return NULL;
765
766         os_memcpy(key, data->emsk, EAP_EMSK_LEN);
767         *len = EAP_EMSK_LEN;
768
769         return key;
770 }
771
772
773 static Boolean eap_pwd_is_success(struct eap_sm *sm, void *priv)
774 {
775         struct eap_pwd_data *data = priv;
776         return data->state == SUCCESS;
777 }
778
779
780 static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv)
781 {
782         struct eap_pwd_data *data = priv;
783         return (data->state == SUCCESS) || (data->state == FAILURE);
784 }
785
786
787 int eap_server_pwd_register(void)
788 {
789         struct eap_method *eap;
790         int ret;
791         struct timeval tp;
792         struct timezone tz;
793         u32 sr;
794
795         EVP_add_digest(EVP_sha256());
796
797         sr = 0xdeaddada;
798         (void) gettimeofday(&tp, &tz);
799         sr ^= (tp.tv_sec ^ tp.tv_usec);
800         srandom(sr);
801
802         eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
803                                       EAP_VENDOR_IETF, EAP_TYPE_PWD,
804                                       "PWD");
805         if (eap == NULL)
806                 return -1;
807
808         eap->init = eap_pwd_init;
809         eap->reset = eap_pwd_reset;
810         eap->buildReq = eap_pwd_build_req;
811         eap->check = eap_pwd_check;
812         eap->process = eap_pwd_process;
813         eap->isDone = eap_pwd_is_done;
814         eap->getKey = eap_pwd_getkey;
815         eap->get_emsk = eap_pwd_get_emsk;
816         eap->isSuccess = eap_pwd_is_success;
817
818         ret = eap_server_method_register(eap);
819         if (ret)
820                 eap_server_method_free(eap);
821         return ret;
822 }
823