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