1/*
2 * hostapd / EAP-PAX (RFC 4746) server
3 * Copyright (c) 2005-2007, 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_server/eap_i.h"
14#include "eap_common/eap_pax_common.h"
15
16/*
17 * Note: only PAX_STD subprotocol is currently supported
18 *
19 * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
20 * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
21 * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
22 * RSAES-OAEP).
23 */
24
25struct eap_pax_data {
26	enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
27	u8 mac_id;
28	union {
29		u8 e[2 * EAP_PAX_RAND_LEN];
30		struct {
31			u8 x[EAP_PAX_RAND_LEN]; /* server rand */
32			u8 y[EAP_PAX_RAND_LEN]; /* client rand */
33		} r;
34	} rand;
35	u8 ak[EAP_PAX_AK_LEN];
36	u8 mk[EAP_PAX_MK_LEN];
37	u8 ck[EAP_PAX_CK_LEN];
38	u8 ick[EAP_PAX_ICK_LEN];
39	u8 mid[EAP_PAX_MID_LEN];
40	int keys_set;
41	char *cid;
42	size_t cid_len;
43};
44
45
46static void * eap_pax_init(struct eap_sm *sm)
47{
48	struct eap_pax_data *data;
49
50	data = os_zalloc(sizeof(*data));
51	if (data == NULL)
52		return NULL;
53	data->state = PAX_STD_1;
54	/*
55	 * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
56	 * supported
57	 */
58	data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
59
60	return data;
61}
62
63
64static void eap_pax_reset(struct eap_sm *sm, void *priv)
65{
66	struct eap_pax_data *data = priv;
67	os_free(data->cid);
68	bin_clear_free(data, sizeof(*data));
69}
70
71
72static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
73					   struct eap_pax_data *data, u8 id)
74{
75	struct wpabuf *req;
76	struct eap_pax_hdr *pax;
77	u8 *pos;
78
79	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
80
81	if (random_get_bytes(data->rand.r.x, EAP_PAX_RAND_LEN)) {
82		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
83		data->state = FAILURE;
84		return NULL;
85	}
86
87	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
88			    sizeof(*pax) + 2 + EAP_PAX_RAND_LEN +
89			    EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
90	if (req == NULL) {
91		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
92			   "request");
93		data->state = FAILURE;
94		return NULL;
95	}
96
97	pax = wpabuf_put(req, sizeof(*pax));
98	pax->op_code = EAP_PAX_OP_STD_1;
99	pax->flags = 0;
100	pax->mac_id = data->mac_id;
101	pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
102	pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
103
104	wpabuf_put_be16(req, EAP_PAX_RAND_LEN);
105	wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN);
106	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
107		    data->rand.r.x, EAP_PAX_RAND_LEN);
108
109	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
110	if (eap_pax_mac(data->mac_id, (u8 *) "", 0,
111			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
112			NULL, 0, NULL, 0, pos) < 0) {
113		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
114		data->state = FAILURE;
115		wpabuf_free(req);
116		return NULL;
117	}
118	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
119
120	return req;
121}
122
123
124static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
125					   struct eap_pax_data *data, u8 id)
126{
127	struct wpabuf *req;
128	struct eap_pax_hdr *pax;
129	u8 *pos;
130
131	wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
132
133	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
134			    sizeof(*pax) + 2 + EAP_PAX_MAC_LEN +
135			    EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
136	if (req == NULL) {
137		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
138			   "request");
139		data->state = FAILURE;
140		return NULL;
141	}
142
143	pax = wpabuf_put(req, sizeof(*pax));
144	pax->op_code = EAP_PAX_OP_STD_3;
145	pax->flags = 0;
146	pax->mac_id = data->mac_id;
147	pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
148	pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
149
150	wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
151	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
152	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
153			data->rand.r.y, EAP_PAX_RAND_LEN,
154			(u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) {
155		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC");
156		data->state = FAILURE;
157		wpabuf_free(req);
158		return NULL;
159	}
160	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
161		    pos, EAP_PAX_MAC_LEN);
162
163	/* Optional ADE could be added here, if needed */
164
165	pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
166	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
167			wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
168			NULL, 0, NULL, 0, pos) < 0) {
169		wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
170		data->state = FAILURE;
171		wpabuf_free(req);
172		return NULL;
173	}
174	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
175
176	return req;
177}
178
179
180static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id)
181{
182	struct eap_pax_data *data = priv;
183
184	switch (data->state) {
185	case PAX_STD_1:
186		return eap_pax_build_std_1(sm, data, id);
187	case PAX_STD_3:
188		return eap_pax_build_std_3(sm, data, id);
189	default:
190		wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
191			   data->state);
192		break;
193	}
194	return NULL;
195}
196
197
198static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
199			     struct wpabuf *respData)
200{
201	struct eap_pax_data *data = priv;
202	struct eap_pax_hdr *resp;
203	const u8 *pos;
204	size_t len, mlen;
205	u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
206
207	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
208	if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
209		wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
210		return TRUE;
211	}
212
213	mlen = sizeof(struct eap_hdr) + 1 + len;
214	resp = (struct eap_pax_hdr *) pos;
215
216	wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
217		   "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
218		   "public_key_id 0x%x",
219		   resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
220		   resp->public_key_id);
221	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
222		    (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
223
224	if (data->state == PAX_STD_1 &&
225	    resp->op_code != EAP_PAX_OP_STD_2) {
226		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
227			   "ignore op %d", resp->op_code);
228		return TRUE;
229	}
230
231	if (data->state == PAX_STD_3 &&
232	    resp->op_code != EAP_PAX_OP_ACK) {
233		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
234			   "ignore op %d", resp->op_code);
235		return TRUE;
236	}
237
238	if (resp->op_code != EAP_PAX_OP_STD_2 &&
239	    resp->op_code != EAP_PAX_OP_ACK) {
240		wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
241			   resp->op_code);
242	}
243
244	if (data->mac_id != resp->mac_id) {
245		wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
246			   "received 0x%x", data->mac_id, resp->mac_id);
247		return TRUE;
248	}
249
250	if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
251		wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
252			   "received 0x%x", EAP_PAX_DH_GROUP_NONE,
253			   resp->dh_group_id);
254		return TRUE;
255	}
256
257	if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
258		wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
259			   "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
260			   resp->public_key_id);
261		return TRUE;
262	}
263
264	if (resp->flags & EAP_PAX_FLAGS_MF) {
265		/* TODO: add support for reassembling fragments */
266		wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
267		return TRUE;
268	}
269
270	if (resp->flags & EAP_PAX_FLAGS_CE) {
271		wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
272		return TRUE;
273	}
274
275	if (data->keys_set) {
276		if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
277			wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
278			return TRUE;
279		}
280		icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
281		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
282		if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
283				wpabuf_mhead(respData),
284				wpabuf_len(respData) - EAP_PAX_ICV_LEN,
285				NULL, 0, NULL, 0, icvbuf) < 0) {
286			wpa_printf(MSG_INFO,
287				   "EAP-PAX: Failed to calculate ICV");
288			return TRUE;
289		}
290
291		if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
292			wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
293			wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
294				    icvbuf, EAP_PAX_ICV_LEN);
295			return TRUE;
296		}
297	}
298
299	return FALSE;
300}
301
302
303static void eap_pax_process_std_2(struct eap_sm *sm,
304				  struct eap_pax_data *data,
305				  struct wpabuf *respData)
306{
307	struct eap_pax_hdr *resp;
308	u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
309	const u8 *pos;
310	size_t len, left, cid_len;
311	int i;
312
313	if (data->state != PAX_STD_1)
314		return;
315
316	wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
317
318	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
319	if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN)
320		return;
321
322	resp = (struct eap_pax_hdr *) pos;
323	pos = (u8 *) (resp + 1);
324	left = len - sizeof(*resp);
325
326	if (left < 2 + EAP_PAX_RAND_LEN ||
327	    WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
328		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
329		return;
330	}
331	pos += 2;
332	left -= 2;
333	os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
334	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
335		    data->rand.r.y, EAP_PAX_RAND_LEN);
336	pos += EAP_PAX_RAND_LEN;
337	left -= EAP_PAX_RAND_LEN;
338
339	if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
340		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
341		return;
342	}
343	cid_len = WPA_GET_BE16(pos);
344	if (cid_len > 1500) {
345		wpa_printf(MSG_INFO, "EAP-PAX: Too long CID");
346		return;
347	}
348	data->cid_len = cid_len;
349	os_free(data->cid);
350	data->cid = os_memdup(pos + 2, data->cid_len);
351	if (data->cid == NULL) {
352		wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
353			   "CID");
354		return;
355	}
356	pos += 2 + data->cid_len;
357	left -= 2 + data->cid_len;
358	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
359			  (u8 *) data->cid, data->cid_len);
360
361	if (left < 2 + EAP_PAX_MAC_LEN ||
362	    WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
363		wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
364		return;
365	}
366	pos += 2;
367	left -= 2;
368	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
369		    pos, EAP_PAX_MAC_LEN);
370
371	if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
372		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
373				  (u8 *) data->cid, data->cid_len);
374		data->state = FAILURE;
375		return;
376	}
377
378	for (i = 0;
379	     i < EAP_MAX_METHODS &&
380		     (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
381		      sm->user->methods[i].method != EAP_TYPE_NONE);
382	     i++) {
383		if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
384		    sm->user->methods[i].method == EAP_TYPE_PAX)
385			break;
386	}
387
388	if (i >= EAP_MAX_METHODS ||
389	    sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
390	    sm->user->methods[i].method != EAP_TYPE_PAX) {
391		wpa_hexdump_ascii(MSG_DEBUG,
392				  "EAP-PAX: EAP-PAX not enabled for CID",
393				  (u8 *) data->cid, data->cid_len);
394		data->state = FAILURE;
395		return;
396	}
397
398	if (sm->user->password == NULL ||
399	    sm->user->password_len != EAP_PAX_AK_LEN) {
400		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
401				  "user database for CID",
402				  (u8 *) data->cid, data->cid_len);
403		data->state = FAILURE;
404		return;
405	}
406	os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
407
408	if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
409					   data->rand.e, data->mk, data->ck,
410					   data->ick, data->mid) < 0) {
411		wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
412			   "key derivation");
413		data->state = FAILURE;
414		return;
415	}
416	data->keys_set = 1;
417
418	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
419			data->rand.r.x, EAP_PAX_RAND_LEN,
420			data->rand.r.y, EAP_PAX_RAND_LEN,
421			(u8 *) data->cid, data->cid_len, mac) < 0) {
422		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate MAC_CK");
423		data->state = FAILURE;
424		return;
425	}
426
427	if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) {
428		wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
429			   "PAX_STD-2");
430		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
431			    mac, EAP_PAX_MAC_LEN);
432		data->state = FAILURE;
433		return;
434	}
435
436	pos += EAP_PAX_MAC_LEN;
437	left -= EAP_PAX_MAC_LEN;
438
439	if (left < EAP_PAX_ICV_LEN) {
440		wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
441			   "PAX_STD-2", (unsigned long) left);
442		return;
443	}
444	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
445	if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
446			wpabuf_head(respData),
447			wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0,
448			NULL, 0, icvbuf) < 0) {
449		wpa_printf(MSG_INFO, "EAP-PAX: Failed to calculate ICV");
450		return;
451	}
452
453	if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
454		wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
455		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
456			    icvbuf, EAP_PAX_ICV_LEN);
457		return;
458	}
459	pos += EAP_PAX_ICV_LEN;
460	left -= EAP_PAX_ICV_LEN;
461
462	if (left > 0) {
463		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
464			    pos, left);
465	}
466
467	data->state = PAX_STD_3;
468}
469
470
471static void eap_pax_process_ack(struct eap_sm *sm,
472				struct eap_pax_data *data,
473				struct wpabuf *respData)
474{
475	if (data->state != PAX_STD_3)
476		return;
477
478	wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
479		   "completed successfully");
480	data->state = SUCCESS;
481}
482
483
484static void eap_pax_process(struct eap_sm *sm, void *priv,
485			    struct wpabuf *respData)
486{
487	struct eap_pax_data *data = priv;
488	struct eap_pax_hdr *resp;
489	const u8 *pos;
490	size_t len;
491
492	if (sm->user == NULL || sm->user->password == NULL) {
493		wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
494			   "configured");
495		data->state = FAILURE;
496		return;
497	}
498
499	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
500	if (pos == NULL || len < sizeof(*resp))
501		return;
502
503	resp = (struct eap_pax_hdr *) pos;
504
505	switch (resp->op_code) {
506	case EAP_PAX_OP_STD_2:
507		eap_pax_process_std_2(sm, data, respData);
508		break;
509	case EAP_PAX_OP_ACK:
510		eap_pax_process_ack(sm, data, respData);
511		break;
512	}
513}
514
515
516static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
517{
518	struct eap_pax_data *data = priv;
519	return data->state == SUCCESS || data->state == FAILURE;
520}
521
522
523static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
524{
525	struct eap_pax_data *data = priv;
526	u8 *key;
527
528	if (data->state != SUCCESS)
529		return NULL;
530
531	key = os_malloc(EAP_MSK_LEN);
532	if (key == NULL)
533		return NULL;
534
535	*len = EAP_MSK_LEN;
536	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
537		    "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
538		    EAP_MSK_LEN, key);
539
540	return key;
541}
542
543
544static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
545{
546	struct eap_pax_data *data = priv;
547	u8 *key;
548
549	if (data->state != SUCCESS)
550		return NULL;
551
552	key = os_malloc(EAP_EMSK_LEN);
553	if (key == NULL)
554		return NULL;
555
556	*len = EAP_EMSK_LEN;
557	eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
558		    "Extended Master Session Key",
559		    data->rand.e, 2 * EAP_PAX_RAND_LEN,
560		    EAP_EMSK_LEN, key);
561
562	return key;
563}
564
565
566static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
567{
568	struct eap_pax_data *data = priv;
569	return data->state == SUCCESS;
570}
571
572
573static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
574{
575	struct eap_pax_data *data = priv;
576	u8 *sid;
577
578	if (data->state != SUCCESS)
579		return NULL;
580
581	sid = os_malloc(1 + EAP_PAX_MID_LEN);
582	if (sid == NULL)
583		return NULL;
584
585	*len = 1 + EAP_PAX_MID_LEN;
586	sid[0] = EAP_TYPE_PAX;
587	os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
588
589	return sid;
590}
591
592
593int eap_server_pax_register(void)
594{
595	struct eap_method *eap;
596
597	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
598				      EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
599	if (eap == NULL)
600		return -1;
601
602	eap->init = eap_pax_init;
603	eap->reset = eap_pax_reset;
604	eap->buildReq = eap_pax_buildReq;
605	eap->check = eap_pax_check;
606	eap->process = eap_pax_process;
607	eap->isDone = eap_pax_isDone;
608	eap->getKey = eap_pax_getKey;
609	eap->isSuccess = eap_pax_isSuccess;
610	eap->get_emsk = eap_pax_get_emsk;
611	eap->getSessionId = eap_pax_get_session_id;
612
613	return eap_server_method_register(eap);
614}
615