2 * EAP peer method: EAP-PAX (RFC 4746)
3 * Copyright (c) 2005-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_pax_common.h"
25 * Note: only PAX_STD subprotocol is currently supported
27 * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
28 * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
29 * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
34 enum { PAX_INIT
, PAX_STD_2_SENT
, PAX_DONE
} state
;
35 u8 mac_id
, dh_group_id
, public_key_id
;
37 u8 e
[2 * EAP_PAX_RAND_LEN
];
39 u8 x
[EAP_PAX_RAND_LEN
]; /* server rand */
40 u8 y
[EAP_PAX_RAND_LEN
]; /* client rand */
45 u8 ak
[EAP_PAX_AK_LEN
];
46 u8 mk
[EAP_PAX_MK_LEN
];
47 u8 ck
[EAP_PAX_CK_LEN
];
48 u8 ick
[EAP_PAX_ICK_LEN
];
52 static void eap_pax_deinit(struct eap_sm
*sm
, void *priv
);
55 static void * eap_pax_init(struct eap_sm
*sm
)
57 struct wpa_ssid
*config
= eap_get_config(sm
);
58 struct eap_pax_data
*data
;
60 if (config
== NULL
|| !config
->nai
||
61 (!config
->eappsk
&& !config
->password
)) {
62 wpa_printf(MSG_INFO
, "EAP-PAX: CID (nai) or key "
63 "(eappsk/password) not configured");
67 if (config
->eappsk
&& config
->eappsk_len
!= EAP_PAX_AK_LEN
) {
68 wpa_printf(MSG_INFO
, "EAP-PAX: incorrect key length (eappsk); "
69 "expected %d", EAP_PAX_AK_LEN
);
73 data
= os_zalloc(sizeof(*data
));
76 data
->state
= PAX_INIT
;
78 data
->cid
= os_malloc(config
->nai_len
);
79 if (data
->cid
== NULL
) {
80 eap_pax_deinit(sm
, data
);
83 os_memcpy(data
->cid
, config
->nai
, config
->nai_len
);
84 data
->cid_len
= config
->nai_len
;
87 os_memcpy(data
->ak
, config
->eappsk
, EAP_PAX_AK_LEN
);
89 u8 hash
[SHA1_MAC_LEN
];
90 const unsigned char *addr
[1];
92 addr
[0] = config
->password
;
93 len
[0] = config
->password_len
;
94 sha1_vector(1, addr
, len
, hash
);
95 os_memcpy(data
->ak
, hash
, EAP_PAX_AK_LEN
);
102 static void eap_pax_deinit(struct eap_sm
*sm
, void *priv
)
104 struct eap_pax_data
*data
= priv
;
110 static struct eap_pax_hdr
* eap_pax_alloc_resp(const struct eap_pax_hdr
*req
,
111 u16 resp_len
, u8 op_code
)
113 struct eap_pax_hdr
*resp
;
115 resp
= os_malloc(resp_len
);
118 resp
->code
= EAP_CODE_RESPONSE
;
119 resp
->identifier
= req
->identifier
;
120 resp
->length
= host_to_be16(resp_len
);
121 resp
->type
= EAP_TYPE_PAX
;
122 resp
->op_code
= op_code
;
124 resp
->mac_id
= req
->mac_id
;
125 resp
->dh_group_id
= req
->dh_group_id
;
126 resp
->public_key_id
= req
->public_key_id
;
131 static u8
* eap_pax_process_std_1(struct eap_pax_data
*data
,
132 struct eap_method_ret
*ret
,
133 const u8
*reqData
, size_t reqDataLen
,
136 const struct eap_pax_hdr
*req
;
137 struct eap_pax_hdr
*resp
;
142 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-1 (received)");
143 req
= (const struct eap_pax_hdr
*) reqData
;
145 if (data
->state
!= PAX_INIT
) {
146 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 received in "
147 "unexpected state (%d) - ignored", data
->state
);
152 if (req
->flags
& EAP_PAX_FLAGS_CE
) {
153 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 with CE flag set - "
159 left
= reqDataLen
- sizeof(*req
);
161 if (left
< 2 + EAP_PAX_RAND_LEN
) {
162 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 with too short "
168 pos
= (const u8
*) (req
+ 1);
169 if (WPA_GET_BE16(pos
) != EAP_PAX_RAND_LEN
) {
170 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 with incorrect A "
171 "length %d (expected %d)",
172 WPA_GET_BE16(pos
), EAP_PAX_RAND_LEN
);
179 os_memcpy(data
->rand
.r
.x
, pos
, EAP_PAX_RAND_LEN
);
180 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: X (server rand)",
181 data
->rand
.r
.x
, EAP_PAX_RAND_LEN
);
182 pos
+= EAP_PAX_RAND_LEN
;
183 left
-= EAP_PAX_RAND_LEN
;
186 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ignored extra payload",
190 if (hostapd_get_rand(data
->rand
.r
.y
, EAP_PAX_RAND_LEN
)) {
191 wpa_printf(MSG_ERROR
, "EAP-PAX: Failed to get random data");
195 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: Y (client rand)",
196 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
);
198 if (eap_pax_initial_key_derivation(req
->mac_id
, data
->ak
, data
->rand
.e
,
199 data
->mk
, data
->ck
, data
->ick
) < 0)
205 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-2 (sending)");
207 *respDataLen
= sizeof(*resp
) + 2 + EAP_PAX_RAND_LEN
+
208 2 + data
->cid_len
+ 2 + EAP_PAX_MAC_LEN
+ EAP_PAX_ICV_LEN
;
209 resp
= eap_pax_alloc_resp(req
, *respDataLen
, EAP_PAX_OP_STD_2
);
213 rpos
= (u8
*) (resp
+ 1);
215 *rpos
++ = EAP_PAX_RAND_LEN
;
216 os_memcpy(rpos
, data
->rand
.r
.y
, EAP_PAX_RAND_LEN
);
217 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: B = Y (client rand)",
218 rpos
, EAP_PAX_RAND_LEN
);
219 rpos
+= EAP_PAX_RAND_LEN
;
221 WPA_PUT_BE16(rpos
, data
->cid_len
);
223 os_memcpy(rpos
, data
->cid
, data
->cid_len
);
224 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-PAX: CID", rpos
, data
->cid_len
);
225 rpos
+= data
->cid_len
;
228 *rpos
++ = EAP_PAX_MAC_LEN
;
229 eap_pax_mac(req
->mac_id
, data
->ck
, EAP_PAX_CK_LEN
,
230 data
->rand
.r
.x
, EAP_PAX_RAND_LEN
,
231 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
,
232 (u8
*) data
->cid
, data
->cid_len
, rpos
);
233 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: MAC_CK(A, B, CID)",
234 rpos
, EAP_PAX_MAC_LEN
);
235 rpos
+= EAP_PAX_MAC_LEN
;
237 /* Optional ADE could be added here, if needed */
239 eap_pax_mac(req
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
240 (u8
*) resp
, *respDataLen
- EAP_PAX_ICV_LEN
,
241 NULL
, 0, NULL
, 0, rpos
);
242 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", rpos
, EAP_PAX_ICV_LEN
);
243 rpos
+= EAP_PAX_ICV_LEN
;
245 data
->state
= PAX_STD_2_SENT
;
246 data
->mac_id
= req
->mac_id
;
247 data
->dh_group_id
= req
->dh_group_id
;
248 data
->public_key_id
= req
->public_key_id
;
254 static u8
* eap_pax_process_std_3(struct eap_pax_data
*data
,
255 struct eap_method_ret
*ret
,
256 const u8
*reqData
, size_t reqDataLen
,
259 const struct eap_pax_hdr
*req
;
260 struct eap_pax_hdr
*resp
;
261 u8
*rpos
, mac
[EAP_PAX_MAC_LEN
];
265 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-3 (received)");
266 req
= (const struct eap_pax_hdr
*) reqData
;
268 if (data
->state
!= PAX_STD_2_SENT
) {
269 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 received in "
270 "unexpected state (%d) - ignored", data
->state
);
275 if (req
->flags
& EAP_PAX_FLAGS_CE
) {
276 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 with CE flag set - "
282 left
= reqDataLen
- sizeof(*req
);
284 if (left
< 2 + EAP_PAX_MAC_LEN
) {
285 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 with too short "
291 pos
= (const u8
*) (req
+ 1);
292 if (WPA_GET_BE16(pos
) != EAP_PAX_MAC_LEN
) {
293 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 with incorrect "
294 "MAC_CK length %d (expected %d)",
295 WPA_GET_BE16(pos
), EAP_PAX_MAC_LEN
);
301 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: MAC_CK(B, CID)",
302 pos
, EAP_PAX_MAC_LEN
);
303 eap_pax_mac(data
->mac_id
, data
->ck
, EAP_PAX_CK_LEN
,
304 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
,
305 (u8
*) data
->cid
, data
->cid_len
, NULL
, 0, mac
);
306 if (os_memcmp(pos
, mac
, EAP_PAX_MAC_LEN
) != 0) {
307 wpa_printf(MSG_INFO
, "EAP-PAX: Invalid MAC_CK(B, CID) "
309 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: expected MAC_CK(B, CID)",
310 mac
, EAP_PAX_MAC_LEN
);
311 ret
->methodState
= METHOD_DONE
;
312 ret
->decision
= DECISION_FAIL
;
316 pos
+= EAP_PAX_MAC_LEN
;
317 left
-= EAP_PAX_MAC_LEN
;
320 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ignored extra payload",
324 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX-ACK (sending)");
326 *respDataLen
= sizeof(*resp
) + EAP_PAX_ICV_LEN
;
327 resp
= eap_pax_alloc_resp(req
, *respDataLen
, EAP_PAX_OP_ACK
);
331 rpos
= (u8
*) (resp
+ 1);
333 /* Optional ADE could be added here, if needed */
335 eap_pax_mac(data
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
336 (u8
*) resp
, *respDataLen
- EAP_PAX_ICV_LEN
,
337 NULL
, 0, NULL
, 0, rpos
);
338 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", rpos
, EAP_PAX_ICV_LEN
);
340 data
->state
= PAX_DONE
;
341 ret
->methodState
= METHOD_DONE
;
342 ret
->decision
= DECISION_UNCOND_SUCC
;
343 ret
->allowNotifications
= FALSE
;
349 static u8
* eap_pax_process(struct eap_sm
*sm
, void *priv
,
350 struct eap_method_ret
*ret
,
351 const u8
*reqData
, size_t reqDataLen
,
354 struct eap_pax_data
*data
= priv
;
355 const struct eap_pax_hdr
*req
;
356 u8
*resp
, icvbuf
[EAP_PAX_ICV_LEN
];
361 pos
= eap_hdr_validate(EAP_VENDOR_IETF
, EAP_TYPE_PAX
,
362 reqData
, reqDataLen
, &len
);
363 if (pos
== NULL
|| len
< EAP_PAX_ICV_LEN
) {
367 req
= (const struct eap_pax_hdr
*) reqData
;
368 flen
= be_to_host16(req
->length
) - EAP_PAX_ICV_LEN
;
370 wpa_printf(MSG_DEBUG
, "EAP-PAX: received frame: op_code 0x%x "
371 "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
372 "public_key_id 0x%x",
373 req
->op_code
, req
->flags
, req
->mac_id
, req
->dh_group_id
,
375 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: received payload",
376 pos
, len
- EAP_PAX_ICV_LEN
);
378 if (data
->state
!= PAX_INIT
&& data
->mac_id
!= req
->mac_id
) {
379 wpa_printf(MSG_INFO
, "EAP-PAX: MAC ID changed during "
380 "authentication (was 0x%d, is 0x%d)",
381 data
->mac_id
, req
->mac_id
);
386 if (data
->state
!= PAX_INIT
&& data
->dh_group_id
!= req
->dh_group_id
) {
387 wpa_printf(MSG_INFO
, "EAP-PAX: DH Group ID changed during "
388 "authentication (was 0x%d, is 0x%d)",
389 data
->dh_group_id
, req
->dh_group_id
);
394 if (data
->state
!= PAX_INIT
&&
395 data
->public_key_id
!= req
->public_key_id
) {
396 wpa_printf(MSG_INFO
, "EAP-PAX: Public Key ID changed during "
397 "authentication (was 0x%d, is 0x%d)",
398 data
->public_key_id
, req
->public_key_id
);
403 /* TODO: add support EAP_PAX_HMAC_SHA256_128 */
404 if (req
->mac_id
!= EAP_PAX_MAC_HMAC_SHA1_128
) {
405 wpa_printf(MSG_INFO
, "EAP-PAX: Unsupported MAC ID 0x%x",
411 if (req
->dh_group_id
!= EAP_PAX_DH_GROUP_NONE
) {
412 wpa_printf(MSG_INFO
, "EAP-PAX: Unsupported DH Group ID 0x%x",
418 if (req
->public_key_id
!= EAP_PAX_PUBLIC_KEY_NONE
) {
419 wpa_printf(MSG_INFO
, "EAP-PAX: Unsupported Public Key ID 0x%x",
425 if (req
->flags
& EAP_PAX_FLAGS_MF
) {
426 /* TODO: add support for reassembling fragments */
427 wpa_printf(MSG_INFO
, "EAP-PAX: fragmentation not supported - "
433 icv
= pos
+ len
- EAP_PAX_ICV_LEN
;
434 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", icv
, EAP_PAX_ICV_LEN
);
435 if (req
->op_code
== EAP_PAX_OP_STD_1
) {
436 eap_pax_mac(req
->mac_id
, (u8
*) "", 0,
437 reqData
, flen
, NULL
, 0, NULL
, 0, icvbuf
);
439 eap_pax_mac(req
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
440 reqData
, flen
, NULL
, 0, NULL
, 0, icvbuf
);
442 if (os_memcmp(icv
, icvbuf
, EAP_PAX_ICV_LEN
) != 0) {
443 wpa_printf(MSG_DEBUG
, "EAP-PAX: invalid ICV - ignoring the "
445 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: expected ICV",
446 icvbuf
, EAP_PAX_ICV_LEN
);
452 ret
->methodState
= METHOD_MAY_CONT
;
453 ret
->decision
= DECISION_FAIL
;
454 ret
->allowNotifications
= TRUE
;
456 switch (req
->op_code
) {
457 case EAP_PAX_OP_STD_1
:
458 resp
= eap_pax_process_std_1(data
, ret
, reqData
, flen
,
461 case EAP_PAX_OP_STD_3
:
462 resp
= eap_pax_process_std_3(data
, ret
, reqData
, flen
,
466 wpa_printf(MSG_DEBUG
, "EAP-PAX: ignoring message with unknown "
467 "op_code %d", req
->op_code
);
472 if (ret
->methodState
== METHOD_DONE
) {
473 ret
->allowNotifications
= FALSE
;
480 static Boolean
eap_pax_isKeyAvailable(struct eap_sm
*sm
, void *priv
)
482 struct eap_pax_data
*data
= priv
;
483 return data
->state
== PAX_DONE
;
487 static u8
* eap_pax_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
489 struct eap_pax_data
*data
= priv
;
492 if (data
->state
!= PAX_DONE
)
495 key
= os_malloc(EAP_MSK_LEN
);
500 eap_pax_kdf(data
->mac_id
, data
->mk
, EAP_PAX_MK_LEN
,
501 "Master Session Key", data
->rand
.e
, 2 * EAP_PAX_RAND_LEN
,
508 static u8
* eap_pax_get_emsk(struct eap_sm
*sm
, void *priv
, size_t *len
)
510 struct eap_pax_data
*data
= priv
;
513 if (data
->state
!= PAX_DONE
)
516 key
= os_malloc(EAP_EMSK_LEN
);
521 eap_pax_kdf(data
->mac_id
, data
->mk
, EAP_PAX_MK_LEN
,
522 "Extended Master Session Key",
523 data
->rand
.e
, 2 * EAP_PAX_RAND_LEN
,
530 int eap_peer_pax_register(void)
532 struct eap_method
*eap
;
535 eap
= eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION
,
536 EAP_VENDOR_IETF
, EAP_TYPE_PAX
, "PAX");
540 eap
->init
= eap_pax_init
;
541 eap
->deinit
= eap_pax_deinit
;
542 eap
->process
= eap_pax_process
;
543 eap
->isKeyAvailable
= eap_pax_isKeyAvailable
;
544 eap
->getKey
= eap_pax_getKey
;
545 eap
->get_emsk
= eap_pax_get_emsk
;
547 ret
= eap_peer_method_register(eap
);
549 eap_peer_method_free(eap
);