1189251Ssam/* 2189251Ssam * EAP peer method: EAP-PAX (RFC 4746) 3189251Ssam * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12252726Srpaulo#include "crypto/random.h" 13189251Ssam#include "eap_common/eap_pax_common.h" 14214734Srpaulo#include "eap_i.h" 15189251Ssam 16189251Ssam/* 17189251Ssam * Note: only PAX_STD subprotocol is currently supported 18189251Ssam * 19189251Ssam * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite 20189251Ssam * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and 21189251Ssam * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits), 22189251Ssam * RSAES-OAEP). 23189251Ssam */ 24189251Ssam 25189251Ssamstruct eap_pax_data { 26189251Ssam enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state; 27189251Ssam u8 mac_id, dh_group_id, public_key_id; 28189251Ssam union { 29189251Ssam u8 e[2 * EAP_PAX_RAND_LEN]; 30189251Ssam struct { 31189251Ssam u8 x[EAP_PAX_RAND_LEN]; /* server rand */ 32189251Ssam u8 y[EAP_PAX_RAND_LEN]; /* client rand */ 33189251Ssam } r; 34189251Ssam } rand; 35189251Ssam char *cid; 36189251Ssam size_t cid_len; 37189251Ssam u8 ak[EAP_PAX_AK_LEN]; 38189251Ssam u8 mk[EAP_PAX_MK_LEN]; 39189251Ssam u8 ck[EAP_PAX_CK_LEN]; 40189251Ssam u8 ick[EAP_PAX_ICK_LEN]; 41189251Ssam}; 42189251Ssam 43189251Ssam 44189251Ssamstatic void eap_pax_deinit(struct eap_sm *sm, void *priv); 45189251Ssam 46189251Ssam 47189251Ssamstatic void * eap_pax_init(struct eap_sm *sm) 48189251Ssam{ 49189251Ssam struct eap_pax_data *data; 50189251Ssam const u8 *identity, *password; 51189251Ssam size_t identity_len, password_len; 52189251Ssam 53189251Ssam identity = eap_get_config_identity(sm, &identity_len); 54189251Ssam password = eap_get_config_password(sm, &password_len); 55189251Ssam if (!identity || !password) { 56189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) " 57189251Ssam "not configured"); 58189251Ssam return NULL; 59189251Ssam } 60189251Ssam 61189251Ssam if (password_len != EAP_PAX_AK_LEN) { 62189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length"); 63189251Ssam return NULL; 64189251Ssam } 65189251Ssam 66189251Ssam data = os_zalloc(sizeof(*data)); 67189251Ssam if (data == NULL) 68189251Ssam return NULL; 69189251Ssam data->state = PAX_INIT; 70189251Ssam 71189251Ssam data->cid = os_malloc(identity_len); 72189251Ssam if (data->cid == NULL) { 73189251Ssam eap_pax_deinit(sm, data); 74189251Ssam return NULL; 75189251Ssam } 76189251Ssam os_memcpy(data->cid, identity, identity_len); 77189251Ssam data->cid_len = identity_len; 78189251Ssam 79189251Ssam os_memcpy(data->ak, password, EAP_PAX_AK_LEN); 80189251Ssam 81189251Ssam return data; 82189251Ssam} 83189251Ssam 84189251Ssam 85189251Ssamstatic void eap_pax_deinit(struct eap_sm *sm, void *priv) 86189251Ssam{ 87189251Ssam struct eap_pax_data *data = priv; 88189251Ssam os_free(data->cid); 89189251Ssam os_free(data); 90189251Ssam} 91189251Ssam 92189251Ssam 93189251Ssamstatic struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req, 94189251Ssam u8 id, u8 op_code, size_t plen) 95189251Ssam{ 96189251Ssam struct wpabuf *resp; 97189251Ssam struct eap_pax_hdr *pax; 98189251Ssam 99189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX, 100189251Ssam sizeof(*pax) + plen, EAP_CODE_RESPONSE, id); 101189251Ssam if (resp == NULL) 102189251Ssam return NULL; 103189251Ssam 104189251Ssam pax = wpabuf_put(resp, sizeof(*pax)); 105189251Ssam pax->op_code = op_code; 106189251Ssam pax->flags = 0; 107189251Ssam pax->mac_id = req->mac_id; 108189251Ssam pax->dh_group_id = req->dh_group_id; 109189251Ssam pax->public_key_id = req->public_key_id; 110189251Ssam 111189251Ssam return resp; 112189251Ssam} 113189251Ssam 114189251Ssam 115189251Ssamstatic struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data, 116189251Ssam struct eap_method_ret *ret, u8 id, 117189251Ssam const struct eap_pax_hdr *req, 118189251Ssam size_t req_plen) 119189251Ssam{ 120189251Ssam struct wpabuf *resp; 121189251Ssam const u8 *pos; 122189251Ssam u8 *rpos; 123189251Ssam size_t left, plen; 124189251Ssam 125189251Ssam wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)"); 126189251Ssam 127189251Ssam if (data->state != PAX_INIT) { 128189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in " 129189251Ssam "unexpected state (%d) - ignored", data->state); 130189251Ssam ret->ignore = TRUE; 131189251Ssam return NULL; 132189251Ssam } 133189251Ssam 134189251Ssam if (req->flags & EAP_PAX_FLAGS_CE) { 135189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - " 136189251Ssam "ignored"); 137189251Ssam ret->ignore = TRUE; 138189251Ssam return NULL; 139189251Ssam } 140189251Ssam 141189251Ssam left = req_plen - sizeof(*req); 142189251Ssam 143189251Ssam if (left < 2 + EAP_PAX_RAND_LEN) { 144189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short " 145189251Ssam "payload"); 146189251Ssam ret->ignore = TRUE; 147189251Ssam return NULL; 148189251Ssam } 149189251Ssam 150189251Ssam pos = (const u8 *) (req + 1); 151189251Ssam if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) { 152189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A " 153189251Ssam "length %d (expected %d)", 154189251Ssam WPA_GET_BE16(pos), EAP_PAX_RAND_LEN); 155189251Ssam ret->ignore = TRUE; 156189251Ssam return NULL; 157189251Ssam } 158189251Ssam 159189251Ssam pos += 2; 160189251Ssam left -= 2; 161189251Ssam os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN); 162189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)", 163189251Ssam data->rand.r.x, EAP_PAX_RAND_LEN); 164189251Ssam pos += EAP_PAX_RAND_LEN; 165189251Ssam left -= EAP_PAX_RAND_LEN; 166189251Ssam 167189251Ssam if (left > 0) { 168189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", 169189251Ssam pos, left); 170189251Ssam } 171189251Ssam 172252726Srpaulo if (random_get_bytes(data->rand.r.y, EAP_PAX_RAND_LEN)) { 173189251Ssam wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); 174189251Ssam ret->ignore = TRUE; 175189251Ssam return NULL; 176189251Ssam } 177189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", 178189251Ssam data->rand.r.y, EAP_PAX_RAND_LEN); 179189251Ssam 180189251Ssam if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e, 181189251Ssam data->mk, data->ck, data->ick) < 0) 182189251Ssam { 183189251Ssam ret->ignore = TRUE; 184189251Ssam return NULL; 185189251Ssam } 186189251Ssam 187189251Ssam wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)"); 188189251Ssam 189189251Ssam plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN + 190189251Ssam EAP_PAX_ICV_LEN; 191189251Ssam resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen); 192189251Ssam if (resp == NULL) 193189251Ssam return NULL; 194189251Ssam 195189251Ssam wpabuf_put_be16(resp, EAP_PAX_RAND_LEN); 196189251Ssam wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN); 197189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)", 198189251Ssam data->rand.r.y, EAP_PAX_RAND_LEN); 199189251Ssam 200189251Ssam wpabuf_put_be16(resp, data->cid_len); 201189251Ssam wpabuf_put_data(resp, data->cid, data->cid_len); 202189251Ssam wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", 203189251Ssam (u8 *) data->cid, data->cid_len); 204189251Ssam 205189251Ssam wpabuf_put_be16(resp, EAP_PAX_MAC_LEN); 206189251Ssam rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN); 207189251Ssam eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN, 208189251Ssam data->rand.r.x, EAP_PAX_RAND_LEN, 209189251Ssam data->rand.r.y, EAP_PAX_RAND_LEN, 210189251Ssam (u8 *) data->cid, data->cid_len, rpos); 211189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", 212189251Ssam rpos, EAP_PAX_MAC_LEN); 213189251Ssam 214189251Ssam /* Optional ADE could be added here, if needed */ 215189251Ssam 216189251Ssam rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); 217189251Ssam eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, 218189251Ssam wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, 219189251Ssam NULL, 0, NULL, 0, rpos); 220189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); 221189251Ssam 222189251Ssam data->state = PAX_STD_2_SENT; 223189251Ssam data->mac_id = req->mac_id; 224189251Ssam data->dh_group_id = req->dh_group_id; 225189251Ssam data->public_key_id = req->public_key_id; 226189251Ssam 227189251Ssam return resp; 228189251Ssam} 229189251Ssam 230189251Ssam 231189251Ssamstatic struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data, 232189251Ssam struct eap_method_ret *ret, u8 id, 233189251Ssam const struct eap_pax_hdr *req, 234189251Ssam size_t req_plen) 235189251Ssam{ 236189251Ssam struct wpabuf *resp; 237189251Ssam u8 *rpos, mac[EAP_PAX_MAC_LEN]; 238189251Ssam const u8 *pos; 239189251Ssam size_t left; 240189251Ssam 241189251Ssam wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)"); 242189251Ssam 243189251Ssam if (data->state != PAX_STD_2_SENT) { 244189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in " 245189251Ssam "unexpected state (%d) - ignored", data->state); 246189251Ssam ret->ignore = TRUE; 247189251Ssam return NULL; 248189251Ssam } 249189251Ssam 250189251Ssam if (req->flags & EAP_PAX_FLAGS_CE) { 251189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - " 252189251Ssam "ignored"); 253189251Ssam ret->ignore = TRUE; 254189251Ssam return NULL; 255189251Ssam } 256189251Ssam 257189251Ssam left = req_plen - sizeof(*req); 258189251Ssam 259189251Ssam if (left < 2 + EAP_PAX_MAC_LEN) { 260189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short " 261189251Ssam "payload"); 262189251Ssam ret->ignore = TRUE; 263189251Ssam return NULL; 264189251Ssam } 265189251Ssam 266189251Ssam pos = (const u8 *) (req + 1); 267189251Ssam if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) { 268189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect " 269189251Ssam "MAC_CK length %d (expected %d)", 270189251Ssam WPA_GET_BE16(pos), EAP_PAX_MAC_LEN); 271189251Ssam ret->ignore = TRUE; 272189251Ssam return NULL; 273189251Ssam } 274189251Ssam pos += 2; 275189251Ssam left -= 2; 276189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", 277189251Ssam pos, EAP_PAX_MAC_LEN); 278189251Ssam eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, 279189251Ssam data->rand.r.y, EAP_PAX_RAND_LEN, 280189251Ssam (u8 *) data->cid, data->cid_len, NULL, 0, mac); 281189251Ssam if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) { 282189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) " 283189251Ssam "received"); 284189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)", 285189251Ssam mac, EAP_PAX_MAC_LEN); 286189251Ssam ret->methodState = METHOD_DONE; 287189251Ssam ret->decision = DECISION_FAIL; 288189251Ssam return NULL; 289189251Ssam } 290189251Ssam 291189251Ssam pos += EAP_PAX_MAC_LEN; 292189251Ssam left -= EAP_PAX_MAC_LEN; 293189251Ssam 294189251Ssam if (left > 0) { 295189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", 296189251Ssam pos, left); 297189251Ssam } 298189251Ssam 299189251Ssam wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)"); 300189251Ssam 301189251Ssam resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN); 302189251Ssam if (resp == NULL) 303189251Ssam return NULL; 304189251Ssam 305189251Ssam /* Optional ADE could be added here, if needed */ 306189251Ssam 307189251Ssam rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN); 308189251Ssam eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, 309189251Ssam wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN, 310189251Ssam NULL, 0, NULL, 0, rpos); 311189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN); 312189251Ssam 313189251Ssam data->state = PAX_DONE; 314189251Ssam ret->methodState = METHOD_DONE; 315189251Ssam ret->decision = DECISION_UNCOND_SUCC; 316189251Ssam ret->allowNotifications = FALSE; 317189251Ssam 318189251Ssam return resp; 319189251Ssam} 320189251Ssam 321189251Ssam 322189251Ssamstatic struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv, 323189251Ssam struct eap_method_ret *ret, 324189251Ssam const struct wpabuf *reqData) 325189251Ssam{ 326189251Ssam struct eap_pax_data *data = priv; 327189251Ssam const struct eap_pax_hdr *req; 328189251Ssam struct wpabuf *resp; 329189251Ssam u8 icvbuf[EAP_PAX_ICV_LEN], id; 330189251Ssam const u8 *icv, *pos; 331189251Ssam size_t len; 332189251Ssam u16 flen, mlen; 333189251Ssam 334189251Ssam pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len); 335189251Ssam if (pos == NULL || len < EAP_PAX_ICV_LEN) { 336189251Ssam ret->ignore = TRUE; 337189251Ssam return NULL; 338189251Ssam } 339189251Ssam id = eap_get_id(reqData); 340189251Ssam req = (const struct eap_pax_hdr *) pos; 341189251Ssam flen = len - EAP_PAX_ICV_LEN; 342189251Ssam mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN; 343189251Ssam 344189251Ssam wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " 345189251Ssam "flags 0x%x mac_id 0x%x dh_group_id 0x%x " 346189251Ssam "public_key_id 0x%x", 347189251Ssam req->op_code, req->flags, req->mac_id, req->dh_group_id, 348189251Ssam req->public_key_id); 349189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", 350189251Ssam pos, len - EAP_PAX_ICV_LEN); 351189251Ssam 352189251Ssam if (data->state != PAX_INIT && data->mac_id != req->mac_id) { 353189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during " 354189251Ssam "authentication (was 0x%d, is 0x%d)", 355189251Ssam data->mac_id, req->mac_id); 356189251Ssam ret->ignore = TRUE; 357189251Ssam return NULL; 358189251Ssam } 359189251Ssam 360189251Ssam if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) { 361189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during " 362189251Ssam "authentication (was 0x%d, is 0x%d)", 363189251Ssam data->dh_group_id, req->dh_group_id); 364189251Ssam ret->ignore = TRUE; 365189251Ssam return NULL; 366189251Ssam } 367189251Ssam 368189251Ssam if (data->state != PAX_INIT && 369189251Ssam data->public_key_id != req->public_key_id) { 370189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during " 371189251Ssam "authentication (was 0x%d, is 0x%d)", 372189251Ssam data->public_key_id, req->public_key_id); 373189251Ssam ret->ignore = TRUE; 374189251Ssam return NULL; 375189251Ssam } 376189251Ssam 377189251Ssam /* TODO: add support EAP_PAX_HMAC_SHA256_128 */ 378189251Ssam if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) { 379189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x", 380189251Ssam req->mac_id); 381189251Ssam ret->ignore = TRUE; 382189251Ssam return NULL; 383189251Ssam } 384189251Ssam 385189251Ssam if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) { 386189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x", 387189251Ssam req->dh_group_id); 388189251Ssam ret->ignore = TRUE; 389189251Ssam return NULL; 390189251Ssam } 391189251Ssam 392189251Ssam if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { 393189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x", 394189251Ssam req->public_key_id); 395189251Ssam ret->ignore = TRUE; 396189251Ssam return NULL; 397189251Ssam } 398189251Ssam 399189251Ssam if (req->flags & EAP_PAX_FLAGS_MF) { 400189251Ssam /* TODO: add support for reassembling fragments */ 401189251Ssam wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - " 402189251Ssam "ignored packet"); 403189251Ssam ret->ignore = TRUE; 404189251Ssam return NULL; 405189251Ssam } 406189251Ssam 407189251Ssam icv = pos + len - EAP_PAX_ICV_LEN; 408189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); 409189251Ssam if (req->op_code == EAP_PAX_OP_STD_1) { 410189251Ssam eap_pax_mac(req->mac_id, (u8 *) "", 0, 411189251Ssam wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, 412189251Ssam icvbuf); 413189251Ssam } else { 414189251Ssam eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN, 415189251Ssam wpabuf_head(reqData), mlen, NULL, 0, NULL, 0, 416189251Ssam icvbuf); 417189251Ssam } 418189251Ssam if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) { 419189251Ssam wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the " 420189251Ssam "message"); 421189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV", 422189251Ssam icvbuf, EAP_PAX_ICV_LEN); 423189251Ssam ret->ignore = TRUE; 424189251Ssam return NULL; 425189251Ssam } 426189251Ssam 427189251Ssam ret->ignore = FALSE; 428189251Ssam ret->methodState = METHOD_MAY_CONT; 429189251Ssam ret->decision = DECISION_FAIL; 430189251Ssam ret->allowNotifications = TRUE; 431189251Ssam 432189251Ssam switch (req->op_code) { 433189251Ssam case EAP_PAX_OP_STD_1: 434189251Ssam resp = eap_pax_process_std_1(data, ret, id, req, flen); 435189251Ssam break; 436189251Ssam case EAP_PAX_OP_STD_3: 437189251Ssam resp = eap_pax_process_std_3(data, ret, id, req, flen); 438189251Ssam break; 439189251Ssam default: 440189251Ssam wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown " 441189251Ssam "op_code %d", req->op_code); 442189251Ssam ret->ignore = TRUE; 443189251Ssam return NULL; 444189251Ssam } 445189251Ssam 446189251Ssam if (ret->methodState == METHOD_DONE) { 447189251Ssam ret->allowNotifications = FALSE; 448189251Ssam } 449189251Ssam 450189251Ssam return resp; 451189251Ssam} 452189251Ssam 453189251Ssam 454189251Ssamstatic Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv) 455189251Ssam{ 456189251Ssam struct eap_pax_data *data = priv; 457189251Ssam return data->state == PAX_DONE; 458189251Ssam} 459189251Ssam 460189251Ssam 461189251Ssamstatic u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) 462189251Ssam{ 463189251Ssam struct eap_pax_data *data = priv; 464189251Ssam u8 *key; 465189251Ssam 466189251Ssam if (data->state != PAX_DONE) 467189251Ssam return NULL; 468189251Ssam 469189251Ssam key = os_malloc(EAP_MSK_LEN); 470189251Ssam if (key == NULL) 471189251Ssam return NULL; 472189251Ssam 473189251Ssam *len = EAP_MSK_LEN; 474189251Ssam eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 475189251Ssam "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, 476189251Ssam EAP_MSK_LEN, key); 477189251Ssam 478189251Ssam return key; 479189251Ssam} 480189251Ssam 481189251Ssam 482189251Ssamstatic u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 483189251Ssam{ 484189251Ssam struct eap_pax_data *data = priv; 485189251Ssam u8 *key; 486189251Ssam 487189251Ssam if (data->state != PAX_DONE) 488189251Ssam return NULL; 489189251Ssam 490189251Ssam key = os_malloc(EAP_EMSK_LEN); 491189251Ssam if (key == NULL) 492189251Ssam return NULL; 493189251Ssam 494189251Ssam *len = EAP_EMSK_LEN; 495189251Ssam eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, 496189251Ssam "Extended Master Session Key", 497189251Ssam data->rand.e, 2 * EAP_PAX_RAND_LEN, 498189251Ssam EAP_EMSK_LEN, key); 499189251Ssam 500189251Ssam return key; 501189251Ssam} 502189251Ssam 503189251Ssam 504189251Ssamint eap_peer_pax_register(void) 505189251Ssam{ 506189251Ssam struct eap_method *eap; 507189251Ssam int ret; 508189251Ssam 509189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 510189251Ssam EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); 511189251Ssam if (eap == NULL) 512189251Ssam return -1; 513189251Ssam 514189251Ssam eap->init = eap_pax_init; 515189251Ssam eap->deinit = eap_pax_deinit; 516189251Ssam eap->process = eap_pax_process; 517189251Ssam eap->isKeyAvailable = eap_pax_isKeyAvailable; 518189251Ssam eap->getKey = eap_pax_getKey; 519189251Ssam eap->get_emsk = eap_pax_get_emsk; 520189251Ssam 521189251Ssam ret = eap_peer_method_register(eap); 522189251Ssam if (ret) 523189251Ssam eap_peer_method_free(eap); 524189251Ssam return ret; 525189251Ssam} 526