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