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