1189251Ssam/* 2209158Srpaulo * EAP peer method: EAP-GPSK (RFC 5433) 3281806Srpaulo * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12252726Srpaulo#include "crypto/random.h" 13189251Ssam#include "eap_peer/eap_i.h" 14189251Ssam#include "eap_common/eap_gpsk_common.h" 15189251Ssam 16189251Ssamstruct eap_gpsk_data { 17189251Ssam enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state; 18189251Ssam u8 rand_server[EAP_GPSK_RAND_LEN]; 19189251Ssam u8 rand_peer[EAP_GPSK_RAND_LEN]; 20189251Ssam u8 msk[EAP_MSK_LEN]; 21189251Ssam u8 emsk[EAP_EMSK_LEN]; 22189251Ssam u8 sk[EAP_GPSK_MAX_SK_LEN]; 23189251Ssam size_t sk_len; 24189251Ssam u8 pk[EAP_GPSK_MAX_PK_LEN]; 25189251Ssam size_t pk_len; 26281806Srpaulo u8 session_id[128]; 27281806Srpaulo size_t id_len; 28189251Ssam u8 *id_peer; 29189251Ssam size_t id_peer_len; 30189251Ssam u8 *id_server; 31189251Ssam size_t id_server_len; 32189251Ssam int vendor; /* CSuite/Specifier */ 33189251Ssam int specifier; /* CSuite/Specifier */ 34189251Ssam u8 *psk; 35189251Ssam size_t psk_len; 36281806Srpaulo u16 forced_cipher; /* force cipher or 0 to allow all supported */ 37189251Ssam}; 38189251Ssam 39189251Ssam 40189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, 41189251Ssam u8 identifier, 42189251Ssam const u8 *csuite_list, 43189251Ssam size_t csuite_list_len); 44189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, 45189251Ssam u8 identifier); 46189251Ssam 47189251Ssam 48189251Ssam#ifndef CONFIG_NO_STDOUT_DEBUG 49189251Ssamstatic const char * eap_gpsk_state_txt(int state) 50189251Ssam{ 51189251Ssam switch (state) { 52189251Ssam case GPSK_1: 53189251Ssam return "GPSK-1"; 54189251Ssam case GPSK_3: 55189251Ssam return "GPSK-3"; 56189251Ssam case SUCCESS: 57189251Ssam return "SUCCESS"; 58189251Ssam case FAILURE: 59189251Ssam return "FAILURE"; 60189251Ssam default: 61189251Ssam return "?"; 62189251Ssam } 63189251Ssam} 64189251Ssam#endif /* CONFIG_NO_STDOUT_DEBUG */ 65189251Ssam 66189251Ssam 67189251Ssamstatic void eap_gpsk_state(struct eap_gpsk_data *data, int state) 68189251Ssam{ 69189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s", 70189251Ssam eap_gpsk_state_txt(data->state), 71189251Ssam eap_gpsk_state_txt(state)); 72189251Ssam data->state = state; 73189251Ssam} 74189251Ssam 75189251Ssam 76189251Ssamstatic void eap_gpsk_deinit(struct eap_sm *sm, void *priv); 77189251Ssam 78189251Ssam 79189251Ssamstatic void * eap_gpsk_init(struct eap_sm *sm) 80189251Ssam{ 81189251Ssam struct eap_gpsk_data *data; 82189251Ssam const u8 *identity, *password; 83189251Ssam size_t identity_len, password_len; 84281806Srpaulo const char *phase1; 85189251Ssam 86189251Ssam password = eap_get_config_password(sm, &password_len); 87189251Ssam if (password == NULL) { 88189251Ssam wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured"); 89189251Ssam return NULL; 90189251Ssam } 91189251Ssam 92189251Ssam data = os_zalloc(sizeof(*data)); 93189251Ssam if (data == NULL) 94189251Ssam return NULL; 95189251Ssam data->state = GPSK_1; 96189251Ssam 97189251Ssam identity = eap_get_config_identity(sm, &identity_len); 98189251Ssam if (identity) { 99346981Scy data->id_peer = os_memdup(identity, identity_len); 100189251Ssam if (data->id_peer == NULL) { 101189251Ssam eap_gpsk_deinit(sm, data); 102189251Ssam return NULL; 103189251Ssam } 104189251Ssam data->id_peer_len = identity_len; 105189251Ssam } 106189251Ssam 107281806Srpaulo phase1 = eap_get_config_phase1(sm); 108281806Srpaulo if (phase1) { 109281806Srpaulo const char *pos; 110281806Srpaulo 111281806Srpaulo pos = os_strstr(phase1, "cipher="); 112281806Srpaulo if (pos) { 113281806Srpaulo data->forced_cipher = atoi(pos + 7); 114281806Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u", 115281806Srpaulo data->forced_cipher); 116281806Srpaulo } 117281806Srpaulo } 118281806Srpaulo 119346981Scy data->psk = os_memdup(password, password_len); 120189251Ssam if (data->psk == NULL) { 121189251Ssam eap_gpsk_deinit(sm, data); 122189251Ssam return NULL; 123189251Ssam } 124189251Ssam data->psk_len = password_len; 125189251Ssam 126189251Ssam return data; 127189251Ssam} 128189251Ssam 129189251Ssam 130189251Ssamstatic void eap_gpsk_deinit(struct eap_sm *sm, void *priv) 131189251Ssam{ 132189251Ssam struct eap_gpsk_data *data = priv; 133189251Ssam os_free(data->id_server); 134189251Ssam os_free(data->id_peer); 135281806Srpaulo if (data->psk) { 136281806Srpaulo os_memset(data->psk, 0, data->psk_len); 137281806Srpaulo os_free(data->psk); 138281806Srpaulo } 139281806Srpaulo bin_clear_free(data, sizeof(*data)); 140189251Ssam} 141189251Ssam 142189251Ssam 143189251Ssamstatic const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data, 144189251Ssam const u8 *pos, const u8 *end) 145189251Ssam{ 146189251Ssam u16 alen; 147189251Ssam 148189251Ssam if (end - pos < 2) { 149189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); 150189251Ssam return NULL; 151189251Ssam } 152189251Ssam alen = WPA_GET_BE16(pos); 153189251Ssam pos += 2; 154189251Ssam if (end - pos < alen) { 155189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow"); 156189251Ssam return NULL; 157189251Ssam } 158189251Ssam os_free(data->id_server); 159346981Scy data->id_server = os_memdup(pos, alen); 160189251Ssam if (data->id_server == NULL) { 161189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server"); 162189251Ssam return NULL; 163189251Ssam } 164189251Ssam data->id_server_len = alen; 165189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server", 166189251Ssam data->id_server, data->id_server_len); 167189251Ssam pos += alen; 168189251Ssam 169189251Ssam return pos; 170189251Ssam} 171189251Ssam 172189251Ssam 173189251Ssamstatic const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data, 174189251Ssam const u8 *pos, const u8 *end) 175189251Ssam{ 176189251Ssam if (pos == NULL) 177189251Ssam return NULL; 178189251Ssam 179189251Ssam if (end - pos < EAP_GPSK_RAND_LEN) { 180189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow"); 181189251Ssam return NULL; 182189251Ssam } 183189251Ssam os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN); 184189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server", 185189251Ssam data->rand_server, EAP_GPSK_RAND_LEN); 186189251Ssam pos += EAP_GPSK_RAND_LEN; 187189251Ssam 188189251Ssam return pos; 189189251Ssam} 190189251Ssam 191189251Ssam 192189251Ssamstatic int eap_gpsk_select_csuite(struct eap_sm *sm, 193189251Ssam struct eap_gpsk_data *data, 194189251Ssam const u8 *csuite_list, 195189251Ssam size_t csuite_list_len) 196189251Ssam{ 197189251Ssam struct eap_gpsk_csuite *csuite; 198189251Ssam int i, count; 199189251Ssam 200189251Ssam count = csuite_list_len / sizeof(struct eap_gpsk_csuite); 201189251Ssam data->vendor = EAP_GPSK_VENDOR_IETF; 202189251Ssam data->specifier = EAP_GPSK_CIPHER_RESERVED; 203189251Ssam csuite = (struct eap_gpsk_csuite *) csuite_list; 204189251Ssam for (i = 0; i < count; i++) { 205189251Ssam int vendor, specifier; 206189251Ssam vendor = WPA_GET_BE32(csuite->vendor); 207189251Ssam specifier = WPA_GET_BE16(csuite->specifier); 208189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d", 209189251Ssam i, vendor, specifier); 210189251Ssam if (data->vendor == EAP_GPSK_VENDOR_IETF && 211189251Ssam data->specifier == EAP_GPSK_CIPHER_RESERVED && 212281806Srpaulo eap_gpsk_supported_ciphersuite(vendor, specifier) && 213281806Srpaulo (!data->forced_cipher || data->forced_cipher == specifier)) 214281806Srpaulo { 215189251Ssam data->vendor = vendor; 216189251Ssam data->specifier = specifier; 217189251Ssam } 218189251Ssam csuite++; 219189251Ssam } 220189251Ssam if (data->vendor == EAP_GPSK_VENDOR_IETF && 221189251Ssam data->specifier == EAP_GPSK_CIPHER_RESERVED) { 222189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported " 223189251Ssam "ciphersuite found"); 224189251Ssam return -1; 225189251Ssam } 226189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d", 227189251Ssam data->vendor, data->specifier); 228189251Ssam 229189251Ssam return 0; 230189251Ssam} 231189251Ssam 232189251Ssam 233189251Ssamstatic const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, 234189251Ssam struct eap_gpsk_data *data, 235189251Ssam const u8 **list, 236189251Ssam size_t *list_len, 237189251Ssam const u8 *pos, const u8 *end) 238189251Ssam{ 239281806Srpaulo size_t len; 240281806Srpaulo 241189251Ssam if (pos == NULL) 242189251Ssam return NULL; 243189251Ssam 244189251Ssam if (end - pos < 2) { 245189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); 246189251Ssam return NULL; 247189251Ssam } 248281806Srpaulo len = WPA_GET_BE16(pos); 249189251Ssam pos += 2; 250281806Srpaulo if (len > (size_t) (end - pos)) { 251189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow"); 252189251Ssam return NULL; 253189251Ssam } 254281806Srpaulo if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) { 255189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu", 256281806Srpaulo (unsigned long) len); 257189251Ssam return NULL; 258189251Ssam } 259189251Ssam 260281806Srpaulo if (eap_gpsk_select_csuite(sm, data, pos, len) < 0) 261189251Ssam return NULL; 262189251Ssam 263281806Srpaulo *list = pos; 264281806Srpaulo *list_len = len; 265281806Srpaulo pos += len; 266281806Srpaulo 267189251Ssam return pos; 268189251Ssam} 269189251Ssam 270189251Ssam 271189251Ssamstatic struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, 272189251Ssam struct eap_gpsk_data *data, 273189251Ssam struct eap_method_ret *ret, 274289549Srpaulo u8 identifier, 275189251Ssam const u8 *payload, 276189251Ssam size_t payload_len) 277189251Ssam{ 278189251Ssam size_t csuite_list_len; 279189251Ssam const u8 *csuite_list, *pos, *end; 280189251Ssam struct wpabuf *resp; 281189251Ssam 282189251Ssam if (data->state != GPSK_1) { 283189251Ssam ret->ignore = TRUE; 284189251Ssam return NULL; 285189251Ssam } 286189251Ssam 287189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1"); 288189251Ssam 289189251Ssam end = payload + payload_len; 290189251Ssam 291189251Ssam pos = eap_gpsk_process_id_server(data, payload, end); 292189251Ssam pos = eap_gpsk_process_rand_server(data, pos, end); 293189251Ssam pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list, 294189251Ssam &csuite_list_len, pos, end); 295189251Ssam if (pos == NULL) { 296281806Srpaulo ret->methodState = METHOD_DONE; 297189251Ssam eap_gpsk_state(data, FAILURE); 298189251Ssam return NULL; 299189251Ssam } 300189251Ssam 301289549Srpaulo resp = eap_gpsk_send_gpsk_2(data, identifier, 302189251Ssam csuite_list, csuite_list_len); 303189251Ssam if (resp == NULL) 304189251Ssam return NULL; 305189251Ssam 306189251Ssam eap_gpsk_state(data, GPSK_3); 307189251Ssam 308189251Ssam return resp; 309189251Ssam} 310189251Ssam 311189251Ssam 312189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, 313189251Ssam u8 identifier, 314189251Ssam const u8 *csuite_list, 315189251Ssam size_t csuite_list_len) 316189251Ssam{ 317189251Ssam struct wpabuf *resp; 318189251Ssam size_t len, miclen; 319189251Ssam u8 *rpos, *start; 320189251Ssam struct eap_gpsk_csuite *csuite; 321189251Ssam 322189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2"); 323189251Ssam 324189251Ssam miclen = eap_gpsk_mic_len(data->vendor, data->specifier); 325189251Ssam len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len + 326189251Ssam 2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len + 327189251Ssam sizeof(struct eap_gpsk_csuite) + 2 + miclen; 328189251Ssam 329189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, 330189251Ssam EAP_CODE_RESPONSE, identifier); 331189251Ssam if (resp == NULL) 332189251Ssam return NULL; 333189251Ssam 334189251Ssam wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2); 335189251Ssam start = wpabuf_put(resp, 0); 336189251Ssam 337189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", 338189251Ssam data->id_peer, data->id_peer_len); 339189251Ssam wpabuf_put_be16(resp, data->id_peer_len); 340189251Ssam wpabuf_put_data(resp, data->id_peer, data->id_peer_len); 341189251Ssam 342189251Ssam wpabuf_put_be16(resp, data->id_server_len); 343189251Ssam wpabuf_put_data(resp, data->id_server, data->id_server_len); 344189251Ssam 345252726Srpaulo if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) { 346189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data " 347189251Ssam "for RAND_Peer"); 348189251Ssam eap_gpsk_state(data, FAILURE); 349189251Ssam wpabuf_free(resp); 350189251Ssam return NULL; 351189251Ssam } 352189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", 353189251Ssam data->rand_peer, EAP_GPSK_RAND_LEN); 354189251Ssam wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN); 355189251Ssam wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN); 356189251Ssam 357189251Ssam wpabuf_put_be16(resp, csuite_list_len); 358189251Ssam wpabuf_put_data(resp, csuite_list, csuite_list_len); 359189251Ssam 360189251Ssam csuite = wpabuf_put(resp, sizeof(*csuite)); 361189251Ssam WPA_PUT_BE32(csuite->vendor, data->vendor); 362189251Ssam WPA_PUT_BE16(csuite->specifier, data->specifier); 363189251Ssam 364189251Ssam if (eap_gpsk_derive_keys(data->psk, data->psk_len, 365189251Ssam data->vendor, data->specifier, 366189251Ssam data->rand_peer, data->rand_server, 367189251Ssam data->id_peer, data->id_peer_len, 368189251Ssam data->id_server, data->id_server_len, 369189251Ssam data->msk, data->emsk, 370189251Ssam data->sk, &data->sk_len, 371189251Ssam data->pk, &data->pk_len) < 0) { 372189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys"); 373189251Ssam eap_gpsk_state(data, FAILURE); 374189251Ssam wpabuf_free(resp); 375189251Ssam return NULL; 376189251Ssam } 377189251Ssam 378281806Srpaulo if (eap_gpsk_derive_session_id(data->psk, data->psk_len, 379281806Srpaulo data->vendor, data->specifier, 380281806Srpaulo data->rand_peer, data->rand_server, 381281806Srpaulo data->id_peer, data->id_peer_len, 382281806Srpaulo data->id_server, data->id_server_len, 383281806Srpaulo EAP_TYPE_GPSK, 384281806Srpaulo data->session_id, &data->id_len) < 0) { 385281806Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); 386281806Srpaulo eap_gpsk_state(data, FAILURE); 387281806Srpaulo wpabuf_free(resp); 388281806Srpaulo return NULL; 389281806Srpaulo } 390281806Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", 391281806Srpaulo data->session_id, data->id_len); 392281806Srpaulo 393189251Ssam /* No PD_Payload_1 */ 394189251Ssam wpabuf_put_be16(resp, 0); 395189251Ssam 396189251Ssam rpos = wpabuf_put(resp, miclen); 397189251Ssam if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 398189251Ssam data->specifier, start, rpos - start, rpos) < 399189251Ssam 0) { 400189251Ssam eap_gpsk_state(data, FAILURE); 401189251Ssam wpabuf_free(resp); 402189251Ssam return NULL; 403189251Ssam } 404189251Ssam 405189251Ssam return resp; 406189251Ssam} 407189251Ssam 408189251Ssam 409189251Ssamstatic const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data, 410189251Ssam const u8 *pos, const u8 *end) 411189251Ssam{ 412189251Ssam if (end - pos < EAP_GPSK_RAND_LEN) { 413189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 414189251Ssam "RAND_Peer"); 415189251Ssam return NULL; 416189251Ssam } 417189251Ssam if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) { 418189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and " 419189251Ssam "GPSK-3 did not match"); 420189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2", 421189251Ssam data->rand_peer, EAP_GPSK_RAND_LEN); 422189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3", 423189251Ssam pos, EAP_GPSK_RAND_LEN); 424189251Ssam return NULL; 425189251Ssam } 426189251Ssam pos += EAP_GPSK_RAND_LEN; 427189251Ssam 428189251Ssam if (end - pos < EAP_GPSK_RAND_LEN) { 429189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 430189251Ssam "RAND_Server"); 431189251Ssam return NULL; 432189251Ssam } 433189251Ssam if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) { 434189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " 435189251Ssam "GPSK-3 did not match"); 436189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", 437189251Ssam data->rand_server, EAP_GPSK_RAND_LEN); 438189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3", 439189251Ssam pos, EAP_GPSK_RAND_LEN); 440189251Ssam return NULL; 441189251Ssam } 442189251Ssam pos += EAP_GPSK_RAND_LEN; 443189251Ssam 444189251Ssam return pos; 445189251Ssam} 446189251Ssam 447189251Ssam 448189251Ssamstatic const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data, 449189251Ssam const u8 *pos, const u8 *end) 450189251Ssam{ 451189251Ssam size_t len; 452189251Ssam 453189251Ssam if (pos == NULL) 454189251Ssam return NULL; 455189251Ssam 456189251Ssam if (end - pos < (int) 2) { 457189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 458189251Ssam "length(ID_Server)"); 459189251Ssam return NULL; 460189251Ssam } 461189251Ssam 462189251Ssam len = WPA_GET_BE16(pos); 463189251Ssam pos += 2; 464189251Ssam 465189251Ssam if (end - pos < (int) len) { 466189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 467189251Ssam "ID_Server"); 468189251Ssam return NULL; 469189251Ssam } 470189251Ssam 471189251Ssam if (len != data->id_server_len || 472189251Ssam os_memcmp(pos, data->id_server, len) != 0) { 473189251Ssam wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with " 474189251Ssam "the one used in GPSK-1"); 475189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1", 476189251Ssam data->id_server, data->id_server_len); 477189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3", 478189251Ssam pos, len); 479189251Ssam return NULL; 480189251Ssam } 481189251Ssam 482189251Ssam pos += len; 483189251Ssam 484189251Ssam return pos; 485189251Ssam} 486189251Ssam 487189251Ssam 488189251Ssamstatic const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data, 489189251Ssam const u8 *pos, const u8 *end) 490189251Ssam{ 491189251Ssam int vendor, specifier; 492189251Ssam const struct eap_gpsk_csuite *csuite; 493189251Ssam 494189251Ssam if (pos == NULL) 495189251Ssam return NULL; 496189251Ssam 497189251Ssam if (end - pos < (int) sizeof(*csuite)) { 498189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 499189251Ssam "CSuite_Sel"); 500189251Ssam return NULL; 501189251Ssam } 502189251Ssam csuite = (const struct eap_gpsk_csuite *) pos; 503189251Ssam vendor = WPA_GET_BE32(csuite->vendor); 504189251Ssam specifier = WPA_GET_BE16(csuite->specifier); 505189251Ssam pos += sizeof(*csuite); 506189251Ssam if (vendor != data->vendor || specifier != data->specifier) { 507189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not " 508189251Ssam "match with the one sent in GPSK-2 (%d:%d)", 509189251Ssam vendor, specifier, data->vendor, data->specifier); 510189251Ssam return NULL; 511189251Ssam } 512189251Ssam 513189251Ssam return pos; 514189251Ssam} 515189251Ssam 516189251Ssam 517189251Ssamstatic const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data, 518189251Ssam const u8 *pos, const u8 *end) 519189251Ssam{ 520189251Ssam u16 alen; 521189251Ssam 522189251Ssam if (pos == NULL) 523189251Ssam return NULL; 524189251Ssam 525189251Ssam if (end - pos < 2) { 526189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 527189251Ssam "PD_Payload_2 length"); 528189251Ssam return NULL; 529189251Ssam } 530189251Ssam alen = WPA_GET_BE16(pos); 531189251Ssam pos += 2; 532189251Ssam if (end - pos < alen) { 533189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 534189251Ssam "%d-octet PD_Payload_2", alen); 535189251Ssam return NULL; 536189251Ssam } 537189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen); 538189251Ssam pos += alen; 539189251Ssam 540189251Ssam return pos; 541189251Ssam} 542189251Ssam 543189251Ssam 544189251Ssamstatic const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, 545189251Ssam const u8 *payload, 546189251Ssam const u8 *pos, const u8 *end) 547189251Ssam{ 548189251Ssam size_t miclen; 549189251Ssam u8 mic[EAP_GPSK_MAX_MIC_LEN]; 550189251Ssam 551189251Ssam if (pos == NULL) 552189251Ssam return NULL; 553189251Ssam 554189251Ssam miclen = eap_gpsk_mic_len(data->vendor, data->specifier); 555189251Ssam if (end - pos < (int) miclen) { 556189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " 557189251Ssam "(left=%lu miclen=%lu)", 558189251Ssam (unsigned long) (end - pos), 559189251Ssam (unsigned long) miclen); 560189251Ssam return NULL; 561189251Ssam } 562189251Ssam if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 563189251Ssam data->specifier, payload, pos - payload, mic) 564189251Ssam < 0) { 565189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); 566189251Ssam return NULL; 567189251Ssam } 568281806Srpaulo if (os_memcmp_const(mic, pos, miclen) != 0) { 569189251Ssam wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3"); 570189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); 571189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); 572189251Ssam return NULL; 573189251Ssam } 574189251Ssam pos += miclen; 575189251Ssam 576189251Ssam return pos; 577189251Ssam} 578189251Ssam 579189251Ssam 580189251Ssamstatic struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, 581189251Ssam struct eap_gpsk_data *data, 582189251Ssam struct eap_method_ret *ret, 583289549Srpaulo u8 identifier, 584189251Ssam const u8 *payload, 585189251Ssam size_t payload_len) 586189251Ssam{ 587189251Ssam struct wpabuf *resp; 588189251Ssam const u8 *pos, *end; 589189251Ssam 590189251Ssam if (data->state != GPSK_3) { 591189251Ssam ret->ignore = TRUE; 592189251Ssam return NULL; 593189251Ssam } 594189251Ssam 595189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3"); 596189251Ssam 597189251Ssam end = payload + payload_len; 598189251Ssam 599189251Ssam pos = eap_gpsk_validate_rand(data, payload, end); 600189251Ssam pos = eap_gpsk_validate_id_server(data, pos, end); 601189251Ssam pos = eap_gpsk_validate_csuite(data, pos, end); 602189251Ssam pos = eap_gpsk_validate_pd_payload_2(data, pos, end); 603189251Ssam pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end); 604189251Ssam 605189251Ssam if (pos == NULL) { 606189251Ssam eap_gpsk_state(data, FAILURE); 607189251Ssam return NULL; 608189251Ssam } 609189251Ssam if (pos != end) { 610189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " 611189251Ssam "data in the end of GPSK-2", 612189251Ssam (unsigned long) (end - pos)); 613189251Ssam } 614189251Ssam 615289549Srpaulo resp = eap_gpsk_send_gpsk_4(data, identifier); 616189251Ssam if (resp == NULL) 617189251Ssam return NULL; 618189251Ssam 619189251Ssam eap_gpsk_state(data, SUCCESS); 620189251Ssam ret->methodState = METHOD_DONE; 621189251Ssam ret->decision = DECISION_UNCOND_SUCC; 622189251Ssam 623189251Ssam return resp; 624189251Ssam} 625189251Ssam 626189251Ssam 627189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, 628189251Ssam u8 identifier) 629189251Ssam{ 630189251Ssam struct wpabuf *resp; 631189251Ssam u8 *rpos, *start; 632189251Ssam size_t mlen; 633189251Ssam 634189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4"); 635189251Ssam 636189251Ssam mlen = eap_gpsk_mic_len(data->vendor, data->specifier); 637189251Ssam 638189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen, 639189251Ssam EAP_CODE_RESPONSE, identifier); 640189251Ssam if (resp == NULL) 641189251Ssam return NULL; 642189251Ssam 643189251Ssam wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4); 644189251Ssam start = wpabuf_put(resp, 0); 645189251Ssam 646189251Ssam /* No PD_Payload_3 */ 647189251Ssam wpabuf_put_be16(resp, 0); 648189251Ssam 649189251Ssam rpos = wpabuf_put(resp, mlen); 650189251Ssam if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 651189251Ssam data->specifier, start, rpos - start, rpos) < 652189251Ssam 0) { 653189251Ssam eap_gpsk_state(data, FAILURE); 654189251Ssam wpabuf_free(resp); 655189251Ssam return NULL; 656189251Ssam } 657189251Ssam 658189251Ssam return resp; 659189251Ssam} 660189251Ssam 661189251Ssam 662189251Ssamstatic struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, 663189251Ssam struct eap_method_ret *ret, 664189251Ssam const struct wpabuf *reqData) 665189251Ssam{ 666189251Ssam struct eap_gpsk_data *data = priv; 667189251Ssam struct wpabuf *resp; 668189251Ssam const u8 *pos; 669189251Ssam size_t len; 670289549Srpaulo u8 opcode, id; 671189251Ssam 672189251Ssam pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len); 673189251Ssam if (pos == NULL || len < 1) { 674189251Ssam ret->ignore = TRUE; 675189251Ssam return NULL; 676189251Ssam } 677189251Ssam 678289549Srpaulo id = eap_get_id(reqData); 679289549Srpaulo opcode = *pos++; 680289549Srpaulo len--; 681289549Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", opcode); 682189251Ssam 683189251Ssam ret->ignore = FALSE; 684189251Ssam ret->methodState = METHOD_MAY_CONT; 685189251Ssam ret->decision = DECISION_FAIL; 686189251Ssam ret->allowNotifications = FALSE; 687189251Ssam 688289549Srpaulo switch (opcode) { 689189251Ssam case EAP_GPSK_OPCODE_GPSK_1: 690289549Srpaulo resp = eap_gpsk_process_gpsk_1(sm, data, ret, id, pos, len); 691189251Ssam break; 692189251Ssam case EAP_GPSK_OPCODE_GPSK_3: 693289549Srpaulo resp = eap_gpsk_process_gpsk_3(sm, data, ret, id, pos, len); 694189251Ssam break; 695189251Ssam default: 696289549Srpaulo wpa_printf(MSG_DEBUG, 697289549Srpaulo "EAP-GPSK: Ignoring message with unknown opcode %d", 698289549Srpaulo opcode); 699189251Ssam ret->ignore = TRUE; 700189251Ssam return NULL; 701189251Ssam } 702189251Ssam 703189251Ssam return resp; 704189251Ssam} 705189251Ssam 706189251Ssam 707189251Ssamstatic Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv) 708189251Ssam{ 709189251Ssam struct eap_gpsk_data *data = priv; 710189251Ssam return data->state == SUCCESS; 711189251Ssam} 712189251Ssam 713189251Ssam 714189251Ssamstatic u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) 715189251Ssam{ 716189251Ssam struct eap_gpsk_data *data = priv; 717189251Ssam u8 *key; 718189251Ssam 719189251Ssam if (data->state != SUCCESS) 720189251Ssam return NULL; 721189251Ssam 722346981Scy key = os_memdup(data->msk, EAP_MSK_LEN); 723189251Ssam if (key == NULL) 724189251Ssam return NULL; 725189251Ssam *len = EAP_MSK_LEN; 726189251Ssam 727189251Ssam return key; 728189251Ssam} 729189251Ssam 730189251Ssam 731189251Ssamstatic u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 732189251Ssam{ 733189251Ssam struct eap_gpsk_data *data = priv; 734189251Ssam u8 *key; 735189251Ssam 736189251Ssam if (data->state != SUCCESS) 737189251Ssam return NULL; 738189251Ssam 739346981Scy key = os_memdup(data->emsk, EAP_EMSK_LEN); 740189251Ssam if (key == NULL) 741189251Ssam return NULL; 742189251Ssam *len = EAP_EMSK_LEN; 743189251Ssam 744189251Ssam return key; 745189251Ssam} 746189251Ssam 747189251Ssam 748281806Srpaulostatic u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 749281806Srpaulo{ 750281806Srpaulo struct eap_gpsk_data *data = priv; 751281806Srpaulo u8 *sid; 752281806Srpaulo 753281806Srpaulo if (data->state != SUCCESS) 754281806Srpaulo return NULL; 755281806Srpaulo 756346981Scy sid = os_memdup(data->session_id, data->id_len); 757281806Srpaulo if (sid == NULL) 758281806Srpaulo return NULL; 759281806Srpaulo *len = data->id_len; 760281806Srpaulo 761281806Srpaulo return sid; 762281806Srpaulo} 763281806Srpaulo 764281806Srpaulo 765189251Ssamint eap_peer_gpsk_register(void) 766189251Ssam{ 767189251Ssam struct eap_method *eap; 768189251Ssam 769189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 770189251Ssam EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); 771189251Ssam if (eap == NULL) 772189251Ssam return -1; 773189251Ssam 774189251Ssam eap->init = eap_gpsk_init; 775189251Ssam eap->deinit = eap_gpsk_deinit; 776189251Ssam eap->process = eap_gpsk_process; 777189251Ssam eap->isKeyAvailable = eap_gpsk_isKeyAvailable; 778189251Ssam eap->getKey = eap_gpsk_getKey; 779189251Ssam eap->get_emsk = eap_gpsk_get_emsk; 780281806Srpaulo eap->getSessionId = eap_gpsk_get_session_id; 781189251Ssam 782337817Scy return eap_peer_method_register(eap); 783189251Ssam} 784