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,