eap_pwd.c revision 21de214b4ba4271ca20843f3b8fba9f1501b2a89
1/*
2 * EAP peer method: EAP-pwd (RFC 5931)
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_peer/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,
20		SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE
21	} state;
22	u8 *id_peer;
23	size_t id_peer_len;
24	u8 *id_server;
25	size_t id_server_len;
26	u8 *password;
27	size_t password_len;
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 *server_scalar;
40	BIGNUM *my_scalar;
41	EC_POINT *my_element;
42	EC_POINT *server_element;
43
44	u8 msk[EAP_MSK_LEN];
45	u8 emsk[EAP_EMSK_LEN];
46
47	BN_CTX *bnctx;
48};
49
50
51#ifndef CONFIG_NO_STDOUT_DEBUG
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_ON_FRAG_COMPLETION:
62		return "SUCCESS_ON_FRAG_COMPLETION";
63        case SUCCESS:
64		return "SUCCESS";
65        case FAILURE:
66		return "FAILURE";
67        default:
68		return "PWD-UNK";
69	}
70}
71#endif  /* CONFIG_NO_STDOUT_DEBUG */
72
73
74static void eap_pwd_state(struct eap_pwd_data *data, int state)
75{
76	wpa_printf(MSG_DEBUG, "EAP-PWD: %s -> %s",
77		   eap_pwd_state_txt(data->state), eap_pwd_state_txt(state));
78	data->state = state;
79}
80
81
82static void * eap_pwd_init(struct eap_sm *sm)
83{
84	struct eap_pwd_data *data;
85	const u8 *identity, *password;
86	size_t identity_len, password_len;
87	int fragment_size;
88
89	password = eap_get_config_password(sm, &password_len);
90	if (password == NULL) {
91		wpa_printf(MSG_INFO, "EAP-PWD: No password configured!");
92		return NULL;
93	}
94
95	identity = eap_get_config_identity(sm, &identity_len);
96	if (identity == NULL) {
97		wpa_printf(MSG_INFO, "EAP-PWD: No identity configured!");
98		return NULL;
99	}
100
101	if ((data = os_zalloc(sizeof(*data))) == NULL) {
102		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation data fail");
103		return NULL;
104	}
105
106	if ((data->bnctx = BN_CTX_new()) == NULL) {
107		wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
108		os_free(data);
109		return NULL;
110	}
111
112	if ((data->id_peer = os_malloc(identity_len)) == NULL) {
113		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
114		BN_CTX_free(data->bnctx);
115		os_free(data);
116		return NULL;
117	}
118
119	os_memcpy(data->id_peer, identity, identity_len);
120	data->id_peer_len = identity_len;
121
122	if ((data->password = os_malloc(password_len)) == NULL) {
123		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
124		BN_CTX_free(data->bnctx);
125		os_free(data->id_peer);
126		os_free(data);
127		return NULL;
128	}
129	os_memcpy(data->password, password, password_len);
130	data->password_len = password_len;
131
132	data->out_frag_pos = data->in_frag_pos = 0;
133	data->inbuf = data->outbuf = NULL;
134	fragment_size = eap_get_config_fragment_size(sm);
135	if (fragment_size <= 0)
136		data->mtu = 1020; /* default from RFC 5931 */
137	else
138		data->mtu = fragment_size;
139
140	data->state = PWD_ID_Req;
141
142	return data;
143}
144
145
146static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
147{
148	struct eap_pwd_data *data = priv;
149
150	BN_free(data->private_value);
151	BN_free(data->server_scalar);
152	BN_free(data->my_scalar);
153	BN_free(data->k);
154	BN_CTX_free(data->bnctx);
155	EC_POINT_free(data->my_element);
156	EC_POINT_free(data->server_element);
157	os_free(data->id_peer);
158	os_free(data->id_server);
159	os_free(data->password);
160	if (data->grp) {
161		EC_GROUP_free(data->grp->group);
162		EC_POINT_free(data->grp->pwe);
163		BN_free(data->grp->order);
164		BN_free(data->grp->prime);
165		os_free(data->grp);
166	}
167	wpabuf_free(data->inbuf);
168	wpabuf_free(data->outbuf);
169	os_free(data);
170}
171
172
173static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
174{
175	struct eap_pwd_data *data = priv;
176	u8 *key;
177
178	if (data->state != SUCCESS)
179		return NULL;
180
181	key = os_malloc(EAP_MSK_LEN);
182	if (key == NULL)
183		return NULL;
184
185	os_memcpy(key, data->msk, EAP_MSK_LEN);
186	*len = EAP_MSK_LEN;
187
188	return key;
189}
190
191
192static void
193eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
194			    struct eap_method_ret *ret,
195			    const struct wpabuf *reqData,
196			    const u8 *payload, size_t payload_len)
197{
198	struct eap_pwd_id *id;
199
200	if (data->state != PWD_ID_Req) {
201		ret->ignore = TRUE;
202		eap_pwd_state(data, FAILURE);
203		return;
204	}
205
206	if (payload_len < sizeof(struct eap_pwd_id)) {
207		ret->ignore = TRUE;
208		eap_pwd_state(data, FAILURE);
209		return;
210	}
211
212	id = (struct eap_pwd_id *) payload;
213	data->group_num = be_to_host16(id->group_num);
214	if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
215	    (id->prf != EAP_PWD_DEFAULT_PRF)) {
216		ret->ignore = TRUE;
217		eap_pwd_state(data, FAILURE);
218		return;
219	}
220
221	wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d",
222		   data->group_num);
223
224	data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id));
225	if (data->id_server == NULL) {
226		wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail");
227		eap_pwd_state(data, FAILURE);
228		return;
229	}
230	data->id_server_len = payload_len - sizeof(struct eap_pwd_id);
231	os_memcpy(data->id_server, id->identity, data->id_server_len);
232	wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
233			  data->id_server, data->id_server_len);
234
235	if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) ==
236	    NULL) {
237		wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
238			   "group");
239		eap_pwd_state(data, FAILURE);
240		return;
241	}
242
243	/* compute PWE */
244	if (compute_password_element(data->grp, data->group_num,
245				     data->password, data->password_len,
246				     data->id_server, data->id_server_len,
247				     data->id_peer, data->id_peer_len,
248				     id->token)) {
249		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
250		eap_pwd_state(data, FAILURE);
251		return;
252	}
253
254	wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...",
255		   BN_num_bits(data->grp->prime));
256
257	data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) +
258				    data->id_peer_len);
259	if (data->outbuf == NULL) {
260		eap_pwd_state(data, FAILURE);
261		return;
262	}
263	wpabuf_put_be16(data->outbuf, data->group_num);
264	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
265	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
266	wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
267	wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
268	wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
269
270	eap_pwd_state(data, PWD_Commit_Req);
271}
272
273
274static void
275eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
276				struct eap_method_ret *ret,
277				const struct wpabuf *reqData,
278				const u8 *payload, size_t payload_len)
279{
280	EC_POINT *K = NULL, *point = NULL;
281	BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
282	u16 offset;
283	u8 *ptr, *scalar = NULL, *element = NULL;
284
285	if (((data->private_value = BN_new()) == NULL) ||
286	    ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
287	    ((cofactor = BN_new()) == NULL) ||
288	    ((data->my_scalar = BN_new()) == NULL) ||
289	    ((mask = BN_new()) == NULL)) {
290		wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
291		goto fin;
292	}
293
294	if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) {
295		wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
296			   "for curve");
297		goto fin;
298	}
299
300	BN_rand_range(data->private_value, data->grp->order);
301	BN_rand_range(mask, data->grp->order);
302	BN_add(data->my_scalar, data->private_value, mask);
303	BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
304	       data->bnctx);
305
306	if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
307			  data->grp->pwe, mask, data->bnctx)) {
308		wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation "
309			   "fail");
310		eap_pwd_state(data, FAILURE);
311		goto fin;
312	}
313
314	if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx))
315	{
316		wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
317		goto fin;
318	}
319	BN_free(mask);
320
321	if (((x = BN_new()) == NULL) ||
322	    ((y = BN_new()) == NULL)) {
323		wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail");
324		goto fin;
325	}
326
327	/* process the request */
328	if (((data->server_scalar = BN_new()) == NULL) ||
329	    ((data->k = BN_new()) == NULL) ||
330	    ((K = EC_POINT_new(data->grp->group)) == NULL) ||
331	    ((point = EC_POINT_new(data->grp->group)) == NULL) ||
332	    ((data->server_element = EC_POINT_new(data->grp->group)) == NULL))
333	{
334		wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
335			   "fail");
336		goto fin;
337	}
338
339	/* element, x then y, followed by scalar */
340	ptr = (u8 *) payload;
341	BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x);
342	ptr += BN_num_bytes(data->grp->prime);
343	BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y);
344	ptr += BN_num_bytes(data->grp->prime);
345	BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar);
346	if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group,
347						 data->server_element, x, y,
348						 data->bnctx)) {
349		wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
350			   "fail");
351		goto fin;
352	}
353
354	/* check to ensure server's element is not in a small sub-group */
355	if (BN_cmp(cofactor, BN_value_one())) {
356		if (!EC_POINT_mul(data->grp->group, point, NULL,
357				  data->server_element, cofactor, NULL)) {
358			wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
359				   "server element by order!\n");
360			goto fin;
361		}
362		if (EC_POINT_is_at_infinity(data->grp->group, point)) {
363			wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
364				   "is at infinity!\n");
365			goto fin;
366		}
367	}
368
369	/* compute the shared key, k */
370	if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe,
371			   data->server_scalar, data->bnctx)) ||
372	    (!EC_POINT_add(data->grp->group, K, K, data->server_element,
373			   data->bnctx)) ||
374	    (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value,
375			   data->bnctx))) {
376		wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key "
377			   "fail");
378		goto fin;
379	}
380
381	/* ensure that the shared key isn't in a small sub-group */
382	if (BN_cmp(cofactor, BN_value_one())) {
383		if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor,
384				  NULL)) {
385			wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
386				   "shared key point by order");
387			goto fin;
388		}
389	}
390
391	/*
392	 * This check is strictly speaking just for the case above where
393	 * co-factor > 1 but it was suggested that even though this is probably
394	 * never going to happen it is a simple and safe check "just to be
395	 * sure" so let's be safe.
396	 */
397	if (EC_POINT_is_at_infinity(data->grp->group, K)) {
398		wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at "
399			   "infinity!\n");
400		goto fin;
401	}
402
403	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k,
404						 NULL, data->bnctx)) {
405		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract "
406			   "shared secret from point");
407		goto fin;
408	}
409
410	/* now do the response */
411	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
412						 data->my_element, x, y,
413						 data->bnctx)) {
414		wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail");
415		goto fin;
416	}
417
418	if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) ||
419	    ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) ==
420	     NULL)) {
421		wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
422		goto fin;
423	}
424
425	/*
426	 * bignums occupy as little memory as possible so one that is
427	 * sufficiently smaller than the prime or order might need pre-pending
428	 * with zeros.
429	 */
430	os_memset(scalar, 0, BN_num_bytes(data->grp->order));
431	os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2);
432	offset = BN_num_bytes(data->grp->order) -
433		BN_num_bytes(data->my_scalar);
434	BN_bn2bin(data->my_scalar, scalar + offset);
435
436	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
437	BN_bn2bin(x, element + offset);
438	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
439	BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset);
440
441	data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) +
442				    2 * BN_num_bytes(data->grp->prime));
443	if (data->outbuf == NULL)
444		goto fin;
445
446	/* we send the element as (x,y) follwed by the scalar */
447	wpabuf_put_data(data->outbuf, element,
448			2 * BN_num_bytes(data->grp->prime));
449	wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order));
450
451fin:
452	os_free(scalar);
453	os_free(element);
454	BN_free(x);
455	BN_free(y);
456	BN_free(cofactor);
457	EC_POINT_free(K);
458	EC_POINT_free(point);
459	if (data->outbuf == NULL)
460		eap_pwd_state(data, FAILURE);
461	else
462		eap_pwd_state(data, PWD_Confirm_Req);
463}
464
465
466static void
467eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
468				 struct eap_method_ret *ret,
469				 const struct wpabuf *reqData,
470				 const u8 *payload, size_t payload_len)
471{
472	BIGNUM *x = NULL, *y = NULL;
473	struct crypto_hash *hash;
474	u32 cs;
475	u16 grp;
476	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
477	int offset;
478
479	/*
480	 * first build up the ciphersuite which is group | random_function |
481	 *	prf
482	 */
483	grp = htons(data->group_num);
484	ptr = (u8 *) &cs;
485	os_memcpy(ptr, &grp, sizeof(u16));
486	ptr += sizeof(u16);
487	*ptr = EAP_PWD_DEFAULT_RAND_FUNC;
488	ptr += sizeof(u8);
489	*ptr = EAP_PWD_DEFAULT_PRF;
490
491	/* each component of the cruft will be at most as big as the prime */
492	if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) ||
493	    ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) {
494		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation "
495			   "fail");
496		goto fin;
497	}
498
499	/*
500	 * server's commit is H(k | server_element | server_scalar |
501	 *			peer_element | peer_scalar | ciphersuite)
502	 */
503	hash = eap_pwd_h_init();
504	if (hash == NULL)
505		goto fin;
506
507	/*
508	 * zero the memory each time because this is mod prime math and some
509	 * value may start with a few zeros and the previous one did not.
510	 */
511	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
512	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
513	BN_bn2bin(data->k, cruft + offset);
514	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
515
516	/* server element: x, y */
517	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
518						 data->server_element, x, y,
519						 data->bnctx)) {
520		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
521			   "assignment fail");
522		goto fin;
523	}
524	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
525	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
526	BN_bn2bin(x, cruft + offset);
527	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
528	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
529	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
530	BN_bn2bin(y, cruft + offset);
531	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
532
533	/* server scalar */
534	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
535	offset = BN_num_bytes(data->grp->order) -
536		BN_num_bytes(data->server_scalar);
537	BN_bn2bin(data->server_scalar, cruft + offset);
538	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
539
540	/* my element: x, y */
541	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
542						 data->my_element, x, y,
543						 data->bnctx)) {
544		wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point "
545			   "assignment fail");
546		goto fin;
547	}
548
549	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
550	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
551	BN_bn2bin(x, cruft + offset);
552	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
553	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
554	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
555	BN_bn2bin(y, cruft + offset);
556	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
557
558	/* my scalar */
559	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
560	offset = BN_num_bytes(data->grp->order) -
561		BN_num_bytes(data->my_scalar);
562	BN_bn2bin(data->my_scalar, cruft + offset);
563	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
564
565	/* the ciphersuite */
566	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
567
568	/* random function fin */
569	eap_pwd_h_final(hash, conf);
570
571	ptr = (u8 *) payload;
572	if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
573		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
574		goto fin;
575	}
576
577	wpa_printf(MSG_DEBUG, "EAP-pwd (peer): confirm verified");
578
579	/*
580	 * compute confirm:
581	 *  H(k | peer_element | peer_scalar | server_element | server_scalar |
582	 *    ciphersuite)
583	 */
584	hash = eap_pwd_h_init();
585	if (hash == NULL)
586		goto fin;
587
588	/* k */
589	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
590	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k);
591	BN_bn2bin(data->k, cruft + offset);
592	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
593
594	/* my element */
595	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
596						 data->my_element, x, y,
597						 data->bnctx)) {
598		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
599			   "assignment fail");
600		goto fin;
601	}
602	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
603	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
604	BN_bn2bin(x, cruft + offset);
605	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
606	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
607	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
608	BN_bn2bin(y, cruft + offset);
609	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
610
611	/* my scalar */
612	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
613	offset = BN_num_bytes(data->grp->order) -
614		BN_num_bytes(data->my_scalar);
615	BN_bn2bin(data->my_scalar, cruft + offset);
616	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
617
618	/* server element: x, y */
619	if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group,
620						 data->server_element, x, y,
621						 data->bnctx)) {
622		wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point "
623			   "assignment fail");
624		goto fin;
625	}
626	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
627	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x);
628	BN_bn2bin(x, cruft + offset);
629	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
630	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
631	offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y);
632	BN_bn2bin(y, cruft + offset);
633	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime));
634
635	/* server scalar */
636	os_memset(cruft, 0, BN_num_bytes(data->grp->prime));
637	offset = BN_num_bytes(data->grp->order) -
638		BN_num_bytes(data->server_scalar);
639	BN_bn2bin(data->server_scalar, cruft + offset);
640	eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order));
641
642	/* the ciphersuite */
643	eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32));
644
645	/* all done */
646	eap_pwd_h_final(hash, conf);
647
648	if (compute_keys(data->grp, data->bnctx, data->k,
649			 data->my_scalar, data->server_scalar, conf, ptr,
650			 &cs, data->msk, data->emsk) < 0) {
651		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
652			   "EMSK");
653		goto fin;
654	}
655
656	data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
657	if (data->outbuf == NULL)
658		goto fin;
659
660	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
661
662fin:
663	os_free(cruft);
664	BN_free(x);
665	BN_free(y);
666	if (data->outbuf == NULL) {
667		ret->methodState = METHOD_DONE;
668		ret->decision = DECISION_FAIL;
669		eap_pwd_state(data, FAILURE);
670	} else {
671		eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
672	}
673}
674
675
676static struct wpabuf *
677eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
678		const struct wpabuf *reqData)
679{
680	struct eap_pwd_data *data = priv;
681	struct wpabuf *resp = NULL;
682	const u8 *pos, *buf;
683	size_t len;
684	u16 tot_len = 0;
685	u8 lm_exch;
686
687	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PWD, reqData, &len);
688	if ((pos == NULL) || (len < 1)) {
689		wpa_printf(MSG_DEBUG, "EAP-pwd: Got a frame but pos is %s and "
690			   "len is %d",
691			   pos == NULL ? "NULL" : "not NULL", (int) len);
692		ret->ignore = TRUE;
693		return NULL;
694	}
695
696	ret->ignore = FALSE;
697	ret->methodState = METHOD_MAY_CONT;
698	ret->decision = DECISION_FAIL;
699	ret->allowNotifications = FALSE;
700
701	lm_exch = *pos;
702	pos++;                  /* skip over the bits and the exch */
703	len--;
704
705	/*
706	 * we're fragmenting so send out the next fragment
707	 */
708	if (data->out_frag_pos) {
709		/*
710		 * this should be an ACK
711		 */
712		if (len)
713			wpa_printf(MSG_INFO, "Bad Response! Fragmenting but "
714				   "not an ACK");
715
716		wpa_printf(MSG_DEBUG, "EAP-pwd: Got an ACK for a fragment");
717		/*
718		 * check if there are going to be more fragments
719		 */
720		len = wpabuf_len(data->outbuf) - data->out_frag_pos;
721		if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
722			len = data->mtu - EAP_PWD_HDR_SIZE;
723			EAP_PWD_SET_MORE_BIT(lm_exch);
724		}
725		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
726				     EAP_PWD_HDR_SIZE + len,
727				     EAP_CODE_RESPONSE, eap_get_id(reqData));
728		if (resp == NULL) {
729			wpa_printf(MSG_INFO, "Unable to allocate memory for "
730				   "next fragment!");
731			return NULL;
732		}
733		wpabuf_put_u8(resp, lm_exch);
734		buf = wpabuf_head_u8(data->outbuf);
735		wpabuf_put_data(resp, buf + data->out_frag_pos, len);
736		data->out_frag_pos += len;
737		/*
738		 * this is the last fragment so get rid of the out buffer
739		 */
740		if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
741			wpabuf_free(data->outbuf);
742			data->outbuf = NULL;
743			data->out_frag_pos = 0;
744		}
745		wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes",
746			   data->out_frag_pos == 0 ? "last" : "next",
747			   (int) len);
748		if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
749			ret->methodState = METHOD_DONE;
750			ret->decision = DECISION_UNCOND_SUCC;
751			eap_pwd_state(data, SUCCESS);
752		}
753		return resp;
754	}
755
756	/*
757	 * see if this is a fragment that needs buffering
758	 *
759	 * if it's the first fragment there'll be a length field
760	 */
761	if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
762		tot_len = WPA_GET_BE16(pos);
763		wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose "
764			   "total length = %d", tot_len);
765		data->inbuf = wpabuf_alloc(tot_len);
766		if (data->inbuf == NULL) {
767			wpa_printf(MSG_INFO, "Out of memory to buffer "
768				   "fragments!");
769			return NULL;
770		}
771		pos += sizeof(u16);
772		len -= sizeof(u16);
773	}
774	/*
775	 * buffer and ACK the fragment
776	 */
777	if (EAP_PWD_GET_MORE_BIT(lm_exch)) {
778		data->in_frag_pos += len;
779		if (data->in_frag_pos > wpabuf_size(data->inbuf)) {
780			wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack "
781				   "detected (%d vs. %d)!",
782				   (int) data->in_frag_pos,
783				   (int) wpabuf_len(data->inbuf));
784			wpabuf_free(data->inbuf);
785			data->inbuf = NULL;
786			data->in_frag_pos = 0;
787			return NULL;
788		}
789		wpabuf_put_data(data->inbuf, pos, len);
790
791		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
792				     EAP_PWD_HDR_SIZE,
793				     EAP_CODE_RESPONSE, eap_get_id(reqData));
794		if (resp != NULL)
795			wpabuf_put_u8(resp, (EAP_PWD_GET_EXCHANGE(lm_exch)));
796		wpa_printf(MSG_DEBUG, "EAP-pwd: ACKing a %d byte fragment",
797			   (int) len);
798		return resp;
799	}
800	/*
801	 * we're buffering and this is the last fragment
802	 */
803	if (data->in_frag_pos) {
804		wpabuf_put_data(data->inbuf, pos, len);
805		wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
806			   (int) len);
807		data->in_frag_pos += len;
808		pos = wpabuf_head_u8(data->inbuf);
809		len = data->in_frag_pos;
810	}
811	wpa_printf(MSG_DEBUG, "EAP-pwd: processing frame: exch %d, len %d",
812		   EAP_PWD_GET_EXCHANGE(lm_exch), (int) len);
813
814	switch (EAP_PWD_GET_EXCHANGE(lm_exch)) {
815	case EAP_PWD_OPCODE_ID_EXCH:
816		eap_pwd_perform_id_exchange(sm, data, ret, reqData,
817					    pos, len);
818		break;
819	case EAP_PWD_OPCODE_COMMIT_EXCH:
820		eap_pwd_perform_commit_exchange(sm, data, ret, reqData,
821						pos, len);
822		break;
823	case EAP_PWD_OPCODE_CONFIRM_EXCH:
824		eap_pwd_perform_confirm_exchange(sm, data, ret, reqData,
825						 pos, len);
826		break;
827	default:
828		wpa_printf(MSG_INFO, "EAP-pwd: Ignoring message with unknown "
829			   "opcode %d", lm_exch);
830		break;
831	}
832	/*
833	 * if we buffered the just processed input now's the time to free it
834	 */
835	if (data->in_frag_pos) {
836		wpabuf_free(data->inbuf);
837		data->inbuf = NULL;
838		data->in_frag_pos = 0;
839	}
840
841	if (data->outbuf == NULL)
842		return NULL;        /* generic failure */
843
844	/*
845	 * we have output! Do we need to fragment it?
846	 */
847	len = wpabuf_len(data->outbuf);
848	if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
849		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu,
850				     EAP_CODE_RESPONSE, eap_get_id(reqData));
851		/*
852		 * if so it's the first so include a length field
853		 */
854		EAP_PWD_SET_LENGTH_BIT(lm_exch);
855		EAP_PWD_SET_MORE_BIT(lm_exch);
856		tot_len = len;
857		/*
858		 * keep the packet at the MTU
859		 */
860		len = data->mtu - EAP_PWD_HDR_SIZE - sizeof(u16);
861		wpa_printf(MSG_DEBUG, "EAP-pwd: Fragmenting output, total "
862			   "length = %d", tot_len);
863	} else {
864		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD,
865				     EAP_PWD_HDR_SIZE + len,
866				     EAP_CODE_RESPONSE, eap_get_id(reqData));
867	}
868	if (resp == NULL)
869		return NULL;
870
871	wpabuf_put_u8(resp, lm_exch);
872	if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
873		wpabuf_put_be16(resp, tot_len);
874		data->out_frag_pos += len;
875	}
876	buf = wpabuf_head_u8(data->outbuf);
877	wpabuf_put_data(resp, buf, len);
878	/*
879	 * if we're not fragmenting then there's no need to carry this around
880	 */
881	if (data->out_frag_pos == 0) {
882		wpabuf_free(data->outbuf);
883		data->outbuf = NULL;
884		data->out_frag_pos = 0;
885		if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
886			ret->methodState = METHOD_DONE;
887			ret->decision = DECISION_UNCOND_SUCC;
888			eap_pwd_state(data, SUCCESS);
889		}
890	}
891
892	return resp;
893}
894
895
896static Boolean eap_pwd_key_available(struct eap_sm *sm, void *priv)
897{
898	struct eap_pwd_data *data = priv;
899	return data->state == SUCCESS;
900}
901
902
903static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
904{
905	struct eap_pwd_data *data = priv;
906	u8 *key;
907
908	if (data->state != SUCCESS)
909		return NULL;
910
911	if ((key = os_malloc(EAP_EMSK_LEN)) == NULL)
912		return NULL;
913
914	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
915	*len = EAP_EMSK_LEN;
916
917	return key;
918}
919
920
921int eap_peer_pwd_register(void)
922{
923	struct eap_method *eap;
924	int ret;
925
926	EVP_add_digest(EVP_sha256());
927	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
928				    EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
929	if (eap == NULL)
930		return -1;
931
932	eap->init = eap_pwd_init;
933	eap->deinit = eap_pwd_deinit;
934	eap->process = eap_pwd_process;
935	eap->isKeyAvailable = eap_pwd_key_available;
936	eap->getKey = eap_pwd_getkey;
937	eap->get_emsk = eap_pwd_get_emsk;
938
939	ret = eap_peer_method_register(eap);
940	if (ret)
941		eap_peer_method_free(eap);
942	return ret;
943}
944