1189251Ssam/*
2189251Ssam * EAP peer method: EAP-SAKE (RFC 4763)
3189251Ssam * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12252726Srpaulo#include "crypto/random.h"
13189251Ssam#include "eap_peer/eap_i.h"
14189251Ssam#include "eap_common/eap_sake_common.h"
15189251Ssam
16189251Ssamstruct eap_sake_data {
17189251Ssam	enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
18189251Ssam	u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN];
19189251Ssam	u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN];
20189251Ssam	u8 rand_s[EAP_SAKE_RAND_LEN];
21189251Ssam	u8 rand_p[EAP_SAKE_RAND_LEN];
22189251Ssam	struct {
23189251Ssam		u8 auth[EAP_SAKE_TEK_AUTH_LEN];
24189251Ssam		u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
25189251Ssam	} tek;
26189251Ssam	u8 msk[EAP_MSK_LEN];
27189251Ssam	u8 emsk[EAP_EMSK_LEN];
28189251Ssam	u8 session_id;
29189251Ssam	int session_id_set;
30189251Ssam	u8 *peerid;
31189251Ssam	size_t peerid_len;
32189251Ssam	u8 *serverid;
33189251Ssam	size_t serverid_len;
34189251Ssam};
35189251Ssam
36189251Ssam
37189251Ssamstatic const char * eap_sake_state_txt(int state)
38189251Ssam{
39189251Ssam	switch (state) {
40189251Ssam	case IDENTITY:
41189251Ssam		return "IDENTITY";
42189251Ssam	case CHALLENGE:
43189251Ssam		return "CHALLENGE";
44189251Ssam	case CONFIRM:
45189251Ssam		return "CONFIRM";
46189251Ssam	case SUCCESS:
47189251Ssam		return "SUCCESS";
48189251Ssam	case FAILURE:
49189251Ssam		return "FAILURE";
50189251Ssam	default:
51189251Ssam		return "?";
52189251Ssam	}
53189251Ssam}
54189251Ssam
55189251Ssam
56189251Ssamstatic void eap_sake_state(struct eap_sake_data *data, int state)
57189251Ssam{
58189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
59189251Ssam		   eap_sake_state_txt(data->state),
60189251Ssam		   eap_sake_state_txt(state));
61189251Ssam	data->state = state;
62189251Ssam}
63189251Ssam
64189251Ssam
65189251Ssamstatic void eap_sake_deinit(struct eap_sm *sm, void *priv);
66189251Ssam
67189251Ssam
68189251Ssamstatic void * eap_sake_init(struct eap_sm *sm)
69189251Ssam{
70189251Ssam	struct eap_sake_data *data;
71189251Ssam	const u8 *identity, *password;
72189251Ssam	size_t identity_len, password_len;
73189251Ssam
74189251Ssam	password = eap_get_config_password(sm, &password_len);
75189251Ssam	if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
76189251Ssam		wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length "
77189251Ssam			   "configured");
78189251Ssam		return NULL;
79189251Ssam	}
80189251Ssam
81189251Ssam	data = os_zalloc(sizeof(*data));
82189251Ssam	if (data == NULL)
83189251Ssam		return NULL;
84189251Ssam	data->state = IDENTITY;
85189251Ssam
86189251Ssam	identity = eap_get_config_identity(sm, &identity_len);
87189251Ssam	if (identity) {
88189251Ssam		data->peerid = os_malloc(identity_len);
89189251Ssam		if (data->peerid == NULL) {
90189251Ssam			eap_sake_deinit(sm, data);
91189251Ssam			return NULL;
92189251Ssam		}
93189251Ssam		os_memcpy(data->peerid, identity, identity_len);
94189251Ssam		data->peerid_len = identity_len;
95189251Ssam	}
96189251Ssam
97189251Ssam	os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN);
98189251Ssam	os_memcpy(data->root_secret_b,
99189251Ssam		  password + EAP_SAKE_ROOT_SECRET_LEN,
100189251Ssam		  EAP_SAKE_ROOT_SECRET_LEN);
101189251Ssam
102189251Ssam	return data;
103189251Ssam}
104189251Ssam
105189251Ssam
106189251Ssamstatic void eap_sake_deinit(struct eap_sm *sm, void *priv)
107189251Ssam{
108189251Ssam	struct eap_sake_data *data = priv;
109189251Ssam	os_free(data->serverid);
110189251Ssam	os_free(data->peerid);
111189251Ssam	os_free(data);
112189251Ssam}
113189251Ssam
114189251Ssam
115189251Ssamstatic struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
116189251Ssam					  int id, size_t length, u8 subtype)
117189251Ssam{
118189251Ssam	struct eap_sake_hdr *sake;
119189251Ssam	struct wpabuf *msg;
120189251Ssam	size_t plen;
121189251Ssam
122189251Ssam	plen = length + sizeof(struct eap_sake_hdr);
123189251Ssam
124189251Ssam	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
125189251Ssam			    EAP_CODE_RESPONSE, id);
126189251Ssam	if (msg == NULL) {
127189251Ssam		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
128189251Ssam			   "request");
129189251Ssam		return NULL;
130189251Ssam	}
131189251Ssam
132189251Ssam	sake = wpabuf_put(msg, sizeof(*sake));
133189251Ssam	sake->version = EAP_SAKE_VERSION;
134189251Ssam	sake->session_id = data->session_id;
135189251Ssam	sake->subtype = subtype;
136189251Ssam
137189251Ssam	return msg;
138189251Ssam}
139189251Ssam
140189251Ssam
141189251Ssamstatic struct wpabuf * eap_sake_process_identity(struct eap_sm *sm,
142189251Ssam						 struct eap_sake_data *data,
143189251Ssam						 struct eap_method_ret *ret,
144189251Ssam						 const struct wpabuf *reqData,
145189251Ssam						 const u8 *payload,
146189251Ssam						 size_t payload_len)
147189251Ssam{
148189251Ssam	struct eap_sake_parse_attr attr;
149189251Ssam	struct wpabuf *resp;
150189251Ssam
151189251Ssam	if (data->state != IDENTITY) {
152189251Ssam		ret->ignore = TRUE;
153189251Ssam		return NULL;
154189251Ssam	}
155189251Ssam
156189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity");
157189251Ssam
158189251Ssam	if (eap_sake_parse_attributes(payload, payload_len, &attr))
159189251Ssam		return NULL;
160189251Ssam
161189251Ssam	if (!attr.perm_id_req && !attr.any_id_req) {
162189251Ssam		wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or "
163189251Ssam			   "AT_ANY_ID_REQ in Request/Identity");
164189251Ssam		return NULL;
165189251Ssam	}
166189251Ssam
167189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
168189251Ssam
169189251Ssam	resp = eap_sake_build_msg(data, eap_get_id(reqData),
170189251Ssam				  2 + data->peerid_len,
171189251Ssam				  EAP_SAKE_SUBTYPE_IDENTITY);
172189251Ssam	if (resp == NULL)
173189251Ssam		return NULL;
174189251Ssam
175189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
176189251Ssam	eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
177189251Ssam			  data->peerid, data->peerid_len);
178189251Ssam
179189251Ssam	eap_sake_state(data, CHALLENGE);
180189251Ssam
181189251Ssam	return resp;
182189251Ssam}
183189251Ssam
184189251Ssam
185189251Ssamstatic struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
186189251Ssam						  struct eap_sake_data *data,
187189251Ssam						  struct eap_method_ret *ret,
188189251Ssam						  const struct wpabuf *reqData,
189189251Ssam						  const u8 *payload,
190189251Ssam						  size_t payload_len)
191189251Ssam{
192189251Ssam	struct eap_sake_parse_attr attr;
193189251Ssam	struct wpabuf *resp;
194189251Ssam	u8 *rpos;
195189251Ssam	size_t rlen;
196189251Ssam
197189251Ssam	if (data->state != IDENTITY && data->state != CHALLENGE) {
198189251Ssam		wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received "
199189251Ssam			   "in unexpected state (%d)", data->state);
200189251Ssam		ret->ignore = TRUE;
201189251Ssam		return NULL;
202189251Ssam	}
203189251Ssam	if (data->state == IDENTITY)
204189251Ssam		eap_sake_state(data, CHALLENGE);
205189251Ssam
206189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge");
207189251Ssam
208189251Ssam	if (eap_sake_parse_attributes(payload, payload_len, &attr))
209189251Ssam		return NULL;
210189251Ssam
211189251Ssam	if (!attr.rand_s) {
212189251Ssam		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not "
213189251Ssam			   "include AT_RAND_S");
214189251Ssam		return NULL;
215189251Ssam	}
216189251Ssam
217189251Ssam	os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN);
218189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
219189251Ssam		    data->rand_s, EAP_SAKE_RAND_LEN);
220189251Ssam
221252726Srpaulo	if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) {
222189251Ssam		wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
223189251Ssam		return NULL;
224189251Ssam	}
225189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)",
226189251Ssam		    data->rand_p, EAP_SAKE_RAND_LEN);
227189251Ssam
228189251Ssam	os_free(data->serverid);
229189251Ssam	data->serverid = NULL;
230189251Ssam	data->serverid_len = 0;
231189251Ssam	if (attr.serverid) {
232189251Ssam		wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
233189251Ssam				  attr.serverid, attr.serverid_len);
234189251Ssam		data->serverid = os_malloc(attr.serverid_len);
235189251Ssam		if (data->serverid == NULL)
236189251Ssam			return NULL;
237189251Ssam		os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
238189251Ssam		data->serverid_len = attr.serverid_len;
239189251Ssam	}
240189251Ssam
241189251Ssam	eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
242189251Ssam			     data->rand_s, data->rand_p,
243189251Ssam			     (u8 *) &data->tek, data->msk, data->emsk);
244189251Ssam
245189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
246189251Ssam
247189251Ssam	rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
248189251Ssam	if (data->peerid)
249189251Ssam		rlen += 2 + data->peerid_len;
250189251Ssam	resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen,
251189251Ssam				  EAP_SAKE_SUBTYPE_CHALLENGE);
252189251Ssam	if (resp == NULL)
253189251Ssam		return NULL;
254189251Ssam
255189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P");
256189251Ssam	eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P,
257189251Ssam			  data->rand_p, EAP_SAKE_RAND_LEN);
258189251Ssam
259189251Ssam	if (data->peerid) {
260189251Ssam		wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
261189251Ssam		eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
262189251Ssam				  data->peerid, data->peerid_len);
263189251Ssam	}
264189251Ssam
265189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
266189251Ssam	wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
267189251Ssam	wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
268189251Ssam	rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
269189251Ssam	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
270189251Ssam				 data->serverid, data->serverid_len,
271189251Ssam				 data->peerid, data->peerid_len, 1,
272189251Ssam				 wpabuf_head(resp), wpabuf_len(resp), rpos,
273189251Ssam				 rpos)) {
274189251Ssam		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
275189251Ssam		wpabuf_free(resp);
276189251Ssam		return NULL;
277189251Ssam	}
278189251Ssam
279189251Ssam	eap_sake_state(data, CONFIRM);
280189251Ssam
281189251Ssam	return resp;
282189251Ssam}
283189251Ssam
284189251Ssam
285189251Ssamstatic struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
286189251Ssam						struct eap_sake_data *data,
287189251Ssam						struct eap_method_ret *ret,
288189251Ssam						const struct wpabuf *reqData,
289189251Ssam						const u8 *payload,
290189251Ssam						size_t payload_len)
291189251Ssam{
292189251Ssam	struct eap_sake_parse_attr attr;
293189251Ssam	u8 mic_s[EAP_SAKE_MIC_LEN];
294189251Ssam	struct wpabuf *resp;
295189251Ssam	u8 *rpos;
296189251Ssam
297189251Ssam	if (data->state != CONFIRM) {
298189251Ssam		ret->ignore = TRUE;
299189251Ssam		return NULL;
300189251Ssam	}
301189251Ssam
302189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm");
303189251Ssam
304189251Ssam	if (eap_sake_parse_attributes(payload, payload_len, &attr))
305189251Ssam		return NULL;
306189251Ssam
307189251Ssam	if (!attr.mic_s) {
308189251Ssam		wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not "
309189251Ssam			   "include AT_MIC_S");
310189251Ssam		return NULL;
311189251Ssam	}
312189251Ssam
313189251Ssam	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
314189251Ssam			     data->serverid, data->serverid_len,
315189251Ssam			     data->peerid, data->peerid_len, 0,
316189251Ssam			     wpabuf_head(reqData), wpabuf_len(reqData),
317189251Ssam			     attr.mic_s, mic_s);
318189251Ssam	if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
319189251Ssam		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
320189251Ssam		eap_sake_state(data, FAILURE);
321189251Ssam		ret->methodState = METHOD_DONE;
322189251Ssam		ret->decision = DECISION_FAIL;
323189251Ssam		ret->allowNotifications = FALSE;
324189251Ssam		wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
325189251Ssam			   "Response/Auth-Reject");
326189251Ssam		return eap_sake_build_msg(data, eap_get_id(reqData), 0,
327189251Ssam					  EAP_SAKE_SUBTYPE_AUTH_REJECT);
328189251Ssam	}
329189251Ssam
330189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
331189251Ssam
332189251Ssam	resp = eap_sake_build_msg(data, eap_get_id(reqData),
333189251Ssam				  2 + EAP_SAKE_MIC_LEN,
334189251Ssam				  EAP_SAKE_SUBTYPE_CONFIRM);
335189251Ssam	if (resp == NULL)
336189251Ssam		return NULL;
337189251Ssam
338189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
339189251Ssam	wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
340189251Ssam	wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
341189251Ssam	rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
342189251Ssam	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
343189251Ssam				 data->serverid, data->serverid_len,
344189251Ssam				 data->peerid, data->peerid_len, 1,
345189251Ssam				 wpabuf_head(resp), wpabuf_len(resp), rpos,
346189251Ssam				 rpos)) {
347189251Ssam		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
348189251Ssam		wpabuf_free(resp);
349189251Ssam		return NULL;
350189251Ssam	}
351189251Ssam
352189251Ssam	eap_sake_state(data, SUCCESS);
353189251Ssam	ret->methodState = METHOD_DONE;
354189251Ssam	ret->decision = DECISION_UNCOND_SUCC;
355189251Ssam	ret->allowNotifications = FALSE;
356189251Ssam
357189251Ssam	return resp;
358189251Ssam}
359189251Ssam
360189251Ssam
361189251Ssamstatic struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv,
362189251Ssam					struct eap_method_ret *ret,
363189251Ssam					const struct wpabuf *reqData)
364189251Ssam{
365189251Ssam	struct eap_sake_data *data = priv;
366189251Ssam	const struct eap_sake_hdr *req;
367189251Ssam	struct wpabuf *resp;
368189251Ssam	const u8 *pos, *end;
369189251Ssam	size_t len;
370189251Ssam	u8 subtype, session_id;
371189251Ssam
372189251Ssam	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len);
373189251Ssam	if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
374189251Ssam		ret->ignore = TRUE;
375189251Ssam		return NULL;
376189251Ssam	}
377189251Ssam
378189251Ssam	req = (const struct eap_sake_hdr *) pos;
379189251Ssam	end = pos + len;
380189251Ssam	subtype = req->subtype;
381189251Ssam	session_id = req->session_id;
382189251Ssam	pos = (const u8 *) (req + 1);
383189251Ssam
384189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d "
385189251Ssam		   "session_id %d", subtype, session_id);
386189251Ssam	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
387189251Ssam		    pos, end - pos);
388189251Ssam
389189251Ssam	if (data->session_id_set && data->session_id != session_id) {
390189251Ssam		wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
391189251Ssam			   session_id, data->session_id);
392189251Ssam		ret->ignore = TRUE;
393189251Ssam		return NULL;
394189251Ssam	}
395189251Ssam	data->session_id = session_id;
396189251Ssam	data->session_id_set = 1;
397189251Ssam
398189251Ssam	ret->ignore = FALSE;
399189251Ssam	ret->methodState = METHOD_MAY_CONT;
400189251Ssam	ret->decision = DECISION_FAIL;
401189251Ssam	ret->allowNotifications = TRUE;
402189251Ssam
403189251Ssam	switch (subtype) {
404189251Ssam	case EAP_SAKE_SUBTYPE_IDENTITY:
405189251Ssam		resp = eap_sake_process_identity(sm, data, ret, reqData,
406189251Ssam						 pos, end - pos);
407189251Ssam		break;
408189251Ssam	case EAP_SAKE_SUBTYPE_CHALLENGE:
409189251Ssam		resp = eap_sake_process_challenge(sm, data, ret, reqData,
410189251Ssam						  pos, end - pos);
411189251Ssam		break;
412189251Ssam	case EAP_SAKE_SUBTYPE_CONFIRM:
413189251Ssam		resp = eap_sake_process_confirm(sm, data, ret, reqData,
414189251Ssam						pos, end - pos);
415189251Ssam		break;
416189251Ssam	default:
417189251Ssam		wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with "
418189251Ssam			   "unknown subtype %d", subtype);
419189251Ssam		ret->ignore = TRUE;
420189251Ssam		return NULL;
421189251Ssam	}
422189251Ssam
423189251Ssam	if (ret->methodState == METHOD_DONE)
424189251Ssam		ret->allowNotifications = FALSE;
425189251Ssam
426189251Ssam	return resp;
427189251Ssam}
428189251Ssam
429189251Ssam
430189251Ssamstatic Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv)
431189251Ssam{
432189251Ssam	struct eap_sake_data *data = priv;
433189251Ssam	return data->state == SUCCESS;
434189251Ssam}
435189251Ssam
436189251Ssam
437189251Ssamstatic u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
438189251Ssam{
439189251Ssam	struct eap_sake_data *data = priv;
440189251Ssam	u8 *key;
441189251Ssam
442189251Ssam	if (data->state != SUCCESS)
443189251Ssam		return NULL;
444189251Ssam
445189251Ssam	key = os_malloc(EAP_MSK_LEN);
446189251Ssam	if (key == NULL)
447189251Ssam		return NULL;
448189251Ssam	os_memcpy(key, data->msk, EAP_MSK_LEN);
449189251Ssam	*len = EAP_MSK_LEN;
450189251Ssam
451189251Ssam	return key;
452189251Ssam}
453189251Ssam
454189251Ssam
455189251Ssamstatic u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
456189251Ssam{
457189251Ssam	struct eap_sake_data *data = priv;
458189251Ssam	u8 *key;
459189251Ssam
460189251Ssam	if (data->state != SUCCESS)
461189251Ssam		return NULL;
462189251Ssam
463189251Ssam	key = os_malloc(EAP_EMSK_LEN);
464189251Ssam	if (key == NULL)
465189251Ssam		return NULL;
466189251Ssam	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
467189251Ssam	*len = EAP_EMSK_LEN;
468189251Ssam
469189251Ssam	return key;
470189251Ssam}
471189251Ssam
472189251Ssam
473189251Ssamint eap_peer_sake_register(void)
474189251Ssam{
475189251Ssam	struct eap_method *eap;
476189251Ssam	int ret;
477189251Ssam
478189251Ssam	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
479189251Ssam				    EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
480189251Ssam	if (eap == NULL)
481189251Ssam		return -1;
482189251Ssam
483189251Ssam	eap->init = eap_sake_init;
484189251Ssam	eap->deinit = eap_sake_deinit;
485189251Ssam	eap->process = eap_sake_process;
486189251Ssam	eap->isKeyAvailable = eap_sake_isKeyAvailable;
487189251Ssam	eap->getKey = eap_sake_getKey;
488189251Ssam	eap->get_emsk = eap_sake_get_emsk;
489189251Ssam
490189251Ssam	ret = eap_peer_method_register(eap);
491189251Ssam	if (ret)
492189251Ssam		eap_peer_method_free(eap);
493189251Ssam	return ret;
494189251Ssam}
495