eap_server_eke.c revision 281681
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); 470281681Srpaulo data->peerid = os_malloc(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 os_memcpy(data->peerid, pos, end - pos); 477281681Srpaulo data->peerid_len = end - pos; 478281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type); 479281681Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity", 480281681Srpaulo data->peerid, data->peerid_len); 481281681Srpaulo 482281681Srpaulo if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) { 483281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database"); 484281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); 485281681Srpaulo return; 486281681Srpaulo } 487281681Srpaulo 488281681Srpaulo for (i = 0; i < EAP_MAX_METHODS; i++) { 489281681Srpaulo if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && 490281681Srpaulo sm->user->methods[i].method == EAP_TYPE_EKE) 491281681Srpaulo break; 492281681Srpaulo } 493281681Srpaulo if (i == EAP_MAX_METHODS) { 494281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE"); 495281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); 496281681Srpaulo return; 497281681Srpaulo } 498281681Srpaulo 499281681Srpaulo if (sm->user->password == NULL || sm->user->password_len == 0) { 500281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer"); 501281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND); 502281681Srpaulo return; 503281681Srpaulo } 504281681Srpaulo 505281681Srpaulo if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { 506281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 507281681Srpaulo return; 508281681Srpaulo } 509281681Srpaulo wpabuf_put_buf(data->msgs, respData); 510281681Srpaulo 511281681Srpaulo eap_eke_state(data, COMMIT); 512281681Srpaulo} 513281681Srpaulo 514281681Srpaulo 515281681Srpaulostatic void eap_eke_process_commit(struct eap_sm *sm, 516281681Srpaulo struct eap_eke_data *data, 517281681Srpaulo const struct wpabuf *respData, 518281681Srpaulo const u8 *payload, size_t payloadlen) 519281681Srpaulo{ 520281681Srpaulo const u8 *pos, *end, *dhcomp, *pnonce; 521281681Srpaulo size_t decrypt_len; 522281681Srpaulo 523281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit"); 524281681Srpaulo 525281681Srpaulo if (data->state != COMMIT) { 526281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 527281681Srpaulo return; 528281681Srpaulo } 529281681Srpaulo 530281681Srpaulo pos = payload; 531281681Srpaulo end = payload + payloadlen; 532281681Srpaulo 533281681Srpaulo if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) { 534281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit"); 535281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 536281681Srpaulo return; 537281681Srpaulo } 538281681Srpaulo 539281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P", 540281681Srpaulo pos, data->sess.dhcomp_len); 541281681Srpaulo dhcomp = pos; 542281681Srpaulo pos += data->sess.dhcomp_len; 543281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len); 544281681Srpaulo pnonce = pos; 545281681Srpaulo pos += data->sess.pnonce_len; 546281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos); 547281681Srpaulo 548281681Srpaulo if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp) 549281681Srpaulo < 0) { 550281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret"); 551281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 552281681Srpaulo return; 553281681Srpaulo } 554281681Srpaulo 555281681Srpaulo if (eap_eke_derive_ke_ki(&data->sess, 556281681Srpaulo sm->server_id, sm->server_id_len, 557281681Srpaulo data->peerid, data->peerid_len) < 0) { 558281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki"); 559281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 560281681Srpaulo return; 561281681Srpaulo } 562281681Srpaulo 563281681Srpaulo decrypt_len = sizeof(data->nonce_p); 564281681Srpaulo if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len, 565281681Srpaulo data->nonce_p, &decrypt_len) < 0) { 566281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P"); 567281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 568281681Srpaulo return; 569281681Srpaulo } 570281681Srpaulo if (decrypt_len < (size_t) data->sess.nonce_len) { 571281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P"); 572281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 573281681Srpaulo return; 574281681Srpaulo } 575281681Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P", 576281681Srpaulo data->nonce_p, data->sess.nonce_len); 577281681Srpaulo 578281681Srpaulo if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) { 579281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 580281681Srpaulo return; 581281681Srpaulo } 582281681Srpaulo wpabuf_put_buf(data->msgs, respData); 583281681Srpaulo 584281681Srpaulo eap_eke_state(data, CONFIRM); 585281681Srpaulo} 586281681Srpaulo 587281681Srpaulo 588281681Srpaulostatic void eap_eke_process_confirm(struct eap_sm *sm, 589281681Srpaulo struct eap_eke_data *data, 590281681Srpaulo const struct wpabuf *respData, 591281681Srpaulo const u8 *payload, size_t payloadlen) 592281681Srpaulo{ 593281681Srpaulo size_t decrypt_len; 594281681Srpaulo u8 nonce[EAP_EKE_MAX_NONCE_LEN]; 595281681Srpaulo u8 auth_p[EAP_EKE_MAX_HASH_LEN]; 596281681Srpaulo 597281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); 598281681Srpaulo 599281681Srpaulo if (data->state != CONFIRM) { 600281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 601281681Srpaulo return; 602281681Srpaulo } 603281681Srpaulo 604281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm"); 605281681Srpaulo 606281681Srpaulo if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) { 607281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm"); 608281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR); 609281681Srpaulo return; 610281681Srpaulo } 611281681Srpaulo 612281681Srpaulo decrypt_len = sizeof(nonce); 613281681Srpaulo if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len, 614281681Srpaulo nonce, &decrypt_len) < 0) { 615281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S"); 616281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 617281681Srpaulo return; 618281681Srpaulo } 619281681Srpaulo if (decrypt_len < (size_t) data->sess.nonce_len) { 620281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S"); 621281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 622281681Srpaulo return; 623281681Srpaulo } 624281681Srpaulo wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S", 625281681Srpaulo nonce, data->sess.nonce_len); 626281681Srpaulo if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) { 627281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S"); 628281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 629281681Srpaulo return; 630281681Srpaulo } 631281681Srpaulo 632281681Srpaulo if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) { 633281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P"); 634281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 635281681Srpaulo return; 636281681Srpaulo } 637281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len); 638281681Srpaulo if (os_memcmp_const(auth_p, payload + data->sess.pnonce_len, 639281681Srpaulo data->sess.prf_len) != 0) { 640281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match"); 641281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL); 642281681Srpaulo return; 643281681Srpaulo } 644281681Srpaulo 645281681Srpaulo if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len, 646281681Srpaulo data->peerid, data->peerid_len, 647281681Srpaulo data->nonce_s, data->nonce_p, 648281681Srpaulo data->msk, data->emsk) < 0) { 649281681Srpaulo wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK"); 650281681Srpaulo eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); 651281681Srpaulo return; 652281681Srpaulo } 653281681Srpaulo 654281681Srpaulo os_memset(data->dh_priv, 0, sizeof(data->dh_priv)); 655281681Srpaulo os_memset(data->key, 0, sizeof(data->key)); 656281681Srpaulo eap_eke_session_clean(&data->sess); 657281681Srpaulo 658281681Srpaulo eap_eke_state(data, SUCCESS); 659281681Srpaulo} 660281681Srpaulo 661281681Srpaulo 662281681Srpaulostatic void eap_eke_process_failure(struct eap_sm *sm, 663281681Srpaulo struct eap_eke_data *data, 664281681Srpaulo const struct wpabuf *respData, 665281681Srpaulo const u8 *payload, size_t payloadlen) 666281681Srpaulo{ 667281681Srpaulo u32 code; 668281681Srpaulo 669281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure"); 670281681Srpaulo 671281681Srpaulo if (payloadlen < 4) { 672281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure"); 673281681Srpaulo eap_eke_state(data, FAILURE); 674281681Srpaulo return; 675281681Srpaulo } 676281681Srpaulo 677281681Srpaulo code = WPA_GET_BE32(payload); 678281681Srpaulo wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code); 679281681Srpaulo 680281681Srpaulo eap_eke_state(data, FAILURE); 681281681Srpaulo} 682281681Srpaulo 683281681Srpaulo 684281681Srpaulostatic void eap_eke_process(struct eap_sm *sm, void *priv, 685281681Srpaulo struct wpabuf *respData) 686281681Srpaulo{ 687281681Srpaulo struct eap_eke_data *data = priv; 688281681Srpaulo u8 eke_exch; 689281681Srpaulo size_t len; 690281681Srpaulo const u8 *pos, *end; 691281681Srpaulo 692281681Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len); 693281681Srpaulo if (pos == NULL || len < 1) 694281681Srpaulo return; 695281681Srpaulo 696281681Srpaulo eke_exch = *pos; 697281681Srpaulo end = pos + len; 698281681Srpaulo pos++; 699281681Srpaulo 700281681Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos); 701281681Srpaulo 702281681Srpaulo switch (eke_exch) { 703281681Srpaulo case EAP_EKE_ID: 704281681Srpaulo eap_eke_process_identity(sm, data, respData, pos, end - pos); 705281681Srpaulo break; 706281681Srpaulo case EAP_EKE_COMMIT: 707281681Srpaulo eap_eke_process_commit(sm, data, respData, pos, end - pos); 708281681Srpaulo break; 709281681Srpaulo case EAP_EKE_CONFIRM: 710281681Srpaulo eap_eke_process_confirm(sm, data, respData, pos, end - pos); 711281681Srpaulo break; 712281681Srpaulo case EAP_EKE_FAILURE: 713281681Srpaulo eap_eke_process_failure(sm, data, respData, pos, end - pos); 714281681Srpaulo break; 715281681Srpaulo } 716281681Srpaulo} 717281681Srpaulo 718281681Srpaulo 719281681Srpaulostatic Boolean eap_eke_isDone(struct eap_sm *sm, void *priv) 720281681Srpaulo{ 721281681Srpaulo struct eap_eke_data *data = priv; 722281681Srpaulo return data->state == SUCCESS || data->state == FAILURE; 723281681Srpaulo} 724281681Srpaulo 725281681Srpaulo 726281681Srpaulostatic u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) 727281681Srpaulo{ 728281681Srpaulo struct eap_eke_data *data = priv; 729281681Srpaulo u8 *key; 730281681Srpaulo 731281681Srpaulo if (data->state != SUCCESS) 732281681Srpaulo return NULL; 733281681Srpaulo 734281681Srpaulo key = os_malloc(EAP_MSK_LEN); 735281681Srpaulo if (key == NULL) 736281681Srpaulo return NULL; 737281681Srpaulo os_memcpy(key, data->msk, EAP_MSK_LEN); 738281681Srpaulo *len = EAP_MSK_LEN; 739281681Srpaulo 740281681Srpaulo return key; 741281681Srpaulo} 742281681Srpaulo 743281681Srpaulo 744281681Srpaulostatic u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 745281681Srpaulo{ 746281681Srpaulo struct eap_eke_data *data = priv; 747281681Srpaulo u8 *key; 748281681Srpaulo 749281681Srpaulo if (data->state != SUCCESS) 750281681Srpaulo return NULL; 751281681Srpaulo 752281681Srpaulo key = os_malloc(EAP_EMSK_LEN); 753281681Srpaulo if (key == NULL) 754281681Srpaulo return NULL; 755281681Srpaulo os_memcpy(key, data->emsk, EAP_EMSK_LEN); 756281681Srpaulo *len = EAP_EMSK_LEN; 757281681Srpaulo 758281681Srpaulo return key; 759281681Srpaulo} 760281681Srpaulo 761281681Srpaulo 762281681Srpaulostatic Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv) 763281681Srpaulo{ 764281681Srpaulo struct eap_eke_data *data = priv; 765281681Srpaulo return data->state == SUCCESS; 766281681Srpaulo} 767281681Srpaulo 768281681Srpaulo 769281681Srpauloint eap_server_eke_register(void) 770281681Srpaulo{ 771281681Srpaulo struct eap_method *eap; 772281681Srpaulo int ret; 773281681Srpaulo 774281681Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 775281681Srpaulo EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE"); 776281681Srpaulo if (eap == NULL) 777281681Srpaulo return -1; 778281681Srpaulo 779281681Srpaulo eap->init = eap_eke_init; 780281681Srpaulo eap->reset = eap_eke_reset; 781281681Srpaulo eap->buildReq = eap_eke_buildReq; 782281681Srpaulo eap->check = eap_eke_check; 783281681Srpaulo eap->process = eap_eke_process; 784281681Srpaulo eap->isDone = eap_eke_isDone; 785281681Srpaulo eap->getKey = eap_eke_getKey; 786281681Srpaulo eap->isSuccess = eap_eke_isSuccess; 787281681Srpaulo eap->get_emsk = eap_eke_get_emsk; 788281681Srpaulo 789281681Srpaulo ret = eap_server_method_register(eap); 790281681Srpaulo if (ret) 791281681Srpaulo eap_server_method_free(eap); 792281681Srpaulo return ret; 793281681Srpaulo} 794