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