2 * EAP peer method: EAP-PAX (RFC 4746)
3 * Copyright (c) 2005-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_pax_common.h"
24 * Note: only PAX_STD subprotocol is currently supported
26 * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
27 * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
28 * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
33 enum { PAX_INIT
, PAX_STD_2_SENT
, PAX_DONE
} state
;
34 u8 mac_id
, dh_group_id
, public_key_id
;
36 u8 e
[2 * EAP_PAX_RAND_LEN
];
38 u8 x
[EAP_PAX_RAND_LEN
]; /* server rand */
39 u8 y
[EAP_PAX_RAND_LEN
]; /* client rand */
44 u8 ak
[EAP_PAX_AK_LEN
];
45 u8 mk
[EAP_PAX_MK_LEN
];
46 u8 ck
[EAP_PAX_CK_LEN
];
47 u8 ick
[EAP_PAX_ICK_LEN
];
51 static void eap_pax_deinit(struct eap_sm
*sm
, void *priv
);
54 static void * eap_pax_init(struct eap_sm
*sm
)
56 struct eap_pax_data
*data
;
57 const u8
*identity
, *password
;
58 size_t identity_len
, password_len
;
60 identity
= eap_get_config_identity(sm
, &identity_len
);
61 password
= eap_get_config_password(sm
, &password_len
);
62 if (!identity
|| !password
) {
63 wpa_printf(MSG_INFO
, "EAP-PAX: CID (nai) or key (password) "
68 if (password_len
!= EAP_PAX_AK_LEN
) {
69 wpa_printf(MSG_INFO
, "EAP-PAX: Invalid PSK length");
73 data
= os_zalloc(sizeof(*data
));
76 data
->state
= PAX_INIT
;
78 data
->cid
= os_malloc(identity_len
);
79 if (data
->cid
== NULL
) {
80 eap_pax_deinit(sm
, data
);
83 os_memcpy(data
->cid
, identity
, identity_len
);
84 data
->cid_len
= identity_len
;
86 os_memcpy(data
->ak
, password
, EAP_PAX_AK_LEN
);
92 static void eap_pax_deinit(struct eap_sm
*sm
, void *priv
)
94 struct eap_pax_data
*data
= priv
;
100 static struct wpabuf
* eap_pax_alloc_resp(const struct eap_pax_hdr
*req
,
101 u8 id
, u8 op_code
, size_t plen
)
104 struct eap_pax_hdr
*pax
;
106 resp
= eap_msg_alloc(EAP_VENDOR_IETF
, EAP_TYPE_PAX
,
107 sizeof(*pax
) + plen
, EAP_CODE_RESPONSE
, id
);
111 pax
= wpabuf_put(resp
, sizeof(*pax
));
112 pax
->op_code
= op_code
;
114 pax
->mac_id
= req
->mac_id
;
115 pax
->dh_group_id
= req
->dh_group_id
;
116 pax
->public_key_id
= req
->public_key_id
;
122 static struct wpabuf
* eap_pax_process_std_1(struct eap_pax_data
*data
,
123 struct eap_method_ret
*ret
, u8 id
,
124 const struct eap_pax_hdr
*req
,
132 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-1 (received)");
134 if (data
->state
!= PAX_INIT
) {
135 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 received in "
136 "unexpected state (%d) - ignored", data
->state
);
141 if (req
->flags
& EAP_PAX_FLAGS_CE
) {
142 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 with CE flag set - "
148 left
= req_plen
- sizeof(*req
);
150 if (left
< 2 + EAP_PAX_RAND_LEN
) {
151 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 with too short "
157 pos
= (const u8
*) (req
+ 1);
158 if (WPA_GET_BE16(pos
) != EAP_PAX_RAND_LEN
) {
159 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 with incorrect A "
160 "length %d (expected %d)",
161 WPA_GET_BE16(pos
), EAP_PAX_RAND_LEN
);
168 os_memcpy(data
->rand
.r
.x
, pos
, EAP_PAX_RAND_LEN
);
169 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: X (server rand)",
170 data
->rand
.r
.x
, EAP_PAX_RAND_LEN
);
171 pos
+= EAP_PAX_RAND_LEN
;
172 left
-= EAP_PAX_RAND_LEN
;
175 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ignored extra payload",
179 if (os_get_random(data
->rand
.r
.y
, EAP_PAX_RAND_LEN
)) {
180 wpa_printf(MSG_ERROR
, "EAP-PAX: Failed to get random data");
184 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: Y (client rand)",
185 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
);
187 if (eap_pax_initial_key_derivation(req
->mac_id
, data
->ak
, data
->rand
.e
,
188 data
->mk
, data
->ck
, data
->ick
) < 0)
194 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-2 (sending)");
196 plen
= 2 + EAP_PAX_RAND_LEN
+ 2 + data
->cid_len
+ 2 + EAP_PAX_MAC_LEN
+
198 resp
= eap_pax_alloc_resp(req
, id
, EAP_PAX_OP_STD_2
, plen
);
202 wpabuf_put_be16(resp
, EAP_PAX_RAND_LEN
);
203 wpabuf_put_data(resp
, data
->rand
.r
.y
, EAP_PAX_RAND_LEN
);
204 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: B = Y (client rand)",
205 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
);
207 wpabuf_put_be16(resp
, data
->cid_len
);
208 wpabuf_put_data(resp
, data
->cid
, data
->cid_len
);
209 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-PAX: CID",
210 (u8
*) data
->cid
, data
->cid_len
);
212 wpabuf_put_be16(resp
, EAP_PAX_MAC_LEN
);
213 rpos
= wpabuf_put(resp
, EAP_PAX_MAC_LEN
);
214 eap_pax_mac(req
->mac_id
, data
->ck
, EAP_PAX_CK_LEN
,
215 data
->rand
.r
.x
, EAP_PAX_RAND_LEN
,
216 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
,
217 (u8
*) data
->cid
, data
->cid_len
, rpos
);
218 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: MAC_CK(A, B, CID)",
219 rpos
, EAP_PAX_MAC_LEN
);
221 /* Optional ADE could be added here, if needed */
223 rpos
= wpabuf_put(resp
, EAP_PAX_ICV_LEN
);
224 eap_pax_mac(req
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
225 wpabuf_head(resp
), wpabuf_len(resp
) - EAP_PAX_ICV_LEN
,
226 NULL
, 0, NULL
, 0, rpos
);
227 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", rpos
, EAP_PAX_ICV_LEN
);
229 data
->state
= PAX_STD_2_SENT
;
230 data
->mac_id
= req
->mac_id
;
231 data
->dh_group_id
= req
->dh_group_id
;
232 data
->public_key_id
= req
->public_key_id
;
238 static struct wpabuf
* eap_pax_process_std_3(struct eap_pax_data
*data
,
239 struct eap_method_ret
*ret
, u8 id
,
240 const struct eap_pax_hdr
*req
,
244 u8
*rpos
, mac
[EAP_PAX_MAC_LEN
];
248 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-3 (received)");
250 if (data
->state
!= PAX_STD_2_SENT
) {
251 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 received in "
252 "unexpected state (%d) - ignored", data
->state
);
257 if (req
->flags
& EAP_PAX_FLAGS_CE
) {
258 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 with CE flag set - "
264 left
= req_plen
- sizeof(*req
);
266 if (left
< 2 + EAP_PAX_MAC_LEN
) {
267 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 with too short "
273 pos
= (const u8
*) (req
+ 1);
274 if (WPA_GET_BE16(pos
) != EAP_PAX_MAC_LEN
) {
275 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 with incorrect "
276 "MAC_CK length %d (expected %d)",
277 WPA_GET_BE16(pos
), EAP_PAX_MAC_LEN
);
283 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: MAC_CK(B, CID)",
284 pos
, EAP_PAX_MAC_LEN
);
285 eap_pax_mac(data
->mac_id
, data
->ck
, EAP_PAX_CK_LEN
,
286 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
,
287 (u8
*) data
->cid
, data
->cid_len
, NULL
, 0, mac
);
288 if (os_memcmp(pos
, mac
, EAP_PAX_MAC_LEN
) != 0) {
289 wpa_printf(MSG_INFO
, "EAP-PAX: Invalid MAC_CK(B, CID) "
291 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: expected MAC_CK(B, CID)",
292 mac
, EAP_PAX_MAC_LEN
);
293 ret
->methodState
= METHOD_DONE
;
294 ret
->decision
= DECISION_FAIL
;
298 pos
+= EAP_PAX_MAC_LEN
;
299 left
-= EAP_PAX_MAC_LEN
;
302 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ignored extra payload",
306 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX-ACK (sending)");
308 resp
= eap_pax_alloc_resp(req
, id
, EAP_PAX_OP_ACK
, EAP_PAX_ICV_LEN
);
312 /* Optional ADE could be added here, if needed */
314 rpos
= wpabuf_put(resp
, EAP_PAX_ICV_LEN
);
315 eap_pax_mac(data
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
316 wpabuf_head(resp
), wpabuf_len(resp
) - EAP_PAX_ICV_LEN
,
317 NULL
, 0, NULL
, 0, rpos
);
318 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", rpos
, EAP_PAX_ICV_LEN
);
320 data
->state
= PAX_DONE
;
321 ret
->methodState
= METHOD_DONE
;
322 ret
->decision
= DECISION_UNCOND_SUCC
;
323 ret
->allowNotifications
= FALSE
;
329 static struct wpabuf
* eap_pax_process(struct eap_sm
*sm
, void *priv
,
330 struct eap_method_ret
*ret
,
331 const struct wpabuf
*reqData
)
333 struct eap_pax_data
*data
= priv
;
334 const struct eap_pax_hdr
*req
;
336 u8 icvbuf
[EAP_PAX_ICV_LEN
], id
;
341 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_PAX
, reqData
, &len
);
342 if (pos
== NULL
|| len
< EAP_PAX_ICV_LEN
) {
346 id
= eap_get_id(reqData
);
347 req
= (const struct eap_pax_hdr
*) pos
;
348 flen
= len
- EAP_PAX_ICV_LEN
;
349 mlen
= wpabuf_len(reqData
) - EAP_PAX_ICV_LEN
;
351 wpa_printf(MSG_DEBUG
, "EAP-PAX: received frame: op_code 0x%x "
352 "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
353 "public_key_id 0x%x",
354 req
->op_code
, req
->flags
, req
->mac_id
, req
->dh_group_id
,
356 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: received payload",
357 pos
, len
- EAP_PAX_ICV_LEN
);
359 if (data
->state
!= PAX_INIT
&& data
->mac_id
!= req
->mac_id
) {
360 wpa_printf(MSG_INFO
, "EAP-PAX: MAC ID changed during "
361 "authentication (was 0x%d, is 0x%d)",
362 data
->mac_id
, req
->mac_id
);
367 if (data
->state
!= PAX_INIT
&& data
->dh_group_id
!= req
->dh_group_id
) {
368 wpa_printf(MSG_INFO
, "EAP-PAX: DH Group ID changed during "
369 "authentication (was 0x%d, is 0x%d)",
370 data
->dh_group_id
, req
->dh_group_id
);
375 if (data
->state
!= PAX_INIT
&&
376 data
->public_key_id
!= req
->public_key_id
) {
377 wpa_printf(MSG_INFO
, "EAP-PAX: Public Key ID changed during "
378 "authentication (was 0x%d, is 0x%d)",
379 data
->public_key_id
, req
->public_key_id
);
384 /* TODO: add support EAP_PAX_HMAC_SHA256_128 */
385 if (req
->mac_id
!= EAP_PAX_MAC_HMAC_SHA1_128
) {
386 wpa_printf(MSG_INFO
, "EAP-PAX: Unsupported MAC ID 0x%x",
392 if (req
->dh_group_id
!= EAP_PAX_DH_GROUP_NONE
) {
393 wpa_printf(MSG_INFO
, "EAP-PAX: Unsupported DH Group ID 0x%x",
399 if (req
->public_key_id
!= EAP_PAX_PUBLIC_KEY_NONE
) {
400 wpa_printf(MSG_INFO
, "EAP-PAX: Unsupported Public Key ID 0x%x",
406 if (req
->flags
& EAP_PAX_FLAGS_MF
) {
407 /* TODO: add support for reassembling fragments */
408 wpa_printf(MSG_INFO
, "EAP-PAX: fragmentation not supported - "
414 icv
= pos
+ len
- EAP_PAX_ICV_LEN
;
415 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", icv
, EAP_PAX_ICV_LEN
);
416 if (req
->op_code
== EAP_PAX_OP_STD_1
) {
417 eap_pax_mac(req
->mac_id
, (u8
*) "", 0,
418 wpabuf_head(reqData
), mlen
, NULL
, 0, NULL
, 0,
421 eap_pax_mac(req
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
422 wpabuf_head(reqData
), mlen
, NULL
, 0, NULL
, 0,
425 if (os_memcmp(icv
, icvbuf
, EAP_PAX_ICV_LEN
) != 0) {
426 wpa_printf(MSG_DEBUG
, "EAP-PAX: invalid ICV - ignoring the "
428 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: expected ICV",
429 icvbuf
, EAP_PAX_ICV_LEN
);
435 ret
->methodState
= METHOD_MAY_CONT
;
436 ret
->decision
= DECISION_FAIL
;
437 ret
->allowNotifications
= TRUE
;
439 switch (req
->op_code
) {
440 case EAP_PAX_OP_STD_1
:
441 resp
= eap_pax_process_std_1(data
, ret
, id
, req
, flen
);
443 case EAP_PAX_OP_STD_3
:
444 resp
= eap_pax_process_std_3(data
, ret
, id
, req
, flen
);
447 wpa_printf(MSG_DEBUG
, "EAP-PAX: ignoring message with unknown "
448 "op_code %d", req
->op_code
);
453 if (ret
->methodState
== METHOD_DONE
) {
454 ret
->allowNotifications
= FALSE
;
461 static Boolean
eap_pax_isKeyAvailable(struct eap_sm
*sm
, void *priv
)
463 struct eap_pax_data
*data
= priv
;
464 return data
->state
== PAX_DONE
;
468 static u8
* eap_pax_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
470 struct eap_pax_data
*data
= priv
;
473 if (data
->state
!= PAX_DONE
)
476 key
= os_malloc(EAP_MSK_LEN
);
481 eap_pax_kdf(data
->mac_id
, data
->mk
, EAP_PAX_MK_LEN
,
482 "Master Session Key", data
->rand
.e
, 2 * EAP_PAX_RAND_LEN
,
489 static u8
* eap_pax_get_emsk(struct eap_sm
*sm
, void *priv
, size_t *len
)
491 struct eap_pax_data
*data
= priv
;
494 if (data
->state
!= PAX_DONE
)
497 key
= os_malloc(EAP_EMSK_LEN
);
502 eap_pax_kdf(data
->mac_id
, data
->mk
, EAP_PAX_MK_LEN
,
503 "Extended Master Session Key",
504 data
->rand
.e
, 2 * EAP_PAX_RAND_LEN
,
511 int eap_peer_pax_register(void)
513 struct eap_method
*eap
;
516 eap
= eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION
,
517 EAP_VENDOR_IETF
, EAP_TYPE_PAX
, "PAX");
521 eap
->init
= eap_pax_init
;
522 eap
->deinit
= eap_pax_deinit
;
523 eap
->process
= eap_pax_process
;
524 eap
->isKeyAvailable
= eap_pax_isKeyAvailable
;
525 eap
->getKey
= eap_pax_getKey
;
526 eap
->get_emsk
= eap_pax_get_emsk
;
528 ret
= eap_peer_method_register(eap
);
530 eap_peer_method_free(eap
);