2 * hostapd / EAP-pwd (RFC 5931) server
3 * Copyright (c) 2010, Dan Harkins <dharkins@lounge.org>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
12 #include "crypto/sha256.h"
13 #include "crypto/ms_funcs.h"
14 #include "eap_server/eap_i.h"
15 #include "eap_common/eap_pwd_common.h"
20 PWD_ID_Req
, PWD_Commit_Req
, PWD_Confirm_Req
, SUCCESS
, FAILURE
35 struct wpabuf
*outbuf
;
40 BIGNUM
*private_value
;
44 EC_POINT
*peer_element
;
46 u8 my_confirm
[SHA256_MAC_LEN
];
49 u8 emsk
[EAP_EMSK_LEN
];
50 u8 session_id
[1 + SHA256_MAC_LEN
];
56 static const char * eap_pwd_state_txt(int state
)
62 return "PWD-Commit-Req";
64 return "PWD-Confirm-Req";
75 static void eap_pwd_state(struct eap_pwd_data
*data
, int state
)
77 wpa_printf(MSG_DEBUG
, "EAP-pwd: %s -> %s",
78 eap_pwd_state_txt(data
->state
), eap_pwd_state_txt(state
));
83 static void * eap_pwd_init(struct eap_sm
*sm
)
85 struct eap_pwd_data
*data
;
87 if (sm
->user
== NULL
|| sm
->user
->password
== NULL
||
88 sm
->user
->password_len
== 0) {
89 wpa_printf(MSG_INFO
, "EAP-PWD (server): Password is not "
94 data
= os_zalloc(sizeof(*data
));
98 data
->group_num
= sm
->pwd_group
;
99 wpa_printf(MSG_DEBUG
, "EAP-pwd: Selected group number %d",
101 data
->state
= PWD_ID_Req
;
103 data
->id_server
= (u8
*) os_strdup("server");
105 data
->id_server_len
= os_strlen((char *) data
->id_server
);
107 data
->password
= os_malloc(sm
->user
->password_len
);
108 if (data
->password
== NULL
) {
109 wpa_printf(MSG_INFO
, "EAP-PWD: Memory allocation password "
111 bin_clear_free(data
->id_server
, data
->id_server_len
);
115 data
->password_len
= sm
->user
->password_len
;
116 os_memcpy(data
->password
, sm
->user
->password
, data
->password_len
);
117 data
->password_hash
= sm
->user
->password_hash
;
119 data
->bnctx
= BN_CTX_new();
120 if (data
->bnctx
== NULL
) {
121 wpa_printf(MSG_INFO
, "EAP-PWD: bn context allocation fail");
122 bin_clear_free(data
->password
, data
->password_len
);
123 bin_clear_free(data
->id_server
, data
->id_server_len
);
128 data
->in_frag_pos
= data
->out_frag_pos
= 0;
129 data
->inbuf
= data
->outbuf
= NULL
;
130 /* use default MTU from RFC 5931 if not configured otherwise */
131 data
->mtu
= sm
->fragment_size
> 0 ? sm
->fragment_size
: 1020;
137 static void eap_pwd_reset(struct eap_sm
*sm
, void *priv
)
139 struct eap_pwd_data
*data
= priv
;
141 BN_clear_free(data
->private_value
);
142 BN_clear_free(data
->peer_scalar
);
143 BN_clear_free(data
->my_scalar
);
144 BN_clear_free(data
->k
);
145 BN_CTX_free(data
->bnctx
);
146 EC_POINT_clear_free(data
->my_element
);
147 EC_POINT_clear_free(data
->peer_element
);
148 bin_clear_free(data
->id_peer
, data
->id_peer_len
);
149 bin_clear_free(data
->id_server
, data
->id_server_len
);
150 bin_clear_free(data
->password
, data
->password_len
);
152 EC_GROUP_free(data
->grp
->group
);
153 EC_POINT_clear_free(data
->grp
->pwe
);
154 BN_clear_free(data
->grp
->order
);
155 BN_clear_free(data
->grp
->prime
);
158 wpabuf_free(data
->inbuf
);
159 wpabuf_free(data
->outbuf
);
160 bin_clear_free(data
, sizeof(*data
));
164 static void eap_pwd_build_id_req(struct eap_sm
*sm
, struct eap_pwd_data
*data
,
167 wpa_printf(MSG_DEBUG
, "EAP-pwd: ID/Request");
169 * if we're fragmenting then we already have an id request, just return
171 if (data
->out_frag_pos
)
174 data
->outbuf
= wpabuf_alloc(sizeof(struct eap_pwd_id
) +
175 data
->id_server_len
);
176 if (data
->outbuf
== NULL
) {
177 eap_pwd_state(data
, FAILURE
);
181 if (os_get_random((u8
*) &data
->token
, sizeof(data
->token
)) < 0) {
182 wpabuf_free(data
->outbuf
);
184 eap_pwd_state(data
, FAILURE
);
188 wpabuf_put_be16(data
->outbuf
, data
->group_num
);
189 wpabuf_put_u8(data
->outbuf
, EAP_PWD_DEFAULT_RAND_FUNC
);
190 wpabuf_put_u8(data
->outbuf
, EAP_PWD_DEFAULT_PRF
);
191 wpabuf_put_data(data
->outbuf
, &data
->token
, sizeof(data
->token
));
192 wpabuf_put_u8(data
->outbuf
, data
->password_hash
? EAP_PWD_PREP_MS
:
194 wpabuf_put_data(data
->outbuf
, data
->id_server
, data
->id_server_len
);
198 static void eap_pwd_build_commit_req(struct eap_sm
*sm
,
199 struct eap_pwd_data
*data
, u8 id
)
201 BIGNUM
*mask
= NULL
, *x
= NULL
, *y
= NULL
;
202 u8
*scalar
= NULL
, *element
= NULL
;
205 wpa_printf(MSG_DEBUG
, "EAP-pwd: Commit/Request");
207 * if we're fragmenting then we already have an commit request, just
210 if (data
->out_frag_pos
)
213 if (((data
->private_value
= BN_new()) == NULL
) ||
214 ((data
->my_element
= EC_POINT_new(data
->grp
->group
)) == NULL
) ||
215 ((data
->my_scalar
= BN_new()) == NULL
) ||
216 ((mask
= BN_new()) == NULL
)) {
217 wpa_printf(MSG_INFO
, "EAP-PWD (server): scalar allocation "
222 if (BN_rand_range(data
->private_value
, data
->grp
->order
) != 1 ||
223 BN_rand_range(mask
, data
->grp
->order
) != 1 ||
224 BN_add(data
->my_scalar
, data
->private_value
, mask
) != 1 ||
225 BN_mod(data
->my_scalar
, data
->my_scalar
, data
->grp
->order
,
228 "EAP-pwd (server): unable to get randomness");
232 if (!EC_POINT_mul(data
->grp
->group
, data
->my_element
, NULL
,
233 data
->grp
->pwe
, mask
, data
->bnctx
)) {
234 wpa_printf(MSG_INFO
, "EAP-PWD (server): element allocation "
236 eap_pwd_state(data
, FAILURE
);
240 if (!EC_POINT_invert(data
->grp
->group
, data
->my_element
, data
->bnctx
))
242 wpa_printf(MSG_INFO
, "EAP-PWD (server): element inversion "
248 if (((x
= BN_new()) == NULL
) ||
249 ((y
= BN_new()) == NULL
)) {
250 wpa_printf(MSG_INFO
, "EAP-PWD (server): point allocation "
254 if (!EC_POINT_get_affine_coordinates_GFp(data
->grp
->group
,
255 data
->my_element
, x
, y
,
257 wpa_printf(MSG_INFO
, "EAP-PWD (server): point assignment "
262 if (((scalar
= os_malloc(BN_num_bytes(data
->grp
->order
))) == NULL
) ||
263 ((element
= os_malloc(BN_num_bytes(data
->grp
->prime
) * 2)) ==
265 wpa_printf(MSG_INFO
, "EAP-PWD (server): data allocation fail");
270 * bignums occupy as little memory as possible so one that is
271 * sufficiently smaller than the prime or order might need pre-pending
274 os_memset(scalar
, 0, BN_num_bytes(data
->grp
->order
));
275 os_memset(element
, 0, BN_num_bytes(data
->grp
->prime
) * 2);
276 offset
= BN_num_bytes(data
->grp
->order
) -
277 BN_num_bytes(data
->my_scalar
);
278 BN_bn2bin(data
->my_scalar
, scalar
+ offset
);
280 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(x
);
281 BN_bn2bin(x
, element
+ offset
);
282 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(y
);
283 BN_bn2bin(y
, element
+ BN_num_bytes(data
->grp
->prime
) + offset
);
285 data
->outbuf
= wpabuf_alloc(2 * BN_num_bytes(data
->grp
->prime
) +
286 BN_num_bytes(data
->grp
->order
));
287 if (data
->outbuf
== NULL
)
290 /* We send the element as (x,y) followed by the scalar */
291 wpabuf_put_data(data
->outbuf
, element
,
292 2 * BN_num_bytes(data
->grp
->prime
));
293 wpabuf_put_data(data
->outbuf
, scalar
, BN_num_bytes(data
->grp
->order
));
300 if (data
->outbuf
== NULL
)
301 eap_pwd_state(data
, FAILURE
);
305 static void eap_pwd_build_confirm_req(struct eap_sm
*sm
,
306 struct eap_pwd_data
*data
, u8 id
)
308 BIGNUM
*x
= NULL
, *y
= NULL
;
309 struct crypto_hash
*hash
;
310 u8 conf
[SHA256_MAC_LEN
], *cruft
= NULL
, *ptr
;
314 wpa_printf(MSG_DEBUG
, "EAP-pwd: Confirm/Request");
316 * if we're fragmenting then we already have an confirm request, just
319 if (data
->out_frag_pos
)
322 /* Each component of the cruft will be at most as big as the prime */
323 if (((cruft
= os_malloc(BN_num_bytes(data
->grp
->prime
))) == NULL
) ||
324 ((x
= BN_new()) == NULL
) || ((y
= BN_new()) == NULL
)) {
325 wpa_printf(MSG_INFO
, "EAP-PWD (server): debug allocation "
331 * commit is H(k | server_element | server_scalar | peer_element |
332 * peer_scalar | ciphersuite)
334 hash
= eap_pwd_h_init();
339 * Zero the memory each time because this is mod prime math and some
340 * value may start with a few zeros and the previous one did not.
344 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
345 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(data
->k
);
346 BN_bn2bin(data
->k
, cruft
+ offset
);
347 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
349 /* server element: x, y */
350 if (!EC_POINT_get_affine_coordinates_GFp(data
->grp
->group
,
351 data
->my_element
, x
, y
,
353 wpa_printf(MSG_INFO
, "EAP-PWD (server): confirm point "
358 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
359 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(x
);
360 BN_bn2bin(x
, cruft
+ offset
);
361 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
362 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
363 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(y
);
364 BN_bn2bin(y
, cruft
+ offset
);
365 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
368 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
369 offset
= BN_num_bytes(data
->grp
->order
) -
370 BN_num_bytes(data
->my_scalar
);
371 BN_bn2bin(data
->my_scalar
, cruft
+ offset
);
372 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->order
));
374 /* peer element: x, y */
375 if (!EC_POINT_get_affine_coordinates_GFp(data
->grp
->group
,
376 data
->peer_element
, x
, y
,
378 wpa_printf(MSG_INFO
, "EAP-PWD (server): confirm point "
383 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
384 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(x
);
385 BN_bn2bin(x
, cruft
+ offset
);
386 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
387 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
388 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(y
);
389 BN_bn2bin(y
, cruft
+ offset
);
390 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
393 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
394 offset
= BN_num_bytes(data
->grp
->order
) -
395 BN_num_bytes(data
->peer_scalar
);
396 BN_bn2bin(data
->peer_scalar
, cruft
+ offset
);
397 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->order
));
400 grp
= htons(data
->group_num
);
401 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
403 os_memcpy(ptr
, &grp
, sizeof(u16
));
405 *ptr
= EAP_PWD_DEFAULT_RAND_FUNC
;
407 *ptr
= EAP_PWD_DEFAULT_PRF
;
409 eap_pwd_h_update(hash
, cruft
, ptr
- cruft
);
411 /* all done with the random function */
412 eap_pwd_h_final(hash
, conf
);
413 os_memcpy(data
->my_confirm
, conf
, SHA256_MAC_LEN
);
415 data
->outbuf
= wpabuf_alloc(SHA256_MAC_LEN
);
416 if (data
->outbuf
== NULL
)
419 wpabuf_put_data(data
->outbuf
, conf
, SHA256_MAC_LEN
);
422 bin_clear_free(cruft
, BN_num_bytes(data
->grp
->prime
));
425 if (data
->outbuf
== NULL
)
426 eap_pwd_state(data
, FAILURE
);
430 static struct wpabuf
*
431 eap_pwd_build_req(struct eap_sm
*sm
, void *priv
, u8 id
)
433 struct eap_pwd_data
*data
= priv
;
441 * if we're buffering response fragments then just ACK
443 if (data
->in_frag_pos
) {
444 wpa_printf(MSG_DEBUG
, "EAP-pwd: ACKing a fragment!!");
445 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_PWD
,
446 EAP_PWD_HDR_SIZE
, EAP_CODE_REQUEST
, id
);
448 eap_pwd_state(data
, FAILURE
);
451 switch (data
->state
) {
453 wpabuf_put_u8(req
, EAP_PWD_OPCODE_ID_EXCH
);
456 wpabuf_put_u8(req
, EAP_PWD_OPCODE_COMMIT_EXCH
);
458 case PWD_Confirm_Req
:
459 wpabuf_put_u8(req
, EAP_PWD_OPCODE_CONFIRM_EXCH
);
462 eap_pwd_state(data
, FAILURE
); /* just to be sure */
470 * build the data portion of a request
472 switch (data
->state
) {
474 eap_pwd_build_id_req(sm
, data
, id
);
475 lm_exch
= EAP_PWD_OPCODE_ID_EXCH
;
478 eap_pwd_build_commit_req(sm
, data
, id
);
479 lm_exch
= EAP_PWD_OPCODE_COMMIT_EXCH
;
481 case PWD_Confirm_Req
:
482 eap_pwd_build_confirm_req(sm
, data
, id
);
483 lm_exch
= EAP_PWD_OPCODE_CONFIRM_EXCH
;
486 wpa_printf(MSG_INFO
, "EAP-pwd: Unknown state %d in build_req",
488 eap_pwd_state(data
, FAILURE
);
489 lm_exch
= 0; /* hush now, sweet compiler */
493 if (data
->state
== FAILURE
)
497 * determine whether that data needs to be fragmented
499 len
= wpabuf_len(data
->outbuf
) - data
->out_frag_pos
;
500 if ((len
+ EAP_PWD_HDR_SIZE
) > data
->mtu
) {
501 len
= data
->mtu
- EAP_PWD_HDR_SIZE
;
502 EAP_PWD_SET_MORE_BIT(lm_exch
);
504 * if this is the first fragment, need to set the M bit
505 * and add the total length to the eap_pwd_hdr
507 if (data
->out_frag_pos
== 0) {
508 EAP_PWD_SET_LENGTH_BIT(lm_exch
);
509 totlen
= wpabuf_len(data
->outbuf
) +
510 EAP_PWD_HDR_SIZE
+ sizeof(u16
);
512 wpa_printf(MSG_DEBUG
, "EAP-pwd: Fragmenting output, "
513 "total length = %d", totlen
);
515 wpa_printf(MSG_DEBUG
, "EAP-pwd: Send a %d byte fragment",
520 * alloc an eap request and populate it with the data
522 req
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_PWD
,
523 EAP_PWD_HDR_SIZE
+ len
+
524 (totlen
? sizeof(u16
) : 0),
525 EAP_CODE_REQUEST
, id
);
527 eap_pwd_state(data
, FAILURE
);
531 wpabuf_put_u8(req
, lm_exch
);
532 if (EAP_PWD_GET_LENGTH_BIT(lm_exch
))
533 wpabuf_put_be16(req
, totlen
);
535 buf
= wpabuf_head_u8(data
->outbuf
);
536 wpabuf_put_data(req
, buf
+ data
->out_frag_pos
, len
);
537 data
->out_frag_pos
+= len
;
539 * either not fragged or last fragment, either way free up the data
541 if (data
->out_frag_pos
>= wpabuf_len(data
->outbuf
)) {
542 wpabuf_free(data
->outbuf
);
544 data
->out_frag_pos
= 0;
551 static Boolean
eap_pwd_check(struct eap_sm
*sm
, void *priv
,
552 struct wpabuf
*respData
)
554 struct eap_pwd_data
*data
= priv
;
558 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_PWD
, respData
, &len
);
559 if (pos
== NULL
|| len
< 1) {
560 wpa_printf(MSG_INFO
, "EAP-pwd: Invalid frame");
564 wpa_printf(MSG_DEBUG
, "EAP-pwd: Received frame: exch = %d, len = %d",
565 EAP_PWD_GET_EXCHANGE(*pos
), (int) len
);
567 if (data
->state
== PWD_ID_Req
&&
568 ((EAP_PWD_GET_EXCHANGE(*pos
)) == EAP_PWD_OPCODE_ID_EXCH
))
571 if (data
->state
== PWD_Commit_Req
&&
572 ((EAP_PWD_GET_EXCHANGE(*pos
)) == EAP_PWD_OPCODE_COMMIT_EXCH
))
575 if (data
->state
== PWD_Confirm_Req
&&
576 ((EAP_PWD_GET_EXCHANGE(*pos
)) == EAP_PWD_OPCODE_CONFIRM_EXCH
))
579 wpa_printf(MSG_INFO
, "EAP-pwd: Unexpected opcode=%d in state=%d",
586 static void eap_pwd_process_id_resp(struct eap_sm
*sm
,
587 struct eap_pwd_data
*data
,
588 const u8
*payload
, size_t payload_len
)
590 struct eap_pwd_id
*id
;
596 if (payload_len
< sizeof(struct eap_pwd_id
)) {
597 wpa_printf(MSG_INFO
, "EAP-pwd: Invalid ID response");
601 id
= (struct eap_pwd_id
*) payload
;
602 if ((data
->group_num
!= be_to_host16(id
->group_num
)) ||
603 (id
->random_function
!= EAP_PWD_DEFAULT_RAND_FUNC
) ||
604 (os_memcmp(id
->token
, (u8
*)&data
->token
, sizeof(data
->token
))) ||
605 (id
->prf
!= EAP_PWD_DEFAULT_PRF
) ||
607 data
->password_hash
? EAP_PWD_PREP_MS
: EAP_PWD_PREP_NONE
) {
608 wpa_printf(MSG_INFO
, "EAP-pwd: peer changed parameters");
609 eap_pwd_state(data
, FAILURE
);
612 data
->id_peer
= os_malloc(payload_len
- sizeof(struct eap_pwd_id
));
613 if (data
->id_peer
== NULL
) {
614 wpa_printf(MSG_INFO
, "EAP-PWD: memory allocation id fail");
617 data
->id_peer_len
= payload_len
- sizeof(struct eap_pwd_id
);
618 os_memcpy(data
->id_peer
, id
->identity
, data
->id_peer_len
);
619 wpa_hexdump_ascii(MSG_DEBUG
, "EAP-PWD (server): peer sent id of",
620 data
->id_peer
, data
->id_peer_len
);
622 data
->grp
= os_zalloc(sizeof(EAP_PWD_group
));
623 if (data
->grp
== NULL
) {
624 wpa_printf(MSG_INFO
, "EAP-PWD: failed to allocate memory for "
629 if (data
->password_hash
) {
630 res
= hash_nt_password_hash(data
->password
, pwhashhash
);
633 password
= pwhashhash
;
634 password_len
= sizeof(pwhashhash
);
636 password
= data
->password
;
637 password_len
= data
->password_len
;
640 res
= compute_password_element(data
->grp
, data
->group_num
,
641 password
, password_len
,
642 data
->id_server
, data
->id_server_len
,
643 data
->id_peer
, data
->id_peer_len
,
644 (u8
*) &data
->token
);
645 os_memset(pwhashhash
, 0, sizeof(pwhashhash
));
647 wpa_printf(MSG_INFO
, "EAP-PWD (server): unable to compute "
651 wpa_printf(MSG_DEBUG
, "EAP-PWD (server): computed %d bit PWE...",
652 BN_num_bits(data
->grp
->prime
));
654 eap_pwd_state(data
, PWD_Commit_Req
);
659 eap_pwd_process_commit_resp(struct eap_sm
*sm
, struct eap_pwd_data
*data
,
660 const u8
*payload
, size_t payload_len
)
663 BIGNUM
*x
= NULL
, *y
= NULL
, *cofactor
= NULL
;
664 EC_POINT
*K
= NULL
, *point
= NULL
;
666 size_t prime_len
, order_len
;
668 wpa_printf(MSG_DEBUG
, "EAP-pwd: Received commit response");
670 prime_len
= BN_num_bytes(data
->grp
->prime
);
671 order_len
= BN_num_bytes(data
->grp
->order
);
673 if (payload_len
!= 2 * prime_len
+ order_len
) {
675 "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
676 (unsigned int) payload_len
,
677 (unsigned int) (2 * prime_len
+ order_len
));
681 if (((data
->peer_scalar
= BN_new()) == NULL
) ||
682 ((data
->k
= BN_new()) == NULL
) ||
683 ((cofactor
= BN_new()) == NULL
) ||
684 ((x
= BN_new()) == NULL
) ||
685 ((y
= BN_new()) == NULL
) ||
686 ((point
= EC_POINT_new(data
->grp
->group
)) == NULL
) ||
687 ((K
= EC_POINT_new(data
->grp
->group
)) == NULL
) ||
688 ((data
->peer_element
= EC_POINT_new(data
->grp
->group
)) == NULL
)) {
689 wpa_printf(MSG_INFO
, "EAP-PWD (server): peer data allocation "
694 if (!EC_GROUP_get_cofactor(data
->grp
->group
, cofactor
, NULL
)) {
695 wpa_printf(MSG_INFO
, "EAP-PWD (server): unable to get "
696 "cofactor for curve");
700 /* element, x then y, followed by scalar */
701 ptr
= (u8
*) payload
;
702 BN_bin2bn(ptr
, BN_num_bytes(data
->grp
->prime
), x
);
703 ptr
+= BN_num_bytes(data
->grp
->prime
);
704 BN_bin2bn(ptr
, BN_num_bytes(data
->grp
->prime
), y
);
705 ptr
+= BN_num_bytes(data
->grp
->prime
);
706 BN_bin2bn(ptr
, BN_num_bytes(data
->grp
->order
), data
->peer_scalar
);
707 if (!EC_POINT_set_affine_coordinates_GFp(data
->grp
->group
,
708 data
->peer_element
, x
, y
,
710 wpa_printf(MSG_INFO
, "EAP-PWD (server): setting peer element "
715 /* check to ensure peer's element is not in a small sub-group */
716 if (BN_cmp(cofactor
, BN_value_one())) {
717 if (!EC_POINT_mul(data
->grp
->group
, point
, NULL
,
718 data
->peer_element
, cofactor
, NULL
)) {
719 wpa_printf(MSG_INFO
, "EAP-PWD (server): cannot "
720 "multiply peer element by order");
723 if (EC_POINT_is_at_infinity(data
->grp
->group
, point
)) {
724 wpa_printf(MSG_INFO
, "EAP-PWD (server): peer element "
725 "is at infinity!\n");
730 /* compute the shared key, k */
731 if ((!EC_POINT_mul(data
->grp
->group
, K
, NULL
, data
->grp
->pwe
,
732 data
->peer_scalar
, data
->bnctx
)) ||
733 (!EC_POINT_add(data
->grp
->group
, K
, K
, data
->peer_element
,
735 (!EC_POINT_mul(data
->grp
->group
, K
, NULL
, K
, data
->private_value
,
737 wpa_printf(MSG_INFO
, "EAP-PWD (server): computing shared key "
742 /* ensure that the shared key isn't in a small sub-group */
743 if (BN_cmp(cofactor
, BN_value_one())) {
744 if (!EC_POINT_mul(data
->grp
->group
, K
, NULL
, K
, cofactor
,
746 wpa_printf(MSG_INFO
, "EAP-PWD (server): cannot "
747 "multiply shared key point by order!\n");
753 * This check is strictly speaking just for the case above where
754 * co-factor > 1 but it was suggested that even though this is probably
755 * never going to happen it is a simple and safe check "just to be
756 * sure" so let's be safe.
758 if (EC_POINT_is_at_infinity(data
->grp
->group
, K
)) {
759 wpa_printf(MSG_INFO
, "EAP-PWD (server): shared key point is "
763 if (!EC_POINT_get_affine_coordinates_GFp(data
->grp
->group
, K
, data
->k
,
764 NULL
, data
->bnctx
)) {
765 wpa_printf(MSG_INFO
, "EAP-PWD (server): unable to extract "
766 "shared secret from secret point");
772 EC_POINT_clear_free(K
);
773 EC_POINT_clear_free(point
);
774 BN_clear_free(cofactor
);
779 eap_pwd_state(data
, PWD_Confirm_Req
);
781 eap_pwd_state(data
, FAILURE
);
786 eap_pwd_process_confirm_resp(struct eap_sm
*sm
, struct eap_pwd_data
*data
,
787 const u8
*payload
, size_t payload_len
)
789 BIGNUM
*x
= NULL
, *y
= NULL
;
790 struct crypto_hash
*hash
;
793 u8 conf
[SHA256_MAC_LEN
], *cruft
= NULL
, *ptr
;
796 if (payload_len
!= SHA256_MAC_LEN
) {
798 "EAP-pwd: Unexpected Confirm payload length %u (expected %u)",
799 (unsigned int) payload_len
, SHA256_MAC_LEN
);
803 /* build up the ciphersuite: group | random_function | prf */
804 grp
= htons(data
->group_num
);
806 os_memcpy(ptr
, &grp
, sizeof(u16
));
808 *ptr
= EAP_PWD_DEFAULT_RAND_FUNC
;
810 *ptr
= EAP_PWD_DEFAULT_PRF
;
812 /* each component of the cruft will be at most as big as the prime */
813 if (((cruft
= os_malloc(BN_num_bytes(data
->grp
->prime
))) == NULL
) ||
814 ((x
= BN_new()) == NULL
) || ((y
= BN_new()) == NULL
)) {
815 wpa_printf(MSG_INFO
, "EAP-PWD (peer): allocation fail");
820 * commit is H(k | peer_element | peer_scalar | server_element |
821 * server_scalar | ciphersuite)
823 hash
= eap_pwd_h_init();
828 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
829 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(data
->k
);
830 BN_bn2bin(data
->k
, cruft
+ offset
);
831 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
833 /* peer element: x, y */
834 if (!EC_POINT_get_affine_coordinates_GFp(data
->grp
->group
,
835 data
->peer_element
, x
, y
,
837 wpa_printf(MSG_INFO
, "EAP-PWD (server): confirm point "
841 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
842 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(x
);
843 BN_bn2bin(x
, cruft
+ offset
);
844 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
845 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
846 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(y
);
847 BN_bn2bin(y
, cruft
+ offset
);
848 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
851 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
852 offset
= BN_num_bytes(data
->grp
->order
) -
853 BN_num_bytes(data
->peer_scalar
);
854 BN_bn2bin(data
->peer_scalar
, cruft
+ offset
);
855 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->order
));
857 /* server element: x, y */
858 if (!EC_POINT_get_affine_coordinates_GFp(data
->grp
->group
,
859 data
->my_element
, x
, y
,
861 wpa_printf(MSG_INFO
, "EAP-PWD (server): confirm point "
866 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
867 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(x
);
868 BN_bn2bin(x
, cruft
+ offset
);
869 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
870 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
871 offset
= BN_num_bytes(data
->grp
->prime
) - BN_num_bytes(y
);
872 BN_bn2bin(y
, cruft
+ offset
);
873 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->prime
));
876 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
877 offset
= BN_num_bytes(data
->grp
->order
) -
878 BN_num_bytes(data
->my_scalar
);
879 BN_bn2bin(data
->my_scalar
, cruft
+ offset
);
880 eap_pwd_h_update(hash
, cruft
, BN_num_bytes(data
->grp
->order
));
883 os_memset(cruft
, 0, BN_num_bytes(data
->grp
->prime
));
884 eap_pwd_h_update(hash
, (u8
*) &cs
, sizeof(u32
));
887 eap_pwd_h_final(hash
, conf
);
889 ptr
= (u8
*) payload
;
890 if (os_memcmp_const(conf
, ptr
, SHA256_MAC_LEN
)) {
891 wpa_printf(MSG_INFO
, "EAP-PWD (server): confirm did not "
896 wpa_printf(MSG_DEBUG
, "EAP-pwd (server): confirm verified");
897 if (compute_keys(data
->grp
, data
->bnctx
, data
->k
,
898 data
->peer_scalar
, data
->my_scalar
, conf
,
899 data
->my_confirm
, &cs
, data
->msk
, data
->emsk
,
900 data
->session_id
) < 0)
901 eap_pwd_state(data
, FAILURE
);
903 eap_pwd_state(data
, SUCCESS
);
906 bin_clear_free(cruft
, BN_num_bytes(data
->grp
->prime
));
912 static void eap_pwd_process(struct eap_sm
*sm
, void *priv
,
913 struct wpabuf
*respData
)
915 struct eap_pwd_data
*data
= priv
;
921 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_PWD
, respData
, &len
);
922 if ((pos
== NULL
) || (len
< 1)) {
923 wpa_printf(MSG_INFO
, "Bad EAP header! pos %s and len = %d",
924 (pos
== NULL
) ? "is NULL" : "is not NULL",
930 pos
++; /* skip over the bits and the exch */
934 * if we're fragmenting then this should be an ACK with no data,
935 * just return and continue fragmenting in the "build" section above
937 if (data
->out_frag_pos
) {
939 wpa_printf(MSG_INFO
, "EAP-pwd: Bad response! "
940 "Fragmenting but not an ACK");
942 wpa_printf(MSG_DEBUG
, "EAP-pwd: received ACK from "
947 * if we're receiving fragmented packets then we need to buffer...
949 * the first fragment has a total length
951 if (EAP_PWD_GET_LENGTH_BIT(lm_exch
)) {
953 wpa_printf(MSG_DEBUG
,
954 "EAP-pwd: Frame too short to contain Total-Length field");
957 tot_len
= WPA_GET_BE16(pos
);
958 wpa_printf(MSG_DEBUG
, "EAP-pwd: Incoming fragments, total "
959 "length = %d", tot_len
);
963 wpa_printf(MSG_DEBUG
,
964 "EAP-pwd: Unexpected new fragment start when previous fragment is still in use");
967 data
->inbuf
= wpabuf_alloc(tot_len
);
968 if (data
->inbuf
== NULL
) {
969 wpa_printf(MSG_INFO
, "EAP-pwd: Out of memory to "
970 "buffer fragments!");
973 data
->in_frag_pos
= 0;
978 * the first and all intermediate fragments have the M bit set
980 if (EAP_PWD_GET_MORE_BIT(lm_exch
) || data
->in_frag_pos
) {
981 if ((data
->in_frag_pos
+ len
) > wpabuf_size(data
->inbuf
)) {
982 wpa_printf(MSG_DEBUG
, "EAP-pwd: Buffer overflow "
983 "attack detected! (%d+%d > %d)",
984 (int) data
->in_frag_pos
, (int) len
,
985 (int) wpabuf_size(data
->inbuf
));
986 eap_pwd_state(data
, FAILURE
);
989 wpabuf_put_data(data
->inbuf
, pos
, len
);
990 data
->in_frag_pos
+= len
;
992 if (EAP_PWD_GET_MORE_BIT(lm_exch
)) {
993 wpa_printf(MSG_DEBUG
, "EAP-pwd: Got a %d byte fragment",
998 * last fragment won't have the M bit set (but we're obviously
999 * buffering fragments so that's how we know it's the last)
1001 if (data
->in_frag_pos
) {
1002 pos
= wpabuf_head_u8(data
->inbuf
);
1003 len
= data
->in_frag_pos
;
1004 wpa_printf(MSG_DEBUG
, "EAP-pwd: Last fragment, %d bytes",
1007 switch (EAP_PWD_GET_EXCHANGE(lm_exch
)) {
1008 case EAP_PWD_OPCODE_ID_EXCH
:
1009 eap_pwd_process_id_resp(sm
, data
, pos
, len
);
1011 case EAP_PWD_OPCODE_COMMIT_EXCH
:
1012 eap_pwd_process_commit_resp(sm
, data
, pos
, len
);
1014 case EAP_PWD_OPCODE_CONFIRM_EXCH
:
1015 eap_pwd_process_confirm_resp(sm
, data
, pos
, len
);
1019 * if we had been buffering fragments, here's a great place
1022 if (data
->in_frag_pos
) {
1023 wpabuf_free(data
->inbuf
);
1025 data
->in_frag_pos
= 0;
1030 static u8
* eap_pwd_getkey(struct eap_sm
*sm
, void *priv
, size_t *len
)
1032 struct eap_pwd_data
*data
= priv
;
1035 if (data
->state
!= SUCCESS
)
1038 key
= os_memdup(data
->msk
, EAP_MSK_LEN
);
1048 static u8
* eap_pwd_get_emsk(struct eap_sm
*sm
, void *priv
, size_t *len
)
1050 struct eap_pwd_data
*data
= priv
;
1053 if (data
->state
!= SUCCESS
)
1056 key
= os_memdup(data
->emsk
, EAP_EMSK_LEN
);
1060 *len
= EAP_EMSK_LEN
;
1066 static Boolean
eap_pwd_is_success(struct eap_sm
*sm
, void *priv
)
1068 struct eap_pwd_data
*data
= priv
;
1069 return data
->state
== SUCCESS
;
1073 static Boolean
eap_pwd_is_done(struct eap_sm
*sm
, void *priv
)
1075 struct eap_pwd_data
*data
= priv
;
1076 return (data
->state
== SUCCESS
) || (data
->state
== FAILURE
);
1080 static u8
* eap_pwd_get_session_id(struct eap_sm
*sm
, void *priv
, size_t *len
)
1082 struct eap_pwd_data
*data
= priv
;
1085 if (data
->state
!= SUCCESS
)
1088 id
= os_memdup(data
->session_id
, 1 + SHA256_MAC_LEN
);
1092 *len
= 1 + SHA256_MAC_LEN
;
1098 int eap_server_pwd_register(void)
1100 struct eap_method
*eap
;
1106 (void) gettimeofday(&tp
, &tz
);
1107 sr
^= (tp
.tv_sec
^ tp
.tv_usec
);
1110 eap
= eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION
,
1111 EAP_VENDOR_IETF
, EAP_TYPE_PWD
,
1116 eap
->init
= eap_pwd_init
;
1117 eap
->reset
= eap_pwd_reset
;
1118 eap
->buildReq
= eap_pwd_build_req
;
1119 eap
->check
= eap_pwd_check
;
1120 eap
->process
= eap_pwd_process
;
1121 eap
->isDone
= eap_pwd_is_done
;
1122 eap
->getKey
= eap_pwd_getkey
;
1123 eap
->get_emsk
= eap_pwd_get_emsk
;
1124 eap
->isSuccess
= eap_pwd_is_success
;
1125 eap
->getSessionId
= eap_pwd_get_session_id
;
1127 return eap_server_method_register(eap
);