133549Sjkh/* 233549Sjkh * EAP peer method: EAP-SAKE (RFC 4763) 3330449Seadler * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> 4330449Seadler * 5330449Seadler * This software may be distributed under the terms of the BSD license. 62892Sdfr * See README for more details. 72892Sdfr */ 82892Sdfr 92892Sdfr#include "includes.h" 102892Sdfr 112892Sdfr#include "common.h" 122892Sdfr#include "crypto/random.h" 132892Sdfr#include "eap_peer/eap_i.h" 142892Sdfr#include "eap_common/eap_sake_common.h" 152892Sdfr 162892Sdfrstruct eap_sake_data { 172892Sdfr enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state; 182892Sdfr u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN]; 192892Sdfr u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN]; 202892Sdfr u8 rand_s[EAP_SAKE_RAND_LEN]; 212892Sdfr u8 rand_p[EAP_SAKE_RAND_LEN]; 222892Sdfr struct { 232892Sdfr u8 auth[EAP_SAKE_TEK_AUTH_LEN]; 242892Sdfr u8 cipher[EAP_SAKE_TEK_CIPHER_LEN]; 252892Sdfr } tek; 262892Sdfr u8 msk[EAP_MSK_LEN]; 272892Sdfr u8 emsk[EAP_EMSK_LEN]; 282892Sdfr u8 session_id; 292892Sdfr int session_id_set; 302892Sdfr u8 *peerid; 312892Sdfr size_t peerid_len; 322892Sdfr u8 *serverid; 332892Sdfr size_t serverid_len; 342892Sdfr}; 352892Sdfr 3615771Swollman 3750476Speterstatic const char * eap_sake_state_txt(int state) 382892Sdfr{ 392892Sdfr switch (state) { 402892Sdfr case IDENTITY: 412892Sdfr return "IDENTITY"; 422892Sdfr case CHALLENGE: 43120492Sfjoe return "CHALLENGE"; 44121363Strhodes case CONFIRM: 45121429Strhodes return "CONFIRM"; 4623335Sbde case SUCCESS: 472892Sdfr return "SUCCESS"; 482892Sdfr case FAILURE: 49340953Seugen return "FAILURE"; 502892Sdfr default: 5133761Sache return "?"; 522892Sdfr } 532892Sdfr} 5455613Sache 5555613Sache 562892Sdfrstatic void eap_sake_state(struct eap_sake_data *data, int state) 572892Sdfr{ 5815771Swollman wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s", 592892Sdfr eap_sake_state_txt(data->state), 60121429Strhodes eap_sake_state_txt(state)); 612892Sdfr data->state = state; 622892Sdfr} 6392882Simp 6492882Simp 6592882Simpstatic void eap_sake_deinit(struct eap_sm *sm, void *priv); 6692882Simp 67152809Savatar 682892Sdfrstatic void * eap_sake_init(struct eap_sm *sm) 692892Sdfr{ 70121373Strhodes struct eap_sake_data *data; 712892Sdfr const u8 *identity, *password; 72152416Smaxim size_t identity_len, password_len; 73152416Smaxim 742892Sdfr password = eap_get_config_password(sm, &password_len); 75247856Sjkim if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) { 76120492Sfjoe wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length " 77152362Srodrigc "configured"); 78166326Srodrigc return NULL; 79152362Srodrigc } 80152362Srodrigc 81152362Srodrigc data = os_zalloc(sizeof(*data)); 82152362Srodrigc if (data == NULL) 83152362Srodrigc return NULL; 842892Sdfr data->state = IDENTITY; 85247856Sjkim 862892Sdfr identity = eap_get_config_identity(sm, &identity_len); 87152731Savatar if (identity) { 882892Sdfr data->peerid = os_malloc(identity_len); 8933549Sjkh if (data->peerid == NULL) { 90152362Srodrigc eap_sake_deinit(sm, data); 9133549Sjkh return NULL; 9233549Sjkh } 93152362Srodrigc os_memcpy(data->peerid, identity, identity_len); 9433549Sjkh data->peerid_len = identity_len; 9533549Sjkh } 96152731Savatar 9733549Sjkh os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN); 982892Sdfr os_memcpy(data->root_secret_b, 99152362Srodrigc password + EAP_SAKE_ROOT_SECRET_LEN, 1002892Sdfr EAP_SAKE_ROOT_SECRET_LEN); 1012892Sdfr 1022892Sdfr return data; 103152362Srodrigc} 1042892Sdfr 1052892Sdfr 1062892Sdfrstatic void eap_sake_deinit(struct eap_sm *sm, void *priv) 107152362Srodrigc{ 1082892Sdfr struct eap_sake_data *data = priv; 1092892Sdfr os_free(data->serverid); 110118837Strhodes os_free(data->peerid); 111152362Srodrigc os_free(data); 112118837Strhodes} 113118837Strhodes 114152362Srodrigc 115152362Srodrigcstatic struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data, 116120492Sfjoe int id, size_t length, u8 subtype) 117120492Sfjoe{ 118120492Sfjoe struct eap_sake_hdr *sake; 119120492Sfjoe struct wpabuf *msg; 120120492Sfjoe size_t plen; 121152362Srodrigc 122152362Srodrigc plen = length + sizeof(struct eap_sake_hdr); 123152974Savatar 124152362Srodrigc msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen, 12533761Sache EAP_CODE_RESPONSE, id); 126120492Sfjoe if (msg == NULL) { 127152362Srodrigc wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory " 128152362Srodrigc "request"); 12933749Sache return NULL; 130152362Srodrigc } 131152362Srodrigc 132152362Srodrigc sake = wpabuf_put(msg, sizeof(*sake)); 133152416Smaxim sake->version = EAP_SAKE_VERSION; 134152416Smaxim sake->session_id = data->session_id; 135152362Srodrigc sake->subtype = subtype; 136152362Srodrigc 137152362Srodrigc return msg; 138152362Srodrigc} 139152362Srodrigc 140152362Srodrigc 1412892Sdfrstatic struct wpabuf * eap_sake_process_identity(struct eap_sm *sm, 142120492Sfjoe struct eap_sake_data *data, 143120492Sfjoe struct eap_method_ret *ret, 144152362Srodrigc const struct wpabuf *reqData, 145152362Srodrigc const u8 *payload, 146120492Sfjoe size_t payload_len) 147152362Srodrigc{ 148152362Srodrigc struct eap_sake_parse_attr attr; 149120492Sfjoe struct wpabuf *resp; 150152362Srodrigc 151152362Srodrigc if (data->state != IDENTITY) { 152120492Sfjoe ret->ignore = TRUE; 153152362Srodrigc return NULL; 154152362Srodrigc } 155120492Sfjoe 156120492Sfjoe wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity"); 157120492Sfjoe 158152362Srodrigc if (eap_sake_parse_attributes(payload, payload_len, &attr)) 159152362Srodrigc return NULL; 160120492Sfjoe 1612892Sdfr if (!attr.perm_id_req && !attr.any_id_req) { 1622892Sdfr wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or " 1632892Sdfr "AT_ANY_ID_REQ in Request/Identity"); 1642892Sdfr return NULL; 1652892Sdfr } 1662892Sdfr 1672892Sdfr wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity"); 1682892Sdfr 1692892Sdfr resp = eap_sake_build_msg(data, eap_get_id(reqData), 1702892Sdfr 2 + data->peerid_len, 171118837Strhodes EAP_SAKE_SUBTYPE_IDENTITY); 172152362Srodrigc if (resp == NULL) 173118837Strhodes return NULL; 174118837Strhodes 175118837Strhodes wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); 176152362Srodrigc eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, 177118837Strhodes data->peerid, data->peerid_len); 178118837Strhodes 179118837Strhodes eap_sake_state(data, CHALLENGE); 1802892Sdfr 1812892Sdfr return resp; 1822892Sdfr} 183152362Srodrigc 184152809Savatar 185120492Sfjoestatic struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, 186152362Srodrigc struct eap_sake_data *data, 187152362Srodrigc struct eap_method_ret *ret, 188152362Srodrigc const struct wpabuf *reqData, 189152809Savatar const u8 *payload, 190120492Sfjoe size_t payload_len) 191152362Srodrigc{ 192120492Sfjoe struct eap_sake_parse_attr attr; 193120492Sfjoe struct wpabuf *resp; 19452055Sphk u8 *rpos; 19552055Sphk size_t rlen; 19652055Sphk 19752055Sphk if (data->state != IDENTITY && data->state != CHALLENGE) { 198230226Sjh wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received " 199230226Sjh "in unexpected state (%d)", data->state); 20052055Sphk ret->ignore = TRUE; 20152055Sphk return NULL; 2022892Sdfr } 20352055Sphk if (data->state == IDENTITY) 20452055Sphk eap_sake_state(data, CHALLENGE); 2052892Sdfr 2062892Sdfr wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge"); 207152362Srodrigc 2082892Sdfr if (eap_sake_parse_attributes(payload, payload_len, &attr)) 209152362Srodrigc return NULL; 2102892Sdfr 211152362Srodrigc if (!attr.rand_s) { 212118837Strhodes wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not " 2132892Sdfr "include AT_RAND_S"); 2142892Sdfr return NULL; 215152362Srodrigc } 216152362Srodrigc 217152362Srodrigc os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN); 218166326Srodrigc wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", 219152362Srodrigc data->rand_s, EAP_SAKE_RAND_LEN); 220152362Srodrigc 221152362Srodrigc if (random_get_bytes(data->rand_p, EAP_SAKE_RAND_LEN)) { 222152362Srodrigc wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); 2232892Sdfr return NULL; 224247856Sjkim } 225185422Simp wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)", 226185422Simp data->rand_p, EAP_SAKE_RAND_LEN); 227185422Simp 228185422Simp os_free(data->serverid); 229185422Simp data->serverid = NULL; 230152362Srodrigc data->serverid_len = 0; 2312892Sdfr if (attr.serverid) { 2322892Sdfr wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID", 2332892Sdfr attr.serverid, attr.serverid_len); 2342892Sdfr data->serverid = os_malloc(attr.serverid_len); 235201227Sed if (data->serverid == NULL) 2362892Sdfr return NULL; 2372892Sdfr os_memcpy(data->serverid, attr.serverid, attr.serverid_len); 2382892Sdfr data->serverid_len = attr.serverid_len; 2392892Sdfr } 2402892Sdfr 2412892Sdfr eap_sake_derive_keys(data->root_secret_a, data->root_secret_b, 2422892Sdfr data->rand_s, data->rand_p, 2432892Sdfr (u8 *) &data->tek, data->msk, data->emsk); 2442892Sdfr 2452892Sdfr wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge"); 2462892Sdfr 2472892Sdfr rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN; 24815771Swollman if (data->peerid) 2492892Sdfr rlen += 2 + data->peerid_len; 2502892Sdfr resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen, 2512892Sdfr EAP_SAKE_SUBTYPE_CHALLENGE); 2522892Sdfr if (resp == NULL) 2532892Sdfr return NULL; 254201227Sed 2552892Sdfr wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P"); 2562892Sdfr eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P, 2572892Sdfr data->rand_p, EAP_SAKE_RAND_LEN); 2582892Sdfr 2592892Sdfr if (data->peerid) { 2602892Sdfr wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID"); 2612892Sdfr eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID, 2622892Sdfr data->peerid, data->peerid_len); 2632892Sdfr } 2642892Sdfr 2652892Sdfr wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); 2662892Sdfr wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); 26715771Swollman wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); 2682892Sdfr rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); 2692892Sdfr if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, 2702892Sdfr data->serverid, data->serverid_len, 2712892Sdfr data->peerid, data->peerid_len, 1, 2722892Sdfr wpabuf_head(resp), wpabuf_len(resp), rpos, 273201227Sed rpos)) { 2742892Sdfr wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); 2752892Sdfr wpabuf_free(resp); 2762892Sdfr return NULL; 2772892Sdfr } 2782892Sdfr 27933549Sjkh eap_sake_state(data, CONFIRM); 2802892Sdfr 2812892Sdfr return resp; 2822892Sdfr} 2832892Sdfr 2842892Sdfr 28515771Swollmanstatic struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm, 2862892Sdfr struct eap_sake_data *data, 2872892Sdfr struct eap_method_ret *ret, 2882892Sdfr const struct wpabuf *reqData, 2892892Sdfr const u8 *payload, 290201227Sed size_t payload_len) 2912892Sdfr{ 292121007Sfjoe struct eap_sake_parse_attr attr; 293121429Strhodes u8 mic_s[EAP_SAKE_MIC_LEN]; 294121429Strhodes struct wpabuf *resp; 295121429Strhodes u8 *rpos; 29615771Swollman 2972892Sdfr if (data->state != CONFIRM) { 29833749Sache ret->ignore = TRUE; 299120492Sfjoe return NULL; 300152809Savatar } 30133749Sache 302120492Sfjoe wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm"); 30333749Sache 304120492Sfjoe if (eap_sake_parse_attributes(payload, payload_len, &attr)) 305120492Sfjoe return NULL; 306134565Strhodes 307120492Sfjoe if (!attr.mic_s) { 308120492Sfjoe wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not " 309120492Sfjoe "include AT_MIC_S"); 310152809Savatar return NULL; 311152362Srodrigc } 312340953Seugen 313120492Sfjoe eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, 314152362Srodrigc data->serverid, data->serverid_len, 315152362Srodrigc data->peerid, data->peerid_len, 0, 316340953Seugen wpabuf_head(reqData), wpabuf_len(reqData), 317120492Sfjoe attr.mic_s, mic_s); 318120492Sfjoe if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) { 319152809Savatar wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S"); 320152362Srodrigc eap_sake_state(data, FAILURE); 321120492Sfjoe ret->methodState = METHOD_DONE; 322340953Seugen ret->decision = DECISION_FAIL; 323120492Sfjoe ret->allowNotifications = FALSE; 32433749Sache wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending " 32533761Sache "Response/Auth-Reject"); 326120492Sfjoe return eap_sake_build_msg(data, eap_get_id(reqData), 0, 32733761Sache EAP_SAKE_SUBTYPE_AUTH_REJECT); 328 } 329 330 wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm"); 331 332 resp = eap_sake_build_msg(data, eap_get_id(reqData), 333 2 + EAP_SAKE_MIC_LEN, 334 EAP_SAKE_SUBTYPE_CONFIRM); 335 if (resp == NULL) 336 return NULL; 337 338 wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P"); 339 wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P); 340 wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN); 341 rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN); 342 if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, 343 data->serverid, data->serverid_len, 344 data->peerid, data->peerid_len, 1, 345 wpabuf_head(resp), wpabuf_len(resp), rpos, 346 rpos)) { 347 wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); 348 wpabuf_free(resp); 349 return NULL; 350 } 351 352 eap_sake_state(data, SUCCESS); 353 ret->methodState = METHOD_DONE; 354 ret->decision = DECISION_UNCOND_SUCC; 355 ret->allowNotifications = FALSE; 356 357 return resp; 358} 359 360 361static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv, 362 struct eap_method_ret *ret, 363 const struct wpabuf *reqData) 364{ 365 struct eap_sake_data *data = priv; 366 const struct eap_sake_hdr *req; 367 struct wpabuf *resp; 368 const u8 *pos, *end; 369 size_t len; 370 u8 subtype, session_id; 371 372 pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len); 373 if (pos == NULL || len < sizeof(struct eap_sake_hdr)) { 374 ret->ignore = TRUE; 375 return NULL; 376 } 377 378 req = (const struct eap_sake_hdr *) pos; 379 end = pos + len; 380 subtype = req->subtype; 381 session_id = req->session_id; 382 pos = (const u8 *) (req + 1); 383 384 wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d " 385 "session_id %d", subtype, session_id); 386 wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes", 387 pos, end - pos); 388 389 if (data->session_id_set && data->session_id != session_id) { 390 wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)", 391 session_id, data->session_id); 392 ret->ignore = TRUE; 393 return NULL; 394 } 395 data->session_id = session_id; 396 data->session_id_set = 1; 397 398 ret->ignore = FALSE; 399 ret->methodState = METHOD_MAY_CONT; 400 ret->decision = DECISION_FAIL; 401 ret->allowNotifications = TRUE; 402 403 switch (subtype) { 404 case EAP_SAKE_SUBTYPE_IDENTITY: 405 resp = eap_sake_process_identity(sm, data, ret, reqData, 406 pos, end - pos); 407 break; 408 case EAP_SAKE_SUBTYPE_CHALLENGE: 409 resp = eap_sake_process_challenge(sm, data, ret, reqData, 410 pos, end - pos); 411 break; 412 case EAP_SAKE_SUBTYPE_CONFIRM: 413 resp = eap_sake_process_confirm(sm, data, ret, reqData, 414 pos, end - pos); 415 break; 416 default: 417 wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with " 418 "unknown subtype %d", subtype); 419 ret->ignore = TRUE; 420 return NULL; 421 } 422 423 if (ret->methodState == METHOD_DONE) 424 ret->allowNotifications = FALSE; 425 426 return resp; 427} 428 429 430static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv) 431{ 432 struct eap_sake_data *data = priv; 433 return data->state == SUCCESS; 434} 435 436 437static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) 438{ 439 struct eap_sake_data *data = priv; 440 u8 *key; 441 442 if (data->state != SUCCESS) 443 return NULL; 444 445 key = os_malloc(EAP_MSK_LEN); 446 if (key == NULL) 447 return NULL; 448 os_memcpy(key, data->msk, EAP_MSK_LEN); 449 *len = EAP_MSK_LEN; 450 451 return key; 452} 453 454 455static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 456{ 457 struct eap_sake_data *data = priv; 458 u8 *key; 459 460 if (data->state != SUCCESS) 461 return NULL; 462 463 key = os_malloc(EAP_EMSK_LEN); 464 if (key == NULL) 465 return NULL; 466 os_memcpy(key, data->emsk, EAP_EMSK_LEN); 467 *len = EAP_EMSK_LEN; 468 469 return key; 470} 471 472 473int eap_peer_sake_register(void) 474{ 475 struct eap_method *eap; 476 int ret; 477 478 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 479 EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); 480 if (eap == NULL) 481 return -1; 482 483 eap->init = eap_sake_init; 484 eap->deinit = eap_sake_deinit; 485 eap->process = eap_sake_process; 486 eap->isKeyAvailable = eap_sake_isKeyAvailable; 487 eap->getKey = eap_sake_getKey; 488 eap->get_emsk = eap_sake_get_emsk; 489 490 ret = eap_peer_method_register(eap); 491 if (ret) 492 eap_peer_method_free(eap); 493 return ret; 494} 495