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