2 * hostapd / EAP-pwd (RFC 5931) server
3 * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the BSD license.
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
12 * See README and COPYING for more details.
18 #include "eap_server/eap_i.h"
19 #include "eap_common/eap_pwd_common.h"
24 PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
37 BIGNUM *private_value;
41 EC_POINT *peer_element;
44 u8 emsk[EAP_EMSK_LEN];
50 static const char * eap_pwd_state_txt(int state)
56 return "PWD-Commit-Req";
58 return "PWD-Confirm-Req";
69 static void eap_pwd_state(struct eap_pwd_data *data, int state)
71 wpa_printf(MSG_DEBUG, "EAP-pwd: %s -> %s",
72 eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
77 static void * eap_pwd_init(struct eap_sm *sm)
79 struct eap_pwd_data *data;
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 "
88 data = os_zalloc(sizeof(*data));
92 data->group_num = sm->pwd_group;
93 wpa_printf(MSG_DEBUG, "EAP-pwd: Selected group number %d",
95 data->state = PWD_ID_Req;
97 data->id_server = (u8 *) os_strdup("server");
99 data->id_server_len = os_strlen((char *)data->id_server);
101 data->password = os_malloc(sm->user->password_len);
102 if (data->password == NULL) {
103 wpa_printf(MSG_INFO, "EAP-PWD: Mmemory allocation password "
107 data->password_len = sm->user->password_len;
108 os_memcpy(data->password, sm->user->password, data->password_len);
110 bnctx = BN_CTX_new();
112 wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
120 static void eap_pwd_reset(struct eap_sm *sm, void *priv)
122 struct eap_pwd_data *data = priv;
124 BN_free(data->private_value);
125 BN_free(data->peer_scalar);
126 BN_free(data->my_scalar);
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);
138 static struct wpabuf *
139 eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
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);
149 eap_pwd_state(data, FAILURE);
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);
167 static struct wpabuf *
168 eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
170 struct wpabuf *req = NULL;
171 BIGNUM *mask = NULL, *x = NULL, *y = NULL;
172 u8 *scalar = NULL, *element = NULL;
175 wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
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 "
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);
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 "
195 eap_pwd_state(data, FAILURE);
199 if (!EC_POINT_invert(data->grp->group, data->my_element, bnctx)) {
200 wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion "
206 if (((x = BN_new()) == NULL) ||
207 ((y = BN_new()) == NULL)) {
208 wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation "
212 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
213 data->my_element, x, y,
215 wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
220 if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
221 ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
223 wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
228 * bignums occupy as little memory as possible so one that is
229 * sufficiently smaller than the prime or order might need pre-pending
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);
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);
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);
250 wpabuf_put_u8(req, EAP_PWD_OPCODE_COMMIT_EXCH);
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));
262 eap_pwd_state(data, FAILURE);
268 static struct wpabuf *
269 eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id)
271 struct wpabuf *req = NULL;
272 BIGNUM *x = NULL, *y = NULL;
274 u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
276 wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request");
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 "
287 * commit is H(k | server_element | server_scalar | peer_element |
288 * peer_scalar | ciphersuite)
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.
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));
302 /* server element: x, y */
303 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
304 data->my_element, x, y,
306 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
311 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
313 H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
314 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
316 H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
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));
323 /* peer element: x, y */
324 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
325 data->peer_element, x, y,
327 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
332 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
334 H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
335 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
337 H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
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));
345 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
347 os_memcpy(ptr, &data->group_num, sizeof(u16));
349 *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
351 *ptr = EAP_PWD_DEFAULT_PRF;
353 H_Update(&ctx, cruft, ptr-cruft);
355 /* all done with the random function */
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);
364 wpabuf_put_u8(req, EAP_PWD_OPCODE_CONFIRM_EXCH);
365 wpabuf_put_data(req, conf, SHA256_DIGEST_LENGTH);
372 eap_pwd_state(data, FAILURE);
378 static struct wpabuf *
379 eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
381 struct eap_pwd_data *data = priv;
383 switch (data->state) {
385 return eap_pwd_build_id_req(sm, data, id);
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);
391 wpa_printf(MSG_INFO, "EAP-pwd: Unknown state %d in build_req",
400 static Boolean eap_pwd_check(struct eap_sm *sm, void *priv,
401 struct wpabuf *respData)
403 struct eap_pwd_data *data = priv;
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");
413 wpa_printf(MSG_DEBUG, "EAP-pwd: Received frame: opcode=%d", *pos);
415 if (data->state == PWD_ID_Req && *pos == EAP_PWD_OPCODE_ID_EXCH)
418 if (data->state == PWD_Commit_Req &&
419 *pos == EAP_PWD_OPCODE_COMMIT_EXCH)
422 if (data->state == PWD_Confirm_Req &&
423 *pos == EAP_PWD_OPCODE_CONFIRM_EXCH)
426 wpa_printf(MSG_INFO, "EAP-pwd: Unexpected opcode=%d in state=%d",
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)
437 struct eap_pwd_id *id;
439 if (payload_len < sizeof(struct eap_pwd_id)) {
440 wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response");
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);
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");
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);
463 if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) {
464 wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
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 "
477 wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...",
478 BN_num_bits(data->grp->prime));
480 eap_pwd_state(data, PWD_Commit_Req);
485 eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
486 const u8 *payload, size_t payload_len)
489 BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
490 EC_POINT *K = NULL, *point = NULL;
493 wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
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 "
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");
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,
524 wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
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");
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");
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,
549 (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
551 wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key "
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,
560 wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
561 "multiply shared key point by order!\n");
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.
572 if (EC_POINT_is_at_infinity(data->grp->group, K)) {
573 wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is "
577 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
579 wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract "
580 "shared secret from secret point");
587 EC_POINT_free(point);
593 eap_pwd_state(data, PWD_Confirm_Req);
595 eap_pwd_state(data, FAILURE);
600 eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
601 const u8 *payload, size_t payload_len)
603 BIGNUM *x = NULL, *y = NULL;
606 u8 conf[SHA256_DIGEST_LENGTH], *cruft = NULL, *ptr;
608 /* build up the ciphersuite: group | random_function | prf */
610 os_memcpy(ptr, &data->group_num, sizeof(u16));
612 *ptr = EAP_PWD_DEFAULT_RAND_FUNC;
614 *ptr = EAP_PWD_DEFAULT_PRF;
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");
624 * commit is H(k | peer_element | peer_scalar | server_element |
625 * server_scalar | ciphersuite)
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));
634 /* peer element: x, y */
635 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
636 data->peer_element, x, y,
638 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
642 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
644 H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
645 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
647 H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
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));
654 /* server element: x, y */
655 if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
656 data->my_element, x, y,
658 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
663 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
665 H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
666 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
668 H_Update(&ctx, cruft, BN_num_bytes(data->grp->prime));
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));
676 os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
677 H_Update(&ctx, (u8 *)&cs, sizeof(u32));
682 ptr = (u8 *) payload;
683 if (os_memcmp(conf, ptr, SHA256_DIGEST_LENGTH)) {
684 wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
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);
695 eap_pwd_state(data, SUCCESS);
704 static void eap_pwd_process(struct eap_sm *sm, void *priv,
705 struct wpabuf *respData)
707 struct eap_pwd_data *data = priv;
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",
722 case EAP_PWD_OPCODE_ID_EXCH:
723 eap_pwd_process_id_resp(sm, data, pos + 1, len - 1);
725 case EAP_PWD_OPCODE_COMMIT_EXCH:
726 eap_pwd_process_commit_resp(sm, data, pos + 1, len - 1);
728 case EAP_PWD_OPCODE_CONFIRM_EXCH:
729 eap_pwd_process_confirm_resp(sm, data, pos + 1, len - 1);
735 static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
737 struct eap_pwd_data *data = priv;
740 if (data->state != SUCCESS)
743 key = os_malloc(EAP_MSK_LEN);
747 os_memcpy(key, data->msk, EAP_MSK_LEN);
754 static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
756 struct eap_pwd_data *data = priv;
759 if (data->state != SUCCESS)
762 key = os_malloc(EAP_EMSK_LEN);
766 os_memcpy(key, data->emsk, EAP_EMSK_LEN);
773 static Boolean eap_pwd_is_success(struct eap_sm *sm, void *priv)
775 struct eap_pwd_data *data = priv;
776 return data->state == SUCCESS;
780 static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv)
782 struct eap_pwd_data *data = priv;
783 return (data->state == SUCCESS) || (data->state == FAILURE);
787 int eap_server_pwd_register(void)
789 struct eap_method *eap;
795 EVP_add_digest(EVP_sha256());
798 (void) gettimeofday(&tp, &tz);
799 sr ^= (tp.tv_sec ^ tp.tv_usec);
802 eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
803 EAP_VENDOR_IETF, EAP_TYPE_PWD,
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;
818 ret = eap_server_method_register(eap);
820 eap_server_method_free(eap);