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