2 * EAP peer method: EAP-SAKE (RFC 4763)
3 * Copyright (c) 2006-2008, 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_peer/eap_i.h"
19 #include "eap_common/eap_sake_common.h"
21 struct eap_sake_data
{
22 enum { IDENTITY
, CHALLENGE
, CONFIRM
, SUCCESS
, FAILURE
} state
;
23 u8 root_secret_a
[EAP_SAKE_ROOT_SECRET_LEN
];
24 u8 root_secret_b
[EAP_SAKE_ROOT_SECRET_LEN
];
25 u8 rand_s
[EAP_SAKE_RAND_LEN
];
26 u8 rand_p
[EAP_SAKE_RAND_LEN
];
28 u8 auth
[EAP_SAKE_TEK_AUTH_LEN
];
29 u8 cipher
[EAP_SAKE_TEK_CIPHER_LEN
];
32 u8 emsk
[EAP_EMSK_LEN
];
42 static const char * eap_sake_state_txt(int state
)
61 static void eap_sake_state(struct eap_sake_data
*data
, int state
)
63 wpa_printf(MSG_DEBUG
, "EAP-SAKE: %s -> %s",
64 eap_sake_state_txt(data
->state
),
65 eap_sake_state_txt(state
));
70 static void eap_sake_deinit(struct eap_sm
*sm
, void *priv
);
73 static void * eap_sake_init(struct eap_sm
*sm
)
75 struct eap_sake_data
*data
;
76 const u8
*identity
, *password
;
77 size_t identity_len
, password_len
;
79 password
= eap_get_config_password(sm
, &password_len
);
80 if (!password
|| password_len
!= 2 * EAP_SAKE_ROOT_SECRET_LEN
) {
81 wpa_printf(MSG_INFO
, "EAP-SAKE: No key of correct length "
86 data
= os_zalloc(sizeof(*data
));
89 data
->state
= IDENTITY
;
91 identity
= eap_get_config_identity(sm
, &identity_len
);
93 data
->peerid
= os_malloc(identity_len
);
94 if (data
->peerid
== NULL
) {
95 eap_sake_deinit(sm
, data
);
98 os_memcpy(data
->peerid
, identity
, identity_len
);
99 data
->peerid_len
= identity_len
;
102 os_memcpy(data
->root_secret_a
, password
, EAP_SAKE_ROOT_SECRET_LEN
);
103 os_memcpy(data
->root_secret_b
,
104 password
+ EAP_SAKE_ROOT_SECRET_LEN
,
105 EAP_SAKE_ROOT_SECRET_LEN
);
111 static void eap_sake_deinit(struct eap_sm
*sm
, void *priv
)
113 struct eap_sake_data
*data
= priv
;
114 os_free(data
->serverid
);
115 os_free(data
->peerid
);
120 static struct wpabuf
* eap_sake_build_msg(struct eap_sake_data
*data
,
121 int id
, size_t length
, u8 subtype
)
123 struct eap_sake_hdr
*sake
;
127 plen
= length
+ sizeof(struct eap_sake_hdr
);
129 msg
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, plen
,
130 EAP_CODE_RESPONSE
, id
);
132 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to allocate memory "
137 sake
= wpabuf_put(msg
, sizeof(*sake
));
138 sake
->version
= EAP_SAKE_VERSION
;
139 sake
->session_id
= data
->session_id
;
140 sake
->subtype
= subtype
;
146 static struct wpabuf
* eap_sake_process_identity(struct eap_sm
*sm
,
147 struct eap_sake_data
*data
,
148 struct eap_method_ret
*ret
,
149 const struct wpabuf
*reqData
,
153 struct eap_sake_parse_attr attr
;
156 if (data
->state
!= IDENTITY
) {
161 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Request/Identity");
163 if (eap_sake_parse_attributes(payload
, payload_len
, &attr
))
166 if (!attr
.perm_id_req
&& !attr
.any_id_req
) {
167 wpa_printf(MSG_INFO
, "EAP-SAKE: No AT_PERM_ID_REQ or "
168 "AT_ANY_ID_REQ in Request/Identity");
172 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Identity");
174 resp
= eap_sake_build_msg(data
, eap_get_id(reqData
),
175 2 + data
->peerid_len
,
176 EAP_SAKE_SUBTYPE_IDENTITY
);
180 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_PEERID");
181 eap_sake_add_attr(resp
, EAP_SAKE_AT_PEERID
,
182 data
->peerid
, data
->peerid_len
);
184 eap_sake_state(data
, CHALLENGE
);
190 static struct wpabuf
* eap_sake_process_challenge(struct eap_sm
*sm
,
191 struct eap_sake_data
*data
,
192 struct eap_method_ret
*ret
,
193 const struct wpabuf
*reqData
,
197 struct eap_sake_parse_attr attr
;
202 if (data
->state
!= IDENTITY
&& data
->state
!= CHALLENGE
) {
203 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Request/Challenge received "
204 "in unexpected state (%d)", data
->state
);
208 if (data
->state
== IDENTITY
)
209 eap_sake_state(data
, CHALLENGE
);
211 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Request/Challenge");
213 if (eap_sake_parse_attributes(payload
, payload_len
, &attr
))
217 wpa_printf(MSG_INFO
, "EAP-SAKE: Request/Challenge did not "
218 "include AT_RAND_S");
222 os_memcpy(data
->rand_s
, attr
.rand_s
, EAP_SAKE_RAND_LEN
);
223 wpa_hexdump(MSG_MSGDUMP
, "EAP-SAKE: RAND_S (server rand)",
224 data
->rand_s
, EAP_SAKE_RAND_LEN
);
226 if (os_get_random(data
->rand_p
, EAP_SAKE_RAND_LEN
)) {
227 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to get random data");
230 wpa_hexdump(MSG_MSGDUMP
, "EAP-SAKE: RAND_P (peer rand)",
231 data
->rand_p
, EAP_SAKE_RAND_LEN
);
233 os_free(data
->serverid
);
234 data
->serverid
= NULL
;
235 data
->serverid_len
= 0;
237 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-SAKE: SERVERID",
238 attr
.serverid
, attr
.serverid_len
);
239 data
->serverid
= os_malloc(attr
.serverid_len
);
240 if (data
->serverid
== NULL
)
242 os_memcpy(data
->serverid
, attr
.serverid
, attr
.serverid_len
);
243 data
->serverid_len
= attr
.serverid_len
;
246 eap_sake_derive_keys(data
->root_secret_a
, data
->root_secret_b
,
247 data
->rand_s
, data
->rand_p
,
248 (u8
*) &data
->tek
, data
->msk
, data
->emsk
);
250 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Challenge");
252 rlen
= 2 + EAP_SAKE_RAND_LEN
+ 2 + EAP_SAKE_MIC_LEN
;
254 rlen
+= 2 + data
->peerid_len
;
255 resp
= eap_sake_build_msg(data
, eap_get_id(reqData
), rlen
,
256 EAP_SAKE_SUBTYPE_CHALLENGE
);
260 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_RAND_P");
261 eap_sake_add_attr(resp
, EAP_SAKE_AT_RAND_P
,
262 data
->rand_p
, EAP_SAKE_RAND_LEN
);
265 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_PEERID");
266 eap_sake_add_attr(resp
, EAP_SAKE_AT_PEERID
,
267 data
->peerid
, data
->peerid_len
);
270 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_MIC_P");
271 wpabuf_put_u8(resp
, EAP_SAKE_AT_MIC_P
);
272 wpabuf_put_u8(resp
, 2 + EAP_SAKE_MIC_LEN
);
273 rpos
= wpabuf_put(resp
, EAP_SAKE_MIC_LEN
);
274 if (eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
275 data
->serverid
, data
->serverid_len
,
276 data
->peerid
, data
->peerid_len
, 1,
277 wpabuf_head(resp
), wpabuf_len(resp
), rpos
,
279 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to compute MIC");
284 eap_sake_state(data
, CONFIRM
);
290 static struct wpabuf
* eap_sake_process_confirm(struct eap_sm
*sm
,
291 struct eap_sake_data
*data
,
292 struct eap_method_ret
*ret
,
293 const struct wpabuf
*reqData
,
297 struct eap_sake_parse_attr attr
;
298 u8 mic_s
[EAP_SAKE_MIC_LEN
];
302 if (data
->state
!= CONFIRM
) {
307 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Request/Confirm");
309 if (eap_sake_parse_attributes(payload
, payload_len
, &attr
))
313 wpa_printf(MSG_INFO
, "EAP-SAKE: Request/Confirm did not "
318 eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
319 data
->serverid
, data
->serverid_len
,
320 data
->peerid
, data
->peerid_len
, 0,
321 wpabuf_head(reqData
), wpabuf_len(reqData
),
323 if (os_memcmp(attr
.mic_s
, mic_s
, EAP_SAKE_MIC_LEN
) != 0) {
324 wpa_printf(MSG_INFO
, "EAP-SAKE: Incorrect AT_MIC_S");
325 eap_sake_state(data
, FAILURE
);
326 ret
->methodState
= METHOD_DONE
;
327 ret
->decision
= DECISION_FAIL
;
328 ret
->allowNotifications
= FALSE
;
329 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending "
330 "Response/Auth-Reject");
331 return eap_sake_build_msg(data
, eap_get_id(reqData
), 0,
332 EAP_SAKE_SUBTYPE_AUTH_REJECT
);
335 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Confirm");
337 resp
= eap_sake_build_msg(data
, eap_get_id(reqData
),
338 2 + EAP_SAKE_MIC_LEN
,
339 EAP_SAKE_SUBTYPE_CONFIRM
);
343 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_MIC_P");
344 wpabuf_put_u8(resp
, EAP_SAKE_AT_MIC_P
);
345 wpabuf_put_u8(resp
, 2 + EAP_SAKE_MIC_LEN
);
346 rpos
= wpabuf_put(resp
, EAP_SAKE_MIC_LEN
);
347 if (eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
348 data
->serverid
, data
->serverid_len
,
349 data
->peerid
, data
->peerid_len
, 1,
350 wpabuf_head(resp
), wpabuf_len(resp
), rpos
,
352 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to compute MIC");
357 eap_sake_state(data
, SUCCESS
);
358 ret
->methodState
= METHOD_DONE
;
359 ret
->decision
= DECISION_UNCOND_SUCC
;
360 ret
->allowNotifications
= FALSE
;
366 static struct wpabuf
* eap_sake_process(struct eap_sm
*sm
, void *priv
,
367 struct eap_method_ret
*ret
,
368 const struct wpabuf
*reqData
)
370 struct eap_sake_data
*data
= priv
;
371 const struct eap_sake_hdr
*req
;
375 u8 subtype
, session_id
;
377 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, reqData
, &len
);
378 if (pos
== NULL
|| len
< sizeof(struct eap_sake_hdr
)) {
383 req
= (const struct eap_sake_hdr
*) pos
;
385 subtype
= req
->subtype
;
386 session_id
= req
->session_id
;
387 pos
= (const u8
*) (req
+ 1);
389 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received frame: subtype %d "
390 "session_id %d", subtype
, session_id
);
391 wpa_hexdump(MSG_DEBUG
, "EAP-SAKE: Received attributes",
394 if (data
->session_id_set
&& data
->session_id
!= session_id
) {
395 wpa_printf(MSG_INFO
, "EAP-SAKE: Session ID mismatch (%d,%d)",
396 session_id
, data
->session_id
);
400 data
->session_id
= session_id
;
401 data
->session_id_set
= 1;
404 ret
->methodState
= METHOD_MAY_CONT
;
405 ret
->decision
= DECISION_FAIL
;
406 ret
->allowNotifications
= TRUE
;
409 case EAP_SAKE_SUBTYPE_IDENTITY
:
410 resp
= eap_sake_process_identity(sm
, data
, ret
, reqData
,
413 case EAP_SAKE_SUBTYPE_CHALLENGE
:
414 resp
= eap_sake_process_challenge(sm
, data
, ret
, reqData
,
417 case EAP_SAKE_SUBTYPE_CONFIRM
:
418 resp
= eap_sake_process_confirm(sm
, data
, ret
, reqData
,
422 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Ignoring message with "
423 "unknown subtype %d", subtype
);
428 if (ret
->methodState
== METHOD_DONE
)
429 ret
->allowNotifications
= FALSE
;
435 static Boolean
eap_sake_isKeyAvailable(struct eap_sm
*sm
, void *priv
)
437 struct eap_sake_data
*data
= priv
;
438 return data
->state
== SUCCESS
;
442 static u8
* eap_sake_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
444 struct eap_sake_data
*data
= priv
;
447 if (data
->state
!= SUCCESS
)
450 key
= os_malloc(EAP_MSK_LEN
);
453 os_memcpy(key
, data
->msk
, EAP_MSK_LEN
);
460 static u8
* eap_sake_get_emsk(struct eap_sm
*sm
, void *priv
, size_t *len
)
462 struct eap_sake_data
*data
= priv
;
465 if (data
->state
!= SUCCESS
)
468 key
= os_malloc(EAP_EMSK_LEN
);
471 os_memcpy(key
, data
->emsk
, EAP_EMSK_LEN
);
478 int eap_peer_sake_register(void)
480 struct eap_method
*eap
;
483 eap
= eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION
,
484 EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, "SAKE");
488 eap
->init
= eap_sake_init
;
489 eap
->deinit
= eap_sake_deinit
;
490 eap
->process
= eap_sake_process
;
491 eap
->isKeyAvailable
= eap_sake_isKeyAvailable
;
492 eap
->getKey
= eap_sake_getKey
;
493 eap
->get_emsk
= eap_sake_get_emsk
;
495 ret
= eap_peer_method_register(eap
);
497 eap_peer_method_free(eap
);