1/*
2 * EAP peer method: EAP-SAKE (RFC 4763)
3 * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "crypto/random.h"
13#include "eap_peer/eap_i.h"
14#include "eap_common/eap_sake_common.h"
15
16struct eap_sake_data {
17	enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
18	u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN];
19	u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN];
20	u8 rand_s[EAP_SAKE_RAND_LEN];
21	u8 rand_p[EAP_SAKE_RAND_LEN];
22	struct {
23		u8 auth[EAP_SAKE_TEK_AUTH_LEN];
24		u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
25	} tek;
26	u8 msk[EAP_MSK_LEN];
27	u8 emsk[EAP_EMSK_LEN];
28	u8 session_id;
29	int session_id_set;
30	u8 *peerid;
31	size_t peerid_len;
32	u8 *serverid;
33	size_t serverid_len;
34};
35
36
37static const char * eap_sake_state_txt(int state)
38{
39	switch (state) {
40	case IDENTITY:
41		return "IDENTITY";
42	case CHALLENGE:
43		return "CHALLENGE";
44	case CONFIRM:
45		return "CONFIRM";
46	case SUCCESS:
47		return "SUCCESS";
48	case FAILURE:
49		return "FAILURE";
50	default:
51		return "?";
52	}
53}
54
55
56static void eap_sake_state(struct eap_sake_data *data, int state)
57{
58	wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
59		   eap_sake_state_txt(data->state),
60		   eap_sake_state_txt(state));
61	data->state = state;
62}
63
64
65static void eap_sake_deinit(struct eap_sm *sm, void *priv);
66
67
68static void * eap_sake_init(struct eap_sm *sm)
69{
70	struct eap_sake_data *data;
71	const u8 *identity, *password;
72	size_t identity_len, password_len;
73
74	password = eap_get_config_password(sm, &password_len);
75	if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
76		wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length "
77			   "configured");
78		return NULL;
79	}
80
81	data = os_zalloc(sizeof(*data));
82	if (data == NULL)
83		return NULL;
84	data->state = IDENTITY;
85
86	identity = eap_get_config_identity(sm, &identity_len);
87	if (identity) {
88		data->peerid = os_malloc(identity_len);
89		if (data->peerid == NULL) {
90			eap_sake_deinit(sm, data);
91			return NULL;
92		}
93		os_memcpy(data->peerid, identity, identity_len);
94		data->peerid_len = identity_len;
95	}
96
97	os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN);
98	os_memcpy(data->root_secret_b,
99		  password + EAP_SAKE_ROOT_SECRET_LEN,
100		  EAP_SAKE_ROOT_SECRET_LEN);
101
102	return data;
103}
104
105
106static void eap_sake_deinit(struct eap_sm *sm, void *priv)
107{
108	struct eap_sake_data *data = priv;
109	os_free(data->serverid);
110	os_free(data->peerid);
111	bin_clear_free(data, sizeof(*data));
112}
113
114
115static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
116					  int id, size_t length, u8 subtype)
117{
118	struct eap_sake_hdr *sake;
119	struct wpabuf *msg;
120	size_t plen;
121
122	plen = length + sizeof(struct eap_sake_hdr);
123
124	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
125			    EAP_CODE_RESPONSE, id);
126	if (msg == NULL) {
127		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
128			   "request");
129		return NULL;
130	}
131
132	sake = wpabuf_put(msg, sizeof(*sake));
133	sake->version = EAP_SAKE_VERSION;
134	sake->session_id = data->session_id;
135	sake->subtype = subtype;
136
137	return msg;
138}
139
140
141static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm,
142						 struct eap_sake_data *data,
143						 struct eap_method_ret *ret,
144						 u8 id,
145						 const u8 *payload,
146						 size_t payload_len)
147{
148	struct eap_sake_parse_attr attr;
149	struct wpabuf *resp;
150
151	if (data->state != IDENTITY) {
152		ret->ignore = TRUE;
153		return NULL;
154	}
155
156	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity");
157
158	if (eap_sake_parse_attributes(payload, payload_len, &attr))
159		return NULL;
160
161	if (!attr.perm_id_req && !attr.any_id_req) {
162		wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or "
163			   "AT_ANY_ID_REQ in Request/Identity");
164		return NULL;
165	}
166
167	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
168
169	resp = eap_sake_build_msg(data, id, 2 + data->peerid_len,
170				  EAP_SAKE_SUBTYPE_IDENTITY);
171	if (resp == NULL)
172		return NULL;
173
174	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
175	eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
176			  data->peerid, data->peerid_len);
177
178	eap_sake_state(data, CHALLENGE);
179
180	return resp;
181}
182
183
184static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
185						  struct eap_sake_data *data,
186						  struct eap_method_ret *ret,
187						  u8 id,
188						  const u8 *payload,
189						  size_t payload_len)
190{
191	struct eap_sake_parse_attr attr;
192	struct wpabuf *resp;
193	u8 *rpos;
194	size_t rlen;
195
196	if (data->state != IDENTITY && data->state != CHALLENGE) {
197		wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received "
198			   "in unexpected state (%d)", data->state);
199		ret->ignore = TRUE;
200		return NULL;
201	}
202	if (data->state == IDENTITY)
203		eap_sake_state(data, CHALLENGE);
204
205	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge");
206
207	if (eap_sake_parse_attributes(payload, payload_len, &attr))
208		return NULL;
209
210	if (!attr.rand_s) {
211		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not "
212			   "include AT_RAND_S");
213		return NULL;
214	}
215
216	os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN);
217	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
218		    data->rand_s, EAP_SAKE_RAND_LEN);
219
220	if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) {
221		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
222		return NULL;
223	}
224	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)",
225		    data->rand_p, EAP_SAKE_RAND_LEN);
226
227	os_free(data->serverid);
228	data->serverid = NULL;
229	data->serverid_len = 0;
230	if (attr.serverid) {
231		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
232				  attr.serverid, attr.serverid_len);
233		data->serverid = os_malloc(attr.serverid_len);
234		if (data->serverid == NULL)
235			return NULL;
236		os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
237		data->serverid_len = attr.serverid_len;
238	}
239
240	eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
241			     data->rand_s, data->rand_p,
242			     (u8 *) &data->tek, data->msk, data->emsk);
243
244	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
245
246	rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
247	if (data->peerid)
248		rlen += 2 + data->peerid_len;
249	resp = eap_sake_build_msg(data, id, rlen, EAP_SAKE_SUBTYPE_CHALLENGE);
250	if (resp == NULL)
251		return NULL;
252
253	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P");
254	eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P,
255			  data->rand_p, EAP_SAKE_RAND_LEN);
256
257	if (data->peerid) {
258		wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
259		eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
260				  data->peerid, data->peerid_len);
261	}
262
263	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
264	wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
265	wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
266	rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
267	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
268				 data->serverid, data->serverid_len,
269				 data->peerid, data->peerid_len, 1,
270				 wpabuf_head(resp), wpabuf_len(resp), rpos,
271				 rpos)) {
272		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
273		wpabuf_free(resp);
274		return NULL;
275	}
276
277	eap_sake_state(data, CONFIRM);
278
279	return resp;
280}
281
282
283static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
284						struct eap_sake_data *data,
285						struct eap_method_ret *ret,
286						u8 id,
287						const struct wpabuf *reqData,
288						const u8 *payload,
289						size_t payload_len)
290{
291	struct eap_sake_parse_attr attr;
292	u8 mic_s[EAP_SAKE_MIC_LEN];
293	struct wpabuf *resp;
294	u8 *rpos;
295
296	if (data->state != CONFIRM) {
297		ret->ignore = TRUE;
298		return NULL;
299	}
300
301	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm");
302
303	if (eap_sake_parse_attributes(payload, payload_len, &attr))
304		return NULL;
305
306	if (!attr.mic_s) {
307		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not "
308			   "include AT_MIC_S");
309		return NULL;
310	}
311
312	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
313			     data->serverid, data->serverid_len,
314			     data->peerid, data->peerid_len, 0,
315			     wpabuf_head(reqData), wpabuf_len(reqData),
316			     attr.mic_s, mic_s);
317	if (os_memcmp_const(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
318		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
319		eap_sake_state(data, FAILURE);
320		ret->methodState = METHOD_DONE;
321		ret->decision = DECISION_FAIL;
322		ret->allowNotifications = FALSE;
323		wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
324			   "Response/Auth-Reject");
325		return eap_sake_build_msg(data, id, 0,
326					  EAP_SAKE_SUBTYPE_AUTH_REJECT);
327	}
328
329	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
330
331	resp = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
332				  EAP_SAKE_SUBTYPE_CONFIRM);
333	if (resp == NULL)
334		return NULL;
335
336	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
337	wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
338	wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
339	rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
340	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
341				 data->serverid, data->serverid_len,
342				 data->peerid, data->peerid_len, 1,
343				 wpabuf_head(resp), wpabuf_len(resp), rpos,
344				 rpos)) {
345		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
346		wpabuf_free(resp);
347		return NULL;
348	}
349
350	eap_sake_state(data, SUCCESS);
351	ret->methodState = METHOD_DONE;
352	ret->decision = DECISION_UNCOND_SUCC;
353	ret->allowNotifications = FALSE;
354
355	return resp;
356}
357
358
359static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv,
360					struct eap_method_ret *ret,
361					const struct wpabuf *reqData)
362{
363	struct eap_sake_data *data = priv;
364	const struct eap_sake_hdr *req;
365	struct wpabuf *resp;
366	const u8 *pos, *end;
367	size_t len;
368	u8 subtype, session_id, id;
369
370	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len);
371	if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
372		ret->ignore = TRUE;
373		return NULL;
374	}
375
376	req = (const struct eap_sake_hdr *) pos;
377	end = pos + len;
378	id = eap_get_id(reqData);
379	subtype = req->subtype;
380	session_id = req->session_id;
381	pos = (const u8 *) (req + 1);
382
383	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d "
384		   "session_id %d", subtype, session_id);
385	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
386		    pos, end - pos);
387
388	if (data->session_id_set && data->session_id != session_id) {
389		wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
390			   session_id, data->session_id);
391		ret->ignore = TRUE;
392		return NULL;
393	}
394	data->session_id = session_id;
395	data->session_id_set = 1;
396
397	ret->ignore = FALSE;
398	ret->methodState = METHOD_MAY_CONT;
399	ret->decision = DECISION_FAIL;
400	ret->allowNotifications = TRUE;
401
402	switch (subtype) {
403	case EAP_SAKE_SUBTYPE_IDENTITY:
404		resp = eap_sake_process_identity(sm, data, ret, id,
405						 pos, end - pos);
406		break;
407	case EAP_SAKE_SUBTYPE_CHALLENGE:
408		resp = eap_sake_process_challenge(sm, data, ret, id,
409						  pos, end - pos);
410		break;
411	case EAP_SAKE_SUBTYPE_CONFIRM:
412		resp = eap_sake_process_confirm(sm, data, ret, id, reqData,
413						pos, end - pos);
414		break;
415	default:
416		wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with "
417			   "unknown subtype %d", subtype);
418		ret->ignore = TRUE;
419		return NULL;
420	}
421
422	if (ret->methodState == METHOD_DONE)
423		ret->allowNotifications = FALSE;
424
425	return resp;
426}
427
428
429static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv)
430{
431	struct eap_sake_data *data = priv;
432	return data->state == SUCCESS;
433}
434
435
436static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
437{
438	struct eap_sake_data *data = priv;
439	u8 *key;
440
441	if (data->state != SUCCESS)
442		return NULL;
443
444	key = os_malloc(EAP_MSK_LEN);
445	if (key == NULL)
446		return NULL;
447	os_memcpy(key, data->msk, EAP_MSK_LEN);
448	*len = EAP_MSK_LEN;
449
450	return key;
451}
452
453
454static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
455{
456	struct eap_sake_data *data = priv;
457	u8 *id;
458
459	if (data->state != SUCCESS)
460		return NULL;
461
462	*len = 1 + 2 * EAP_SAKE_RAND_LEN;
463	id = os_malloc(*len);
464	if (id == NULL)
465		return NULL;
466
467	id[0] = EAP_TYPE_SAKE;
468	os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
469	os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
470	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
471
472	return id;
473}
474
475
476static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
477{
478	struct eap_sake_data *data = priv;
479	u8 *key;
480
481	if (data->state != SUCCESS)
482		return NULL;
483
484	key = os_malloc(EAP_EMSK_LEN);
485	if (key == NULL)
486		return NULL;
487	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
488	*len = EAP_EMSK_LEN;
489
490	return key;
491}
492
493
494int eap_peer_sake_register(void)
495{
496	struct eap_method *eap;
497
498	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
499				    EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
500	if (eap == NULL)
501		return -1;
502
503	eap->init = eap_sake_init;
504	eap->deinit = eap_sake_deinit;
505	eap->process = eap_sake_process;
506	eap->isKeyAvailable = eap_sake_isKeyAvailable;
507	eap->getKey = eap_sake_getKey;
508	eap->getSessionId = eap_sake_get_session_id;
509	eap->get_emsk = eap_sake_get_emsk;
510
511	return eap_peer_method_register(eap);
512}
513