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