2 * hostapd / EAP-SAKE (RFC 4763) server
3 * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
12 #include "crypto/random.h"
13 #include "eap_server/eap_i.h"
14 #include "eap_common/eap_sake_common.h"
17 struct eap_sake_data
{
18 enum { IDENTITY
, CHALLENGE
, CONFIRM
, SUCCESS
, FAILURE
} state
;
19 u8 rand_s
[EAP_SAKE_RAND_LEN
];
20 u8 rand_p
[EAP_SAKE_RAND_LEN
];
22 u8 auth
[EAP_SAKE_TEK_AUTH_LEN
];
23 u8 cipher
[EAP_SAKE_TEK_CIPHER_LEN
];
26 u8 emsk
[EAP_EMSK_LEN
];
33 static const char * eap_sake_state_txt(int state
)
52 static void eap_sake_state(struct eap_sake_data
*data
, int state
)
54 wpa_printf(MSG_DEBUG
, "EAP-SAKE: %s -> %s",
55 eap_sake_state_txt(data
->state
),
56 eap_sake_state_txt(state
));
61 static void * eap_sake_init(struct eap_sm
*sm
)
63 struct eap_sake_data
*data
;
65 data
= os_zalloc(sizeof(*data
));
68 data
->state
= CHALLENGE
;
70 if (os_get_random(&data
->session_id
, 1)) {
71 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to get random data");
75 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Initialized Session ID %d",
82 static void eap_sake_reset(struct eap_sm
*sm
, void *priv
)
84 struct eap_sake_data
*data
= priv
;
85 os_free(data
->peerid
);
86 bin_clear_free(data
, sizeof(*data
));
90 static struct wpabuf
* eap_sake_build_msg(struct eap_sake_data
*data
,
91 u8 id
, size_t length
, u8 subtype
)
93 struct eap_sake_hdr
*sake
;
97 plen
= sizeof(struct eap_sake_hdr
) + length
;
99 msg
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, plen
,
100 EAP_CODE_REQUEST
, id
);
102 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to allocate memory "
107 sake
= wpabuf_put(msg
, sizeof(*sake
));
108 sake
->version
= EAP_SAKE_VERSION
;
109 sake
->session_id
= data
->session_id
;
110 sake
->subtype
= subtype
;
116 static struct wpabuf
* eap_sake_build_identity(struct eap_sm
*sm
,
117 struct eap_sake_data
*data
,
123 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Request/Identity");
126 plen
+= 2 + sm
->server_id_len
;
127 msg
= eap_sake_build_msg(data
, id
, plen
, EAP_SAKE_SUBTYPE_IDENTITY
);
129 data
->state
= FAILURE
;
133 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_PERM_ID_REQ");
134 eap_sake_add_attr(msg
, EAP_SAKE_AT_PERM_ID_REQ
, NULL
, 2);
136 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_SERVERID");
137 eap_sake_add_attr(msg
, EAP_SAKE_AT_SERVERID
,
138 sm
->server_id
, sm
->server_id_len
);
144 static struct wpabuf
* eap_sake_build_challenge(struct eap_sm
*sm
,
145 struct eap_sake_data
*data
,
151 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Request/Challenge");
153 if (random_get_bytes(data
->rand_s
, EAP_SAKE_RAND_LEN
)) {
154 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to get random data");
155 data
->state
= FAILURE
;
158 wpa_hexdump(MSG_MSGDUMP
, "EAP-SAKE: RAND_S (server rand)",
159 data
->rand_s
, EAP_SAKE_RAND_LEN
);
161 plen
= 2 + EAP_SAKE_RAND_LEN
+ 2 + sm
->server_id_len
;
162 msg
= eap_sake_build_msg(data
, id
, plen
, EAP_SAKE_SUBTYPE_CHALLENGE
);
164 data
->state
= FAILURE
;
168 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_RAND_S");
169 eap_sake_add_attr(msg
, EAP_SAKE_AT_RAND_S
,
170 data
->rand_s
, EAP_SAKE_RAND_LEN
);
172 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_SERVERID");
173 eap_sake_add_attr(msg
, EAP_SAKE_AT_SERVERID
,
174 sm
->server_id
, sm
->server_id_len
);
180 static struct wpabuf
* eap_sake_build_confirm(struct eap_sm
*sm
,
181 struct eap_sake_data
*data
,
187 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Request/Confirm");
189 msg
= eap_sake_build_msg(data
, id
, 2 + EAP_SAKE_MIC_LEN
,
190 EAP_SAKE_SUBTYPE_CONFIRM
);
192 data
->state
= FAILURE
;
196 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_MIC_S");
197 wpabuf_put_u8(msg
, EAP_SAKE_AT_MIC_S
);
198 wpabuf_put_u8(msg
, 2 + EAP_SAKE_MIC_LEN
);
199 mic
= wpabuf_put(msg
, EAP_SAKE_MIC_LEN
);
200 if (eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
201 sm
->server_id
, sm
->server_id_len
,
202 data
->peerid
, data
->peerid_len
, 0,
203 wpabuf_head(msg
), wpabuf_len(msg
), mic
, mic
))
205 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to compute MIC");
206 data
->state
= FAILURE
;
215 static struct wpabuf
* eap_sake_buildReq(struct eap_sm
*sm
, void *priv
, u8 id
)
217 struct eap_sake_data
*data
= priv
;
219 switch (data
->state
) {
221 return eap_sake_build_identity(sm
, data
, id
);
223 return eap_sake_build_challenge(sm
, data
, id
);
225 return eap_sake_build_confirm(sm
, data
, id
);
227 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Unknown state %d in buildReq",
235 static Boolean
eap_sake_check(struct eap_sm
*sm
, void *priv
,
236 struct wpabuf
*respData
)
238 struct eap_sake_data
*data
= priv
;
239 struct eap_sake_hdr
*resp
;
241 u8 version
, session_id
, subtype
;
244 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, respData
, &len
);
245 if (pos
== NULL
|| len
< sizeof(struct eap_sake_hdr
)) {
246 wpa_printf(MSG_INFO
, "EAP-SAKE: Invalid frame");
250 resp
= (struct eap_sake_hdr
*) pos
;
251 version
= resp
->version
;
252 session_id
= resp
->session_id
;
253 subtype
= resp
->subtype
;
255 if (version
!= EAP_SAKE_VERSION
) {
256 wpa_printf(MSG_INFO
, "EAP-SAKE: Unknown version %d", version
);
260 if (session_id
!= data
->session_id
) {
261 wpa_printf(MSG_INFO
, "EAP-SAKE: Session ID mismatch (%d,%d)",
262 session_id
, data
->session_id
);
266 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received frame: subtype=%d", subtype
);
268 if (data
->state
== IDENTITY
&& subtype
== EAP_SAKE_SUBTYPE_IDENTITY
)
271 if (data
->state
== CHALLENGE
&& subtype
== EAP_SAKE_SUBTYPE_CHALLENGE
)
274 if (data
->state
== CONFIRM
&& subtype
== EAP_SAKE_SUBTYPE_CONFIRM
)
277 if (subtype
== EAP_SAKE_SUBTYPE_AUTH_REJECT
)
280 wpa_printf(MSG_INFO
, "EAP-SAKE: Unexpected subtype=%d in state=%d",
281 subtype
, data
->state
);
287 static void eap_sake_process_identity(struct eap_sm
*sm
,
288 struct eap_sake_data
*data
,
289 const struct wpabuf
*respData
,
290 const u8
*payload
, size_t payloadlen
)
292 if (data
->state
!= IDENTITY
)
295 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Response/Identity");
296 /* TODO: update identity and select new user data */
297 eap_sake_state(data
, CHALLENGE
);
301 static void eap_sake_process_challenge(struct eap_sm
*sm
,
302 struct eap_sake_data
*data
,
303 const struct wpabuf
*respData
,
304 const u8
*payload
, size_t payloadlen
)
306 struct eap_sake_parse_attr attr
;
307 u8 mic_p
[EAP_SAKE_MIC_LEN
];
309 if (data
->state
!= CHALLENGE
)
312 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Response/Challenge");
314 if (eap_sake_parse_attributes(payload
, payloadlen
, &attr
))
317 if (!attr
.rand_p
|| !attr
.mic_p
) {
318 wpa_printf(MSG_INFO
, "EAP-SAKE: Response/Challenge did not "
319 "include AT_RAND_P or AT_MIC_P");
323 os_memcpy(data
->rand_p
, attr
.rand_p
, EAP_SAKE_RAND_LEN
);
325 os_free(data
->peerid
);
327 data
->peerid_len
= 0;
329 data
->peerid
= os_memdup(attr
.peerid
, attr
.peerid_len
);
330 if (data
->peerid
== NULL
)
332 data
->peerid_len
= attr
.peerid_len
;
335 if (sm
->user
== NULL
|| sm
->user
->password
== NULL
||
336 sm
->user
->password_len
!= 2 * EAP_SAKE_ROOT_SECRET_LEN
) {
337 wpa_printf(MSG_INFO
, "EAP-SAKE: Plaintext password with "
338 "%d-byte key not configured",
339 2 * EAP_SAKE_ROOT_SECRET_LEN
);
340 data
->state
= FAILURE
;
343 eap_sake_derive_keys(sm
->user
->password
,
344 sm
->user
->password
+ EAP_SAKE_ROOT_SECRET_LEN
,
345 data
->rand_s
, data
->rand_p
,
346 (u8
*) &data
->tek
, data
->msk
, data
->emsk
);
348 eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
349 sm
->server_id
, sm
->server_id_len
,
350 data
->peerid
, data
->peerid_len
, 1,
351 wpabuf_head(respData
), wpabuf_len(respData
),
353 if (os_memcmp_const(attr
.mic_p
, mic_p
, EAP_SAKE_MIC_LEN
) != 0) {
354 wpa_printf(MSG_INFO
, "EAP-SAKE: Incorrect AT_MIC_P");
355 eap_sake_state(data
, FAILURE
);
359 eap_sake_state(data
, CONFIRM
);
363 static void eap_sake_process_confirm(struct eap_sm
*sm
,
364 struct eap_sake_data
*data
,
365 const struct wpabuf
*respData
,
366 const u8
*payload
, size_t payloadlen
)
368 struct eap_sake_parse_attr attr
;
369 u8 mic_p
[EAP_SAKE_MIC_LEN
];
371 if (data
->state
!= CONFIRM
)
374 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Response/Confirm");
376 if (eap_sake_parse_attributes(payload
, payloadlen
, &attr
))
380 wpa_printf(MSG_INFO
, "EAP-SAKE: Response/Confirm did not "
385 eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
386 sm
->server_id
, sm
->server_id_len
,
387 data
->peerid
, data
->peerid_len
, 1,
388 wpabuf_head(respData
), wpabuf_len(respData
),
390 if (os_memcmp_const(attr
.mic_p
, mic_p
, EAP_SAKE_MIC_LEN
) != 0) {
391 wpa_printf(MSG_INFO
, "EAP-SAKE: Incorrect AT_MIC_P");
392 eap_sake_state(data
, FAILURE
);
394 eap_sake_state(data
, SUCCESS
);
398 static void eap_sake_process_auth_reject(struct eap_sm
*sm
,
399 struct eap_sake_data
*data
,
400 const struct wpabuf
*respData
,
401 const u8
*payload
, size_t payloadlen
)
403 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Response/Auth-Reject");
404 eap_sake_state(data
, FAILURE
);
408 static void eap_sake_process(struct eap_sm
*sm
, void *priv
,
409 struct wpabuf
*respData
)
411 struct eap_sake_data
*data
= priv
;
412 struct eap_sake_hdr
*resp
;
417 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, respData
, &len
);
418 if (pos
== NULL
|| len
< sizeof(struct eap_sake_hdr
))
421 resp
= (struct eap_sake_hdr
*) pos
;
423 subtype
= resp
->subtype
;
424 pos
= (u8
*) (resp
+ 1);
426 wpa_hexdump(MSG_DEBUG
, "EAP-SAKE: Received attributes",
430 case EAP_SAKE_SUBTYPE_IDENTITY
:
431 eap_sake_process_identity(sm
, data
, respData
, pos
, end
- pos
);
433 case EAP_SAKE_SUBTYPE_CHALLENGE
:
434 eap_sake_process_challenge(sm
, data
, respData
, pos
, end
- pos
);
436 case EAP_SAKE_SUBTYPE_CONFIRM
:
437 eap_sake_process_confirm(sm
, data
, respData
, pos
, end
- pos
);
439 case EAP_SAKE_SUBTYPE_AUTH_REJECT
:
440 eap_sake_process_auth_reject(sm
, data
, respData
, pos
,
447 static Boolean
eap_sake_isDone(struct eap_sm
*sm
, void *priv
)
449 struct eap_sake_data
*data
= priv
;
450 return data
->state
== SUCCESS
|| data
->state
== FAILURE
;
454 static u8
* eap_sake_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
456 struct eap_sake_data
*data
= priv
;
459 if (data
->state
!= SUCCESS
)
462 key
= os_memdup(data
->msk
, EAP_MSK_LEN
);
471 static u8
* eap_sake_get_emsk(struct eap_sm
*sm
, void *priv
, size_t *len
)
473 struct eap_sake_data
*data
= priv
;
476 if (data
->state
!= SUCCESS
)
479 key
= os_memdup(data
->emsk
, EAP_EMSK_LEN
);
488 static Boolean
eap_sake_isSuccess(struct eap_sm
*sm
, void *priv
)
490 struct eap_sake_data
*data
= priv
;
491 return data
->state
== SUCCESS
;
495 static u8
* eap_sake_get_session_id(struct eap_sm
*sm
, void *priv
, size_t *len
)
497 struct eap_sake_data
*data
= priv
;
500 if (data
->state
!= SUCCESS
)
503 *len
= 1 + 2 * EAP_SAKE_RAND_LEN
;
504 id
= os_malloc(*len
);
508 id
[0] = EAP_TYPE_SAKE
;
509 os_memcpy(id
+ 1, data
->rand_s
, EAP_SAKE_RAND_LEN
);
510 os_memcpy(id
+ 1 + EAP_SAKE_RAND_LEN
, data
->rand_s
, EAP_SAKE_RAND_LEN
);
511 wpa_hexdump(MSG_DEBUG
, "EAP-SAKE: Derived Session-Id", id
, *len
);
517 int eap_server_sake_register(void)
519 struct eap_method
*eap
;
521 eap
= eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION
,
522 EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, "SAKE");
526 eap
->init
= eap_sake_init
;
527 eap
->reset
= eap_sake_reset
;
528 eap
->buildReq
= eap_sake_buildReq
;
529 eap
->check
= eap_sake_check
;
530 eap
->process
= eap_sake_process
;
531 eap
->isDone
= eap_sake_isDone
;
532 eap
->getKey
= eap_sake_getKey
;
533 eap
->isSuccess
= eap_sake_isSuccess
;
534 eap
->get_emsk
= eap_sake_get_emsk
;
535 eap
->getSessionId
= eap_sake_get_session_id
;
537 return eap_server_method_register(eap
);