1281681Srpaulo/* 2281681Srpaulo * hostapd / EAP-EKE (RFC 6124) server 3281681Srpaulo * Copyright (c) 2013, Jouni Malinen <j@w1.fi> 4281681Srpaulo * 5281681Srpaulo * This software may be distributed under the terms of the BSD license. 6281681Srpaulo * See README for more details. 7281681Srpaulo */ 8281681Srpaulo 9281681Srpaulo#include "includes.h" 10281681Srpaulo 11281681Srpaulo#include "common.h" 12281681Srpaulo#include "crypto/random.h" 13281681Srpaulo#include "eap_server/eap_i.h" 14281681Srpaulo#include "eap_common/eap_eke_common.h" 15281681Srpaulo 16281681Srpaulo 17281681Srpaulostruct eap_eke_data { 18281681Srpaulo enum { 19281681Srpaulo IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE 20281681Srpaulo } state; 21281681Srpaulo u8 msk[EAP_MSK_LEN]; 22281681Srpaulo u8 emsk[EAP_EMSK_LEN]; 23281681Srpaulo u8 *peerid; 24281681Srpaulo size_t peerid_len; 25281681Srpaulo u8 peerid_type; 26281681Srpaulo u8 serverid_type; 27281681Srpaulo u8 dh_priv[EAP_EKE_MAX_DH_LEN]; 28281681Srpaulo u8 key[EAP_EKE_MAX_KEY_LEN]; 29281681Srpaulo struct eap_eke_session sess; 30281681Srpaulo u8 nonce_p[EAP_EKE_MAX_NONCE_LEN]; 31281681Srpaulo u8 nonce_s[EAP_EKE_MAX_NONCE_LEN]; 32281681Srpaulo struct wpabuf *msgs; 33281681Srpaulo int phase2; 34281681Srpaulo u32 failure_code; 35281681Srpaulo}; 36281681Srpaulo 37281681Srpaulo 38281681Srpaulostatic const char * eap_eke_state_txt(int state) 39281681Srpaulo{ 40281681Srpaulo switch (state) { 41281681Srpaulo case IDENTITY: 42281681Srpaulo return "IDENTITY"; 43281681Srpaulo case COMMIT: 44281681Srpaulo return "COMMIT"; 45281681Srpaulo case CONFIRM: 46281681Srpaulo return "CONFIRM"; 47281681Srpaulo case FAILURE_REPORT: 48281681Srpaulo return "FAILURE_REPORT"; 49281681Srpaulo case SUCCESS: 50281681Srpaulo return "SUCCESS"; 51281681Srpaulo case FAILURE: 52281681Srpaulo return "FAILURE"; 53281681Srpaulo default: 54281681Srpaulo return "?"; 55281681Srpaulo } 56281681Srpaulo} 57281681Srpaulo 58281681Srpaulo 59281681Srpaulostatic void eap_eke_state(struct eap_eke_data *data, int state) 60281681Srpaulo{ 61281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s", 62281681Srpaulo eap_eke_state_txt(data->state), 63281681Srpaulo eap_eke_state_txt(state)); 64281681Srpaulo data->state = state; 65281681Srpaulo} 66281681Srpaulo 67281681Srpaulo 68281681Srpaulostatic void eap_eke_fail(struct eap_eke_data *data, u32 code) 69281681Srpaulo{ 70281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code); 71281681Srpaulo data->failure_code = code; 72281681Srpaulo eap_eke_state(data, FAILURE_REPORT); 73281681Srpaulo} 74281681Srpaulo 75281681Srpaulo 76281681Srpaulostatic void * eap_eke_init(struct eap_sm *sm) 77281681Srpaulo{ 78281681Srpaulo struct eap_eke_data *data; 79281681Srpaulo size_t i; 80281681Srpaulo 81281681Srpaulo data = os_zalloc(sizeof(*data)); 82281681Srpaulo if (data == NULL) 83281681Srpaulo return NULL; 84281681Srpaulo eap_eke_state(data, IDENTITY); 85281681Srpaulo 86281681Srpaulo data->serverid_type = EAP_EKE_ID_OPAQUE; 87281681Srpaulo for (i = 0; i < sm->server_id_len; i++) { 88281681Srpaulo if (sm->server_id[i] == '.' && 89281681Srpaulo data->serverid_type == EAP_EKE_ID_OPAQUE) 90281681Srpaulo data->serverid_type = EAP_EKE_ID_FQDN; 91281681Srpaulo if (sm->server_id[i] == '@') 92281681Srpaulo data->serverid_type = EAP_EKE_ID_NAI; 93281681Srpaulo } 94281681Srpaulo 95281681Srpaulo data->phase2 = sm->init_phase2; 96281681Srpaulo 97281681Srpaulo return data; 98281681Srpaulo} 99281681Srpaulo 100281681Srpaulo 101281681Srpaulostatic void eap_eke_reset(struct eap_sm *sm, void *priv) 102281681Srpaulo{ 103281681Srpaulo struct eap_eke_data *data = priv; 104281681Srpaulo eap_eke_session_clean(&data->sess); 105281681Srpaulo os_free(data->peerid); 106281681Srpaulo wpabuf_free(data->msgs); 107281681Srpaulo bin_clear_free(data, sizeof(*data)); 108281681Srpaulo} 109281681Srpaulo 110281681Srpaulo 111281681Srpaulostatic struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, 112281681Srpaulo u8 id, size_t length, u8 eke_exch) 113281681Srpaulo{ 114281681Srpaulo struct wpabuf *msg; 115281681Srpaulo size_t plen; 116281681Srpaulo 117281681Srpaulo plen = 1 + length; 118281681Srpaulo 119281681Srpaulo msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen, 120281681Srpaulo EAP_CODE_REQUEST, id); 121281681Srpaulo if (msg == NULL) { 122281681Srpaulo wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory"); 123281681Srpaulo return NULL; 124281681Srpaulo } 125281681Srpaulo 126281681Srpaulo wpabuf_put_u8(msg, eke_exch); 127281681Srpaulo 128281681Srpaulo return msg; 129281681Srpaulo} 130281681Srpaulo 131281681Srpaulo 132281681Srpaulostatic int supported_proposal(const u8 *pos) 133281681Srpaulo{ 134281681Srpaulo if (pos[0] == EAP_EKE_DHGROUP_EKE_16 && 135281681Srpaulo pos[1] == EAP_EKE_ENCR_AES128_CBC && 136281681Srpaulo pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && 137281681Srpaulo pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) 138281681Srpaulo return 1; 139281681Srpaulo 140281681Srpaulo if (pos[0] == EAP_EKE_DHGROUP_EKE_15 && 141281681Srpaulo pos[1] == EAP_EKE_ENCR_AES128_CBC && 142281681Srpaulo pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && 143281681Srpaulo pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) 144281681Srpaulo return 1; 145281681Srpaulo 146281681Srpaulo if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && 147281681Srpaulo pos[1] == EAP_EKE_ENCR_AES128_CBC && 148281681Srpaulo pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 && 149281681Srpaulo pos[3] == EAP_EKE_MAC_HMAC_SHA2_256) 150281681Srpaulo return 1; 151281681Srpaulo 152281681Srpaulo if (pos[0] == EAP_EKE_DHGROUP_EKE_14 && 153281681Srpaulo pos[1] == EAP_EKE_ENCR_AES128_CBC && 154281681Srpaulo pos[2] == EAP_EKE_PRF_HMAC_SHA1 && 155281681Srpaulo pos[3] == EAP_EKE_MAC_HMAC_SHA1) 156281681Srpaulo return 1; 157281681Srpaulo 158281681Srpaulo return 0; 159281681Srpaulo} 160281681Srpaulo 161281681Srpaulo 162281681Srpaulostatic struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id) 163281681Srpaulo{ 164281681Srpaulo struct wpabuf *msg; 165281681Srpaulo 166281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x", 167281681Srpaulo data->failure_code); 168281681Srpaulo 169281681Srpaulo msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE); 170281681Srpaulo if (msg == NULL) { 171281681Srpaulo eap_eke_state(data, FAILURE); 172281681Srpaulo return NULL; 173281681Srpaulo } 174281681Srpaulo wpabuf_put_be32(msg, data->failure_code); 175281681Srpaulo 176281681Srpaulo return msg; 177281681Srpaulo} 178281681Srpaulo 179281681Srpaulo 180281681Srpaulostatic struct wpabuf * eap_eke_build_identity(struct eap_sm *sm, 181281681Srpaulo struct eap_eke_data *data, 182281681Srpaulo u8 id) 183281681Srpaulo{ 184281681Srpaulo struct wpabuf *msg; 185281681Srpaulo size_t plen; 186281681Srpaulo 187281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity"); 188281681Srpaulo 189281681Srpaulo plen = 2 + 4 * 4 + 1 + sm->server_id_len; 190281681Srpaulo msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID); 191281681Srpaulo if (msg == NULL) 192281681Srpaulo return NULL; 193281681Srpaulo 194281681Srpaulo wpabuf_put_u8(msg, 4); /* NumProposals */ 195281681Srpaulo wpabuf_put_u8(msg, 0); /* Reserved */ 196281681Srpaulo 197281681Srpaulo /* Proposal - DH Group 16 with AES128-CBC and SHA256 */ 198281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */ 199281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ 200281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ 201281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ 202281681Srpaulo 203281681Srpaulo /* Proposal - DH Group 15 with AES128-CBC and SHA256 */ 204281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */ 205281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ 206281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ 207281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ 208281681Srpaulo 209281681Srpaulo /* Proposal - DH Group 14 with AES128-CBC and SHA256 */ 210281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ 211281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ 212281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */ 213281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */ 214281681Srpaulo 215281681Srpaulo /* 216281681Srpaulo * Proposal - DH Group 14 with AES128-CBC and SHA1 217281681Srpaulo * (mandatory to implement algorithms) 218281681Srpaulo */ 219281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */ 220281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */ 221281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */ 222281681Srpaulo wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */ 223281681Srpaulo 224281681Srpaulo /* Server IDType + Identity */ 225281681Srpaulo wpabuf_put_u8(msg, data->serverid_type); 226281681Srpaulo wpabuf_put_data(msg, sm->server_id, sm->server_id_len); 227281681Srpaulo 228281681Srpaulo wpabuf_free(data->msgs); 229281681Srpaulo data->msgs = wpabuf_dup(msg); 230281681Srpaulo if (data->msgs == NULL) { 231281681Srpaulo wpabuf_free(msg); 232281681Srpaulo return NULL; 233281681Srpaulo } 234281681Srpaulo 235281681Srpaulo return msg; 236281681Srpaulo} 237281681Srpaulo 238281681Srpaulo 239281681Srpaulostatic struct wpabuf * eap_eke_build_commit(struct eap_sm *sm, 240281681Srpaulo struct eap_eke_data *data, u8 id) 241281681Srpaulo{ 242281681Srpaulo struct wpabuf *msg; 243281681Srpaulo u8 pub[EAP_EKE_MAX_DH_LEN]; 244281681Srpaulo 245281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit"); 246281681Srpaulo 247281681Srpaulo if (sm->user == NULL || sm->user->password == NULL) { 248281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured"); 249281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); 250281681Srpaulo return eap_eke_build_failure(data, id); 251281681Srpaulo } 252281681Srpaulo 253281681Srpaulo if (eap_eke_derive_key(&data->sess, sm->user->password, 254281681Srpaulo sm->user->password_len, 255281681Srpaulo sm->server_id, sm->server_id_len, 256281681Srpaulo data->peerid, data->peerid_len, data->key) < 0) { 257281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key"); 258281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 259281681Srpaulo return eap_eke_build_failure(data, id); 260281681Srpaulo } 261281681Srpaulo 262281681Srpaulo msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len, 263281681Srpaulo EAP_EKE_COMMIT); 264281681Srpaulo if (msg == NULL) { 265281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 266281681Srpaulo return eap_eke_build_failure(data, id); 267281681Srpaulo } 268281681Srpaulo 269281681Srpaulo /* 270281681Srpaulo * y_s = g ^ x_s (mod p) 271281681Srpaulo * x_s = random number 2 .. p-1 272281681Srpaulo * temp = prf(0+, password) 273281681Srpaulo * key = prf+(temp, ID_S | ID_P) 274281681Srpaulo * DHComponent_S = Encr(key, y_s) 275281681Srpaulo */ 276281681Srpaulo 277281681Srpaulo if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) { 278281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH"); 279281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 280281681Srpaulo return eap_eke_build_failure(data, id); 281281681Srpaulo } 282281681Srpaulo 283281681Srpaulo if (eap_eke_dhcomp(&data->sess, data->key, pub, 284281681Srpaulo wpabuf_put(msg, data->sess.dhcomp_len)) 285281681Srpaulo < 0) { 286281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S"); 287281681Srpaulo wpabuf_free(msg); 288281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 289281681Srpaulo return eap_eke_build_failure(data, id); 290281681Srpaulo } 291281681Srpaulo 292281681Srpaulo if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) { 293281681Srpaulo wpabuf_free(msg); 294281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 295281681Srpaulo return eap_eke_build_failure(data, id); 296281681Srpaulo } 297281681Srpaulo wpabuf_put_buf(data->msgs, msg); 298281681Srpaulo 299281681Srpaulo return msg; 300281681Srpaulo} 301281681Srpaulo 302281681Srpaulo 303281681Srpaulostatic struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm, 304281681Srpaulo struct eap_eke_data *data, u8 id) 305281681Srpaulo{ 306281681Srpaulo struct wpabuf *msg; 307281681Srpaulo size_t plen, prot_len; 308281681Srpaulo u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN]; 309281681Srpaulo u8 *auth; 310281681Srpaulo 311281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm"); 312281681Srpaulo 313281681Srpaulo plen = data->sess.pnonce_ps_len + data->sess.prf_len; 314281681Srpaulo msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM); 315281681Srpaulo if (msg == NULL) { 316281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 317281681Srpaulo return eap_eke_build_failure(data, id); 318281681Srpaulo } 319281681Srpaulo 320281681Srpaulo if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) { 321281681Srpaulo wpabuf_free(msg); 322281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 323281681Srpaulo return eap_eke_build_failure(data, id); 324281681Srpaulo } 325281681Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S", 326281681Srpaulo data->nonce_s, data->sess.nonce_len); 327281681Srpaulo 328281681Srpaulo os_memcpy(nonces, data->nonce_p, data->sess.nonce_len); 329281681Srpaulo os_memcpy(nonces + data->sess.nonce_len, data->nonce_s, 330281681Srpaulo data->sess.nonce_len); 331281681Srpaulo prot_len = wpabuf_tailroom(msg); 332281681Srpaulo if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len, 333281681Srpaulo wpabuf_put(msg, 0), &prot_len) < 0) { 334281681Srpaulo wpabuf_free(msg); 335281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 336281681Srpaulo return eap_eke_build_failure(data, id); 337281681Srpaulo } 338281681Srpaulo wpabuf_put(msg, prot_len); 339281681Srpaulo 340281681Srpaulo if (eap_eke_derive_ka(&data->sess, 341281681Srpaulo sm->server_id, sm->server_id_len, 342281681Srpaulo data->peerid, data->peerid_len, 343281681Srpaulo data->nonce_p, data->nonce_s) < 0) { 344281681Srpaulo wpabuf_free(msg); 345281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 346281681Srpaulo return eap_eke_build_failure(data, id); 347281681Srpaulo } 348281681Srpaulo 349281681Srpaulo auth = wpabuf_put(msg, data->sess.prf_len); 350281681Srpaulo if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) { 351281681Srpaulo wpabuf_free(msg); 352281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 353281681Srpaulo return eap_eke_build_failure(data, id); 354281681Srpaulo } 355281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len); 356281681Srpaulo 357281681Srpaulo return msg; 358281681Srpaulo} 359281681Srpaulo 360281681Srpaulo 361281681Srpaulostatic struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id) 362281681Srpaulo{ 363281681Srpaulo struct eap_eke_data *data = priv; 364281681Srpaulo 365281681Srpaulo switch (data->state) { 366281681Srpaulo case IDENTITY: 367281681Srpaulo return eap_eke_build_identity(sm, data, id); 368281681Srpaulo case COMMIT: 369281681Srpaulo return eap_eke_build_commit(sm, data, id); 370281681Srpaulo case CONFIRM: 371281681Srpaulo return eap_eke_build_confirm(sm, data, id); 372281681Srpaulo case FAILURE_REPORT: 373281681Srpaulo return eap_eke_build_failure(data, id); 374281681Srpaulo default: 375281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq", 376281681Srpaulo data->state); 377281681Srpaulo break; 378281681Srpaulo } 379281681Srpaulo return NULL; 380281681Srpaulo} 381281681Srpaulo 382281681Srpaulo 383281681Srpaulostatic Boolean eap_eke_check(struct eap_sm *sm, void *priv, 384281681Srpaulo struct wpabuf *respData) 385281681Srpaulo{ 386281681Srpaulo struct eap_eke_data *data = priv; 387281681Srpaulo size_t len; 388281681Srpaulo const u8 *pos; 389281681Srpaulo u8 eke_exch; 390281681Srpaulo 391281681Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); 392281681Srpaulo if (pos == NULL || len < 1) { 393281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame"); 394281681Srpaulo return TRUE; 395281681Srpaulo } 396281681Srpaulo 397281681Srpaulo eke_exch = *pos; 398281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch); 399281681Srpaulo 400281681Srpaulo if (data->state == IDENTITY && eke_exch == EAP_EKE_ID) 401281681Srpaulo return FALSE; 402281681Srpaulo 403281681Srpaulo if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT) 404281681Srpaulo return FALSE; 405281681Srpaulo 406281681Srpaulo if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM) 407281681Srpaulo return FALSE; 408281681Srpaulo 409281681Srpaulo if (eke_exch == EAP_EKE_FAILURE) 410281681Srpaulo return FALSE; 411281681Srpaulo 412281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d", 413281681Srpaulo eke_exch, data->state); 414281681Srpaulo 415281681Srpaulo return TRUE; 416281681Srpaulo} 417281681Srpaulo 418281681Srpaulo 419281681Srpaulostatic void eap_eke_process_identity(struct eap_sm *sm, 420281681Srpaulo struct eap_eke_data *data, 421281681Srpaulo const struct wpabuf *respData, 422281681Srpaulo const u8 *payload, size_t payloadlen) 423281681Srpaulo{ 424281681Srpaulo const u8 *pos, *end; 425281681Srpaulo int i; 426281681Srpaulo 427281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity"); 428281681Srpaulo 429281681Srpaulo if (data->state != IDENTITY) { 430281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 431281681Srpaulo return; 432281681Srpaulo } 433281681Srpaulo 434281681Srpaulo pos = payload; 435281681Srpaulo end = payload + payloadlen; 436281681Srpaulo 437281681Srpaulo if (pos + 2 + 4 + 1 > end) { 438281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload"); 439281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 440281681Srpaulo return; 441281681Srpaulo } 442281681Srpaulo 443281681Srpaulo if (*pos != 1) { 444281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)", 445281681Srpaulo *pos); 446281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 447281681Srpaulo return; 448281681Srpaulo } 449281681Srpaulo 450281681Srpaulo pos += 2; 451281681Srpaulo 452281681Srpaulo if (!supported_proposal(pos)) { 453281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)", 454281681Srpaulo pos[0], pos[1], pos[2], pos[3]); 455281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 456281681Srpaulo return; 457281681Srpaulo } 458281681Srpaulo 459281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)", 460281681Srpaulo pos[0], pos[1], pos[2], pos[3]); 461281681Srpaulo if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) < 462281681Srpaulo 0) { 463281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 464281681Srpaulo return; 465281681Srpaulo } 466281681Srpaulo pos += 4; 467281681Srpaulo 468281681Srpaulo data->peerid_type = *pos++; 469281681Srpaulo os_free(data->peerid); 470346981Scy data->peerid = os_memdup(pos, end - pos); 471281681Srpaulo if (data->peerid == NULL) { 472281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid"); 473281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 474281681Srpaulo return; 475281681Srpaulo } 476281681Srpaulo data->peerid_len = end - pos; 477281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type); 478281681Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity", 479281681Srpaulo data->peerid, data->peerid_len); 480281681Srpaulo 481281681Srpaulo if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) { 482281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database"); 483281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); 484281681Srpaulo return; 485281681Srpaulo } 486281681Srpaulo 487281681Srpaulo for (i = 0; i < EAP_MAX_METHODS; i++) { 488281681Srpaulo if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && 489281681Srpaulo sm->user->methods[i].method == EAP_TYPE_EKE) 490281681Srpaulo break; 491281681Srpaulo } 492281681Srpaulo if (i == EAP_MAX_METHODS) { 493281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE"); 494281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); 495281681Srpaulo return; 496281681Srpaulo } 497281681Srpaulo 498281681Srpaulo if (sm->user->password == NULL || sm->user->password_len == 0) { 499281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer"); 500281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); 501281681Srpaulo return; 502281681Srpaulo } 503281681Srpaulo 504281681Srpaulo if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { 505281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 506281681Srpaulo return; 507281681Srpaulo } 508281681Srpaulo wpabuf_put_buf(data->msgs, respData); 509281681Srpaulo 510281681Srpaulo eap_eke_state(data, COMMIT); 511281681Srpaulo} 512281681Srpaulo 513281681Srpaulo 514281681Srpaulostatic void eap_eke_process_commit(struct eap_sm *sm, 515281681Srpaulo struct eap_eke_data *data, 516281681Srpaulo const struct wpabuf *respData, 517281681Srpaulo const u8 *payload, size_t payloadlen) 518281681Srpaulo{ 519281681Srpaulo const u8 *pos, *end, *dhcomp, *pnonce; 520281681Srpaulo size_t decrypt_len; 521281681Srpaulo 522281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit"); 523281681Srpaulo 524281681Srpaulo if (data->state != COMMIT) { 525281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 526281681Srpaulo return; 527281681Srpaulo } 528281681Srpaulo 529281681Srpaulo pos = payload; 530281681Srpaulo end = payload + payloadlen; 531281681Srpaulo 532281681Srpaulo if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) { 533281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); 534281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 535281681Srpaulo return; 536281681Srpaulo } 537281681Srpaulo 538281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", 539281681Srpaulo pos, data->sess.dhcomp_len); 540281681Srpaulo dhcomp = pos; 541281681Srpaulo pos += data->sess.dhcomp_len; 542281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len); 543281681Srpaulo pnonce = pos; 544281681Srpaulo pos += data->sess.pnonce_len; 545281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); 546281681Srpaulo 547281681Srpaulo if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp) 548281681Srpaulo < 0) { 549281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); 550281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 551281681Srpaulo return; 552281681Srpaulo } 553281681Srpaulo 554281681Srpaulo if (eap_eke_derive_ke_ki(&data->sess, 555281681Srpaulo sm->server_id, sm->server_id_len, 556281681Srpaulo data->peerid, data->peerid_len) < 0) { 557281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); 558281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 559281681Srpaulo return; 560281681Srpaulo } 561281681Srpaulo 562281681Srpaulo decrypt_len = sizeof(data->nonce_p); 563281681Srpaulo if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len, 564281681Srpaulo data->nonce_p, &decrypt_len) < 0) { 565281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P"); 566281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 567281681Srpaulo return; 568281681Srpaulo } 569281681Srpaulo if (decrypt_len < (size_t) data->sess.nonce_len) { 570281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P"); 571281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 572281681Srpaulo return; 573281681Srpaulo } 574281681Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", 575281681Srpaulo data->nonce_p, data->sess.nonce_len); 576281681Srpaulo 577281681Srpaulo if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { 578281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 579281681Srpaulo return; 580281681Srpaulo } 581281681Srpaulo wpabuf_put_buf(data->msgs, respData); 582281681Srpaulo 583281681Srpaulo eap_eke_state(data, CONFIRM); 584281681Srpaulo} 585281681Srpaulo 586281681Srpaulo 587281681Srpaulostatic void eap_eke_process_confirm(struct eap_sm *sm, 588281681Srpaulo struct eap_eke_data *data, 589281681Srpaulo const struct wpabuf *respData, 590281681Srpaulo const u8 *payload, size_t payloadlen) 591281681Srpaulo{ 592281681Srpaulo size_t decrypt_len; 593281681Srpaulo u8 nonce[EAP_EKE_MAX_NONCE_LEN]; 594281681Srpaulo u8 auth_p[EAP_EKE_MAX_HASH_LEN]; 595281681Srpaulo 596281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); 597281681Srpaulo 598281681Srpaulo if (data->state != CONFIRM) { 599281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 600281681Srpaulo return; 601281681Srpaulo } 602281681Srpaulo 603281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); 604281681Srpaulo 605281681Srpaulo if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) { 606281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); 607281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 608281681Srpaulo return; 609281681Srpaulo } 610281681Srpaulo 611281681Srpaulo decrypt_len = sizeof(nonce); 612281681Srpaulo if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len, 613281681Srpaulo nonce, &decrypt_len) < 0) { 614281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S"); 615281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 616281681Srpaulo return; 617281681Srpaulo } 618281681Srpaulo if (decrypt_len < (size_t) data->sess.nonce_len) { 619281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S"); 620281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 621281681Srpaulo return; 622281681Srpaulo } 623281681Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S", 624281681Srpaulo nonce, data->sess.nonce_len); 625281681Srpaulo if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) { 626281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S"); 627281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 628281681Srpaulo return; 629281681Srpaulo } 630281681Srpaulo 631281681Srpaulo if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) { 632281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P"); 633281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 634281681Srpaulo return; 635281681Srpaulo } 636281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len); 637281681Srpaulo if (os_memcmp_const(auth_p, payload + data->sess.pnonce_len, 638281681Srpaulo data->sess.prf_len) != 0) { 639281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match"); 640281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 641281681Srpaulo return; 642281681Srpaulo } 643281681Srpaulo 644281681Srpaulo if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len, 645281681Srpaulo data->peerid, data->peerid_len, 646281681Srpaulo data->nonce_s, data->nonce_p, 647281681Srpaulo data->msk, data->emsk) < 0) { 648281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); 649281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 650281681Srpaulo return; 651281681Srpaulo } 652281681Srpaulo 653281681Srpaulo os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); 654281681Srpaulo os_memset(data->key, 0, sizeof(data->key)); 655281681Srpaulo eap_eke_session_clean(&data->sess); 656281681Srpaulo 657281681Srpaulo eap_eke_state(data, SUCCESS); 658281681Srpaulo} 659281681Srpaulo 660281681Srpaulo 661281681Srpaulostatic void eap_eke_process_failure(struct eap_sm *sm, 662281681Srpaulo struct eap_eke_data *data, 663281681Srpaulo const struct wpabuf *respData, 664281681Srpaulo const u8 *payload, size_t payloadlen) 665281681Srpaulo{ 666281681Srpaulo u32 code; 667281681Srpaulo 668281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure"); 669281681Srpaulo 670281681Srpaulo if (payloadlen < 4) { 671281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); 672281681Srpaulo eap_eke_state(data, FAILURE); 673281681Srpaulo return; 674281681Srpaulo } 675281681Srpaulo 676281681Srpaulo code = WPA_GET_BE32(payload); 677281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code); 678281681Srpaulo 679281681Srpaulo eap_eke_state(data, FAILURE); 680281681Srpaulo} 681281681Srpaulo 682281681Srpaulo 683281681Srpaulostatic void eap_eke_process(struct eap_sm *sm, void *priv, 684281681Srpaulo struct wpabuf *respData) 685281681Srpaulo{ 686281681Srpaulo struct eap_eke_data *data = priv; 687281681Srpaulo u8 eke_exch; 688281681Srpaulo size_t len; 689281681Srpaulo const u8 *pos, *end; 690281681Srpaulo 691281681Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); 692281681Srpaulo if (pos == NULL || len < 1) 693281681Srpaulo return; 694281681Srpaulo 695281681Srpaulo eke_exch = *pos; 696281681Srpaulo end = pos + len; 697281681Srpaulo pos++; 698281681Srpaulo 699281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos); 700281681Srpaulo 701281681Srpaulo switch (eke_exch) { 702281681Srpaulo case EAP_EKE_ID: 703281681Srpaulo eap_eke_process_identity(sm, data, respData, pos, end - pos); 704281681Srpaulo break; 705281681Srpaulo case EAP_EKE_COMMIT: 706281681Srpaulo eap_eke_process_commit(sm, data, respData, pos, end - pos); 707281681Srpaulo break; 708281681Srpaulo case EAP_EKE_CONFIRM: 709281681Srpaulo eap_eke_process_confirm(sm, data, respData, pos, end - pos); 710281681Srpaulo break; 711281681Srpaulo case EAP_EKE_FAILURE: 712281681Srpaulo eap_eke_process_failure(sm, data, respData, pos, end - pos); 713281681Srpaulo break; 714281681Srpaulo } 715281681Srpaulo} 716281681Srpaulo 717281681Srpaulo 718281681Srpaulostatic Boolean eap_eke_isDone(struct eap_sm *sm, void *priv) 719281681Srpaulo{ 720281681Srpaulo struct eap_eke_data *data = priv; 721281681Srpaulo return data->state == SUCCESS || data->state == FAILURE; 722281681Srpaulo} 723281681Srpaulo 724281681Srpaulo 725281681Srpaulostatic u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) 726281681Srpaulo{ 727281681Srpaulo struct eap_eke_data *data = priv; 728281681Srpaulo u8 *key; 729281681Srpaulo 730281681Srpaulo if (data->state != SUCCESS) 731281681Srpaulo return NULL; 732281681Srpaulo 733346981Scy key = os_memdup(data->msk, EAP_MSK_LEN); 734281681Srpaulo if (key == NULL) 735281681Srpaulo return NULL; 736281681Srpaulo *len = EAP_MSK_LEN; 737281681Srpaulo 738281681Srpaulo return key; 739281681Srpaulo} 740281681Srpaulo 741281681Srpaulo 742281681Srpaulostatic u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 743281681Srpaulo{ 744281681Srpaulo struct eap_eke_data *data = priv; 745281681Srpaulo u8 *key; 746281681Srpaulo 747281681Srpaulo if (data->state != SUCCESS) 748281681Srpaulo return NULL; 749281681Srpaulo 750346981Scy key = os_memdup(data->emsk, EAP_EMSK_LEN); 751281681Srpaulo if (key == NULL) 752281681Srpaulo return NULL; 753281681Srpaulo *len = EAP_EMSK_LEN; 754281681Srpaulo 755281681Srpaulo return key; 756281681Srpaulo} 757281681Srpaulo 758281681Srpaulo 759281681Srpaulostatic Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv) 760281681Srpaulo{ 761281681Srpaulo struct eap_eke_data *data = priv; 762281681Srpaulo return data->state == SUCCESS; 763281681Srpaulo} 764281681Srpaulo 765281681Srpaulo 766289549Srpaulostatic u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 767289549Srpaulo{ 768289549Srpaulo struct eap_eke_data *data = priv; 769289549Srpaulo u8 *sid; 770289549Srpaulo size_t sid_len; 771289549Srpaulo 772289549Srpaulo if (data->state != SUCCESS) 773289549Srpaulo return NULL; 774289549Srpaulo 775289549Srpaulo sid_len = 1 + 2 * data->sess.nonce_len; 776289549Srpaulo sid = os_malloc(sid_len); 777289549Srpaulo if (sid == NULL) 778289549Srpaulo return NULL; 779289549Srpaulo sid[0] = EAP_TYPE_EKE; 780289549Srpaulo os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len); 781289549Srpaulo os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s, 782289549Srpaulo data->sess.nonce_len); 783289549Srpaulo *len = sid_len; 784289549Srpaulo 785289549Srpaulo return sid; 786289549Srpaulo} 787289549Srpaulo 788289549Srpaulo 789281681Srpauloint eap_server_eke_register(void) 790281681Srpaulo{ 791281681Srpaulo struct eap_method *eap; 792281681Srpaulo 793281681Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 794281681Srpaulo EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); 795281681Srpaulo if (eap == NULL) 796281681Srpaulo return -1; 797281681Srpaulo 798281681Srpaulo eap->init = eap_eke_init; 799281681Srpaulo eap->reset = eap_eke_reset; 800281681Srpaulo eap->buildReq = eap_eke_buildReq; 801281681Srpaulo eap->check = eap_eke_check; 802281681Srpaulo eap->process = eap_eke_process; 803281681Srpaulo eap->isDone = eap_eke_isDone; 804281681Srpaulo eap->getKey = eap_eke_getKey; 805281681Srpaulo eap->isSuccess = eap_eke_isSuccess; 806281681Srpaulo eap->get_emsk = eap_eke_get_emsk; 807289549Srpaulo eap->getSessionId = eap_eke_get_session_id; 808281681Srpaulo 809337817Scy return eap_server_method_register(eap); 810281681Srpaulo} 811