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