2 * EAP peer method: EAP-SAKE (RFC 4763)
3 * Copyright (c) 2006, 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.
19 #include "config_ssid.h"
20 #include "eap_sake_common.h"
22 struct eap_sake_data
{
23 enum { IDENTITY
, CHALLENGE
, CONFIRM
, SUCCESS
, FAILURE
} state
;
24 u8 root_secret_a
[EAP_SAKE_ROOT_SECRET_LEN
];
25 u8 root_secret_b
[EAP_SAKE_ROOT_SECRET_LEN
];
26 u8 rand_s
[EAP_SAKE_RAND_LEN
];
27 u8 rand_p
[EAP_SAKE_RAND_LEN
];
29 u8 auth
[EAP_SAKE_TEK_AUTH_LEN
];
30 u8 cipher
[EAP_SAKE_TEK_CIPHER_LEN
];
33 u8 emsk
[EAP_EMSK_LEN
];
43 static const char * eap_sake_state_txt(int state
)
62 static void eap_sake_state(struct eap_sake_data
*data
, int state
)
64 wpa_printf(MSG_DEBUG
, "EAP-SAKE: %s -> %s",
65 eap_sake_state_txt(data
->state
),
66 eap_sake_state_txt(state
));
71 static void eap_sake_deinit(struct eap_sm
*sm
, void *priv
);
74 static void * eap_sake_init(struct eap_sm
*sm
)
76 struct wpa_ssid
*config
= eap_get_config(sm
);
77 struct eap_sake_data
*data
;
80 wpa_printf(MSG_INFO
, "EAP-SAKE: No configuration found");
84 if (!config
->eappsk
||
85 config
->eappsk_len
!= 2 * EAP_SAKE_ROOT_SECRET_LEN
) {
86 wpa_printf(MSG_INFO
, "EAP-SAKE: No key (eappsk) of correct "
91 data
= os_zalloc(sizeof(*data
));
94 data
->state
= IDENTITY
;
97 data
->peerid
= os_malloc(config
->nai_len
);
98 if (data
->peerid
== NULL
) {
99 eap_sake_deinit(sm
, data
);
102 os_memcpy(data
->peerid
, config
->nai
, config
->nai_len
);
103 data
->peerid_len
= config
->nai_len
;
106 os_memcpy(data
->root_secret_a
, config
->eappsk
,
107 EAP_SAKE_ROOT_SECRET_LEN
);
108 os_memcpy(data
->root_secret_b
,
109 config
->eappsk
+ EAP_SAKE_ROOT_SECRET_LEN
,
110 EAP_SAKE_ROOT_SECRET_LEN
);
116 static void eap_sake_deinit(struct eap_sm
*sm
, void *priv
)
118 struct eap_sake_data
*data
= priv
;
119 os_free(data
->serverid
);
120 os_free(data
->peerid
);
125 static u8
* eap_sake_build_msg(struct eap_sake_data
*data
, u8
**payload
,
126 int id
, size_t *length
, u8 subtype
)
128 struct eap_sake_hdr
*req
;
131 *length
+= sizeof(struct eap_sake_hdr
);
133 msg
= os_zalloc(*length
);
135 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to allocate memory "
140 req
= (struct eap_sake_hdr
*) msg
;
141 req
->code
= EAP_CODE_RESPONSE
;
142 req
->identifier
= id
;
143 req
->length
= htons((u16
) *length
);
144 req
->type
= EAP_TYPE_SAKE
;
145 req
->version
= EAP_SAKE_VERSION
;
146 req
->session_id
= data
->session_id
;
147 req
->subtype
= subtype
;
148 *payload
= (u8
*) (req
+ 1);
154 static u8
* eap_sake_process_identity(struct eap_sm
*sm
,
155 struct eap_sake_data
*data
,
156 struct eap_method_ret
*ret
,
157 const u8
*reqData
, size_t reqDataLen
,
158 const u8
*payload
, size_t payload_len
,
161 struct eap_sake_parse_attr attr
;
163 const struct eap_hdr
*hdr
= (const struct eap_hdr
*) reqData
;
165 if (data
->state
!= IDENTITY
) {
170 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Request/Identity");
172 if (eap_sake_parse_attributes(payload
, payload_len
, &attr
))
175 if (!attr
.perm_id_req
&& !attr
.any_id_req
) {
176 wpa_printf(MSG_INFO
, "EAP-SAKE: No AT_PERM_ID_REQ or "
177 "AT_ANY_ID_REQ in Request/Identity");
181 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Identity");
183 *respDataLen
= 2 + data
->peerid_len
;
184 resp
= eap_sake_build_msg(data
, &rpos
, hdr
->identifier
, respDataLen
,
185 EAP_SAKE_SUBTYPE_IDENTITY
);
189 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_PEERID");
190 *rpos
++ = EAP_SAKE_AT_PEERID
;
191 *rpos
++ = 2 + data
->peerid_len
;
193 os_memcpy(rpos
, data
->peerid
, data
->peerid_len
);
195 eap_sake_state(data
, CHALLENGE
);
201 static u8
* eap_sake_process_challenge(struct eap_sm
*sm
,
202 struct eap_sake_data
*data
,
203 struct eap_method_ret
*ret
,
204 const u8
*reqData
, size_t reqDataLen
,
205 const u8
*payload
, size_t payload_len
,
208 struct eap_sake_parse_attr attr
;
210 const struct eap_hdr
*hdr
= (const struct eap_hdr
*) reqData
;
212 if (data
->state
!= IDENTITY
&& data
->state
!= CHALLENGE
) {
213 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Request/Challenge received "
214 "in unexpected state (%d)", data
->state
);
218 if (data
->state
== IDENTITY
)
219 eap_sake_state(data
, CHALLENGE
);
221 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Request/Challenge");
223 if (eap_sake_parse_attributes(payload
, payload_len
, &attr
))
227 wpa_printf(MSG_INFO
, "EAP-SAKE: Request/Challenge did not "
228 "include AT_RAND_S");
232 os_memcpy(data
->rand_s
, attr
.rand_s
, EAP_SAKE_RAND_LEN
);
233 wpa_hexdump(MSG_MSGDUMP
, "EAP-SAKE: RAND_S (server rand)",
234 data
->rand_s
, EAP_SAKE_RAND_LEN
);
236 if (hostapd_get_rand(data
->rand_p
, EAP_SAKE_RAND_LEN
)) {
237 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to get random data");
240 wpa_hexdump(MSG_MSGDUMP
, "EAP-SAKE: RAND_P (peer rand)",
241 data
->rand_p
, EAP_SAKE_RAND_LEN
);
243 os_free(data
->serverid
);
244 data
->serverid
= NULL
;
245 data
->serverid_len
= 0;
247 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-SAKE: SERVERID",
248 attr
.serverid
, attr
.serverid_len
);
249 data
->serverid
= os_malloc(attr
.serverid_len
);
250 if (data
->serverid
== NULL
)
252 os_memcpy(data
->serverid
, attr
.serverid
, attr
.serverid_len
);
253 data
->serverid_len
= attr
.serverid_len
;
256 eap_sake_derive_keys(data
->root_secret_a
, data
->root_secret_b
,
257 data
->rand_s
, data
->rand_p
,
258 (u8
*) &data
->tek
, data
->msk
, data
->emsk
);
260 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Challenge");
262 *respDataLen
= 2 + EAP_SAKE_RAND_LEN
+ 2 + EAP_SAKE_MIC_LEN
;
264 *respDataLen
+= 2 + data
->peerid_len
;
265 resp
= eap_sake_build_msg(data
, &rpos
, hdr
->identifier
, respDataLen
,
266 EAP_SAKE_SUBTYPE_CHALLENGE
);
270 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_RAND_P");
271 *rpos
++ = EAP_SAKE_AT_RAND_P
;
272 *rpos
++ = 2 + EAP_SAKE_RAND_LEN
;
273 os_memcpy(rpos
, data
->rand_p
, EAP_SAKE_RAND_LEN
);
274 rpos
+= EAP_SAKE_RAND_LEN
;
277 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_PEERID");
278 *rpos
++ = EAP_SAKE_AT_PEERID
;
279 *rpos
++ = 2 + data
->peerid_len
;
280 os_memcpy(rpos
, data
->peerid
, data
->peerid_len
);
281 rpos
+= data
->peerid_len
;
284 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_MIC_P");
285 *rpos
++ = EAP_SAKE_AT_MIC_P
;
286 *rpos
++ = 2 + EAP_SAKE_MIC_LEN
;
287 if (eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
288 data
->serverid
, data
->serverid_len
,
289 data
->peerid
, data
->peerid_len
, 1,
290 resp
, *respDataLen
, rpos
, rpos
)) {
291 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to compute MIC");
296 eap_sake_state(data
, CONFIRM
);
302 static u8
* eap_sake_process_confirm(struct eap_sm
*sm
,
303 struct eap_sake_data
*data
,
304 struct eap_method_ret
*ret
,
305 const u8
*reqData
, size_t reqDataLen
,
306 const u8
*payload
, size_t payload_len
,
309 struct eap_sake_parse_attr attr
;
310 u8 mic_s
[EAP_SAKE_MIC_LEN
];
312 const struct eap_hdr
*hdr
= (const struct eap_hdr
*) reqData
;
314 if (data
->state
!= CONFIRM
) {
319 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Request/Confirm");
321 if (eap_sake_parse_attributes(payload
, payload_len
, &attr
))
325 wpa_printf(MSG_INFO
, "EAP-SAKE: Request/Confirm did not "
330 eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
331 data
->serverid
, data
->serverid_len
,
332 data
->peerid
, data
->peerid_len
, 0,
333 reqData
, reqDataLen
, attr
.mic_s
, mic_s
);
334 if (os_memcmp(attr
.mic_s
, mic_s
, EAP_SAKE_MIC_LEN
) != 0) {
335 wpa_printf(MSG_INFO
, "EAP-SAKE: Incorrect AT_MIC_S");
336 eap_sake_state(data
, FAILURE
);
337 ret
->methodState
= METHOD_DONE
;
338 ret
->decision
= DECISION_FAIL
;
339 ret
->allowNotifications
= FALSE
;
341 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending "
342 "Response/Auth-Reject");
343 return eap_sake_build_msg(data
, &rpos
, hdr
->identifier
,
345 EAP_SAKE_SUBTYPE_AUTH_REJECT
);
348 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Confirm");
350 *respDataLen
= 2 + EAP_SAKE_MIC_LEN
;
351 resp
= eap_sake_build_msg(data
, &rpos
, hdr
->identifier
, respDataLen
,
352 EAP_SAKE_SUBTYPE_CONFIRM
);
356 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_MIC_P");
357 *rpos
++ = EAP_SAKE_AT_MIC_P
;
358 *rpos
++ = 2 + EAP_SAKE_MIC_LEN
;
359 if (eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
360 data
->serverid
, data
->serverid_len
,
361 data
->peerid
, data
->peerid_len
, 1,
362 resp
, *respDataLen
, rpos
, rpos
)) {
363 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to compute MIC");
368 eap_sake_state(data
, SUCCESS
);
369 ret
->methodState
= METHOD_DONE
;
370 ret
->decision
= DECISION_UNCOND_SUCC
;
371 ret
->allowNotifications
= FALSE
;
377 static u8
* eap_sake_process(struct eap_sm
*sm
, void *priv
,
378 struct eap_method_ret
*ret
,
379 const u8
*reqData
, size_t reqDataLen
,
382 struct eap_sake_data
*data
= priv
;
383 const struct eap_sake_hdr
*req
;
387 u8 subtype
, session_id
;
389 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
,
390 reqData
, reqDataLen
, &len
);
391 if (pos
== NULL
|| len
< 3) {
396 req
= (const struct eap_sake_hdr
*) reqData
;
397 subtype
= req
->subtype
;
398 session_id
= req
->session_id
;
399 pos
= (const u8
*) (req
+ 1);
400 end
= reqData
+ be_to_host16(req
->length
);
402 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received frame: subtype %d "
403 "session_id %d", subtype
, session_id
);
404 wpa_hexdump(MSG_DEBUG
, "EAP-SAKE: Received attributes",
407 if (data
->session_id_set
&& data
->session_id
!= session_id
) {
408 wpa_printf(MSG_INFO
, "EAP-SAKE: Session ID mismatch (%d,%d)",
409 session_id
, data
->session_id
);
413 data
->session_id
= session_id
;
414 data
->session_id_set
= 1;
417 ret
->methodState
= METHOD_MAY_CONT
;
418 ret
->decision
= DECISION_FAIL
;
419 ret
->allowNotifications
= TRUE
;
422 case EAP_SAKE_SUBTYPE_IDENTITY
:
423 resp
= eap_sake_process_identity(sm
, data
, ret
, reqData
,
424 reqDataLen
, pos
, end
- pos
,
427 case EAP_SAKE_SUBTYPE_CHALLENGE
:
428 resp
= eap_sake_process_challenge(sm
, data
, ret
, reqData
,
429 reqDataLen
, pos
, end
- pos
,
432 case EAP_SAKE_SUBTYPE_CONFIRM
:
433 resp
= eap_sake_process_confirm(sm
, data
, ret
, reqData
,
434 reqDataLen
, pos
, end
- pos
,
438 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Ignoring message with "
439 "unknown subtype %d", subtype
);
444 if (ret
->methodState
== METHOD_DONE
)
445 ret
->allowNotifications
= FALSE
;
451 static Boolean
eap_sake_isKeyAvailable(struct eap_sm
*sm
, void *priv
)
453 struct eap_sake_data
*data
= priv
;
454 return data
->state
== SUCCESS
;
458 static u8
* eap_sake_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
460 struct eap_sake_data
*data
= priv
;
463 if (data
->state
!= SUCCESS
)
466 key
= os_malloc(EAP_MSK_LEN
);
469 os_memcpy(key
, data
->msk
, EAP_MSK_LEN
);
476 static u8
* eap_sake_get_emsk(struct eap_sm
*sm
, void *priv
, size_t *len
)
478 struct eap_sake_data
*data
= priv
;
481 if (data
->state
!= SUCCESS
)
484 key
= os_malloc(EAP_EMSK_LEN
);
487 os_memcpy(key
, data
->emsk
, EAP_EMSK_LEN
);
494 int eap_peer_sake_register(void)
496 struct eap_method
*eap
;
499 eap
= eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION
,
500 EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, "SAKE");
504 eap
->init
= eap_sake_init
;
505 eap
->deinit
= eap_sake_deinit
;
506 eap
->process
= eap_sake_process
;
507 eap
->isKeyAvailable
= eap_sake_isKeyAvailable
;
508 eap
->getKey
= eap_sake_getKey
;
509 eap
->get_emsk
= eap_sake_get_emsk
;
511 ret
= eap_peer_method_register(eap
);
513 eap_peer_method_free(eap
);