2 * EAP peer method: EAP-SAKE (RFC 4763)
3 * Copyright (c) 2006-2019, 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_peer/eap_i.h"
14 #include "eap_common/eap_sake_common.h"
16 struct eap_sake_data
{
17 enum { IDENTITY
, CHALLENGE
, CONFIRM
, SUCCESS
, FAILURE
} state
;
18 u8 root_secret_a
[EAP_SAKE_ROOT_SECRET_LEN
];
19 u8 root_secret_b
[EAP_SAKE_ROOT_SECRET_LEN
];
20 u8 rand_s
[EAP_SAKE_RAND_LEN
];
21 u8 rand_p
[EAP_SAKE_RAND_LEN
];
23 u8 auth
[EAP_SAKE_TEK_AUTH_LEN
];
24 u8 cipher
[EAP_SAKE_TEK_CIPHER_LEN
];
27 u8 emsk
[EAP_EMSK_LEN
];
37 static const char * eap_sake_state_txt(int state
)
56 static void eap_sake_state(struct eap_sake_data
*data
, int state
)
58 wpa_printf(MSG_DEBUG
, "EAP-SAKE: %s -> %s",
59 eap_sake_state_txt(data
->state
),
60 eap_sake_state_txt(state
));
65 static void eap_sake_deinit(struct eap_sm
*sm
, void *priv
);
68 static void * eap_sake_init(struct eap_sm
*sm
)
70 struct eap_sake_data
*data
;
71 const u8
*identity
, *password
;
72 size_t identity_len
, password_len
;
74 password
= eap_get_config_password(sm
, &password_len
);
75 if (!password
|| password_len
!= 2 * EAP_SAKE_ROOT_SECRET_LEN
) {
76 wpa_printf(MSG_INFO
, "EAP-SAKE: No key of correct length "
81 data
= os_zalloc(sizeof(*data
));
84 data
->state
= IDENTITY
;
86 identity
= eap_get_config_identity(sm
, &identity_len
);
88 data
->peerid
= os_memdup(identity
, identity_len
);
89 if (data
->peerid
== NULL
) {
90 eap_sake_deinit(sm
, data
);
93 data
->peerid_len
= identity_len
;
96 os_memcpy(data
->root_secret_a
, password
, EAP_SAKE_ROOT_SECRET_LEN
);
97 os_memcpy(data
->root_secret_b
,
98 password
+ EAP_SAKE_ROOT_SECRET_LEN
,
99 EAP_SAKE_ROOT_SECRET_LEN
);
105 static void eap_sake_deinit(struct eap_sm
*sm
, void *priv
)
107 struct eap_sake_data
*data
= priv
;
108 os_free(data
->serverid
);
109 os_free(data
->peerid
);
110 bin_clear_free(data
, sizeof(*data
));
114 static struct wpabuf
* eap_sake_build_msg(struct eap_sake_data
*data
,
115 int id
, size_t length
, u8 subtype
)
117 struct eap_sake_hdr
*sake
;
121 plen
= length
+ sizeof(struct eap_sake_hdr
);
123 msg
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, plen
,
124 EAP_CODE_RESPONSE
, id
);
126 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to allocate memory "
131 sake
= wpabuf_put(msg
, sizeof(*sake
));
132 sake
->version
= EAP_SAKE_VERSION
;
133 sake
->session_id
= data
->session_id
;
134 sake
->subtype
= subtype
;
140 static struct wpabuf
* eap_sake_process_identity(struct eap_sm
*sm
,
141 struct eap_sake_data
*data
,
142 struct eap_method_ret
*ret
,
147 struct eap_sake_parse_attr attr
;
150 if (data
->state
!= IDENTITY
) {
155 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Request/Identity");
157 if (eap_sake_parse_attributes(payload
, payload_len
, &attr
))
160 if (!attr
.perm_id_req
&& !attr
.any_id_req
) {
161 wpa_printf(MSG_INFO
, "EAP-SAKE: No AT_PERM_ID_REQ or "
162 "AT_ANY_ID_REQ in Request/Identity");
166 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Identity");
168 resp
= eap_sake_build_msg(data
, id
, 2 + data
->peerid_len
,
169 EAP_SAKE_SUBTYPE_IDENTITY
);
173 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_PEERID");
174 eap_sake_add_attr(resp
, EAP_SAKE_AT_PEERID
,
175 data
->peerid
, data
->peerid_len
);
177 eap_sake_state(data
, CHALLENGE
);
183 static struct wpabuf
* eap_sake_process_challenge(struct eap_sm
*sm
,
184 struct eap_sake_data
*data
,
185 struct eap_method_ret
*ret
,
190 struct eap_sake_parse_attr attr
;
195 if (data
->state
!= IDENTITY
&& data
->state
!= CHALLENGE
) {
196 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Request/Challenge received "
197 "in unexpected state (%d)", data
->state
);
201 if (data
->state
== IDENTITY
)
202 eap_sake_state(data
, CHALLENGE
);
204 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Request/Challenge");
206 if (eap_sake_parse_attributes(payload
, payload_len
, &attr
))
210 wpa_printf(MSG_INFO
, "EAP-SAKE: Request/Challenge did not "
211 "include AT_RAND_S");
215 os_memcpy(data
->rand_s
, attr
.rand_s
, EAP_SAKE_RAND_LEN
);
216 wpa_hexdump(MSG_MSGDUMP
, "EAP-SAKE: RAND_S (server rand)",
217 data
->rand_s
, EAP_SAKE_RAND_LEN
);
219 if (random_get_bytes(data
->rand_p
, EAP_SAKE_RAND_LEN
)) {
220 wpa_printf(MSG_ERROR
, "EAP-SAKE: Failed to get random data");
223 wpa_hexdump(MSG_MSGDUMP
, "EAP-SAKE: RAND_P (peer rand)",
224 data
->rand_p
, EAP_SAKE_RAND_LEN
);
226 os_free(data
->serverid
);
227 data
->serverid
= NULL
;
228 data
->serverid_len
= 0;
230 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-SAKE: SERVERID",
231 attr
.serverid
, attr
.serverid_len
);
232 data
->serverid
= os_memdup(attr
.serverid
, attr
.serverid_len
);
233 if (data
->serverid
== NULL
)
235 data
->serverid_len
= attr
.serverid_len
;
238 if (eap_sake_derive_keys(data
->root_secret_a
, data
->root_secret_b
,
239 data
->rand_s
, data
->rand_p
,
240 (u8
*) &data
->tek
, data
->msk
,
242 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to derive keys");
246 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Challenge");
248 rlen
= 2 + EAP_SAKE_RAND_LEN
+ 2 + EAP_SAKE_MIC_LEN
;
250 rlen
+= 2 + data
->peerid_len
;
251 resp
= eap_sake_build_msg(data
, id
, rlen
, EAP_SAKE_SUBTYPE_CHALLENGE
);
255 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_RAND_P");
256 eap_sake_add_attr(resp
, EAP_SAKE_AT_RAND_P
,
257 data
->rand_p
, EAP_SAKE_RAND_LEN
);
260 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_PEERID");
261 eap_sake_add_attr(resp
, EAP_SAKE_AT_PEERID
,
262 data
->peerid
, data
->peerid_len
);
265 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_MIC_P");
266 wpabuf_put_u8(resp
, EAP_SAKE_AT_MIC_P
);
267 wpabuf_put_u8(resp
, 2 + EAP_SAKE_MIC_LEN
);
268 rpos
= wpabuf_put(resp
, EAP_SAKE_MIC_LEN
);
269 if (eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
270 data
->serverid
, data
->serverid_len
,
271 data
->peerid
, data
->peerid_len
, 1,
272 wpabuf_head(resp
), wpabuf_len(resp
), rpos
,
274 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to compute MIC");
279 eap_sake_state(data
, CONFIRM
);
285 static struct wpabuf
* eap_sake_process_confirm(struct eap_sm
*sm
,
286 struct eap_sake_data
*data
,
287 struct eap_method_ret
*ret
,
289 const struct wpabuf
*reqData
,
293 struct eap_sake_parse_attr attr
;
294 u8 mic_s
[EAP_SAKE_MIC_LEN
];
298 if (data
->state
!= CONFIRM
) {
303 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received Request/Confirm");
305 if (eap_sake_parse_attributes(payload
, payload_len
, &attr
))
309 wpa_printf(MSG_INFO
, "EAP-SAKE: Request/Confirm did not "
314 if (eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
315 data
->serverid
, data
->serverid_len
,
316 data
->peerid
, data
->peerid_len
, 0,
317 wpabuf_head(reqData
), wpabuf_len(reqData
),
318 attr
.mic_s
, mic_s
)) {
319 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to compute MIC");
320 eap_sake_state(data
, FAILURE
);
321 ret
->methodState
= METHOD_DONE
;
322 ret
->decision
= DECISION_FAIL
;
323 ret
->allowNotifications
= FALSE
;
324 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Auth-Reject");
325 return eap_sake_build_msg(data
, id
, 0,
326 EAP_SAKE_SUBTYPE_AUTH_REJECT
);
328 if (os_memcmp_const(attr
.mic_s
, mic_s
, EAP_SAKE_MIC_LEN
) != 0) {
329 wpa_printf(MSG_INFO
, "EAP-SAKE: Incorrect AT_MIC_S");
330 eap_sake_state(data
, FAILURE
);
331 ret
->methodState
= METHOD_DONE
;
332 ret
->decision
= DECISION_FAIL
;
333 ret
->allowNotifications
= FALSE
;
334 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending "
335 "Response/Auth-Reject");
336 return eap_sake_build_msg(data
, id
, 0,
337 EAP_SAKE_SUBTYPE_AUTH_REJECT
);
340 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Sending Response/Confirm");
342 resp
= eap_sake_build_msg(data
, id
, 2 + EAP_SAKE_MIC_LEN
,
343 EAP_SAKE_SUBTYPE_CONFIRM
);
347 wpa_printf(MSG_DEBUG
, "EAP-SAKE: * AT_MIC_P");
348 wpabuf_put_u8(resp
, EAP_SAKE_AT_MIC_P
);
349 wpabuf_put_u8(resp
, 2 + EAP_SAKE_MIC_LEN
);
350 rpos
= wpabuf_put(resp
, EAP_SAKE_MIC_LEN
);
351 if (eap_sake_compute_mic(data
->tek
.auth
, data
->rand_s
, data
->rand_p
,
352 data
->serverid
, data
->serverid_len
,
353 data
->peerid
, data
->peerid_len
, 1,
354 wpabuf_head(resp
), wpabuf_len(resp
), rpos
,
356 wpa_printf(MSG_INFO
, "EAP-SAKE: Failed to compute MIC");
361 eap_sake_state(data
, SUCCESS
);
362 ret
->methodState
= METHOD_DONE
;
363 ret
->decision
= DECISION_UNCOND_SUCC
;
364 ret
->allowNotifications
= FALSE
;
370 static struct wpabuf
* eap_sake_process(struct eap_sm
*sm
, void *priv
,
371 struct eap_method_ret
*ret
,
372 const struct wpabuf
*reqData
)
374 struct eap_sake_data
*data
= priv
;
375 const struct eap_sake_hdr
*req
;
379 u8 subtype
, session_id
, id
;
381 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, reqData
, &len
);
382 if (pos
== NULL
|| len
< sizeof(struct eap_sake_hdr
)) {
387 req
= (const struct eap_sake_hdr
*) pos
;
389 id
= eap_get_id(reqData
);
390 subtype
= req
->subtype
;
391 session_id
= req
->session_id
;
392 pos
= (const u8
*) (req
+ 1);
394 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Received frame: subtype %d "
395 "session_id %d", subtype
, session_id
);
396 wpa_hexdump(MSG_DEBUG
, "EAP-SAKE: Received attributes",
399 if (data
->session_id_set
&& data
->session_id
!= session_id
) {
400 wpa_printf(MSG_INFO
, "EAP-SAKE: Session ID mismatch (%d,%d)",
401 session_id
, data
->session_id
);
405 data
->session_id
= session_id
;
406 data
->session_id_set
= 1;
409 ret
->methodState
= METHOD_MAY_CONT
;
410 ret
->decision
= DECISION_FAIL
;
411 ret
->allowNotifications
= TRUE
;
414 case EAP_SAKE_SUBTYPE_IDENTITY
:
415 resp
= eap_sake_process_identity(sm
, data
, ret
, id
,
418 case EAP_SAKE_SUBTYPE_CHALLENGE
:
419 resp
= eap_sake_process_challenge(sm
, data
, ret
, id
,
422 case EAP_SAKE_SUBTYPE_CONFIRM
:
423 resp
= eap_sake_process_confirm(sm
, data
, ret
, id
, reqData
,
427 wpa_printf(MSG_DEBUG
, "EAP-SAKE: Ignoring message with "
428 "unknown subtype %d", subtype
);
433 if (ret
->methodState
== METHOD_DONE
)
434 ret
->allowNotifications
= FALSE
;
440 static Boolean
eap_sake_isKeyAvailable(struct eap_sm
*sm
, void *priv
)
442 struct eap_sake_data
*data
= priv
;
443 return data
->state
== SUCCESS
;
447 static u8
* eap_sake_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
449 struct eap_sake_data
*data
= priv
;
452 if (data
->state
!= SUCCESS
)
455 key
= os_memdup(data
->msk
, EAP_MSK_LEN
);
464 static u8
* eap_sake_get_session_id(struct eap_sm
*sm
, void *priv
, size_t *len
)
466 struct eap_sake_data
*data
= priv
;
469 if (data
->state
!= SUCCESS
)
472 *len
= 1 + 2 * EAP_SAKE_RAND_LEN
;
473 id
= os_malloc(*len
);
477 id
[0] = EAP_TYPE_SAKE
;
478 os_memcpy(id
+ 1, data
->rand_s
, EAP_SAKE_RAND_LEN
);
479 os_memcpy(id
+ 1 + EAP_SAKE_RAND_LEN
, data
->rand_s
, EAP_SAKE_RAND_LEN
);
480 wpa_hexdump(MSG_DEBUG
, "EAP-SAKE: Derived Session-Id", id
, *len
);
486 static u8
* eap_sake_get_emsk(struct eap_sm
*sm
, void *priv
, size_t *len
)
488 struct eap_sake_data
*data
= priv
;
491 if (data
->state
!= SUCCESS
)
494 key
= os_memdup(data
->emsk
, EAP_EMSK_LEN
);
503 int eap_peer_sake_register(void)
505 struct eap_method
*eap
;
507 eap
= eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION
,
508 EAP_VENDOR_IETF
, EAP_TYPE_SAKE
, "SAKE");
512 eap
->init
= eap_sake_init
;
513 eap
->deinit
= eap_sake_deinit
;
514 eap
->process
= eap_sake_process
;
515 eap
->isKeyAvailable
= eap_sake_isKeyAvailable
;
516 eap
->getKey
= eap_sake_getKey
;
517 eap
->getSessionId
= eap_sake_get_session_id
;
518 eap
->get_emsk
= eap_sake_get_emsk
;
520 return eap_peer_method_register(eap
);