2 * hostapd / EAP-SAKE (RFC 4763) server
3 * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
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.
9 * Alternatively, this software may be distributed under the terms of BSD
12 * See README and COPYING for more details.
18 #include "eap_server/eap_i.h"
19 #include "eap_common/eap_sake_common.h"
22 struct eap_sake_data
{
23 enum { IDENTITY
, CHALLENGE
, CONFIRM
, SUCCESS
, FAILURE
} state
;
24 u8 rand_s
[EAP_SAKE_RAND_LEN
];
25 u8 rand_p
[EAP_SAKE_RAND_LEN
];
27 u8 auth
[EAP_SAKE_TEK_AUTH_LEN
];
28 u8 cipher
[EAP_SAKE_TEK_CIPHER_LEN
];
31 u8 emsk
[EAP_EMSK_LEN
];
40 static const char * eap_sake_state_txt(int state
)
59 static void eap_sake_state(struct eap_sake_data
*data
, int state
)
61 wpa_printf(MSG_DEBUG
, "EAP-SAKE: %s -> %s",
62 eap_sake_state_txt(data
->state
),
63 eap_sake_state_txt(state
));
68 static void * eap_sake_init(struct eap_sm
*sm
)
70 struct eap_sake_data
*data
;
72 data
= os_zalloc(sizeof(*data
));
75 data
->state
= CHALLENGE
;
77 if (os_get_random(&data
->session_id
, 1)) {
78 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to get random data");
82 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Initialized Session ID %d",
85 /* TODO: add support for configuring SERVERID */
86 data
->serverid
= (u8
*) os_strdup("hostapd");
88 data
->serverid_len
= os_strlen((char *) data
->serverid
);
94 static void eap_sake_reset(struct eap_sm
*sm
, void *priv
)
96 struct eap_sake_data
*data
= priv
;
97 os_free(data
->serverid
);
98 os_free(data
->peerid
);
103 static struct wpabuf
* eap_sake_build_msg(struct eap_sake_data
*data
,
104 u8 id
, size_t length
, u8 subtype
)
106 struct eap_sake_hdr
*sake
;
110 plen
= sizeof(struct eap_sake_hdr
) + length
;
112 msg
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, plen
,
113 EAP_CODE_REQUEST
, id
);
115 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to allocate memory "
120 sake
= wpabuf_put(msg
, sizeof(*sake
));
121 sake
->version
= EAP_SAKE_VERSION
;
122 sake
->session_id
= data
->session_id
;
123 sake
->subtype
= subtype
;
129 static struct wpabuf
* eap_sake_build_identity(struct eap_sm
*sm
,
130 struct eap_sake_data
*data
,
136 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Request/Identity");
140 plen
+= 2 + data
->serverid_len
;
141 msg
= eap_sake_build_msg(data
, id
, plen
, EAP_SAKE_SUBTYPE_IDENTITY
);
143 data
->state
= FAILURE
;
147 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_PERM_ID_REQ");
148 eap_sake_add_attr(msg
, EAP_SAKE_AT_PERM_ID_REQ
, NULL
, 2);
150 if (data
->serverid
) {
151 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_SERVERID");
152 eap_sake_add_attr(msg
, EAP_SAKE_AT_SERVERID
,
153 data
->serverid
, data
->serverid_len
);
160 static struct wpabuf
* eap_sake_build_challenge(struct eap_sm
*sm
,
161 struct eap_sake_data
*data
,
167 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Request/Challenge");
169 if (os_get_random(data
->rand_s
, EAP_SAKE_RAND_LEN
)) {
170 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to get random data");
171 data
->state
= FAILURE
;
174 wpa_hexdump(MSG_MSGDUMP
, "EAP-SAKE: RAND_S (server rand)",
175 data
->rand_s
, EAP_SAKE_RAND_LEN
);
177 plen
= 2 + EAP_SAKE_RAND_LEN
;
179 plen
+= 2 + data
->serverid_len
;
180 msg
= eap_sake_build_msg(data
, id
, plen
, EAP_SAKE_SUBTYPE_CHALLENGE
);
182 data
->state
= FAILURE
;
186 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_RAND_S");
187 eap_sake_add_attr(msg
, EAP_SAKE_AT_RAND_S
,
188 data
->rand_s
, EAP_SAKE_RAND_LEN
);
190 if (data
->serverid
) {
191 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_SERVERID");
192 eap_sake_add_attr(msg
, EAP_SAKE_AT_SERVERID
,
193 data
->serverid
, data
->serverid_len
);
200 static struct wpabuf
* eap_sake_build_confirm(struct eap_sm
*sm
,
201 struct eap_sake_data
*data
,
207 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Request/Confirm");
209 msg
= eap_sake_build_msg(data
, id
, 2 + EAP_SAKE_MIC_LEN
,
210 EAP_SAKE_SUBTYPE_CONFIRM
);
212 data
->state
= FAILURE
;
216 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_MIC_S");
217 wpabuf_put_u8(msg
, EAP_SAKE_AT_MIC_S
);
218 wpabuf_put_u8(msg
, 2 + EAP_SAKE_MIC_LEN
);
219 mic
= wpabuf_put(msg
, EAP_SAKE_MIC_LEN
);
220 if (eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
221 data
->serverid
, data
->serverid_len
,
222 data
->peerid
, data
->peerid_len
, 0,
223 wpabuf_head(msg
), wpabuf_len(msg
), mic
, mic
))
225 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to compute MIC");
226 data
->state
= FAILURE
;
235 static struct wpabuf
* eap_sake_buildReq(struct eap_sm
*sm
, void *priv
, u8 id
)
237 struct eap_sake_data
*data
= priv
;
239 switch (data
->state
) {
241 return eap_sake_build_identity(sm
, data
, id
);
243 return eap_sake_build_challenge(sm
, data
, id
);
245 return eap_sake_build_confirm(sm
, data
, id
);
247 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Unknown state %d in buildReq",
255 static Boolean
eap_sake_check(struct eap_sm
*sm
, void *priv
,
256 struct wpabuf
*respData
)
258 struct eap_sake_data
*data
= priv
;
259 struct eap_sake_hdr
*resp
;
261 u8 version
, session_id
, subtype
;
264 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, respData
, &len
);
265 if (pos
== NULL
|| len
< sizeof(struct eap_sake_hdr
)) {
266 wpa_printf(MSG_INFO
, "EAP-SAKE: Invalid frame");
270 resp
= (struct eap_sake_hdr
*) pos
;
271 version
= resp
->version
;
272 session_id
= resp
->session_id
;
273 subtype
= resp
->subtype
;
275 if (version
!= EAP_SAKE_VERSION
) {
276 wpa_printf(MSG_INFO
, "EAP-SAKE: Unknown version %d", version
);
280 if (session_id
!= data
->session_id
) {
281 wpa_printf(MSG_INFO
, "EAP-SAKE: Session ID mismatch (%d,%d)",
282 session_id
, data
->session_id
);
286 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received frame: subtype=%d", subtype
);
288 if (data
->state
== IDENTITY
&& subtype
== EAP_SAKE_SUBTYPE_IDENTITY
)
291 if (data
->state
== CHALLENGE
&& subtype
== EAP_SAKE_SUBTYPE_CHALLENGE
)
294 if (data
->state
== CONFIRM
&& subtype
== EAP_SAKE_SUBTYPE_CONFIRM
)
297 if (subtype
== EAP_SAKE_SUBTYPE_AUTH_REJECT
)
300 wpa_printf(MSG_INFO
, "EAP-SAKE: Unexpected subtype=%d in state=%d",
301 subtype
, data
->state
);
307 static void eap_sake_process_identity(struct eap_sm
*sm
,
308 struct eap_sake_data
*data
,
309 const struct wpabuf
*respData
,
310 const u8
*payload
, size_t payloadlen
)
312 if (data
->state
!= IDENTITY
)
315 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Response/Identity");
316 /* TODO: update identity and select new user data */
317 eap_sake_state(data
, CHALLENGE
);
321 static void eap_sake_process_challenge(struct eap_sm
*sm
,
322 struct eap_sake_data
*data
,
323 const struct wpabuf
*respData
,
324 const u8
*payload
, size_t payloadlen
)
326 struct eap_sake_parse_attr attr
;
327 u8 mic_p
[EAP_SAKE_MIC_LEN
];
329 if (data
->state
!= CHALLENGE
)
332 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Response/Challenge");
334 if (eap_sake_parse_attributes(payload
, payloadlen
, &attr
))
337 if (!attr
.rand_p
|| !attr
.mic_p
) {
338 wpa_printf(MSG_INFO
, "EAP-SAKE: Response/Challenge did not "
339 "include AT_RAND_P or AT_MIC_P");
343 os_memcpy(data
->rand_p
, attr
.rand_p
, EAP_SAKE_RAND_LEN
);
345 os_free(data
->peerid
);
347 data
->peerid_len
= 0;
349 data
->peerid
= os_malloc(attr
.peerid_len
);
350 if (data
->peerid
== NULL
)
352 os_memcpy(data
->peerid
, attr
.peerid
, attr
.peerid_len
);
353 data
->peerid_len
= attr
.peerid_len
;
356 if (sm
->user
== NULL
|| sm
->user
->password
== NULL
||
357 sm
->user
->password_len
!= 2 * EAP_SAKE_ROOT_SECRET_LEN
) {
358 wpa_printf(MSG_INFO
, "EAP-SAKE: Plaintext password with "
359 "%d-byte key not configured",
360 2 * EAP_SAKE_ROOT_SECRET_LEN
);
361 data
->state
= FAILURE
;
364 eap_sake_derive_keys(sm
->user
->password
,
365 sm
->user
->password
+ EAP_SAKE_ROOT_SECRET_LEN
,
366 data
->rand_s
, data
->rand_p
,
367 (u8
*) &data
->tek
, data
->msk
, data
->emsk
);
369 eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
370 data
->serverid
, data
->serverid_len
,
371 data
->peerid
, data
->peerid_len
, 1,
372 wpabuf_head(respData
), wpabuf_len(respData
),
374 if (os_memcmp(attr
.mic_p
, mic_p
, EAP_SAKE_MIC_LEN
) != 0) {
375 wpa_printf(MSG_INFO
, "EAP-SAKE: Incorrect AT_MIC_P");
376 eap_sake_state(data
, FAILURE
);
380 eap_sake_state(data
, CONFIRM
);
384 static void eap_sake_process_confirm(struct eap_sm
*sm
,
385 struct eap_sake_data
*data
,
386 const struct wpabuf
*respData
,
387 const u8
*payload
, size_t payloadlen
)
389 struct eap_sake_parse_attr attr
;
390 u8 mic_p
[EAP_SAKE_MIC_LEN
];
392 if (data
->state
!= CONFIRM
)
395 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Response/Confirm");
397 if (eap_sake_parse_attributes(payload
, payloadlen
, &attr
))
401 wpa_printf(MSG_INFO
, "EAP-SAKE: Response/Confirm did not "
406 eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
407 data
->serverid
, data
->serverid_len
,
408 data
->peerid
, data
->peerid_len
, 1,
409 wpabuf_head(respData
), wpabuf_len(respData
),
411 if (os_memcmp(attr
.mic_p
, mic_p
, EAP_SAKE_MIC_LEN
) != 0) {
412 wpa_printf(MSG_INFO
, "EAP-SAKE: Incorrect AT_MIC_P");
413 eap_sake_state(data
, FAILURE
);
415 eap_sake_state(data
, SUCCESS
);
419 static void eap_sake_process_auth_reject(struct eap_sm
*sm
,
420 struct eap_sake_data
*data
,
421 const struct wpabuf
*respData
,
422 const u8
*payload
, size_t payloadlen
)
424 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Response/Auth-Reject");
425 eap_sake_state(data
, FAILURE
);
429 static void eap_sake_process(struct eap_sm
*sm
, void *priv
,
430 struct wpabuf
*respData
)
432 struct eap_sake_data
*data
= priv
;
433 struct eap_sake_hdr
*resp
;
438 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, respData
, &len
);
439 if (pos
== NULL
|| len
< sizeof(struct eap_sake_hdr
))
442 resp
= (struct eap_sake_hdr
*) pos
;
444 subtype
= resp
->subtype
;
445 pos
= (u8
*) (resp
+ 1);
447 wpa_hexdump(MSG_DEBUG
, "EAP-SAKE: Received attributes",
451 case EAP_SAKE_SUBTYPE_IDENTITY
:
452 eap_sake_process_identity(sm
, data
, respData
, pos
, end
- pos
);
454 case EAP_SAKE_SUBTYPE_CHALLENGE
:
455 eap_sake_process_challenge(sm
, data
, respData
, pos
, end
- pos
);
457 case EAP_SAKE_SUBTYPE_CONFIRM
:
458 eap_sake_process_confirm(sm
, data
, respData
, pos
, end
- pos
);
460 case EAP_SAKE_SUBTYPE_AUTH_REJECT
:
461 eap_sake_process_auth_reject(sm
, data
, respData
, pos
,
468 static Boolean
eap_sake_isDone(struct eap_sm
*sm
, void *priv
)
470 struct eap_sake_data
*data
= priv
;
471 return data
->state
== SUCCESS
|| data
->state
== FAILURE
;
475 static u8
* eap_sake_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
477 struct eap_sake_data
*data
= priv
;
480 if (data
->state
!= SUCCESS
)
483 key
= os_malloc(EAP_MSK_LEN
);
486 os_memcpy(key
, data
->msk
, EAP_MSK_LEN
);
493 static u8
* eap_sake_get_emsk(struct eap_sm
*sm
, void *priv
, size_t *len
)
495 struct eap_sake_data
*data
= priv
;
498 if (data
->state
!= SUCCESS
)
501 key
= os_malloc(EAP_EMSK_LEN
);
504 os_memcpy(key
, data
->emsk
, EAP_EMSK_LEN
);
511 static Boolean
eap_sake_isSuccess(struct eap_sm
*sm
, void *priv
)
513 struct eap_sake_data
*data
= priv
;
514 return data
->state
== SUCCESS
;
518 int eap_server_sake_register(void)
520 struct eap_method
*eap
;
523 eap
= eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION
,
524 EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, "SAKE");
528 eap
->init
= eap_sake_init
;
529 eap
->reset
= eap_sake_reset
;
530 eap
->buildReq
= eap_sake_buildReq
;
531 eap
->check
= eap_sake_check
;
532 eap
->process
= eap_sake_process
;
533 eap
->isDone
= eap_sake_isDone
;
534 eap
->getKey
= eap_sake_getKey
;
535 eap
->isSuccess
= eap_sake_isSuccess
;
536 eap
->get_emsk
= eap_sake_get_emsk
;
538 ret
= eap_server_method_register(eap
);
540 eap_server_method_free(eap
);