2 * WPA Supplicant / EAP-PAX (draft-clancy-eap-pax-04.txt)
3 * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.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.
21 #include "wpa_supplicant.h"
22 #include "config_ssid.h"
23 #include "eap_pax_common.h"
28 * Note: only PAX_STD subprotocol is currently supported
32 enum { PAX_INIT
, PAX_STD_2_SENT
, PAX_DONE
} state
;
33 u8 mac_id
, dh_group_id
, public_key_id
;
35 u8 e
[2 * EAP_PAX_RAND_LEN
];
37 u8 x
[EAP_PAX_RAND_LEN
]; /* server rand */
38 u8 y
[EAP_PAX_RAND_LEN
]; /* client rand */
43 u8 ak
[EAP_PAX_AK_LEN
];
44 u8 mk
[EAP_PAX_MK_LEN
];
45 u8 ck
[EAP_PAX_CK_LEN
];
46 u8 ick
[EAP_PAX_ICK_LEN
];
50 static void eap_pax_deinit(struct eap_sm
*sm
, void *priv
);
53 static void * eap_pax_init(struct eap_sm
*sm
)
55 struct wpa_ssid
*config
= eap_get_config(sm
);
56 struct eap_pax_data
*data
;
58 if (config
== NULL
|| !config
->nai
||
59 (!config
->eappsk
&& !config
->password
)) {
60 wpa_printf(MSG_INFO
, "EAP-PAX: CID (nai) or key "
61 "(eappsk/password) not configured");
65 if (config
->eappsk
&& config
->eappsk_len
!= EAP_PAX_AK_LEN
) {
66 wpa_printf(MSG_INFO
, "EAP-PAX: incorrect key length (eappsk); "
67 "expected %d", EAP_PAX_AK_LEN
);
71 data
= malloc(sizeof(*data
));
74 memset(data
, 0, sizeof(*data
));
75 data
->state
= PAX_INIT
;
77 data
->cid
= malloc(config
->nai_len
);
78 if (data
->cid
== NULL
) {
79 eap_pax_deinit(sm
, data
);
82 memcpy(data
->cid
, config
->nai
, config
->nai_len
);
83 data
->cid_len
= config
->nai_len
;
86 memcpy(data
->ak
, config
->eappsk
, EAP_PAX_AK_LEN
);
88 u8 hash
[SHA1_MAC_LEN
];
89 const unsigned char *addr
[1];
91 addr
[0] = config
->password
;
92 len
[0] = config
->password_len
;
93 sha1_vector(1, addr
, len
, hash
);
94 memcpy(data
->ak
, hash
, EAP_PAX_AK_LEN
);
101 static void eap_pax_deinit(struct eap_sm
*sm
, void *priv
)
103 struct eap_pax_data
*data
= priv
;
109 static struct eap_pax_hdr
* eap_pax_alloc_resp(const struct eap_pax_hdr
*req
,
110 u16 resp_len
, u8 op_code
)
112 struct eap_pax_hdr
*resp
;
114 resp
= malloc(resp_len
);
117 resp
->code
= EAP_CODE_RESPONSE
;
118 resp
->identifier
= req
->identifier
;
119 resp
->length
= host_to_be16(resp_len
);
120 resp
->type
= EAP_TYPE_PAX
;
121 resp
->op_code
= op_code
;
123 resp
->mac_id
= req
->mac_id
;
124 resp
->dh_group_id
= req
->dh_group_id
;
125 resp
->public_key_id
= req
->public_key_id
;
130 static u8
* eap_pax_process_std_1(struct eap_sm
*sm
, struct eap_pax_data
*data
,
131 struct eap_method_ret
*ret
,
132 const u8
*reqData
, size_t reqDataLen
,
135 const struct eap_pax_hdr
*req
;
136 struct eap_pax_hdr
*resp
;
141 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-1 (received)");
142 req
= (const struct eap_pax_hdr
*) reqData
;
144 if (data
->state
!= PAX_INIT
) {
145 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 received in "
146 "unexpected state (%d) - ignored", data
->state
);
151 if (req
->flags
& EAP_PAX_FLAGS_CE
) {
152 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 with CE flag set - "
158 left
= reqDataLen
- sizeof(*req
);
160 if (left
< 2 + EAP_PAX_RAND_LEN
) {
161 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 with too short "
167 pos
= (const u8
*) (req
+ 1);
168 if (WPA_GET_BE16(pos
) != EAP_PAX_RAND_LEN
) {
169 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-1 with incorrect A "
170 "length %d (expected %d)",
171 WPA_GET_BE16(pos
), EAP_PAX_RAND_LEN
);
178 memcpy(data
->rand
.r
.x
, pos
, EAP_PAX_RAND_LEN
);
179 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: X (server rand)",
180 data
->rand
.r
.x
, EAP_PAX_RAND_LEN
);
181 pos
+= EAP_PAX_RAND_LEN
;
182 left
-= EAP_PAX_RAND_LEN
;
185 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ignored extra payload",
189 if (hostapd_get_rand(data
->rand
.r
.y
, EAP_PAX_RAND_LEN
)) {
190 wpa_printf(MSG_ERROR
, "EAP-PAX: Failed to get random data");
194 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: Y (client rand)",
195 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
);
197 if (eap_pax_initial_key_derivation(req
->mac_id
, data
->ak
, data
->rand
.e
,
198 data
->mk
, data
->ck
, data
->ick
) < 0)
204 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-2 (sending)");
206 *respDataLen
= sizeof(*resp
) + 2 + EAP_PAX_RAND_LEN
+
207 2 + data
->cid_len
+ 2 + EAP_PAX_MAC_LEN
+ EAP_PAX_ICV_LEN
;
208 resp
= eap_pax_alloc_resp(req
, *respDataLen
, EAP_PAX_OP_STD_2
);
212 rpos
= (u8
*) (resp
+ 1);
214 *rpos
++ = EAP_PAX_RAND_LEN
;
215 memcpy(rpos
, data
->rand
.r
.y
, EAP_PAX_RAND_LEN
);
216 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: B = Y (client rand)",
217 rpos
, EAP_PAX_RAND_LEN
);
218 rpos
+= EAP_PAX_RAND_LEN
;
220 WPA_PUT_BE16(rpos
, data
->cid_len
);
222 memcpy(rpos
, data
->cid
, data
->cid_len
);
223 wpa_hexdump_ascii(MSG_MSGDUMP
, "EAP-PAX: CID", rpos
, data
->cid_len
);
224 rpos
+= data
->cid_len
;
227 *rpos
++ = EAP_PAX_MAC_LEN
;
228 eap_pax_mac(req
->mac_id
, data
->ck
, EAP_PAX_CK_LEN
,
229 data
->rand
.r
.x
, EAP_PAX_RAND_LEN
,
230 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
,
231 (u8
*) data
->cid
, data
->cid_len
, rpos
);
232 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: MAC_CK(A, B, CID)",
233 rpos
, EAP_PAX_MAC_LEN
);
234 rpos
+= EAP_PAX_MAC_LEN
;
236 eap_pax_mac(req
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
237 (u8
*) resp
, *respDataLen
- EAP_PAX_ICV_LEN
,
238 NULL
, 0, NULL
, 0, rpos
);
239 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", rpos
, EAP_PAX_ICV_LEN
);
240 rpos
+= EAP_PAX_ICV_LEN
;
242 data
->state
= PAX_STD_2_SENT
;
243 data
->mac_id
= req
->mac_id
;
244 data
->dh_group_id
= req
->dh_group_id
;
245 data
->public_key_id
= req
->public_key_id
;
251 static u8
* eap_pax_process_std_3(struct eap_sm
*sm
, struct eap_pax_data
*data
,
252 struct eap_method_ret
*ret
,
253 const u8
*reqData
, size_t reqDataLen
,
256 const struct eap_pax_hdr
*req
;
257 struct eap_pax_hdr
*resp
;
258 u8
*rpos
, mac
[EAP_PAX_MAC_LEN
];
262 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX_STD-3 (received)");
263 req
= (const struct eap_pax_hdr
*) reqData
;
265 if (data
->state
!= PAX_STD_2_SENT
) {
266 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 received in "
267 "unexpected state (%d) - ignored", data
->state
);
272 if (req
->flags
& EAP_PAX_FLAGS_CE
) {
273 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 with CE flag set - "
279 left
= reqDataLen
- sizeof(*req
);
281 if (left
< 2 + EAP_PAX_MAC_LEN
) {
282 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 with too short "
288 pos
= (const u8
*) (req
+ 1);
289 if (WPA_GET_BE16(pos
) != EAP_PAX_MAC_LEN
) {
290 wpa_printf(MSG_INFO
, "EAP-PAX: PAX_STD-3 with incorrect "
291 "MAC_CK length %d (expected %d)",
292 WPA_GET_BE16(pos
), EAP_PAX_MAC_LEN
);
298 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: MAC_CK(B, CID)",
299 pos
, EAP_PAX_MAC_LEN
);
300 eap_pax_mac(data
->mac_id
, data
->ck
, EAP_PAX_CK_LEN
,
301 data
->rand
.r
.y
, EAP_PAX_RAND_LEN
,
302 (u8
*) data
->cid
, data
->cid_len
, NULL
, 0, mac
);
303 if (memcmp(pos
, mac
, EAP_PAX_MAC_LEN
) != 0) {
304 wpa_printf(MSG_INFO
, "EAP-PAX: Invalid MAC_CK(B, CID) "
306 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: expected MAC_CK(B, CID)",
307 mac
, EAP_PAX_MAC_LEN
);
308 ret
->methodState
= METHOD_DONE
;
309 ret
->decision
= DECISION_FAIL
;
313 pos
+= EAP_PAX_MAC_LEN
;
314 left
-= EAP_PAX_MAC_LEN
;
317 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ignored extra payload",
321 wpa_printf(MSG_DEBUG
, "EAP-PAX: PAX-ACK (sending)");
323 *respDataLen
= sizeof(*resp
) + EAP_PAX_ICV_LEN
;
324 resp
= eap_pax_alloc_resp(req
, *respDataLen
, EAP_PAX_OP_ACK
);
328 rpos
= (u8
*) (resp
+ 1);
329 eap_pax_mac(data
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
330 (u8
*) resp
, *respDataLen
- EAP_PAX_ICV_LEN
,
331 NULL
, 0, NULL
, 0, rpos
);
332 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", rpos
, EAP_PAX_ICV_LEN
);
334 data
->state
= PAX_DONE
;
335 ret
->methodState
= METHOD_DONE
;
336 ret
->decision
= DECISION_UNCOND_SUCC
;
337 ret
->allowNotifications
= FALSE
;
343 static u8
* eap_pax_process(struct eap_sm
*sm
, void *priv
,
344 struct eap_method_ret
*ret
,
345 const u8
*reqData
, size_t reqDataLen
,
348 struct eap_pax_data
*data
= priv
;
349 const struct eap_pax_hdr
*req
;
350 u8
*resp
, icvbuf
[EAP_PAX_ICV_LEN
];
355 pos
= eap_hdr_validate(EAP_TYPE_PAX
, reqData
, reqDataLen
, &len
);
356 if (pos
== NULL
|| len
< EAP_PAX_ICV_LEN
) {
360 req
= (const struct eap_pax_hdr
*) reqData
;
361 flen
= be_to_host16(req
->length
) - EAP_PAX_ICV_LEN
;
363 wpa_printf(MSG_DEBUG
, "EAP-PAX: received frame: op_code 0x%x "
364 "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
365 "public_key_id 0x%x",
366 req
->op_code
, req
->flags
, req
->mac_id
, req
->dh_group_id
,
368 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: received payload",
369 pos
, len
- EAP_PAX_ICV_LEN
);
371 if (data
->state
!= PAX_INIT
&& data
->mac_id
!= req
->mac_id
) {
372 wpa_printf(MSG_INFO
, "EAP-PAX: MAC ID changed during "
373 "authentication (was 0x%d, is 0x%d)",
374 data
->mac_id
, req
->mac_id
);
379 if (data
->state
!= PAX_INIT
&& data
->dh_group_id
!= req
->dh_group_id
) {
380 wpa_printf(MSG_INFO
, "EAP-PAX: DH Group ID changed during "
381 "authentication (was 0x%d, is 0x%d)",
382 data
->dh_group_id
, req
->dh_group_id
);
387 if (data
->state
!= PAX_INIT
&&
388 data
->public_key_id
!= req
->public_key_id
) {
389 wpa_printf(MSG_INFO
, "EAP-PAX: Public Key ID changed during "
390 "authentication (was 0x%d, is 0x%d)",
391 data
->public_key_id
, req
->public_key_id
);
396 /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */
397 if (req
->mac_id
!= EAP_PAX_MAC_HMAC_SHA1_128
) {
398 wpa_printf(MSG_INFO
, "EAP-PAX: Unsupported MAC ID 0x%x",
404 if (req
->dh_group_id
!= EAP_PAX_DH_GROUP_NONE
) {
405 wpa_printf(MSG_INFO
, "EAP-PAX: Unsupported DH Group ID 0x%x",
411 if (req
->public_key_id
!= EAP_PAX_PUBLIC_KEY_NONE
) {
412 wpa_printf(MSG_INFO
, "EAP-PAX: Unsupported Public Key ID 0x%x",
418 if (req
->flags
& EAP_PAX_FLAGS_MF
) {
419 /* TODO: add support for reassembling fragments */
420 wpa_printf(MSG_INFO
, "EAP-PAX: fragmentation not supported - "
426 icv
= pos
+ len
- EAP_PAX_ICV_LEN
;
427 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: ICV", icv
, EAP_PAX_ICV_LEN
);
428 if (req
->op_code
== EAP_PAX_OP_STD_1
) {
429 eap_pax_mac(req
->mac_id
, (u8
*) "", 0,
430 reqData
, flen
, NULL
, 0, NULL
, 0, icvbuf
);
432 eap_pax_mac(req
->mac_id
, data
->ick
, EAP_PAX_ICK_LEN
,
433 reqData
, flen
, NULL
, 0, NULL
, 0, icvbuf
);
435 if (memcmp(icv
, icvbuf
, EAP_PAX_ICV_LEN
) != 0) {
436 wpa_printf(MSG_DEBUG
, "EAP-PAX: invalid ICV - ignoring the "
438 wpa_hexdump(MSG_MSGDUMP
, "EAP-PAX: expected ICV",
439 icvbuf
, EAP_PAX_ICV_LEN
);
445 ret
->methodState
= METHOD_MAY_CONT
;
446 ret
->decision
= DECISION_FAIL
;
447 ret
->allowNotifications
= TRUE
;
449 switch (req
->op_code
) {
450 case EAP_PAX_OP_STD_1
:
451 resp
= eap_pax_process_std_1(sm
, data
, ret
, reqData
, flen
,
454 case EAP_PAX_OP_STD_3
:
455 resp
= eap_pax_process_std_3(sm
, data
, ret
, reqData
, flen
,
459 wpa_printf(MSG_DEBUG
, "EAP-PAX: ignoring message with unknown "
460 "op_code %d", req
->op_code
);
465 if (ret
->methodState
== METHOD_DONE
) {
466 ret
->allowNotifications
= FALSE
;
473 static Boolean
eap_pax_isKeyAvailable(struct eap_sm
*sm
, void *priv
)
475 struct eap_pax_data
*data
= priv
;
476 return data
->state
== PAX_DONE
;
480 static u8
* eap_pax_getKey(struct eap_sm
*sm
, void *priv
, size_t *len
)
482 struct eap_pax_data
*data
= priv
;
485 if (data
->state
!= PAX_DONE
)
488 key
= malloc(EAP_PAX_MSK_LEN
);
492 *len
= EAP_PAX_MSK_LEN
;
493 eap_pax_kdf(data
->mac_id
, data
->mk
, EAP_PAX_MK_LEN
,
494 "Master Session Key", data
->rand
.e
, 2 * EAP_PAX_RAND_LEN
,
495 EAP_PAX_MSK_LEN
, key
);
501 const struct eap_method eap_method_pax
=
503 .method
= EAP_TYPE_PAX
,
505 .init
= eap_pax_init
,
506 .deinit
= eap_pax_deinit
,
507 .process
= eap_pax_process
,
508 .isKeyAvailable
= eap_pax_isKeyAvailable
,
509 .getKey
= eap_pax_getKey
,