xref: /freebsd/contrib/wpa/src/eap_server/eap_server_sake.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
1e28a4053SRui Paulo /*
2e28a4053SRui Paulo  * hostapd / EAP-SAKE (RFC 4763) server
3e28a4053SRui Paulo  * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7e28a4053SRui Paulo  */
8e28a4053SRui Paulo 
9e28a4053SRui Paulo #include "includes.h"
10e28a4053SRui Paulo 
11e28a4053SRui Paulo #include "common.h"
12f05cddf9SRui Paulo #include "crypto/random.h"
13e28a4053SRui Paulo #include "eap_server/eap_i.h"
14e28a4053SRui Paulo #include "eap_common/eap_sake_common.h"
15e28a4053SRui Paulo 
16e28a4053SRui Paulo 
17e28a4053SRui Paulo struct eap_sake_data {
18e28a4053SRui Paulo 	enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
19e28a4053SRui Paulo 	u8 rand_s[EAP_SAKE_RAND_LEN];
20e28a4053SRui Paulo 	u8 rand_p[EAP_SAKE_RAND_LEN];
21e28a4053SRui Paulo 	struct {
22e28a4053SRui Paulo 		u8 auth[EAP_SAKE_TEK_AUTH_LEN];
23e28a4053SRui Paulo 		u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
24e28a4053SRui Paulo 	} tek;
25e28a4053SRui Paulo 	u8 msk[EAP_MSK_LEN];
26e28a4053SRui Paulo 	u8 emsk[EAP_EMSK_LEN];
27e28a4053SRui Paulo 	u8 session_id;
28e28a4053SRui Paulo 	u8 *peerid;
29e28a4053SRui Paulo 	size_t peerid_len;
30e28a4053SRui Paulo };
31e28a4053SRui Paulo 
32e28a4053SRui Paulo 
eap_sake_state_txt(int state)33e28a4053SRui Paulo static const char * eap_sake_state_txt(int state)
34e28a4053SRui Paulo {
35e28a4053SRui Paulo 	switch (state) {
36e28a4053SRui Paulo 	case IDENTITY:
37e28a4053SRui Paulo 		return "IDENTITY";
38e28a4053SRui Paulo 	case CHALLENGE:
39e28a4053SRui Paulo 		return "CHALLENGE";
40e28a4053SRui Paulo 	case CONFIRM:
41e28a4053SRui Paulo 		return "CONFIRM";
42e28a4053SRui Paulo 	case SUCCESS:
43e28a4053SRui Paulo 		return "SUCCESS";
44e28a4053SRui Paulo 	case FAILURE:
45e28a4053SRui Paulo 		return "FAILURE";
46e28a4053SRui Paulo 	default:
47e28a4053SRui Paulo 		return "?";
48e28a4053SRui Paulo 	}
49e28a4053SRui Paulo }
50e28a4053SRui Paulo 
51e28a4053SRui Paulo 
eap_sake_state(struct eap_sake_data * data,int state)52e28a4053SRui Paulo static void eap_sake_state(struct eap_sake_data *data, int state)
53e28a4053SRui Paulo {
54e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
55e28a4053SRui Paulo 		   eap_sake_state_txt(data->state),
56e28a4053SRui Paulo 		   eap_sake_state_txt(state));
57e28a4053SRui Paulo 	data->state = state;
58e28a4053SRui Paulo }
59e28a4053SRui Paulo 
60e28a4053SRui Paulo 
eap_sake_init(struct eap_sm * sm)61e28a4053SRui Paulo static void * eap_sake_init(struct eap_sm *sm)
62e28a4053SRui Paulo {
63e28a4053SRui Paulo 	struct eap_sake_data *data;
64e28a4053SRui Paulo 
65e28a4053SRui Paulo 	data = os_zalloc(sizeof(*data));
66e28a4053SRui Paulo 	if (data == NULL)
67e28a4053SRui Paulo 		return NULL;
68e28a4053SRui Paulo 	data->state = CHALLENGE;
69e28a4053SRui Paulo 
70e28a4053SRui Paulo 	if (os_get_random(&data->session_id, 1)) {
71e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
72e28a4053SRui Paulo 		os_free(data);
73e28a4053SRui Paulo 		return NULL;
74e28a4053SRui Paulo 	}
75e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
76e28a4053SRui Paulo 		   data->session_id);
77e28a4053SRui Paulo 
78e28a4053SRui Paulo 	return data;
79e28a4053SRui Paulo }
80e28a4053SRui Paulo 
81e28a4053SRui Paulo 
eap_sake_reset(struct eap_sm * sm,void * priv)82e28a4053SRui Paulo static void eap_sake_reset(struct eap_sm *sm, void *priv)
83e28a4053SRui Paulo {
84e28a4053SRui Paulo 	struct eap_sake_data *data = priv;
85e28a4053SRui Paulo 	os_free(data->peerid);
865b9c547cSRui Paulo 	bin_clear_free(data, sizeof(*data));
87e28a4053SRui Paulo }
88e28a4053SRui Paulo 
89e28a4053SRui Paulo 
eap_sake_build_msg(struct eap_sake_data * data,u8 id,size_t length,u8 subtype)90e28a4053SRui Paulo static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
91e28a4053SRui Paulo 					  u8 id, size_t length, u8 subtype)
92e28a4053SRui Paulo {
93e28a4053SRui Paulo 	struct eap_sake_hdr *sake;
94e28a4053SRui Paulo 	struct wpabuf *msg;
95e28a4053SRui Paulo 	size_t plen;
96e28a4053SRui Paulo 
97e28a4053SRui Paulo 	plen = sizeof(struct eap_sake_hdr) + length;
98e28a4053SRui Paulo 
99e28a4053SRui Paulo 	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
100e28a4053SRui Paulo 			    EAP_CODE_REQUEST, id);
101e28a4053SRui Paulo 	if (msg == NULL) {
102e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
103e28a4053SRui Paulo 			   "request");
104e28a4053SRui Paulo 		return NULL;
105e28a4053SRui Paulo 	}
106e28a4053SRui Paulo 
107e28a4053SRui Paulo 	sake = wpabuf_put(msg, sizeof(*sake));
108e28a4053SRui Paulo 	sake->version = EAP_SAKE_VERSION;
109e28a4053SRui Paulo 	sake->session_id = data->session_id;
110e28a4053SRui Paulo 	sake->subtype = subtype;
111e28a4053SRui Paulo 
112e28a4053SRui Paulo 	return msg;
113e28a4053SRui Paulo }
114e28a4053SRui Paulo 
115e28a4053SRui Paulo 
eap_sake_build_identity(struct eap_sm * sm,struct eap_sake_data * data,u8 id)116e28a4053SRui Paulo static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm,
117e28a4053SRui Paulo 					       struct eap_sake_data *data,
118e28a4053SRui Paulo 					       u8 id)
119e28a4053SRui Paulo {
120e28a4053SRui Paulo 	struct wpabuf *msg;
121e28a4053SRui Paulo 	size_t plen;
122e28a4053SRui Paulo 
123e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
124e28a4053SRui Paulo 
125e28a4053SRui Paulo 	plen = 4;
126*c1d255d3SCy Schubert 	plen += 2 + sm->cfg->server_id_len;
127e28a4053SRui Paulo 	msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY);
128e28a4053SRui Paulo 	if (msg == NULL) {
129e28a4053SRui Paulo 		data->state = FAILURE;
130e28a4053SRui Paulo 		return NULL;
131e28a4053SRui Paulo 	}
132e28a4053SRui Paulo 
133e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ");
134e28a4053SRui Paulo 	eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2);
135e28a4053SRui Paulo 
136e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
137e28a4053SRui Paulo 	eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
138*c1d255d3SCy Schubert 			  sm->cfg->server_id, sm->cfg->server_id_len);
139e28a4053SRui Paulo 
140e28a4053SRui Paulo 	return msg;
141e28a4053SRui Paulo }
142e28a4053SRui Paulo 
143e28a4053SRui Paulo 
eap_sake_build_challenge(struct eap_sm * sm,struct eap_sake_data * data,u8 id)144e28a4053SRui Paulo static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
145e28a4053SRui Paulo 						struct eap_sake_data *data,
146e28a4053SRui Paulo 						u8 id)
147e28a4053SRui Paulo {
148e28a4053SRui Paulo 	struct wpabuf *msg;
149e28a4053SRui Paulo 	size_t plen;
150e28a4053SRui Paulo 
151e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
152e28a4053SRui Paulo 
153f05cddf9SRui Paulo 	if (random_get_bytes(data->rand_s, EAP_SAKE_RAND_LEN)) {
154e28a4053SRui Paulo 		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
155e28a4053SRui Paulo 		data->state = FAILURE;
156e28a4053SRui Paulo 		return NULL;
157e28a4053SRui Paulo 	}
158e28a4053SRui Paulo 	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
159e28a4053SRui Paulo 		    data->rand_s, EAP_SAKE_RAND_LEN);
160e28a4053SRui Paulo 
161*c1d255d3SCy Schubert 	plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->cfg->server_id_len;
162e28a4053SRui Paulo 	msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
163e28a4053SRui Paulo 	if (msg == NULL) {
164e28a4053SRui Paulo 		data->state = FAILURE;
165e28a4053SRui Paulo 		return NULL;
166e28a4053SRui Paulo 	}
167e28a4053SRui Paulo 
168e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S");
169e28a4053SRui Paulo 	eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S,
170e28a4053SRui Paulo 			  data->rand_s, EAP_SAKE_RAND_LEN);
171e28a4053SRui Paulo 
172e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
173e28a4053SRui Paulo 	eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
174*c1d255d3SCy Schubert 			  sm->cfg->server_id, sm->cfg->server_id_len);
175e28a4053SRui Paulo 
176e28a4053SRui Paulo 	return msg;
177e28a4053SRui Paulo }
178e28a4053SRui Paulo 
179e28a4053SRui Paulo 
eap_sake_build_confirm(struct eap_sm * sm,struct eap_sake_data * data,u8 id)180e28a4053SRui Paulo static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm,
181e28a4053SRui Paulo 					      struct eap_sake_data *data,
182e28a4053SRui Paulo 					      u8 id)
183e28a4053SRui Paulo {
184e28a4053SRui Paulo 	struct wpabuf *msg;
185e28a4053SRui Paulo 	u8 *mic;
186e28a4053SRui Paulo 
187e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm");
188e28a4053SRui Paulo 
189e28a4053SRui Paulo 	msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
190e28a4053SRui Paulo 				 EAP_SAKE_SUBTYPE_CONFIRM);
191e28a4053SRui Paulo 	if (msg == NULL) {
192e28a4053SRui Paulo 		data->state = FAILURE;
193e28a4053SRui Paulo 		return NULL;
194e28a4053SRui Paulo 	}
195e28a4053SRui Paulo 
196e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S");
197e28a4053SRui Paulo 	wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S);
198e28a4053SRui Paulo 	wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN);
199e28a4053SRui Paulo 	mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN);
200e28a4053SRui Paulo 	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
201*c1d255d3SCy Schubert 				 sm->cfg->server_id, sm->cfg->server_id_len,
202e28a4053SRui Paulo 				 data->peerid, data->peerid_len, 0,
203e28a4053SRui Paulo 				 wpabuf_head(msg), wpabuf_len(msg), mic, mic))
204e28a4053SRui Paulo 	{
205e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
206e28a4053SRui Paulo 		data->state = FAILURE;
2074bc52338SCy Schubert 		wpabuf_free(msg);
208e28a4053SRui Paulo 		return NULL;
209e28a4053SRui Paulo 	}
210e28a4053SRui Paulo 
211e28a4053SRui Paulo 	return msg;
212e28a4053SRui Paulo }
213e28a4053SRui Paulo 
214e28a4053SRui Paulo 
eap_sake_buildReq(struct eap_sm * sm,void * priv,u8 id)215e28a4053SRui Paulo static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id)
216e28a4053SRui Paulo {
217e28a4053SRui Paulo 	struct eap_sake_data *data = priv;
218e28a4053SRui Paulo 
219e28a4053SRui Paulo 	switch (data->state) {
220e28a4053SRui Paulo 	case IDENTITY:
221e28a4053SRui Paulo 		return eap_sake_build_identity(sm, data, id);
222e28a4053SRui Paulo 	case CHALLENGE:
223e28a4053SRui Paulo 		return eap_sake_build_challenge(sm, data, id);
224e28a4053SRui Paulo 	case CONFIRM:
225e28a4053SRui Paulo 		return eap_sake_build_confirm(sm, data, id);
226e28a4053SRui Paulo 	default:
227e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq",
228e28a4053SRui Paulo 			   data->state);
229e28a4053SRui Paulo 		break;
230e28a4053SRui Paulo 	}
231e28a4053SRui Paulo 	return NULL;
232e28a4053SRui Paulo }
233e28a4053SRui Paulo 
234e28a4053SRui Paulo 
eap_sake_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)235*c1d255d3SCy Schubert static bool eap_sake_check(struct eap_sm *sm, void *priv,
236e28a4053SRui Paulo 			   struct wpabuf *respData)
237e28a4053SRui Paulo {
238e28a4053SRui Paulo 	struct eap_sake_data *data = priv;
239e28a4053SRui Paulo 	struct eap_sake_hdr *resp;
240e28a4053SRui Paulo 	size_t len;
241e28a4053SRui Paulo 	u8 version, session_id, subtype;
242e28a4053SRui Paulo 	const u8 *pos;
243e28a4053SRui Paulo 
244e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
245e28a4053SRui Paulo 	if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
246e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame");
247*c1d255d3SCy Schubert 		return true;
248e28a4053SRui Paulo 	}
249e28a4053SRui Paulo 
250e28a4053SRui Paulo 	resp = (struct eap_sake_hdr *) pos;
251e28a4053SRui Paulo 	version = resp->version;
252e28a4053SRui Paulo 	session_id = resp->session_id;
253e28a4053SRui Paulo 	subtype = resp->subtype;
254e28a4053SRui Paulo 
255e28a4053SRui Paulo 	if (version != EAP_SAKE_VERSION) {
256e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version);
257*c1d255d3SCy Schubert 		return true;
258e28a4053SRui Paulo 	}
259e28a4053SRui Paulo 
260e28a4053SRui Paulo 	if (session_id != data->session_id) {
261e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
262e28a4053SRui Paulo 			   session_id, data->session_id);
263*c1d255d3SCy Schubert 		return true;
264e28a4053SRui Paulo 	}
265e28a4053SRui Paulo 
266e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype);
267e28a4053SRui Paulo 
268e28a4053SRui Paulo 	if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY)
269*c1d255d3SCy Schubert 		return false;
270e28a4053SRui Paulo 
271e28a4053SRui Paulo 	if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE)
272*c1d255d3SCy Schubert 		return false;
273e28a4053SRui Paulo 
274e28a4053SRui Paulo 	if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM)
275*c1d255d3SCy Schubert 		return false;
276e28a4053SRui Paulo 
277e28a4053SRui Paulo 	if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT)
278*c1d255d3SCy Schubert 		return false;
279e28a4053SRui Paulo 
280e28a4053SRui Paulo 	wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d",
281e28a4053SRui Paulo 		   subtype, data->state);
282e28a4053SRui Paulo 
283*c1d255d3SCy Schubert 	return true;
284e28a4053SRui Paulo }
285e28a4053SRui Paulo 
286e28a4053SRui Paulo 
eap_sake_process_identity(struct eap_sm * sm,struct eap_sake_data * data,const struct wpabuf * respData,const u8 * payload,size_t payloadlen)287e28a4053SRui Paulo static void eap_sake_process_identity(struct eap_sm *sm,
288e28a4053SRui Paulo 				      struct eap_sake_data *data,
289e28a4053SRui Paulo 				      const struct wpabuf *respData,
290e28a4053SRui Paulo 				      const u8 *payload, size_t payloadlen)
291e28a4053SRui Paulo {
292e28a4053SRui Paulo 	if (data->state != IDENTITY)
293e28a4053SRui Paulo 		return;
294e28a4053SRui Paulo 
295e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity");
296e28a4053SRui Paulo 	/* TODO: update identity and select new user data */
297e28a4053SRui Paulo 	eap_sake_state(data, CHALLENGE);
298e28a4053SRui Paulo }
299e28a4053SRui Paulo 
300e28a4053SRui Paulo 
eap_sake_process_challenge(struct eap_sm * sm,struct eap_sake_data * data,const struct wpabuf * respData,const u8 * payload,size_t payloadlen)301e28a4053SRui Paulo static void eap_sake_process_challenge(struct eap_sm *sm,
302e28a4053SRui Paulo 				       struct eap_sake_data *data,
303e28a4053SRui Paulo 				       const struct wpabuf *respData,
304e28a4053SRui Paulo 				       const u8 *payload, size_t payloadlen)
305e28a4053SRui Paulo {
306e28a4053SRui Paulo 	struct eap_sake_parse_attr attr;
307e28a4053SRui Paulo 	u8 mic_p[EAP_SAKE_MIC_LEN];
308e28a4053SRui Paulo 
309e28a4053SRui Paulo 	if (data->state != CHALLENGE)
310e28a4053SRui Paulo 		return;
311e28a4053SRui Paulo 
312e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge");
313e28a4053SRui Paulo 
314e28a4053SRui Paulo 	if (eap_sake_parse_attributes(payload, payloadlen, &attr))
315e28a4053SRui Paulo 		return;
316e28a4053SRui Paulo 
317e28a4053SRui Paulo 	if (!attr.rand_p || !attr.mic_p) {
318e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not "
319e28a4053SRui Paulo 			   "include AT_RAND_P or AT_MIC_P");
320e28a4053SRui Paulo 		return;
321e28a4053SRui Paulo 	}
322e28a4053SRui Paulo 
323e28a4053SRui Paulo 	os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN);
324e28a4053SRui Paulo 
325e28a4053SRui Paulo 	os_free(data->peerid);
326e28a4053SRui Paulo 	data->peerid = NULL;
327e28a4053SRui Paulo 	data->peerid_len = 0;
328e28a4053SRui Paulo 	if (attr.peerid) {
32985732ac8SCy Schubert 		data->peerid = os_memdup(attr.peerid, attr.peerid_len);
330e28a4053SRui Paulo 		if (data->peerid == NULL)
331e28a4053SRui Paulo 			return;
332e28a4053SRui Paulo 		data->peerid_len = attr.peerid_len;
333e28a4053SRui Paulo 	}
334e28a4053SRui Paulo 
335e28a4053SRui Paulo 	if (sm->user == NULL || sm->user->password == NULL ||
336e28a4053SRui Paulo 	    sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
337e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with "
338e28a4053SRui Paulo 			   "%d-byte key not configured",
339e28a4053SRui Paulo 			   2 * EAP_SAKE_ROOT_SECRET_LEN);
340e28a4053SRui Paulo 		data->state = FAILURE;
341e28a4053SRui Paulo 		return;
342e28a4053SRui Paulo 	}
3434bc52338SCy Schubert 	if (eap_sake_derive_keys(sm->user->password,
344e28a4053SRui Paulo 				 sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
345e28a4053SRui Paulo 				 data->rand_s, data->rand_p,
3464bc52338SCy Schubert 				 (u8 *) &data->tek, data->msk,
3474bc52338SCy Schubert 				 data->emsk) < 0) {
3484bc52338SCy Schubert 		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys");
3494bc52338SCy Schubert 		data->state = FAILURE;
3504bc52338SCy Schubert 		return;
3514bc52338SCy Schubert 	}
352e28a4053SRui Paulo 
3534bc52338SCy Schubert 	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
354*c1d255d3SCy Schubert 				 sm->cfg->server_id, sm->cfg->server_id_len,
355e28a4053SRui Paulo 				 data->peerid, data->peerid_len, 1,
356e28a4053SRui Paulo 				 wpabuf_head(respData), wpabuf_len(respData),
3574bc52338SCy Schubert 				 attr.mic_p, mic_p) < 0) {
3584bc52338SCy Schubert 		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
3594bc52338SCy Schubert 		data->state = FAILURE;
3604bc52338SCy Schubert 		return;
3614bc52338SCy Schubert 	}
3625b9c547cSRui Paulo 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
363e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
364e28a4053SRui Paulo 		eap_sake_state(data, FAILURE);
365e28a4053SRui Paulo 		return;
366e28a4053SRui Paulo 	}
367e28a4053SRui Paulo 
368e28a4053SRui Paulo 	eap_sake_state(data, CONFIRM);
369e28a4053SRui Paulo }
370e28a4053SRui Paulo 
371e28a4053SRui Paulo 
eap_sake_process_confirm(struct eap_sm * sm,struct eap_sake_data * data,const struct wpabuf * respData,const u8 * payload,size_t payloadlen)372e28a4053SRui Paulo static void eap_sake_process_confirm(struct eap_sm *sm,
373e28a4053SRui Paulo 				     struct eap_sake_data *data,
374e28a4053SRui Paulo 				     const struct wpabuf *respData,
375e28a4053SRui Paulo 				     const u8 *payload, size_t payloadlen)
376e28a4053SRui Paulo {
377e28a4053SRui Paulo 	struct eap_sake_parse_attr attr;
378e28a4053SRui Paulo 	u8 mic_p[EAP_SAKE_MIC_LEN];
379e28a4053SRui Paulo 
380e28a4053SRui Paulo 	if (data->state != CONFIRM)
381e28a4053SRui Paulo 		return;
382e28a4053SRui Paulo 
383e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
384e28a4053SRui Paulo 
385e28a4053SRui Paulo 	if (eap_sake_parse_attributes(payload, payloadlen, &attr))
386e28a4053SRui Paulo 		return;
387e28a4053SRui Paulo 
388e28a4053SRui Paulo 	if (!attr.mic_p) {
389e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
390e28a4053SRui Paulo 			   "include AT_MIC_P");
391e28a4053SRui Paulo 		return;
392e28a4053SRui Paulo 	}
393e28a4053SRui Paulo 
3944bc52338SCy Schubert 	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
395*c1d255d3SCy Schubert 				 sm->cfg->server_id, sm->cfg->server_id_len,
396e28a4053SRui Paulo 				 data->peerid, data->peerid_len, 1,
397e28a4053SRui Paulo 				 wpabuf_head(respData), wpabuf_len(respData),
3984bc52338SCy Schubert 				 attr.mic_p, mic_p) < 0) {
3994bc52338SCy Schubert 		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
4004bc52338SCy Schubert 		return;
4014bc52338SCy Schubert 	}
4025b9c547cSRui Paulo 	if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
403e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
404e28a4053SRui Paulo 		eap_sake_state(data, FAILURE);
405e28a4053SRui Paulo 	} else
406e28a4053SRui Paulo 		eap_sake_state(data, SUCCESS);
407e28a4053SRui Paulo }
408e28a4053SRui Paulo 
409e28a4053SRui Paulo 
eap_sake_process_auth_reject(struct eap_sm * sm,struct eap_sake_data * data,const struct wpabuf * respData,const u8 * payload,size_t payloadlen)410e28a4053SRui Paulo static void eap_sake_process_auth_reject(struct eap_sm *sm,
411e28a4053SRui Paulo 					 struct eap_sake_data *data,
412e28a4053SRui Paulo 					 const struct wpabuf *respData,
413e28a4053SRui Paulo 					 const u8 *payload, size_t payloadlen)
414e28a4053SRui Paulo {
415e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
416e28a4053SRui Paulo 	eap_sake_state(data, FAILURE);
417e28a4053SRui Paulo }
418e28a4053SRui Paulo 
419e28a4053SRui Paulo 
eap_sake_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)420e28a4053SRui Paulo static void eap_sake_process(struct eap_sm *sm, void *priv,
421e28a4053SRui Paulo 			     struct wpabuf *respData)
422e28a4053SRui Paulo {
423e28a4053SRui Paulo 	struct eap_sake_data *data = priv;
424e28a4053SRui Paulo 	struct eap_sake_hdr *resp;
425e28a4053SRui Paulo 	u8 subtype;
426e28a4053SRui Paulo 	size_t len;
427e28a4053SRui Paulo 	const u8 *pos, *end;
428e28a4053SRui Paulo 
429e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
430e28a4053SRui Paulo 	if (pos == NULL || len < sizeof(struct eap_sake_hdr))
431e28a4053SRui Paulo 		return;
432e28a4053SRui Paulo 
433e28a4053SRui Paulo 	resp = (struct eap_sake_hdr *) pos;
434e28a4053SRui Paulo 	end = pos + len;
435e28a4053SRui Paulo 	subtype = resp->subtype;
436e28a4053SRui Paulo 	pos = (u8 *) (resp + 1);
437e28a4053SRui Paulo 
438e28a4053SRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
439e28a4053SRui Paulo 		    pos, end - pos);
440e28a4053SRui Paulo 
441e28a4053SRui Paulo 	switch (subtype) {
442e28a4053SRui Paulo 	case EAP_SAKE_SUBTYPE_IDENTITY:
443e28a4053SRui Paulo 		eap_sake_process_identity(sm, data, respData, pos, end - pos);
444e28a4053SRui Paulo 		break;
445e28a4053SRui Paulo 	case EAP_SAKE_SUBTYPE_CHALLENGE:
446e28a4053SRui Paulo 		eap_sake_process_challenge(sm, data, respData, pos, end - pos);
447e28a4053SRui Paulo 		break;
448e28a4053SRui Paulo 	case EAP_SAKE_SUBTYPE_CONFIRM:
449e28a4053SRui Paulo 		eap_sake_process_confirm(sm, data, respData, pos, end - pos);
450e28a4053SRui Paulo 		break;
451e28a4053SRui Paulo 	case EAP_SAKE_SUBTYPE_AUTH_REJECT:
452e28a4053SRui Paulo 		eap_sake_process_auth_reject(sm, data, respData, pos,
453e28a4053SRui Paulo 					     end - pos);
454e28a4053SRui Paulo 		break;
455e28a4053SRui Paulo 	}
456e28a4053SRui Paulo }
457e28a4053SRui Paulo 
458e28a4053SRui Paulo 
eap_sake_isDone(struct eap_sm * sm,void * priv)459*c1d255d3SCy Schubert static bool eap_sake_isDone(struct eap_sm *sm, void *priv)
460e28a4053SRui Paulo {
461e28a4053SRui Paulo 	struct eap_sake_data *data = priv;
462e28a4053SRui Paulo 	return data->state == SUCCESS || data->state == FAILURE;
463e28a4053SRui Paulo }
464e28a4053SRui Paulo 
465e28a4053SRui Paulo 
eap_sake_getKey(struct eap_sm * sm,void * priv,size_t * len)466e28a4053SRui Paulo static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
467e28a4053SRui Paulo {
468e28a4053SRui Paulo 	struct eap_sake_data *data = priv;
469e28a4053SRui Paulo 	u8 *key;
470e28a4053SRui Paulo 
471e28a4053SRui Paulo 	if (data->state != SUCCESS)
472e28a4053SRui Paulo 		return NULL;
473e28a4053SRui Paulo 
47485732ac8SCy Schubert 	key = os_memdup(data->msk, EAP_MSK_LEN);
475e28a4053SRui Paulo 	if (key == NULL)
476e28a4053SRui Paulo 		return NULL;
477e28a4053SRui Paulo 	*len = EAP_MSK_LEN;
478e28a4053SRui Paulo 
479e28a4053SRui Paulo 	return key;
480e28a4053SRui Paulo }
481e28a4053SRui Paulo 
482e28a4053SRui Paulo 
eap_sake_get_emsk(struct eap_sm * sm,void * priv,size_t * len)483e28a4053SRui Paulo static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
484e28a4053SRui Paulo {
485e28a4053SRui Paulo 	struct eap_sake_data *data = priv;
486e28a4053SRui Paulo 	u8 *key;
487e28a4053SRui Paulo 
488e28a4053SRui Paulo 	if (data->state != SUCCESS)
489e28a4053SRui Paulo 		return NULL;
490e28a4053SRui Paulo 
49185732ac8SCy Schubert 	key = os_memdup(data->emsk, EAP_EMSK_LEN);
492e28a4053SRui Paulo 	if (key == NULL)
493e28a4053SRui Paulo 		return NULL;
494e28a4053SRui Paulo 	*len = EAP_EMSK_LEN;
495e28a4053SRui Paulo 
496e28a4053SRui Paulo 	return key;
497e28a4053SRui Paulo }
498e28a4053SRui Paulo 
499e28a4053SRui Paulo 
eap_sake_isSuccess(struct eap_sm * sm,void * priv)500*c1d255d3SCy Schubert static bool eap_sake_isSuccess(struct eap_sm *sm, void *priv)
501e28a4053SRui Paulo {
502e28a4053SRui Paulo 	struct eap_sake_data *data = priv;
503e28a4053SRui Paulo 	return data->state == SUCCESS;
504e28a4053SRui Paulo }
505e28a4053SRui Paulo 
506e28a4053SRui Paulo 
eap_sake_get_session_id(struct eap_sm * sm,void * priv,size_t * len)5075b9c547cSRui Paulo static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
5085b9c547cSRui Paulo {
5095b9c547cSRui Paulo 	struct eap_sake_data *data = priv;
5105b9c547cSRui Paulo 	u8 *id;
5115b9c547cSRui Paulo 
5125b9c547cSRui Paulo 	if (data->state != SUCCESS)
5135b9c547cSRui Paulo 		return NULL;
5145b9c547cSRui Paulo 
5155b9c547cSRui Paulo 	*len = 1 + 2 * EAP_SAKE_RAND_LEN;
5165b9c547cSRui Paulo 	id = os_malloc(*len);
5175b9c547cSRui Paulo 	if (id == NULL)
5185b9c547cSRui Paulo 		return NULL;
5195b9c547cSRui Paulo 
5205b9c547cSRui Paulo 	id[0] = EAP_TYPE_SAKE;
5215b9c547cSRui Paulo 	os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
5225b9c547cSRui Paulo 	os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
5235b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
5245b9c547cSRui Paulo 
5255b9c547cSRui Paulo 	return id;
5265b9c547cSRui Paulo }
5275b9c547cSRui Paulo 
5285b9c547cSRui Paulo 
eap_server_sake_register(void)529e28a4053SRui Paulo int eap_server_sake_register(void)
530e28a4053SRui Paulo {
531e28a4053SRui Paulo 	struct eap_method *eap;
532e28a4053SRui Paulo 
533e28a4053SRui Paulo 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
534e28a4053SRui Paulo 				      EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
535e28a4053SRui Paulo 	if (eap == NULL)
536e28a4053SRui Paulo 		return -1;
537e28a4053SRui Paulo 
538e28a4053SRui Paulo 	eap->init = eap_sake_init;
539e28a4053SRui Paulo 	eap->reset = eap_sake_reset;
540e28a4053SRui Paulo 	eap->buildReq = eap_sake_buildReq;
541e28a4053SRui Paulo 	eap->check = eap_sake_check;
542e28a4053SRui Paulo 	eap->process = eap_sake_process;
543e28a4053SRui Paulo 	eap->isDone = eap_sake_isDone;
544e28a4053SRui Paulo 	eap->getKey = eap_sake_getKey;
545e28a4053SRui Paulo 	eap->isSuccess = eap_sake_isSuccess;
546e28a4053SRui Paulo 	eap->get_emsk = eap_sake_get_emsk;
5475b9c547cSRui Paulo 	eap->getSessionId = eap_sake_get_session_id;
548e28a4053SRui Paulo 
549780fb4a2SCy Schubert 	return eap_server_method_register(eap);
550e28a4053SRui Paulo }
551