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