133549Sjkh/*
233549Sjkh * EAP peer method: EAP-SAKE (RFC 4763)
3330449Seadler * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
4330449Seadler *
5330449Seadler * This software may be distributed under the terms of the BSD license.
62892Sdfr * See README for more details.
72892Sdfr */
82892Sdfr
92892Sdfr#include "includes.h"
102892Sdfr
112892Sdfr#include "common.h"
122892Sdfr#include "crypto/random.h"
132892Sdfr#include "eap_peer/eap_i.h"
142892Sdfr#include "eap_common/eap_sake_common.h"
152892Sdfr
162892Sdfrstruct eap_sake_data {
172892Sdfr	enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
182892Sdfr	u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN];
192892Sdfr	u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN];
202892Sdfr	u8 rand_s[EAP_SAKE_RAND_LEN];
212892Sdfr	u8 rand_p[EAP_SAKE_RAND_LEN];
222892Sdfr	struct {
232892Sdfr		u8 auth[EAP_SAKE_TEK_AUTH_LEN];
242892Sdfr		u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
252892Sdfr	} tek;
262892Sdfr	u8 msk[EAP_MSK_LEN];
272892Sdfr	u8 emsk[EAP_EMSK_LEN];
282892Sdfr	u8 session_id;
292892Sdfr	int session_id_set;
302892Sdfr	u8 *peerid;
312892Sdfr	size_t peerid_len;
322892Sdfr	u8 *serverid;
332892Sdfr	size_t serverid_len;
342892Sdfr};
352892Sdfr
3615771Swollman
3750476Speterstatic const char * eap_sake_state_txt(int state)
382892Sdfr{
392892Sdfr	switch (state) {
402892Sdfr	case IDENTITY:
412892Sdfr		return "IDENTITY";
422892Sdfr	case CHALLENGE:
43120492Sfjoe		return "CHALLENGE";
44121363Strhodes	case CONFIRM:
45121429Strhodes		return "CONFIRM";
4623335Sbde	case SUCCESS:
472892Sdfr		return "SUCCESS";
482892Sdfr	case FAILURE:
49340953Seugen		return "FAILURE";
502892Sdfr	default:
5133761Sache		return "?";
522892Sdfr	}
532892Sdfr}
5455613Sache
5555613Sache
562892Sdfrstatic void eap_sake_state(struct eap_sake_data *data, int state)
572892Sdfr{
5815771Swollman	wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
592892Sdfr		   eap_sake_state_txt(data->state),
60121429Strhodes		   eap_sake_state_txt(state));
612892Sdfr	data->state = state;
622892Sdfr}
6392882Simp
6492882Simp
6592882Simpstatic void eap_sake_deinit(struct eap_sm *sm, void *priv);
6692882Simp
67152809Savatar
682892Sdfrstatic void * eap_sake_init(struct eap_sm *sm)
692892Sdfr{
70121373Strhodes	struct eap_sake_data *data;
712892Sdfr	const u8 *identity, *password;
72152416Smaxim	size_t identity_len, password_len;
73152416Smaxim
742892Sdfr	password = eap_get_config_password(sm, &password_len);
75247856Sjkim	if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
76120492Sfjoe		wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length "
77152362Srodrigc			   "configured");
78166326Srodrigc		return NULL;
79152362Srodrigc	}
80152362Srodrigc
81152362Srodrigc	data = os_zalloc(sizeof(*data));
82152362Srodrigc	if (data == NULL)
83152362Srodrigc		return NULL;
842892Sdfr	data->state = IDENTITY;
85247856Sjkim
862892Sdfr	identity = eap_get_config_identity(sm, &identity_len);
87152731Savatar	if (identity) {
882892Sdfr		data->peerid = os_malloc(identity_len);
8933549Sjkh		if (data->peerid == NULL) {
90152362Srodrigc			eap_sake_deinit(sm, data);
9133549Sjkh			return NULL;
9233549Sjkh		}
93152362Srodrigc		os_memcpy(data->peerid, identity, identity_len);
9433549Sjkh		data->peerid_len = identity_len;
9533549Sjkh	}
96152731Savatar
9733549Sjkh	os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN);
982892Sdfr	os_memcpy(data->root_secret_b,
99152362Srodrigc		  password + EAP_SAKE_ROOT_SECRET_LEN,
1002892Sdfr		  EAP_SAKE_ROOT_SECRET_LEN);
1012892Sdfr
1022892Sdfr	return data;
103152362Srodrigc}
1042892Sdfr
1052892Sdfr
1062892Sdfrstatic void eap_sake_deinit(struct eap_sm *sm, void *priv)
107152362Srodrigc{
1082892Sdfr	struct eap_sake_data *data = priv;
1092892Sdfr	os_free(data->serverid);
110118837Strhodes	os_free(data->peerid);
111152362Srodrigc	os_free(data);
112118837Strhodes}
113118837Strhodes
114152362Srodrigc
115152362Srodrigcstatic struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
116120492Sfjoe					  int id, size_t length, u8 subtype)
117120492Sfjoe{
118120492Sfjoe	struct eap_sake_hdr *sake;
119120492Sfjoe	struct wpabuf *msg;
120120492Sfjoe	size_t plen;
121152362Srodrigc
122152362Srodrigc	plen = length + sizeof(struct eap_sake_hdr);
123152974Savatar
124152362Srodrigc	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
12533761Sache			    EAP_CODE_RESPONSE, id);
126120492Sfjoe	if (msg == NULL) {
127152362Srodrigc		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
128152362Srodrigc			   "request");
12933749Sache		return NULL;
130152362Srodrigc	}
131152362Srodrigc
132152362Srodrigc	sake = wpabuf_put(msg, sizeof(*sake));
133152416Smaxim	sake->version = EAP_SAKE_VERSION;
134152416Smaxim	sake->session_id = data->session_id;
135152362Srodrigc	sake->subtype = subtype;
136152362Srodrigc
137152362Srodrigc	return msg;
138152362Srodrigc}
139152362Srodrigc
140152362Srodrigc
1412892Sdfrstatic struct wpabuf * eap_sake_process_identity(struct eap_sm *sm,
142120492Sfjoe						 struct eap_sake_data *data,
143120492Sfjoe						 struct eap_method_ret *ret,
144152362Srodrigc						 const struct wpabuf *reqData,
145152362Srodrigc						 const u8 *payload,
146120492Sfjoe						 size_t payload_len)
147152362Srodrigc{
148152362Srodrigc	struct eap_sake_parse_attr attr;
149120492Sfjoe	struct wpabuf *resp;
150152362Srodrigc
151152362Srodrigc	if (data->state != IDENTITY) {
152120492Sfjoe		ret->ignore = TRUE;
153152362Srodrigc		return NULL;
154152362Srodrigc	}
155120492Sfjoe
156120492Sfjoe	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity");
157120492Sfjoe
158152362Srodrigc	if (eap_sake_parse_attributes(payload, payload_len, &attr))
159152362Srodrigc		return NULL;
160120492Sfjoe
1612892Sdfr	if (!attr.perm_id_req && !attr.any_id_req) {
1622892Sdfr		wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or "
1632892Sdfr			   "AT_ANY_ID_REQ in Request/Identity");
1642892Sdfr		return NULL;
1652892Sdfr	}
1662892Sdfr
1672892Sdfr	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
1682892Sdfr
1692892Sdfr	resp = eap_sake_build_msg(data, eap_get_id(reqData),
1702892Sdfr				  2 + data->peerid_len,
171118837Strhodes				  EAP_SAKE_SUBTYPE_IDENTITY);
172152362Srodrigc	if (resp == NULL)
173118837Strhodes		return NULL;
174118837Strhodes
175118837Strhodes	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
176152362Srodrigc	eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
177118837Strhodes			  data->peerid, data->peerid_len);
178118837Strhodes
179118837Strhodes	eap_sake_state(data, CHALLENGE);
1802892Sdfr
1812892Sdfr	return resp;
1822892Sdfr}
183152362Srodrigc
184152809Savatar
185120492Sfjoestatic struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
186152362Srodrigc						  struct eap_sake_data *data,
187152362Srodrigc						  struct eap_method_ret *ret,
188152362Srodrigc						  const struct wpabuf *reqData,
189152809Savatar						  const u8 *payload,
190120492Sfjoe						  size_t payload_len)
191152362Srodrigc{
192120492Sfjoe	struct eap_sake_parse_attr attr;
193120492Sfjoe	struct wpabuf *resp;
19452055Sphk	u8 *rpos;
19552055Sphk	size_t rlen;
19652055Sphk
19752055Sphk	if (data->state != IDENTITY && data->state != CHALLENGE) {
198230226Sjh		wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received "
199230226Sjh			   "in unexpected state (%d)", data->state);
20052055Sphk		ret->ignore = TRUE;
20152055Sphk		return NULL;
2022892Sdfr	}
20352055Sphk	if (data->state == IDENTITY)
20452055Sphk		eap_sake_state(data, CHALLENGE);
2052892Sdfr
2062892Sdfr	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge");
207152362Srodrigc
2082892Sdfr	if (eap_sake_parse_attributes(payload, payload_len, &attr))
209152362Srodrigc		return NULL;
2102892Sdfr
211152362Srodrigc	if (!attr.rand_s) {
212118837Strhodes		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not "
2132892Sdfr			   "include AT_RAND_S");
2142892Sdfr		return NULL;
215152362Srodrigc	}
216152362Srodrigc
217152362Srodrigc	os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN);
218166326Srodrigc	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
219152362Srodrigc		    data->rand_s, EAP_SAKE_RAND_LEN);
220152362Srodrigc
221152362Srodrigc	if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) {
222152362Srodrigc		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
2232892Sdfr		return NULL;
224247856Sjkim	}
225185422Simp	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)",
226185422Simp		    data->rand_p, EAP_SAKE_RAND_LEN);
227185422Simp
228185422Simp	os_free(data->serverid);
229185422Simp	data->serverid = NULL;
230152362Srodrigc	data->serverid_len = 0;
2312892Sdfr	if (attr.serverid) {
2322892Sdfr		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
2332892Sdfr				  attr.serverid, attr.serverid_len);
2342892Sdfr		data->serverid = os_malloc(attr.serverid_len);
235201227Sed		if (data->serverid == NULL)
2362892Sdfr			return NULL;
2372892Sdfr		os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
2382892Sdfr		data->serverid_len = attr.serverid_len;
2392892Sdfr	}
2402892Sdfr
2412892Sdfr	eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
2422892Sdfr			     data->rand_s, data->rand_p,
2432892Sdfr			     (u8 *) &data->tek, data->msk, data->emsk);
2442892Sdfr
2452892Sdfr	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
2462892Sdfr
2472892Sdfr	rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
24815771Swollman	if (data->peerid)
2492892Sdfr		rlen += 2 + data->peerid_len;
2502892Sdfr	resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen,
2512892Sdfr				  EAP_SAKE_SUBTYPE_CHALLENGE);
2522892Sdfr	if (resp == NULL)
2532892Sdfr		return NULL;
254201227Sed
2552892Sdfr	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P");
2562892Sdfr	eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P,
2572892Sdfr			  data->rand_p, EAP_SAKE_RAND_LEN);
2582892Sdfr
2592892Sdfr	if (data->peerid) {
2602892Sdfr		wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
2612892Sdfr		eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
2622892Sdfr				  data->peerid, data->peerid_len);
2632892Sdfr	}
2642892Sdfr
2652892Sdfr	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
2662892Sdfr	wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
26715771Swollman	wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
2682892Sdfr	rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
2692892Sdfr	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
2702892Sdfr				 data->serverid, data->serverid_len,
2712892Sdfr				 data->peerid, data->peerid_len, 1,
2722892Sdfr				 wpabuf_head(resp), wpabuf_len(resp), rpos,
273201227Sed				 rpos)) {
2742892Sdfr		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
2752892Sdfr		wpabuf_free(resp);
2762892Sdfr		return NULL;
2772892Sdfr	}
2782892Sdfr
27933549Sjkh	eap_sake_state(data, CONFIRM);
2802892Sdfr
2812892Sdfr	return resp;
2822892Sdfr}
2832892Sdfr
2842892Sdfr
28515771Swollmanstatic struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
2862892Sdfr						struct eap_sake_data *data,
2872892Sdfr						struct eap_method_ret *ret,
2882892Sdfr						const struct wpabuf *reqData,
2892892Sdfr						const u8 *payload,
290201227Sed						size_t payload_len)
2912892Sdfr{
292121007Sfjoe	struct eap_sake_parse_attr attr;
293121429Strhodes	u8 mic_s[EAP_SAKE_MIC_LEN];
294121429Strhodes	struct wpabuf *resp;
295121429Strhodes	u8 *rpos;
29615771Swollman
2972892Sdfr	if (data->state != CONFIRM) {
29833749Sache		ret->ignore = TRUE;
299120492Sfjoe		return NULL;
300152809Savatar	}
30133749Sache
302120492Sfjoe	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm");
30333749Sache
304120492Sfjoe	if (eap_sake_parse_attributes(payload, payload_len, &attr))
305120492Sfjoe		return NULL;
306134565Strhodes
307120492Sfjoe	if (!attr.mic_s) {
308120492Sfjoe		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not "
309120492Sfjoe			   "include AT_MIC_S");
310152809Savatar		return NULL;
311152362Srodrigc	}
312340953Seugen
313120492Sfjoe	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
314152362Srodrigc			     data->serverid, data->serverid_len,
315152362Srodrigc			     data->peerid, data->peerid_len, 0,
316340953Seugen			     wpabuf_head(reqData), wpabuf_len(reqData),
317120492Sfjoe			     attr.mic_s, mic_s);
318120492Sfjoe	if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
319152809Savatar		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
320152362Srodrigc		eap_sake_state(data, FAILURE);
321120492Sfjoe		ret->methodState = METHOD_DONE;
322340953Seugen		ret->decision = DECISION_FAIL;
323120492Sfjoe		ret->allowNotifications = FALSE;
32433749Sache		wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
32533761Sache			   "Response/Auth-Reject");
326120492Sfjoe		return eap_sake_build_msg(data, eap_get_id(reqData), 0,
32733761Sache					  EAP_SAKE_SUBTYPE_AUTH_REJECT);
328	}
329
330	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
331
332	resp = eap_sake_build_msg(data, eap_get_id(reqData),
333				  2 + EAP_SAKE_MIC_LEN,
334				  EAP_SAKE_SUBTYPE_CONFIRM);
335	if (resp == NULL)
336		return NULL;
337
338	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
339	wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
340	wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
341	rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
342	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
343				 data->serverid, data->serverid_len,
344				 data->peerid, data->peerid_len, 1,
345				 wpabuf_head(resp), wpabuf_len(resp), rpos,
346				 rpos)) {
347		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
348		wpabuf_free(resp);
349		return NULL;
350	}
351
352	eap_sake_state(data, SUCCESS);
353	ret->methodState = METHOD_DONE;
354	ret->decision = DECISION_UNCOND_SUCC;
355	ret->allowNotifications = FALSE;
356
357	return resp;
358}
359
360
361static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv,
362					struct eap_method_ret *ret,
363					const struct wpabuf *reqData)
364{
365	struct eap_sake_data *data = priv;
366	const struct eap_sake_hdr *req;
367	struct wpabuf *resp;
368	const u8 *pos, *end;
369	size_t len;
370	u8 subtype, session_id;
371
372	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len);
373	if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
374		ret->ignore = TRUE;
375		return NULL;
376	}
377
378	req = (const struct eap_sake_hdr *) pos;
379	end = pos + len;
380	subtype = req->subtype;
381	session_id = req->session_id;
382	pos = (const u8 *) (req + 1);
383
384	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d "
385		   "session_id %d", subtype, session_id);
386	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
387		    pos, end - pos);
388
389	if (data->session_id_set && data->session_id != session_id) {
390		wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
391			   session_id, data->session_id);
392		ret->ignore = TRUE;
393		return NULL;
394	}
395	data->session_id = session_id;
396	data->session_id_set = 1;
397
398	ret->ignore = FALSE;
399	ret->methodState = METHOD_MAY_CONT;
400	ret->decision = DECISION_FAIL;
401	ret->allowNotifications = TRUE;
402
403	switch (subtype) {
404	case EAP_SAKE_SUBTYPE_IDENTITY:
405		resp = eap_sake_process_identity(sm, data, ret, reqData,
406						 pos, end - pos);
407		break;
408	case EAP_SAKE_SUBTYPE_CHALLENGE:
409		resp = eap_sake_process_challenge(sm, data, ret, reqData,
410						  pos, end - pos);
411		break;
412	case EAP_SAKE_SUBTYPE_CONFIRM:
413		resp = eap_sake_process_confirm(sm, data, ret, reqData,
414						pos, end - pos);
415		break;
416	default:
417		wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with "
418			   "unknown subtype %d", subtype);
419		ret->ignore = TRUE;
420		return NULL;
421	}
422
423	if (ret->methodState == METHOD_DONE)
424		ret->allowNotifications = FALSE;
425
426	return resp;
427}
428
429
430static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv)
431{
432	struct eap_sake_data *data = priv;
433	return data->state == SUCCESS;
434}
435
436
437static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
438{
439	struct eap_sake_data *data = priv;
440	u8 *key;
441
442	if (data->state != SUCCESS)
443		return NULL;
444
445	key = os_malloc(EAP_MSK_LEN);
446	if (key == NULL)
447		return NULL;
448	os_memcpy(key, data->msk, EAP_MSK_LEN);
449	*len = EAP_MSK_LEN;
450
451	return key;
452}
453
454
455static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
456{
457	struct eap_sake_data *data = priv;
458	u8 *key;
459
460	if (data->state != SUCCESS)
461		return NULL;
462
463	key = os_malloc(EAP_EMSK_LEN);
464	if (key == NULL)
465		return NULL;
466	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
467	*len = EAP_EMSK_LEN;
468
469	return key;
470}
471
472
473int eap_peer_sake_register(void)
474{
475	struct eap_method *eap;
476	int ret;
477
478	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
479				    EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
480	if (eap == NULL)
481		return -1;
482
483	eap->init = eap_sake_init;
484	eap->deinit = eap_sake_deinit;
485	eap->process = eap_sake_process;
486	eap->isKeyAvailable = eap_sake_isKeyAvailable;
487	eap->getKey = eap_sake_getKey;
488	eap->get_emsk = eap_sake_get_emsk;
489
490	ret = eap_peer_method_register(eap);
491	if (ret)
492		eap_peer_method_free(eap);
493	return ret;
494}
495