eap_server_gpsk.c revision 346981
1214501Srpaulo/* 2214501Srpaulo * hostapd / EAP-GPSK (RFC 5433) server 3214501Srpaulo * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "includes.h" 10214501Srpaulo 11214501Srpaulo#include "common.h" 12252726Srpaulo#include "crypto/random.h" 13214501Srpaulo#include "eap_server/eap_i.h" 14214501Srpaulo#include "eap_common/eap_gpsk_common.h" 15214501Srpaulo 16214501Srpaulo 17214501Srpaulostruct eap_gpsk_data { 18214501Srpaulo enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state; 19214501Srpaulo u8 rand_server[EAP_GPSK_RAND_LEN]; 20214501Srpaulo u8 rand_peer[EAP_GPSK_RAND_LEN]; 21214501Srpaulo u8 msk[EAP_MSK_LEN]; 22214501Srpaulo u8 emsk[EAP_EMSK_LEN]; 23214501Srpaulo u8 sk[EAP_GPSK_MAX_SK_LEN]; 24214501Srpaulo size_t sk_len; 25214501Srpaulo u8 pk[EAP_GPSK_MAX_PK_LEN]; 26214501Srpaulo size_t pk_len; 27281806Srpaulo u8 session_id[128]; 28281806Srpaulo size_t id_len; 29214501Srpaulo u8 *id_peer; 30214501Srpaulo size_t id_peer_len; 31214501Srpaulo#define MAX_NUM_CSUITES 2 32214501Srpaulo struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES]; 33214501Srpaulo size_t csuite_count; 34214501Srpaulo int vendor; /* CSuite/Vendor */ 35214501Srpaulo int specifier; /* CSuite/Specifier */ 36214501Srpaulo}; 37214501Srpaulo 38214501Srpaulo 39214501Srpaulostatic const char * eap_gpsk_state_txt(int state) 40214501Srpaulo{ 41214501Srpaulo switch (state) { 42214501Srpaulo case GPSK_1: 43214501Srpaulo return "GPSK-1"; 44214501Srpaulo case GPSK_3: 45214501Srpaulo return "GPSK-3"; 46214501Srpaulo case SUCCESS: 47214501Srpaulo return "SUCCESS"; 48214501Srpaulo case FAILURE: 49214501Srpaulo return "FAILURE"; 50214501Srpaulo default: 51214501Srpaulo return "?"; 52214501Srpaulo } 53214501Srpaulo} 54214501Srpaulo 55214501Srpaulo 56214501Srpaulostatic void eap_gpsk_state(struct eap_gpsk_data *data, int state) 57214501Srpaulo{ 58214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s", 59214501Srpaulo eap_gpsk_state_txt(data->state), 60214501Srpaulo eap_gpsk_state_txt(state)); 61214501Srpaulo data->state = state; 62214501Srpaulo} 63214501Srpaulo 64214501Srpaulo 65214501Srpaulostatic void * eap_gpsk_init(struct eap_sm *sm) 66214501Srpaulo{ 67214501Srpaulo struct eap_gpsk_data *data; 68214501Srpaulo 69214501Srpaulo data = os_zalloc(sizeof(*data)); 70214501Srpaulo if (data == NULL) 71214501Srpaulo return NULL; 72214501Srpaulo data->state = GPSK_1; 73214501Srpaulo 74214501Srpaulo data->csuite_count = 0; 75214501Srpaulo if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, 76214501Srpaulo EAP_GPSK_CIPHER_AES)) { 77214501Srpaulo WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor, 78214501Srpaulo EAP_GPSK_VENDOR_IETF); 79214501Srpaulo WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier, 80214501Srpaulo EAP_GPSK_CIPHER_AES); 81214501Srpaulo data->csuite_count++; 82214501Srpaulo } 83214501Srpaulo if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, 84214501Srpaulo EAP_GPSK_CIPHER_SHA256)) { 85214501Srpaulo WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor, 86214501Srpaulo EAP_GPSK_VENDOR_IETF); 87214501Srpaulo WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier, 88214501Srpaulo EAP_GPSK_CIPHER_SHA256); 89214501Srpaulo data->csuite_count++; 90214501Srpaulo } 91214501Srpaulo 92214501Srpaulo return data; 93214501Srpaulo} 94214501Srpaulo 95214501Srpaulo 96214501Srpaulostatic void eap_gpsk_reset(struct eap_sm *sm, void *priv) 97214501Srpaulo{ 98214501Srpaulo struct eap_gpsk_data *data = priv; 99214501Srpaulo os_free(data->id_peer); 100281806Srpaulo bin_clear_free(data, sizeof(*data)); 101214501Srpaulo} 102214501Srpaulo 103214501Srpaulo 104214501Srpaulostatic struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm, 105214501Srpaulo struct eap_gpsk_data *data, u8 id) 106214501Srpaulo{ 107214501Srpaulo size_t len; 108214501Srpaulo struct wpabuf *req; 109214501Srpaulo 110214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1"); 111214501Srpaulo 112252726Srpaulo if (random_get_bytes(data->rand_server, EAP_GPSK_RAND_LEN)) { 113214501Srpaulo wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data"); 114214501Srpaulo eap_gpsk_state(data, FAILURE); 115214501Srpaulo return NULL; 116214501Srpaulo } 117214501Srpaulo wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server", 118214501Srpaulo data->rand_server, EAP_GPSK_RAND_LEN); 119214501Srpaulo 120281806Srpaulo len = 1 + 2 + sm->server_id_len + EAP_GPSK_RAND_LEN + 2 + 121214501Srpaulo data->csuite_count * sizeof(struct eap_gpsk_csuite); 122214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, 123214501Srpaulo EAP_CODE_REQUEST, id); 124214501Srpaulo if (req == NULL) { 125214501Srpaulo wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " 126214501Srpaulo "for request/GPSK-1"); 127214501Srpaulo eap_gpsk_state(data, FAILURE); 128214501Srpaulo return NULL; 129214501Srpaulo } 130214501Srpaulo 131214501Srpaulo wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1); 132281806Srpaulo wpabuf_put_be16(req, sm->server_id_len); 133281806Srpaulo wpabuf_put_data(req, sm->server_id, sm->server_id_len); 134214501Srpaulo wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); 135214501Srpaulo wpabuf_put_be16(req, 136214501Srpaulo data->csuite_count * sizeof(struct eap_gpsk_csuite)); 137214501Srpaulo wpabuf_put_data(req, data->csuite_list, 138214501Srpaulo data->csuite_count * sizeof(struct eap_gpsk_csuite)); 139214501Srpaulo 140214501Srpaulo return req; 141214501Srpaulo} 142214501Srpaulo 143214501Srpaulo 144214501Srpaulostatic struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm, 145214501Srpaulo struct eap_gpsk_data *data, u8 id) 146214501Srpaulo{ 147214501Srpaulo u8 *pos, *start; 148214501Srpaulo size_t len, miclen; 149214501Srpaulo struct eap_gpsk_csuite *csuite; 150214501Srpaulo struct wpabuf *req; 151214501Srpaulo 152214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3"); 153214501Srpaulo 154214501Srpaulo miclen = eap_gpsk_mic_len(data->vendor, data->specifier); 155281806Srpaulo len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len + 156214501Srpaulo sizeof(struct eap_gpsk_csuite) + 2 + miclen; 157214501Srpaulo req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, 158214501Srpaulo EAP_CODE_REQUEST, id); 159214501Srpaulo if (req == NULL) { 160214501Srpaulo wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " 161214501Srpaulo "for request/GPSK-3"); 162214501Srpaulo eap_gpsk_state(data, FAILURE); 163214501Srpaulo return NULL; 164214501Srpaulo } 165214501Srpaulo 166214501Srpaulo wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_3); 167214501Srpaulo start = wpabuf_put(req, 0); 168214501Srpaulo 169214501Srpaulo wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN); 170214501Srpaulo wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN); 171281806Srpaulo wpabuf_put_be16(req, sm->server_id_len); 172281806Srpaulo wpabuf_put_data(req, sm->server_id, sm->server_id_len); 173214501Srpaulo csuite = wpabuf_put(req, sizeof(*csuite)); 174214501Srpaulo WPA_PUT_BE32(csuite->vendor, data->vendor); 175214501Srpaulo WPA_PUT_BE16(csuite->specifier, data->specifier); 176214501Srpaulo 177214501Srpaulo /* no PD_Payload_2 */ 178214501Srpaulo wpabuf_put_be16(req, 0); 179214501Srpaulo 180214501Srpaulo pos = wpabuf_put(req, miclen); 181214501Srpaulo if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 182214501Srpaulo data->specifier, start, pos - start, pos) < 0) 183214501Srpaulo { 184346981Scy wpabuf_free(req); 185214501Srpaulo eap_gpsk_state(data, FAILURE); 186214501Srpaulo return NULL; 187214501Srpaulo } 188214501Srpaulo 189214501Srpaulo return req; 190214501Srpaulo} 191214501Srpaulo 192214501Srpaulo 193214501Srpaulostatic struct wpabuf * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, u8 id) 194214501Srpaulo{ 195214501Srpaulo struct eap_gpsk_data *data = priv; 196214501Srpaulo 197214501Srpaulo switch (data->state) { 198214501Srpaulo case GPSK_1: 199214501Srpaulo return eap_gpsk_build_gpsk_1(sm, data, id); 200214501Srpaulo case GPSK_3: 201214501Srpaulo return eap_gpsk_build_gpsk_3(sm, data, id); 202214501Srpaulo default: 203214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq", 204214501Srpaulo data->state); 205214501Srpaulo break; 206214501Srpaulo } 207214501Srpaulo return NULL; 208214501Srpaulo} 209214501Srpaulo 210214501Srpaulo 211214501Srpaulostatic Boolean eap_gpsk_check(struct eap_sm *sm, void *priv, 212214501Srpaulo struct wpabuf *respData) 213214501Srpaulo{ 214214501Srpaulo struct eap_gpsk_data *data = priv; 215214501Srpaulo const u8 *pos; 216214501Srpaulo size_t len; 217214501Srpaulo 218214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len); 219214501Srpaulo if (pos == NULL || len < 1) { 220214501Srpaulo wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame"); 221214501Srpaulo return TRUE; 222214501Srpaulo } 223214501Srpaulo 224214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode=%d", *pos); 225214501Srpaulo 226214501Srpaulo if (data->state == GPSK_1 && *pos == EAP_GPSK_OPCODE_GPSK_2) 227214501Srpaulo return FALSE; 228214501Srpaulo 229214501Srpaulo if (data->state == GPSK_3 && *pos == EAP_GPSK_OPCODE_GPSK_4) 230214501Srpaulo return FALSE; 231214501Srpaulo 232214501Srpaulo wpa_printf(MSG_INFO, "EAP-GPSK: Unexpected opcode=%d in state=%d", 233214501Srpaulo *pos, data->state); 234214501Srpaulo 235214501Srpaulo return TRUE; 236214501Srpaulo} 237214501Srpaulo 238214501Srpaulo 239214501Srpaulostatic void eap_gpsk_process_gpsk_2(struct eap_sm *sm, 240214501Srpaulo struct eap_gpsk_data *data, 241214501Srpaulo const u8 *payload, size_t payloadlen) 242214501Srpaulo{ 243214501Srpaulo const u8 *pos, *end; 244214501Srpaulo u16 alen; 245214501Srpaulo const struct eap_gpsk_csuite *csuite; 246214501Srpaulo size_t i, miclen; 247214501Srpaulo u8 mic[EAP_GPSK_MAX_MIC_LEN]; 248214501Srpaulo 249214501Srpaulo if (data->state != GPSK_1) 250214501Srpaulo return; 251214501Srpaulo 252214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-2"); 253214501Srpaulo 254214501Srpaulo pos = payload; 255214501Srpaulo end = payload + payloadlen; 256214501Srpaulo 257214501Srpaulo if (end - pos < 2) { 258214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 259214501Srpaulo "ID_Peer length"); 260214501Srpaulo eap_gpsk_state(data, FAILURE); 261214501Srpaulo return; 262214501Srpaulo } 263214501Srpaulo alen = WPA_GET_BE16(pos); 264214501Srpaulo pos += 2; 265214501Srpaulo if (end - pos < alen) { 266214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 267214501Srpaulo "ID_Peer"); 268214501Srpaulo eap_gpsk_state(data, FAILURE); 269214501Srpaulo return; 270214501Srpaulo } 271214501Srpaulo os_free(data->id_peer); 272346981Scy data->id_peer = os_memdup(pos, alen); 273214501Srpaulo if (data->id_peer == NULL) { 274214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store " 275214501Srpaulo "%d-octet ID_Peer", alen); 276214501Srpaulo return; 277214501Srpaulo } 278214501Srpaulo data->id_peer_len = alen; 279214501Srpaulo wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", 280214501Srpaulo data->id_peer, data->id_peer_len); 281214501Srpaulo pos += alen; 282214501Srpaulo 283214501Srpaulo if (end - pos < 2) { 284214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 285214501Srpaulo "ID_Server length"); 286214501Srpaulo eap_gpsk_state(data, FAILURE); 287214501Srpaulo return; 288214501Srpaulo } 289214501Srpaulo alen = WPA_GET_BE16(pos); 290214501Srpaulo pos += 2; 291214501Srpaulo if (end - pos < alen) { 292214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 293214501Srpaulo "ID_Server"); 294214501Srpaulo eap_gpsk_state(data, FAILURE); 295214501Srpaulo return; 296214501Srpaulo } 297281806Srpaulo if (alen != sm->server_id_len || 298281806Srpaulo os_memcmp(pos, sm->server_id, alen) != 0) { 299214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and " 300214501Srpaulo "GPSK-2 did not match"); 301214501Srpaulo eap_gpsk_state(data, FAILURE); 302214501Srpaulo return; 303214501Srpaulo } 304214501Srpaulo pos += alen; 305214501Srpaulo 306214501Srpaulo if (end - pos < EAP_GPSK_RAND_LEN) { 307214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 308214501Srpaulo "RAND_Peer"); 309214501Srpaulo eap_gpsk_state(data, FAILURE); 310214501Srpaulo return; 311214501Srpaulo } 312214501Srpaulo os_memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN); 313214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", 314214501Srpaulo data->rand_peer, EAP_GPSK_RAND_LEN); 315214501Srpaulo pos += EAP_GPSK_RAND_LEN; 316214501Srpaulo 317214501Srpaulo if (end - pos < EAP_GPSK_RAND_LEN) { 318214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 319214501Srpaulo "RAND_Server"); 320214501Srpaulo eap_gpsk_state(data, FAILURE); 321214501Srpaulo return; 322214501Srpaulo } 323214501Srpaulo if (os_memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) { 324214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " 325214501Srpaulo "GPSK-2 did not match"); 326214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", 327214501Srpaulo data->rand_server, EAP_GPSK_RAND_LEN); 328214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-2", 329214501Srpaulo pos, EAP_GPSK_RAND_LEN); 330214501Srpaulo eap_gpsk_state(data, FAILURE); 331214501Srpaulo return; 332214501Srpaulo } 333214501Srpaulo pos += EAP_GPSK_RAND_LEN; 334214501Srpaulo 335214501Srpaulo if (end - pos < 2) { 336214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 337214501Srpaulo "CSuite_List length"); 338214501Srpaulo eap_gpsk_state(data, FAILURE); 339214501Srpaulo return; 340214501Srpaulo } 341214501Srpaulo alen = WPA_GET_BE16(pos); 342214501Srpaulo pos += 2; 343214501Srpaulo if (end - pos < alen) { 344214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 345214501Srpaulo "CSuite_List"); 346214501Srpaulo eap_gpsk_state(data, FAILURE); 347214501Srpaulo return; 348214501Srpaulo } 349214501Srpaulo if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) || 350214501Srpaulo os_memcmp(pos, data->csuite_list, alen) != 0) { 351214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and " 352214501Srpaulo "GPSK-2 did not match"); 353214501Srpaulo eap_gpsk_state(data, FAILURE); 354214501Srpaulo return; 355214501Srpaulo } 356214501Srpaulo pos += alen; 357214501Srpaulo 358214501Srpaulo if (end - pos < (int) sizeof(*csuite)) { 359214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 360214501Srpaulo "CSuite_Sel"); 361214501Srpaulo eap_gpsk_state(data, FAILURE); 362214501Srpaulo return; 363214501Srpaulo } 364214501Srpaulo csuite = (const struct eap_gpsk_csuite *) pos; 365214501Srpaulo for (i = 0; i < data->csuite_count; i++) { 366214501Srpaulo if (os_memcmp(csuite, &data->csuite_list[i], sizeof(*csuite)) 367214501Srpaulo == 0) 368214501Srpaulo break; 369214501Srpaulo } 370214501Srpaulo if (i == data->csuite_count) { 371214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Peer selected unsupported " 372214501Srpaulo "ciphersuite %d:%d", 373214501Srpaulo WPA_GET_BE32(csuite->vendor), 374214501Srpaulo WPA_GET_BE16(csuite->specifier)); 375214501Srpaulo eap_gpsk_state(data, FAILURE); 376214501Srpaulo return; 377214501Srpaulo } 378214501Srpaulo data->vendor = WPA_GET_BE32(csuite->vendor); 379214501Srpaulo data->specifier = WPA_GET_BE16(csuite->specifier); 380214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d", 381214501Srpaulo data->vendor, data->specifier); 382346981Scy pos += sizeof(*csuite); 383214501Srpaulo 384214501Srpaulo if (end - pos < 2) { 385214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 386214501Srpaulo "PD_Payload_1 length"); 387214501Srpaulo eap_gpsk_state(data, FAILURE); 388214501Srpaulo return; 389214501Srpaulo } 390214501Srpaulo alen = WPA_GET_BE16(pos); 391214501Srpaulo pos += 2; 392214501Srpaulo if (end - pos < alen) { 393214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 394214501Srpaulo "PD_Payload_1"); 395214501Srpaulo eap_gpsk_state(data, FAILURE); 396214501Srpaulo return; 397214501Srpaulo } 398214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen); 399214501Srpaulo pos += alen; 400214501Srpaulo 401214501Srpaulo if (sm->user == NULL || sm->user->password == NULL) { 402214501Srpaulo wpa_printf(MSG_INFO, "EAP-GPSK: No PSK/password configured " 403214501Srpaulo "for the user"); 404214501Srpaulo eap_gpsk_state(data, FAILURE); 405214501Srpaulo return; 406214501Srpaulo } 407214501Srpaulo 408214501Srpaulo if (eap_gpsk_derive_keys(sm->user->password, sm->user->password_len, 409214501Srpaulo data->vendor, data->specifier, 410214501Srpaulo data->rand_peer, data->rand_server, 411214501Srpaulo data->id_peer, data->id_peer_len, 412281806Srpaulo sm->server_id, sm->server_id_len, 413214501Srpaulo data->msk, data->emsk, 414214501Srpaulo data->sk, &data->sk_len, 415214501Srpaulo data->pk, &data->pk_len) < 0) { 416214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys"); 417214501Srpaulo eap_gpsk_state(data, FAILURE); 418214501Srpaulo return; 419214501Srpaulo } 420214501Srpaulo 421281806Srpaulo if (eap_gpsk_derive_session_id(sm->user->password, 422281806Srpaulo sm->user->password_len, 423281806Srpaulo data->vendor, data->specifier, 424281806Srpaulo data->rand_peer, data->rand_server, 425281806Srpaulo data->id_peer, data->id_peer_len, 426281806Srpaulo sm->server_id, sm->server_id_len, 427281806Srpaulo EAP_TYPE_GPSK, 428281806Srpaulo data->session_id, &data->id_len) < 0) { 429281806Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); 430281806Srpaulo eap_gpsk_state(data, FAILURE); 431281806Srpaulo return; 432281806Srpaulo } 433281806Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", 434281806Srpaulo data->session_id, data->id_len); 435281806Srpaulo 436214501Srpaulo miclen = eap_gpsk_mic_len(data->vendor, data->specifier); 437214501Srpaulo if (end - pos < (int) miclen) { 438214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " 439214501Srpaulo "(left=%lu miclen=%lu)", 440214501Srpaulo (unsigned long) (end - pos), 441214501Srpaulo (unsigned long) miclen); 442214501Srpaulo eap_gpsk_state(data, FAILURE); 443214501Srpaulo return; 444214501Srpaulo } 445214501Srpaulo if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 446214501Srpaulo data->specifier, payload, pos - payload, mic) 447214501Srpaulo < 0) { 448214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); 449214501Srpaulo eap_gpsk_state(data, FAILURE); 450214501Srpaulo return; 451214501Srpaulo } 452281806Srpaulo if (os_memcmp_const(mic, pos, miclen) != 0) { 453214501Srpaulo wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2"); 454214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); 455214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); 456214501Srpaulo eap_gpsk_state(data, FAILURE); 457214501Srpaulo return; 458214501Srpaulo } 459214501Srpaulo pos += miclen; 460214501Srpaulo 461214501Srpaulo if (pos != end) { 462214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " 463214501Srpaulo "data in the end of GPSK-2", 464214501Srpaulo (unsigned long) (end - pos)); 465214501Srpaulo } 466214501Srpaulo 467214501Srpaulo eap_gpsk_state(data, GPSK_3); 468214501Srpaulo} 469214501Srpaulo 470214501Srpaulo 471214501Srpaulostatic void eap_gpsk_process_gpsk_4(struct eap_sm *sm, 472214501Srpaulo struct eap_gpsk_data *data, 473214501Srpaulo const u8 *payload, size_t payloadlen) 474214501Srpaulo{ 475214501Srpaulo const u8 *pos, *end; 476214501Srpaulo u16 alen; 477214501Srpaulo size_t miclen; 478214501Srpaulo u8 mic[EAP_GPSK_MAX_MIC_LEN]; 479214501Srpaulo 480214501Srpaulo if (data->state != GPSK_3) 481214501Srpaulo return; 482214501Srpaulo 483214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-4"); 484214501Srpaulo 485214501Srpaulo pos = payload; 486214501Srpaulo end = payload + payloadlen; 487214501Srpaulo 488214501Srpaulo if (end - pos < 2) { 489214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 490214501Srpaulo "PD_Payload_1 length"); 491214501Srpaulo eap_gpsk_state(data, FAILURE); 492214501Srpaulo return; 493214501Srpaulo } 494214501Srpaulo alen = WPA_GET_BE16(pos); 495214501Srpaulo pos += 2; 496214501Srpaulo if (end - pos < alen) { 497214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " 498214501Srpaulo "PD_Payload_1"); 499214501Srpaulo eap_gpsk_state(data, FAILURE); 500214501Srpaulo return; 501214501Srpaulo } 502214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen); 503214501Srpaulo pos += alen; 504214501Srpaulo 505214501Srpaulo miclen = eap_gpsk_mic_len(data->vendor, data->specifier); 506214501Srpaulo if (end - pos < (int) miclen) { 507214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " 508214501Srpaulo "(left=%lu miclen=%lu)", 509214501Srpaulo (unsigned long) (end - pos), 510214501Srpaulo (unsigned long) miclen); 511214501Srpaulo eap_gpsk_state(data, FAILURE); 512214501Srpaulo return; 513214501Srpaulo } 514214501Srpaulo if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 515214501Srpaulo data->specifier, payload, pos - payload, mic) 516214501Srpaulo < 0) { 517214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); 518214501Srpaulo eap_gpsk_state(data, FAILURE); 519214501Srpaulo return; 520214501Srpaulo } 521281806Srpaulo if (os_memcmp_const(mic, pos, miclen) != 0) { 522214501Srpaulo wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4"); 523214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); 524214501Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); 525214501Srpaulo eap_gpsk_state(data, FAILURE); 526214501Srpaulo return; 527214501Srpaulo } 528214501Srpaulo pos += miclen; 529214501Srpaulo 530214501Srpaulo if (pos != end) { 531214501Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " 532214501Srpaulo "data in the end of GPSK-4", 533214501Srpaulo (unsigned long) (end - pos)); 534214501Srpaulo } 535214501Srpaulo 536214501Srpaulo eap_gpsk_state(data, SUCCESS); 537214501Srpaulo} 538214501Srpaulo 539214501Srpaulo 540214501Srpaulostatic void eap_gpsk_process(struct eap_sm *sm, void *priv, 541214501Srpaulo struct wpabuf *respData) 542214501Srpaulo{ 543214501Srpaulo struct eap_gpsk_data *data = priv; 544214501Srpaulo const u8 *pos; 545214501Srpaulo size_t len; 546214501Srpaulo 547214501Srpaulo pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len); 548214501Srpaulo if (pos == NULL || len < 1) 549214501Srpaulo return; 550214501Srpaulo 551214501Srpaulo switch (*pos) { 552214501Srpaulo case EAP_GPSK_OPCODE_GPSK_2: 553214501Srpaulo eap_gpsk_process_gpsk_2(sm, data, pos + 1, len - 1); 554214501Srpaulo break; 555214501Srpaulo case EAP_GPSK_OPCODE_GPSK_4: 556214501Srpaulo eap_gpsk_process_gpsk_4(sm, data, pos + 1, len - 1); 557214501Srpaulo break; 558214501Srpaulo } 559214501Srpaulo} 560214501Srpaulo 561214501Srpaulo 562214501Srpaulostatic Boolean eap_gpsk_isDone(struct eap_sm *sm, void *priv) 563214501Srpaulo{ 564214501Srpaulo struct eap_gpsk_data *data = priv; 565214501Srpaulo return data->state == SUCCESS || data->state == FAILURE; 566214501Srpaulo} 567214501Srpaulo 568214501Srpaulo 569214501Srpaulostatic u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) 570214501Srpaulo{ 571214501Srpaulo struct eap_gpsk_data *data = priv; 572214501Srpaulo u8 *key; 573214501Srpaulo 574214501Srpaulo if (data->state != SUCCESS) 575214501Srpaulo return NULL; 576214501Srpaulo 577346981Scy key = os_memdup(data->msk, EAP_MSK_LEN); 578214501Srpaulo if (key == NULL) 579214501Srpaulo return NULL; 580214501Srpaulo *len = EAP_MSK_LEN; 581214501Srpaulo 582214501Srpaulo return key; 583214501Srpaulo} 584214501Srpaulo 585214501Srpaulo 586214501Srpaulostatic u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 587214501Srpaulo{ 588214501Srpaulo struct eap_gpsk_data *data = priv; 589214501Srpaulo u8 *key; 590214501Srpaulo 591214501Srpaulo if (data->state != SUCCESS) 592214501Srpaulo return NULL; 593214501Srpaulo 594346981Scy key = os_memdup(data->emsk, EAP_EMSK_LEN); 595214501Srpaulo if (key == NULL) 596214501Srpaulo return NULL; 597214501Srpaulo *len = EAP_EMSK_LEN; 598214501Srpaulo 599214501Srpaulo return key; 600214501Srpaulo} 601214501Srpaulo 602214501Srpaulo 603214501Srpaulostatic Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv) 604214501Srpaulo{ 605214501Srpaulo struct eap_gpsk_data *data = priv; 606214501Srpaulo return data->state == SUCCESS; 607214501Srpaulo} 608214501Srpaulo 609214501Srpaulo 610281806Srpaulostatic u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 611281806Srpaulo{ 612281806Srpaulo struct eap_gpsk_data *data = priv; 613281806Srpaulo u8 *sid; 614281806Srpaulo 615281806Srpaulo if (data->state != SUCCESS) 616281806Srpaulo return NULL; 617281806Srpaulo 618346981Scy sid = os_memdup(data->session_id, data->id_len); 619281806Srpaulo if (sid == NULL) 620281806Srpaulo return NULL; 621281806Srpaulo *len = data->id_len; 622281806Srpaulo 623281806Srpaulo return sid; 624281806Srpaulo} 625281806Srpaulo 626281806Srpaulo 627214501Srpauloint eap_server_gpsk_register(void) 628214501Srpaulo{ 629214501Srpaulo struct eap_method *eap; 630214501Srpaulo 631214501Srpaulo eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, 632214501Srpaulo EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); 633214501Srpaulo if (eap == NULL) 634214501Srpaulo return -1; 635214501Srpaulo 636214501Srpaulo eap->init = eap_gpsk_init; 637214501Srpaulo eap->reset = eap_gpsk_reset; 638214501Srpaulo eap->buildReq = eap_gpsk_buildReq; 639214501Srpaulo eap->check = eap_gpsk_check; 640214501Srpaulo eap->process = eap_gpsk_process; 641214501Srpaulo eap->isDone = eap_gpsk_isDone; 642214501Srpaulo eap->getKey = eap_gpsk_getKey; 643214501Srpaulo eap->isSuccess = eap_gpsk_isSuccess; 644214501Srpaulo eap->get_emsk = eap_gpsk_get_emsk; 645281806Srpaulo eap->getSessionId = eap_gpsk_get_session_id; 646214501Srpaulo 647337817Scy return eap_server_method_register(eap); 648214501Srpaulo} 649