eap_gpsk.c revision 281806
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) { 99189251Ssam data->id_peer = os_malloc(identity_len); 100189251Ssam if (data->id_peer == NULL) { 101189251Ssam eap_gpsk_deinit(sm, data); 102189251Ssam return NULL; 103189251Ssam } 104189251Ssam os_memcpy(data->id_peer, identity, identity_len); 105189251Ssam data->id_peer_len = identity_len; 106189251Ssam } 107189251Ssam 108281806Srpaulo phase1 = eap_get_config_phase1(sm); 109281806Srpaulo if (phase1) { 110281806Srpaulo const char *pos; 111281806Srpaulo 112281806Srpaulo pos = os_strstr(phase1, "cipher="); 113281806Srpaulo if (pos) { 114281806Srpaulo data->forced_cipher = atoi(pos + 7); 115281806Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u", 116281806Srpaulo data->forced_cipher); 117281806Srpaulo } 118281806Srpaulo } 119281806Srpaulo 120189251Ssam data->psk = os_malloc(password_len); 121189251Ssam if (data->psk == NULL) { 122189251Ssam eap_gpsk_deinit(sm, data); 123189251Ssam return NULL; 124189251Ssam } 125189251Ssam os_memcpy(data->psk, password, password_len); 126189251Ssam data->psk_len = password_len; 127189251Ssam 128189251Ssam return data; 129189251Ssam} 130189251Ssam 131189251Ssam 132189251Ssamstatic void eap_gpsk_deinit(struct eap_sm *sm, void *priv) 133189251Ssam{ 134189251Ssam struct eap_gpsk_data *data = priv; 135189251Ssam os_free(data->id_server); 136189251Ssam os_free(data->id_peer); 137281806Srpaulo if (data->psk) { 138281806Srpaulo os_memset(data->psk, 0, data->psk_len); 139281806Srpaulo os_free(data->psk); 140281806Srpaulo } 141281806Srpaulo bin_clear_free(data, sizeof(*data)); 142189251Ssam} 143189251Ssam 144189251Ssam 145189251Ssamstatic const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data, 146189251Ssam const u8 *pos, const u8 *end) 147189251Ssam{ 148189251Ssam u16 alen; 149189251Ssam 150189251Ssam if (end - pos < 2) { 151189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); 152189251Ssam return NULL; 153189251Ssam } 154189251Ssam alen = WPA_GET_BE16(pos); 155189251Ssam pos += 2; 156189251Ssam if (end - pos < alen) { 157189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow"); 158189251Ssam return NULL; 159189251Ssam } 160189251Ssam os_free(data->id_server); 161189251Ssam data->id_server = os_malloc(alen); 162189251Ssam if (data->id_server == NULL) { 163189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server"); 164189251Ssam return NULL; 165189251Ssam } 166189251Ssam os_memcpy(data->id_server, pos, alen); 167189251Ssam data->id_server_len = alen; 168189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server", 169189251Ssam data->id_server, data->id_server_len); 170189251Ssam pos += alen; 171189251Ssam 172189251Ssam return pos; 173189251Ssam} 174189251Ssam 175189251Ssam 176189251Ssamstatic const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data, 177189251Ssam const u8 *pos, const u8 *end) 178189251Ssam{ 179189251Ssam if (pos == NULL) 180189251Ssam return NULL; 181189251Ssam 182189251Ssam if (end - pos < EAP_GPSK_RAND_LEN) { 183189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow"); 184189251Ssam return NULL; 185189251Ssam } 186189251Ssam os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN); 187189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server", 188189251Ssam data->rand_server, EAP_GPSK_RAND_LEN); 189189251Ssam pos += EAP_GPSK_RAND_LEN; 190189251Ssam 191189251Ssam return pos; 192189251Ssam} 193189251Ssam 194189251Ssam 195189251Ssamstatic int eap_gpsk_select_csuite(struct eap_sm *sm, 196189251Ssam struct eap_gpsk_data *data, 197189251Ssam const u8 *csuite_list, 198189251Ssam size_t csuite_list_len) 199189251Ssam{ 200189251Ssam struct eap_gpsk_csuite *csuite; 201189251Ssam int i, count; 202189251Ssam 203189251Ssam count = csuite_list_len / sizeof(struct eap_gpsk_csuite); 204189251Ssam data->vendor = EAP_GPSK_VENDOR_IETF; 205189251Ssam data->specifier = EAP_GPSK_CIPHER_RESERVED; 206189251Ssam csuite = (struct eap_gpsk_csuite *) csuite_list; 207189251Ssam for (i = 0; i < count; i++) { 208189251Ssam int vendor, specifier; 209189251Ssam vendor = WPA_GET_BE32(csuite->vendor); 210189251Ssam specifier = WPA_GET_BE16(csuite->specifier); 211189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d", 212189251Ssam i, vendor, specifier); 213189251Ssam if (data->vendor == EAP_GPSK_VENDOR_IETF && 214189251Ssam data->specifier == EAP_GPSK_CIPHER_RESERVED && 215281806Srpaulo eap_gpsk_supported_ciphersuite(vendor, specifier) && 216281806Srpaulo (!data->forced_cipher || data->forced_cipher == specifier)) 217281806Srpaulo { 218189251Ssam data->vendor = vendor; 219189251Ssam data->specifier = specifier; 220189251Ssam } 221189251Ssam csuite++; 222189251Ssam } 223189251Ssam if (data->vendor == EAP_GPSK_VENDOR_IETF && 224189251Ssam data->specifier == EAP_GPSK_CIPHER_RESERVED) { 225189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported " 226189251Ssam "ciphersuite found"); 227189251Ssam return -1; 228189251Ssam } 229189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d", 230189251Ssam data->vendor, data->specifier); 231189251Ssam 232189251Ssam return 0; 233189251Ssam} 234189251Ssam 235189251Ssam 236189251Ssamstatic const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, 237189251Ssam struct eap_gpsk_data *data, 238189251Ssam const u8 **list, 239189251Ssam size_t *list_len, 240189251Ssam const u8 *pos, const u8 *end) 241189251Ssam{ 242281806Srpaulo size_t len; 243281806Srpaulo 244189251Ssam if (pos == NULL) 245189251Ssam return NULL; 246189251Ssam 247189251Ssam if (end - pos < 2) { 248189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); 249189251Ssam return NULL; 250189251Ssam } 251281806Srpaulo len = WPA_GET_BE16(pos); 252189251Ssam pos += 2; 253281806Srpaulo if (len > (size_t) (end - pos)) { 254189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow"); 255189251Ssam return NULL; 256189251Ssam } 257281806Srpaulo if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) { 258189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu", 259281806Srpaulo (unsigned long) len); 260189251Ssam return NULL; 261189251Ssam } 262189251Ssam 263281806Srpaulo if (eap_gpsk_select_csuite(sm, data, pos, len) < 0) 264189251Ssam return NULL; 265189251Ssam 266281806Srpaulo *list = pos; 267281806Srpaulo *list_len = len; 268281806Srpaulo pos += len; 269281806Srpaulo 270189251Ssam return pos; 271189251Ssam} 272189251Ssam 273189251Ssam 274189251Ssamstatic struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, 275189251Ssam struct eap_gpsk_data *data, 276189251Ssam struct eap_method_ret *ret, 277189251Ssam const struct wpabuf *reqData, 278189251Ssam const u8 *payload, 279189251Ssam size_t payload_len) 280189251Ssam{ 281189251Ssam size_t csuite_list_len; 282189251Ssam const u8 *csuite_list, *pos, *end; 283189251Ssam struct wpabuf *resp; 284189251Ssam 285189251Ssam if (data->state != GPSK_1) { 286189251Ssam ret->ignore = TRUE; 287189251Ssam return NULL; 288189251Ssam } 289189251Ssam 290189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1"); 291189251Ssam 292189251Ssam end = payload + payload_len; 293189251Ssam 294189251Ssam pos = eap_gpsk_process_id_server(data, payload, end); 295189251Ssam pos = eap_gpsk_process_rand_server(data, pos, end); 296189251Ssam pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list, 297189251Ssam &csuite_list_len, pos, end); 298189251Ssam if (pos == NULL) { 299281806Srpaulo ret->methodState = METHOD_DONE; 300189251Ssam eap_gpsk_state(data, FAILURE); 301189251Ssam return NULL; 302189251Ssam } 303189251Ssam 304189251Ssam resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData), 305189251Ssam csuite_list, csuite_list_len); 306189251Ssam if (resp == NULL) 307189251Ssam return NULL; 308189251Ssam 309189251Ssam eap_gpsk_state(data, GPSK_3); 310189251Ssam 311189251Ssam return resp; 312189251Ssam} 313189251Ssam 314189251Ssam 315189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, 316189251Ssam u8 identifier, 317189251Ssam const u8 *csuite_list, 318189251Ssam size_t csuite_list_len) 319189251Ssam{ 320189251Ssam struct wpabuf *resp; 321189251Ssam size_t len, miclen; 322189251Ssam u8 *rpos, *start; 323189251Ssam struct eap_gpsk_csuite *csuite; 324189251Ssam 325189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2"); 326189251Ssam 327189251Ssam miclen = eap_gpsk_mic_len(data->vendor, data->specifier); 328189251Ssam len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len + 329189251Ssam 2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len + 330189251Ssam sizeof(struct eap_gpsk_csuite) + 2 + miclen; 331189251Ssam 332189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, 333189251Ssam EAP_CODE_RESPONSE, identifier); 334189251Ssam if (resp == NULL) 335189251Ssam return NULL; 336189251Ssam 337189251Ssam wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2); 338189251Ssam start = wpabuf_put(resp, 0); 339189251Ssam 340189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", 341189251Ssam data->id_peer, data->id_peer_len); 342189251Ssam wpabuf_put_be16(resp, data->id_peer_len); 343189251Ssam wpabuf_put_data(resp, data->id_peer, data->id_peer_len); 344189251Ssam 345189251Ssam wpabuf_put_be16(resp, data->id_server_len); 346189251Ssam wpabuf_put_data(resp, data->id_server, data->id_server_len); 347189251Ssam 348252726Srpaulo if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) { 349189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data " 350189251Ssam "for RAND_Peer"); 351189251Ssam eap_gpsk_state(data, FAILURE); 352189251Ssam wpabuf_free(resp); 353189251Ssam return NULL; 354189251Ssam } 355189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", 356189251Ssam data->rand_peer, EAP_GPSK_RAND_LEN); 357189251Ssam wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN); 358189251Ssam wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN); 359189251Ssam 360189251Ssam wpabuf_put_be16(resp, csuite_list_len); 361189251Ssam wpabuf_put_data(resp, csuite_list, csuite_list_len); 362189251Ssam 363189251Ssam csuite = wpabuf_put(resp, sizeof(*csuite)); 364189251Ssam WPA_PUT_BE32(csuite->vendor, data->vendor); 365189251Ssam WPA_PUT_BE16(csuite->specifier, data->specifier); 366189251Ssam 367189251Ssam if (eap_gpsk_derive_keys(data->psk, data->psk_len, 368189251Ssam data->vendor, data->specifier, 369189251Ssam data->rand_peer, data->rand_server, 370189251Ssam data->id_peer, data->id_peer_len, 371189251Ssam data->id_server, data->id_server_len, 372189251Ssam data->msk, data->emsk, 373189251Ssam data->sk, &data->sk_len, 374189251Ssam data->pk, &data->pk_len) < 0) { 375189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys"); 376189251Ssam eap_gpsk_state(data, FAILURE); 377189251Ssam wpabuf_free(resp); 378189251Ssam return NULL; 379189251Ssam } 380189251Ssam 381281806Srpaulo if (eap_gpsk_derive_session_id(data->psk, data->psk_len, 382281806Srpaulo data->vendor, data->specifier, 383281806Srpaulo data->rand_peer, data->rand_server, 384281806Srpaulo data->id_peer, data->id_peer_len, 385281806Srpaulo data->id_server, data->id_server_len, 386281806Srpaulo EAP_TYPE_GPSK, 387281806Srpaulo data->session_id, &data->id_len) < 0) { 388281806Srpaulo wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id"); 389281806Srpaulo eap_gpsk_state(data, FAILURE); 390281806Srpaulo wpabuf_free(resp); 391281806Srpaulo return NULL; 392281806Srpaulo } 393281806Srpaulo wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id", 394281806Srpaulo data->session_id, data->id_len); 395281806Srpaulo 396189251Ssam /* No PD_Payload_1 */ 397189251Ssam wpabuf_put_be16(resp, 0); 398189251Ssam 399189251Ssam rpos = wpabuf_put(resp, miclen); 400189251Ssam if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 401189251Ssam data->specifier, start, rpos - start, rpos) < 402189251Ssam 0) { 403189251Ssam eap_gpsk_state(data, FAILURE); 404189251Ssam wpabuf_free(resp); 405189251Ssam return NULL; 406189251Ssam } 407189251Ssam 408189251Ssam return resp; 409189251Ssam} 410189251Ssam 411189251Ssam 412189251Ssamstatic const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data, 413189251Ssam const u8 *pos, const u8 *end) 414189251Ssam{ 415189251Ssam if (end - pos < EAP_GPSK_RAND_LEN) { 416189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 417189251Ssam "RAND_Peer"); 418189251Ssam return NULL; 419189251Ssam } 420189251Ssam if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) { 421189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and " 422189251Ssam "GPSK-3 did not match"); 423189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2", 424189251Ssam data->rand_peer, EAP_GPSK_RAND_LEN); 425189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3", 426189251Ssam pos, EAP_GPSK_RAND_LEN); 427189251Ssam return NULL; 428189251Ssam } 429189251Ssam pos += EAP_GPSK_RAND_LEN; 430189251Ssam 431189251Ssam if (end - pos < EAP_GPSK_RAND_LEN) { 432189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 433189251Ssam "RAND_Server"); 434189251Ssam return NULL; 435189251Ssam } 436189251Ssam if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) { 437189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " 438189251Ssam "GPSK-3 did not match"); 439189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", 440189251Ssam data->rand_server, EAP_GPSK_RAND_LEN); 441189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3", 442189251Ssam pos, EAP_GPSK_RAND_LEN); 443189251Ssam return NULL; 444189251Ssam } 445189251Ssam pos += EAP_GPSK_RAND_LEN; 446189251Ssam 447189251Ssam return pos; 448189251Ssam} 449189251Ssam 450189251Ssam 451189251Ssamstatic const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data, 452189251Ssam const u8 *pos, const u8 *end) 453189251Ssam{ 454189251Ssam size_t len; 455189251Ssam 456189251Ssam if (pos == NULL) 457189251Ssam return NULL; 458189251Ssam 459189251Ssam if (end - pos < (int) 2) { 460189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 461189251Ssam "length(ID_Server)"); 462189251Ssam return NULL; 463189251Ssam } 464189251Ssam 465189251Ssam len = WPA_GET_BE16(pos); 466189251Ssam pos += 2; 467189251Ssam 468189251Ssam if (end - pos < (int) len) { 469189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 470189251Ssam "ID_Server"); 471189251Ssam return NULL; 472189251Ssam } 473189251Ssam 474189251Ssam if (len != data->id_server_len || 475189251Ssam os_memcmp(pos, data->id_server, len) != 0) { 476189251Ssam wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with " 477189251Ssam "the one used in GPSK-1"); 478189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1", 479189251Ssam data->id_server, data->id_server_len); 480189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3", 481189251Ssam pos, len); 482189251Ssam return NULL; 483189251Ssam } 484189251Ssam 485189251Ssam pos += len; 486189251Ssam 487189251Ssam return pos; 488189251Ssam} 489189251Ssam 490189251Ssam 491189251Ssamstatic const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data, 492189251Ssam const u8 *pos, const u8 *end) 493189251Ssam{ 494189251Ssam int vendor, specifier; 495189251Ssam const struct eap_gpsk_csuite *csuite; 496189251Ssam 497189251Ssam if (pos == NULL) 498189251Ssam return NULL; 499189251Ssam 500189251Ssam if (end - pos < (int) sizeof(*csuite)) { 501189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 502189251Ssam "CSuite_Sel"); 503189251Ssam return NULL; 504189251Ssam } 505189251Ssam csuite = (const struct eap_gpsk_csuite *) pos; 506189251Ssam vendor = WPA_GET_BE32(csuite->vendor); 507189251Ssam specifier = WPA_GET_BE16(csuite->specifier); 508189251Ssam pos += sizeof(*csuite); 509189251Ssam if (vendor != data->vendor || specifier != data->specifier) { 510189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not " 511189251Ssam "match with the one sent in GPSK-2 (%d:%d)", 512189251Ssam vendor, specifier, data->vendor, data->specifier); 513189251Ssam return NULL; 514189251Ssam } 515189251Ssam 516189251Ssam return pos; 517189251Ssam} 518189251Ssam 519189251Ssam 520189251Ssamstatic const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data, 521189251Ssam const u8 *pos, const u8 *end) 522189251Ssam{ 523189251Ssam u16 alen; 524189251Ssam 525189251Ssam if (pos == NULL) 526189251Ssam return NULL; 527189251Ssam 528189251Ssam if (end - pos < 2) { 529189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 530189251Ssam "PD_Payload_2 length"); 531189251Ssam return NULL; 532189251Ssam } 533189251Ssam alen = WPA_GET_BE16(pos); 534189251Ssam pos += 2; 535189251Ssam if (end - pos < alen) { 536189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 537189251Ssam "%d-octet PD_Payload_2", alen); 538189251Ssam return NULL; 539189251Ssam } 540189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen); 541189251Ssam pos += alen; 542189251Ssam 543189251Ssam return pos; 544189251Ssam} 545189251Ssam 546189251Ssam 547189251Ssamstatic const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, 548189251Ssam const u8 *payload, 549189251Ssam const u8 *pos, const u8 *end) 550189251Ssam{ 551189251Ssam size_t miclen; 552189251Ssam u8 mic[EAP_GPSK_MAX_MIC_LEN]; 553189251Ssam 554189251Ssam if (pos == NULL) 555189251Ssam return NULL; 556189251Ssam 557189251Ssam miclen = eap_gpsk_mic_len(data->vendor, data->specifier); 558189251Ssam if (end - pos < (int) miclen) { 559189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " 560189251Ssam "(left=%lu miclen=%lu)", 561189251Ssam (unsigned long) (end - pos), 562189251Ssam (unsigned long) miclen); 563189251Ssam return NULL; 564189251Ssam } 565189251Ssam if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 566189251Ssam data->specifier, payload, pos - payload, mic) 567189251Ssam < 0) { 568189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); 569189251Ssam return NULL; 570189251Ssam } 571281806Srpaulo if (os_memcmp_const(mic, pos, miclen) != 0) { 572189251Ssam wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3"); 573189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); 574189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); 575189251Ssam return NULL; 576189251Ssam } 577189251Ssam pos += miclen; 578189251Ssam 579189251Ssam return pos; 580189251Ssam} 581189251Ssam 582189251Ssam 583189251Ssamstatic struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, 584189251Ssam struct eap_gpsk_data *data, 585189251Ssam struct eap_method_ret *ret, 586189251Ssam const struct wpabuf *reqData, 587189251Ssam const u8 *payload, 588189251Ssam size_t payload_len) 589189251Ssam{ 590189251Ssam struct wpabuf *resp; 591189251Ssam const u8 *pos, *end; 592189251Ssam 593189251Ssam if (data->state != GPSK_3) { 594189251Ssam ret->ignore = TRUE; 595189251Ssam return NULL; 596189251Ssam } 597189251Ssam 598189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3"); 599189251Ssam 600189251Ssam end = payload + payload_len; 601189251Ssam 602189251Ssam pos = eap_gpsk_validate_rand(data, payload, end); 603189251Ssam pos = eap_gpsk_validate_id_server(data, pos, end); 604189251Ssam pos = eap_gpsk_validate_csuite(data, pos, end); 605189251Ssam pos = eap_gpsk_validate_pd_payload_2(data, pos, end); 606189251Ssam pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end); 607189251Ssam 608189251Ssam if (pos == NULL) { 609189251Ssam eap_gpsk_state(data, FAILURE); 610189251Ssam return NULL; 611189251Ssam } 612189251Ssam if (pos != end) { 613189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " 614189251Ssam "data in the end of GPSK-2", 615189251Ssam (unsigned long) (end - pos)); 616189251Ssam } 617189251Ssam 618189251Ssam resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData)); 619189251Ssam if (resp == NULL) 620189251Ssam return NULL; 621189251Ssam 622189251Ssam eap_gpsk_state(data, SUCCESS); 623189251Ssam ret->methodState = METHOD_DONE; 624189251Ssam ret->decision = DECISION_UNCOND_SUCC; 625189251Ssam 626189251Ssam return resp; 627189251Ssam} 628189251Ssam 629189251Ssam 630189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, 631189251Ssam u8 identifier) 632189251Ssam{ 633189251Ssam struct wpabuf *resp; 634189251Ssam u8 *rpos, *start; 635189251Ssam size_t mlen; 636189251Ssam 637189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4"); 638189251Ssam 639189251Ssam mlen = eap_gpsk_mic_len(data->vendor, data->specifier); 640189251Ssam 641189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen, 642189251Ssam EAP_CODE_RESPONSE, identifier); 643189251Ssam if (resp == NULL) 644189251Ssam return NULL; 645189251Ssam 646189251Ssam wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4); 647189251Ssam start = wpabuf_put(resp, 0); 648189251Ssam 649189251Ssam /* No PD_Payload_3 */ 650189251Ssam wpabuf_put_be16(resp, 0); 651189251Ssam 652189251Ssam rpos = wpabuf_put(resp, mlen); 653189251Ssam if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 654189251Ssam data->specifier, start, rpos - start, rpos) < 655189251Ssam 0) { 656189251Ssam eap_gpsk_state(data, FAILURE); 657189251Ssam wpabuf_free(resp); 658189251Ssam return NULL; 659189251Ssam } 660189251Ssam 661189251Ssam return resp; 662189251Ssam} 663189251Ssam 664189251Ssam 665189251Ssamstatic struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, 666189251Ssam struct eap_method_ret *ret, 667189251Ssam const struct wpabuf *reqData) 668189251Ssam{ 669189251Ssam struct eap_gpsk_data *data = priv; 670189251Ssam struct wpabuf *resp; 671189251Ssam const u8 *pos; 672189251Ssam size_t len; 673189251Ssam 674189251Ssam pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len); 675189251Ssam if (pos == NULL || len < 1) { 676189251Ssam ret->ignore = TRUE; 677189251Ssam return NULL; 678189251Ssam } 679189251Ssam 680189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos); 681189251Ssam 682189251Ssam ret->ignore = FALSE; 683189251Ssam ret->methodState = METHOD_MAY_CONT; 684189251Ssam ret->decision = DECISION_FAIL; 685189251Ssam ret->allowNotifications = FALSE; 686189251Ssam 687189251Ssam switch (*pos) { 688189251Ssam case EAP_GPSK_OPCODE_GPSK_1: 689189251Ssam resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData, 690189251Ssam pos + 1, len - 1); 691189251Ssam break; 692189251Ssam case EAP_GPSK_OPCODE_GPSK_3: 693189251Ssam resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData, 694189251Ssam pos + 1, len - 1); 695189251Ssam break; 696189251Ssam default: 697189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with " 698189251Ssam "unknown opcode %d", *pos); 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 722189251Ssam key = os_malloc(EAP_MSK_LEN); 723189251Ssam if (key == NULL) 724189251Ssam return NULL; 725189251Ssam os_memcpy(key, data->msk, EAP_MSK_LEN); 726189251Ssam *len = EAP_MSK_LEN; 727189251Ssam 728189251Ssam return key; 729189251Ssam} 730189251Ssam 731189251Ssam 732189251Ssamstatic u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 733189251Ssam{ 734189251Ssam struct eap_gpsk_data *data = priv; 735189251Ssam u8 *key; 736189251Ssam 737189251Ssam if (data->state != SUCCESS) 738189251Ssam return NULL; 739189251Ssam 740189251Ssam key = os_malloc(EAP_EMSK_LEN); 741189251Ssam if (key == NULL) 742189251Ssam return NULL; 743189251Ssam os_memcpy(key, data->emsk, EAP_EMSK_LEN); 744189251Ssam *len = EAP_EMSK_LEN; 745189251Ssam 746189251Ssam return key; 747189251Ssam} 748189251Ssam 749189251Ssam 750281806Srpaulostatic u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) 751281806Srpaulo{ 752281806Srpaulo struct eap_gpsk_data *data = priv; 753281806Srpaulo u8 *sid; 754281806Srpaulo 755281806Srpaulo if (data->state != SUCCESS) 756281806Srpaulo return NULL; 757281806Srpaulo 758281806Srpaulo sid = os_malloc(data->id_len); 759281806Srpaulo if (sid == NULL) 760281806Srpaulo return NULL; 761281806Srpaulo os_memcpy(sid, data->session_id, data->id_len); 762281806Srpaulo *len = data->id_len; 763281806Srpaulo 764281806Srpaulo return sid; 765281806Srpaulo} 766281806Srpaulo 767281806Srpaulo 768189251Ssamint eap_peer_gpsk_register(void) 769189251Ssam{ 770189251Ssam struct eap_method *eap; 771189251Ssam int ret; 772189251Ssam 773189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 774189251Ssam EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); 775189251Ssam if (eap == NULL) 776189251Ssam return -1; 777189251Ssam 778189251Ssam eap->init = eap_gpsk_init; 779189251Ssam eap->deinit = eap_gpsk_deinit; 780189251Ssam eap->process = eap_gpsk_process; 781189251Ssam eap->isKeyAvailable = eap_gpsk_isKeyAvailable; 782189251Ssam eap->getKey = eap_gpsk_getKey; 783189251Ssam eap->get_emsk = eap_gpsk_get_emsk; 784281806Srpaulo eap->getSessionId = eap_gpsk_get_session_id; 785189251Ssam 786189251Ssam ret = eap_peer_method_register(eap); 787189251Ssam if (ret) 788189251Ssam eap_peer_method_free(eap); 789189251Ssam return ret; 790189251Ssam} 791