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