1189251Ssam/* 2209158Srpaulo * EAP peer method: EAP-GPSK (RFC 5433) 3189251Ssam * Copyright (c) 2006-2008, 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; 26189251Ssam u8 session_id; 27189251Ssam int session_id_set; 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; 36189251Ssam}; 37189251Ssam 38189251Ssam 39189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, 40189251Ssam u8 identifier, 41189251Ssam const u8 *csuite_list, 42189251Ssam size_t csuite_list_len); 43189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, 44189251Ssam u8 identifier); 45189251Ssam 46189251Ssam 47189251Ssam#ifndef CONFIG_NO_STDOUT_DEBUG 48189251Ssamstatic const char * eap_gpsk_state_txt(int state) 49189251Ssam{ 50189251Ssam switch (state) { 51189251Ssam case GPSK_1: 52189251Ssam return "GPSK-1"; 53189251Ssam case GPSK_3: 54189251Ssam return "GPSK-3"; 55189251Ssam case SUCCESS: 56189251Ssam return "SUCCESS"; 57189251Ssam case FAILURE: 58189251Ssam return "FAILURE"; 59189251Ssam default: 60189251Ssam return "?"; 61189251Ssam } 62189251Ssam} 63189251Ssam#endif /* CONFIG_NO_STDOUT_DEBUG */ 64189251Ssam 65189251Ssam 66189251Ssamstatic void eap_gpsk_state(struct eap_gpsk_data *data, int state) 67189251Ssam{ 68189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s", 69189251Ssam eap_gpsk_state_txt(data->state), 70189251Ssam eap_gpsk_state_txt(state)); 71189251Ssam data->state = state; 72189251Ssam} 73189251Ssam 74189251Ssam 75189251Ssamstatic void eap_gpsk_deinit(struct eap_sm *sm, void *priv); 76189251Ssam 77189251Ssam 78189251Ssamstatic void * eap_gpsk_init(struct eap_sm *sm) 79189251Ssam{ 80189251Ssam struct eap_gpsk_data *data; 81189251Ssam const u8 *identity, *password; 82189251Ssam size_t identity_len, password_len; 83189251Ssam 84189251Ssam password = eap_get_config_password(sm, &password_len); 85189251Ssam if (password == NULL) { 86189251Ssam wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured"); 87189251Ssam return NULL; 88189251Ssam } 89189251Ssam 90189251Ssam data = os_zalloc(sizeof(*data)); 91189251Ssam if (data == NULL) 92189251Ssam return NULL; 93189251Ssam data->state = GPSK_1; 94189251Ssam 95189251Ssam identity = eap_get_config_identity(sm, &identity_len); 96189251Ssam if (identity) { 97189251Ssam data->id_peer = os_malloc(identity_len); 98189251Ssam if (data->id_peer == NULL) { 99189251Ssam eap_gpsk_deinit(sm, data); 100189251Ssam return NULL; 101189251Ssam } 102189251Ssam os_memcpy(data->id_peer, identity, identity_len); 103189251Ssam data->id_peer_len = identity_len; 104189251Ssam } 105189251Ssam 106189251Ssam data->psk = os_malloc(password_len); 107189251Ssam if (data->psk == NULL) { 108189251Ssam eap_gpsk_deinit(sm, data); 109189251Ssam return NULL; 110189251Ssam } 111189251Ssam os_memcpy(data->psk, password, password_len); 112189251Ssam data->psk_len = password_len; 113189251Ssam 114189251Ssam return data; 115189251Ssam} 116189251Ssam 117189251Ssam 118189251Ssamstatic void eap_gpsk_deinit(struct eap_sm *sm, void *priv) 119189251Ssam{ 120189251Ssam struct eap_gpsk_data *data = priv; 121189251Ssam os_free(data->id_server); 122189251Ssam os_free(data->id_peer); 123189251Ssam os_free(data->psk); 124189251Ssam os_free(data); 125189251Ssam} 126189251Ssam 127189251Ssam 128189251Ssamstatic const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data, 129189251Ssam const u8 *pos, const u8 *end) 130189251Ssam{ 131189251Ssam u16 alen; 132189251Ssam 133189251Ssam if (end - pos < 2) { 134189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); 135189251Ssam return NULL; 136189251Ssam } 137189251Ssam alen = WPA_GET_BE16(pos); 138189251Ssam pos += 2; 139189251Ssam if (end - pos < alen) { 140189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow"); 141189251Ssam return NULL; 142189251Ssam } 143189251Ssam os_free(data->id_server); 144189251Ssam data->id_server = os_malloc(alen); 145189251Ssam if (data->id_server == NULL) { 146189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server"); 147189251Ssam return NULL; 148189251Ssam } 149189251Ssam os_memcpy(data->id_server, pos, alen); 150189251Ssam data->id_server_len = alen; 151189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server", 152189251Ssam data->id_server, data->id_server_len); 153189251Ssam pos += alen; 154189251Ssam 155189251Ssam return pos; 156189251Ssam} 157189251Ssam 158189251Ssam 159189251Ssamstatic const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data, 160189251Ssam const u8 *pos, const u8 *end) 161189251Ssam{ 162189251Ssam if (pos == NULL) 163189251Ssam return NULL; 164189251Ssam 165189251Ssam if (end - pos < EAP_GPSK_RAND_LEN) { 166189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow"); 167189251Ssam return NULL; 168189251Ssam } 169189251Ssam os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN); 170189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server", 171189251Ssam data->rand_server, EAP_GPSK_RAND_LEN); 172189251Ssam pos += EAP_GPSK_RAND_LEN; 173189251Ssam 174189251Ssam return pos; 175189251Ssam} 176189251Ssam 177189251Ssam 178189251Ssamstatic int eap_gpsk_select_csuite(struct eap_sm *sm, 179189251Ssam struct eap_gpsk_data *data, 180189251Ssam const u8 *csuite_list, 181189251Ssam size_t csuite_list_len) 182189251Ssam{ 183189251Ssam struct eap_gpsk_csuite *csuite; 184189251Ssam int i, count; 185189251Ssam 186189251Ssam count = csuite_list_len / sizeof(struct eap_gpsk_csuite); 187189251Ssam data->vendor = EAP_GPSK_VENDOR_IETF; 188189251Ssam data->specifier = EAP_GPSK_CIPHER_RESERVED; 189189251Ssam csuite = (struct eap_gpsk_csuite *) csuite_list; 190189251Ssam for (i = 0; i < count; i++) { 191189251Ssam int vendor, specifier; 192189251Ssam vendor = WPA_GET_BE32(csuite->vendor); 193189251Ssam specifier = WPA_GET_BE16(csuite->specifier); 194189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d", 195189251Ssam i, vendor, specifier); 196189251Ssam if (data->vendor == EAP_GPSK_VENDOR_IETF && 197189251Ssam data->specifier == EAP_GPSK_CIPHER_RESERVED && 198189251Ssam eap_gpsk_supported_ciphersuite(vendor, specifier)) { 199189251Ssam data->vendor = vendor; 200189251Ssam data->specifier = specifier; 201189251Ssam } 202189251Ssam csuite++; 203189251Ssam } 204189251Ssam if (data->vendor == EAP_GPSK_VENDOR_IETF && 205189251Ssam data->specifier == EAP_GPSK_CIPHER_RESERVED) { 206189251Ssam wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported " 207189251Ssam "ciphersuite found"); 208189251Ssam return -1; 209189251Ssam } 210189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d", 211189251Ssam data->vendor, data->specifier); 212189251Ssam 213189251Ssam return 0; 214189251Ssam} 215189251Ssam 216189251Ssam 217189251Ssamstatic const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm, 218189251Ssam struct eap_gpsk_data *data, 219189251Ssam const u8 **list, 220189251Ssam size_t *list_len, 221189251Ssam const u8 *pos, const u8 *end) 222189251Ssam{ 223189251Ssam if (pos == NULL) 224189251Ssam return NULL; 225189251Ssam 226189251Ssam if (end - pos < 2) { 227189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet"); 228189251Ssam return NULL; 229189251Ssam } 230189251Ssam *list_len = WPA_GET_BE16(pos); 231189251Ssam pos += 2; 232189251Ssam if (end - pos < (int) *list_len) { 233189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow"); 234189251Ssam return NULL; 235189251Ssam } 236189251Ssam if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) { 237189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu", 238189251Ssam (unsigned long) *list_len); 239189251Ssam return NULL; 240189251Ssam } 241189251Ssam *list = pos; 242189251Ssam pos += *list_len; 243189251Ssam 244189251Ssam if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0) 245189251Ssam return NULL; 246189251Ssam 247189251Ssam return pos; 248189251Ssam} 249189251Ssam 250189251Ssam 251189251Ssamstatic struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm, 252189251Ssam struct eap_gpsk_data *data, 253189251Ssam struct eap_method_ret *ret, 254189251Ssam const struct wpabuf *reqData, 255189251Ssam const u8 *payload, 256189251Ssam size_t payload_len) 257189251Ssam{ 258189251Ssam size_t csuite_list_len; 259189251Ssam const u8 *csuite_list, *pos, *end; 260189251Ssam struct wpabuf *resp; 261189251Ssam 262189251Ssam if (data->state != GPSK_1) { 263189251Ssam ret->ignore = TRUE; 264189251Ssam return NULL; 265189251Ssam } 266189251Ssam 267189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1"); 268189251Ssam 269189251Ssam end = payload + payload_len; 270189251Ssam 271189251Ssam pos = eap_gpsk_process_id_server(data, payload, end); 272189251Ssam pos = eap_gpsk_process_rand_server(data, pos, end); 273189251Ssam pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list, 274189251Ssam &csuite_list_len, pos, end); 275189251Ssam if (pos == NULL) { 276189251Ssam eap_gpsk_state(data, FAILURE); 277189251Ssam return NULL; 278189251Ssam } 279189251Ssam 280189251Ssam resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData), 281189251Ssam csuite_list, csuite_list_len); 282189251Ssam if (resp == NULL) 283189251Ssam return NULL; 284189251Ssam 285189251Ssam eap_gpsk_state(data, GPSK_3); 286189251Ssam 287189251Ssam return resp; 288189251Ssam} 289189251Ssam 290189251Ssam 291189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data, 292189251Ssam u8 identifier, 293189251Ssam const u8 *csuite_list, 294189251Ssam size_t csuite_list_len) 295189251Ssam{ 296189251Ssam struct wpabuf *resp; 297189251Ssam size_t len, miclen; 298189251Ssam u8 *rpos, *start; 299189251Ssam struct eap_gpsk_csuite *csuite; 300189251Ssam 301189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2"); 302189251Ssam 303189251Ssam miclen = eap_gpsk_mic_len(data->vendor, data->specifier); 304189251Ssam len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len + 305189251Ssam 2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len + 306189251Ssam sizeof(struct eap_gpsk_csuite) + 2 + miclen; 307189251Ssam 308189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len, 309189251Ssam EAP_CODE_RESPONSE, identifier); 310189251Ssam if (resp == NULL) 311189251Ssam return NULL; 312189251Ssam 313189251Ssam wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2); 314189251Ssam start = wpabuf_put(resp, 0); 315189251Ssam 316189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", 317189251Ssam data->id_peer, data->id_peer_len); 318189251Ssam wpabuf_put_be16(resp, data->id_peer_len); 319189251Ssam wpabuf_put_data(resp, data->id_peer, data->id_peer_len); 320189251Ssam 321189251Ssam wpabuf_put_be16(resp, data->id_server_len); 322189251Ssam wpabuf_put_data(resp, data->id_server, data->id_server_len); 323189251Ssam 324252726Srpaulo if (random_get_bytes(data->rand_peer, EAP_GPSK_RAND_LEN)) { 325189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data " 326189251Ssam "for RAND_Peer"); 327189251Ssam eap_gpsk_state(data, FAILURE); 328189251Ssam wpabuf_free(resp); 329189251Ssam return NULL; 330189251Ssam } 331189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer", 332189251Ssam data->rand_peer, EAP_GPSK_RAND_LEN); 333189251Ssam wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN); 334189251Ssam wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN); 335189251Ssam 336189251Ssam wpabuf_put_be16(resp, csuite_list_len); 337189251Ssam wpabuf_put_data(resp, csuite_list, csuite_list_len); 338189251Ssam 339189251Ssam csuite = wpabuf_put(resp, sizeof(*csuite)); 340189251Ssam WPA_PUT_BE32(csuite->vendor, data->vendor); 341189251Ssam WPA_PUT_BE16(csuite->specifier, data->specifier); 342189251Ssam 343189251Ssam if (eap_gpsk_derive_keys(data->psk, data->psk_len, 344189251Ssam data->vendor, data->specifier, 345189251Ssam data->rand_peer, data->rand_server, 346189251Ssam data->id_peer, data->id_peer_len, 347189251Ssam data->id_server, data->id_server_len, 348189251Ssam data->msk, data->emsk, 349189251Ssam data->sk, &data->sk_len, 350189251Ssam data->pk, &data->pk_len) < 0) { 351189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys"); 352189251Ssam eap_gpsk_state(data, FAILURE); 353189251Ssam wpabuf_free(resp); 354189251Ssam return NULL; 355189251Ssam } 356189251Ssam 357189251Ssam /* No PD_Payload_1 */ 358189251Ssam wpabuf_put_be16(resp, 0); 359189251Ssam 360189251Ssam rpos = wpabuf_put(resp, miclen); 361189251Ssam if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 362189251Ssam data->specifier, start, rpos - start, rpos) < 363189251Ssam 0) { 364189251Ssam eap_gpsk_state(data, FAILURE); 365189251Ssam wpabuf_free(resp); 366189251Ssam return NULL; 367189251Ssam } 368189251Ssam 369189251Ssam return resp; 370189251Ssam} 371189251Ssam 372189251Ssam 373189251Ssamstatic const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data, 374189251Ssam const u8 *pos, const u8 *end) 375189251Ssam{ 376189251Ssam if (end - pos < EAP_GPSK_RAND_LEN) { 377189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 378189251Ssam "RAND_Peer"); 379189251Ssam return NULL; 380189251Ssam } 381189251Ssam if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) { 382189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and " 383189251Ssam "GPSK-3 did not match"); 384189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2", 385189251Ssam data->rand_peer, EAP_GPSK_RAND_LEN); 386189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3", 387189251Ssam pos, EAP_GPSK_RAND_LEN); 388189251Ssam return NULL; 389189251Ssam } 390189251Ssam pos += EAP_GPSK_RAND_LEN; 391189251Ssam 392189251Ssam if (end - pos < EAP_GPSK_RAND_LEN) { 393189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 394189251Ssam "RAND_Server"); 395189251Ssam return NULL; 396189251Ssam } 397189251Ssam if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) { 398189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " 399189251Ssam "GPSK-3 did not match"); 400189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", 401189251Ssam data->rand_server, EAP_GPSK_RAND_LEN); 402189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3", 403189251Ssam pos, EAP_GPSK_RAND_LEN); 404189251Ssam return NULL; 405189251Ssam } 406189251Ssam pos += EAP_GPSK_RAND_LEN; 407189251Ssam 408189251Ssam return pos; 409189251Ssam} 410189251Ssam 411189251Ssam 412189251Ssamstatic const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data, 413189251Ssam const u8 *pos, const u8 *end) 414189251Ssam{ 415189251Ssam size_t len; 416189251Ssam 417189251Ssam if (pos == NULL) 418189251Ssam return NULL; 419189251Ssam 420189251Ssam if (end - pos < (int) 2) { 421189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 422189251Ssam "length(ID_Server)"); 423189251Ssam return NULL; 424189251Ssam } 425189251Ssam 426189251Ssam len = WPA_GET_BE16(pos); 427189251Ssam pos += 2; 428189251Ssam 429189251Ssam if (end - pos < (int) len) { 430189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 431189251Ssam "ID_Server"); 432189251Ssam return NULL; 433189251Ssam } 434189251Ssam 435189251Ssam if (len != data->id_server_len || 436189251Ssam os_memcmp(pos, data->id_server, len) != 0) { 437189251Ssam wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with " 438189251Ssam "the one used in GPSK-1"); 439189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1", 440189251Ssam data->id_server, data->id_server_len); 441189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3", 442189251Ssam pos, len); 443189251Ssam return NULL; 444189251Ssam } 445189251Ssam 446189251Ssam pos += len; 447189251Ssam 448189251Ssam return pos; 449189251Ssam} 450189251Ssam 451189251Ssam 452189251Ssamstatic const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data, 453189251Ssam const u8 *pos, const u8 *end) 454189251Ssam{ 455189251Ssam int vendor, specifier; 456189251Ssam const struct eap_gpsk_csuite *csuite; 457189251Ssam 458189251Ssam if (pos == NULL) 459189251Ssam return NULL; 460189251Ssam 461189251Ssam if (end - pos < (int) sizeof(*csuite)) { 462189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 463189251Ssam "CSuite_Sel"); 464189251Ssam return NULL; 465189251Ssam } 466189251Ssam csuite = (const struct eap_gpsk_csuite *) pos; 467189251Ssam vendor = WPA_GET_BE32(csuite->vendor); 468189251Ssam specifier = WPA_GET_BE16(csuite->specifier); 469189251Ssam pos += sizeof(*csuite); 470189251Ssam if (vendor != data->vendor || specifier != data->specifier) { 471189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not " 472189251Ssam "match with the one sent in GPSK-2 (%d:%d)", 473189251Ssam vendor, specifier, data->vendor, data->specifier); 474189251Ssam return NULL; 475189251Ssam } 476189251Ssam 477189251Ssam return pos; 478189251Ssam} 479189251Ssam 480189251Ssam 481189251Ssamstatic const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data, 482189251Ssam const u8 *pos, const u8 *end) 483189251Ssam{ 484189251Ssam u16 alen; 485189251Ssam 486189251Ssam if (pos == NULL) 487189251Ssam return NULL; 488189251Ssam 489189251Ssam if (end - pos < 2) { 490189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 491189251Ssam "PD_Payload_2 length"); 492189251Ssam return NULL; 493189251Ssam } 494189251Ssam alen = WPA_GET_BE16(pos); 495189251Ssam pos += 2; 496189251Ssam if (end - pos < alen) { 497189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for " 498189251Ssam "%d-octet PD_Payload_2", alen); 499189251Ssam return NULL; 500189251Ssam } 501189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen); 502189251Ssam pos += alen; 503189251Ssam 504189251Ssam return pos; 505189251Ssam} 506189251Ssam 507189251Ssam 508189251Ssamstatic const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data, 509189251Ssam const u8 *payload, 510189251Ssam const u8 *pos, const u8 *end) 511189251Ssam{ 512189251Ssam size_t miclen; 513189251Ssam u8 mic[EAP_GPSK_MAX_MIC_LEN]; 514189251Ssam 515189251Ssam if (pos == NULL) 516189251Ssam return NULL; 517189251Ssam 518189251Ssam miclen = eap_gpsk_mic_len(data->vendor, data->specifier); 519189251Ssam if (end - pos < (int) miclen) { 520189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " 521189251Ssam "(left=%lu miclen=%lu)", 522189251Ssam (unsigned long) (end - pos), 523189251Ssam (unsigned long) miclen); 524189251Ssam return NULL; 525189251Ssam } 526189251Ssam if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 527189251Ssam data->specifier, payload, pos - payload, mic) 528189251Ssam < 0) { 529189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); 530189251Ssam return NULL; 531189251Ssam } 532189251Ssam if (os_memcmp(mic, pos, miclen) != 0) { 533189251Ssam wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3"); 534189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); 535189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); 536189251Ssam return NULL; 537189251Ssam } 538189251Ssam pos += miclen; 539189251Ssam 540189251Ssam return pos; 541189251Ssam} 542189251Ssam 543189251Ssam 544189251Ssamstatic struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm, 545189251Ssam struct eap_gpsk_data *data, 546189251Ssam struct eap_method_ret *ret, 547189251Ssam const struct wpabuf *reqData, 548189251Ssam const u8 *payload, 549189251Ssam size_t payload_len) 550189251Ssam{ 551189251Ssam struct wpabuf *resp; 552189251Ssam const u8 *pos, *end; 553189251Ssam 554189251Ssam if (data->state != GPSK_3) { 555189251Ssam ret->ignore = TRUE; 556189251Ssam return NULL; 557189251Ssam } 558189251Ssam 559189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3"); 560189251Ssam 561189251Ssam end = payload + payload_len; 562189251Ssam 563189251Ssam pos = eap_gpsk_validate_rand(data, payload, end); 564189251Ssam pos = eap_gpsk_validate_id_server(data, pos, end); 565189251Ssam pos = eap_gpsk_validate_csuite(data, pos, end); 566189251Ssam pos = eap_gpsk_validate_pd_payload_2(data, pos, end); 567189251Ssam pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end); 568189251Ssam 569189251Ssam if (pos == NULL) { 570189251Ssam eap_gpsk_state(data, FAILURE); 571189251Ssam return NULL; 572189251Ssam } 573189251Ssam if (pos != end) { 574189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra " 575189251Ssam "data in the end of GPSK-2", 576189251Ssam (unsigned long) (end - pos)); 577189251Ssam } 578189251Ssam 579189251Ssam resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData)); 580189251Ssam if (resp == NULL) 581189251Ssam return NULL; 582189251Ssam 583189251Ssam eap_gpsk_state(data, SUCCESS); 584189251Ssam ret->methodState = METHOD_DONE; 585189251Ssam ret->decision = DECISION_UNCOND_SUCC; 586189251Ssam 587189251Ssam return resp; 588189251Ssam} 589189251Ssam 590189251Ssam 591189251Ssamstatic struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data, 592189251Ssam u8 identifier) 593189251Ssam{ 594189251Ssam struct wpabuf *resp; 595189251Ssam u8 *rpos, *start; 596189251Ssam size_t mlen; 597189251Ssam 598189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4"); 599189251Ssam 600189251Ssam mlen = eap_gpsk_mic_len(data->vendor, data->specifier); 601189251Ssam 602189251Ssam resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen, 603189251Ssam EAP_CODE_RESPONSE, identifier); 604189251Ssam if (resp == NULL) 605189251Ssam return NULL; 606189251Ssam 607189251Ssam wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4); 608189251Ssam start = wpabuf_put(resp, 0); 609189251Ssam 610189251Ssam /* No PD_Payload_3 */ 611189251Ssam wpabuf_put_be16(resp, 0); 612189251Ssam 613189251Ssam rpos = wpabuf_put(resp, mlen); 614189251Ssam if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, 615189251Ssam data->specifier, start, rpos - start, rpos) < 616189251Ssam 0) { 617189251Ssam eap_gpsk_state(data, FAILURE); 618189251Ssam wpabuf_free(resp); 619189251Ssam return NULL; 620189251Ssam } 621189251Ssam 622189251Ssam return resp; 623189251Ssam} 624189251Ssam 625189251Ssam 626189251Ssamstatic struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv, 627189251Ssam struct eap_method_ret *ret, 628189251Ssam const struct wpabuf *reqData) 629189251Ssam{ 630189251Ssam struct eap_gpsk_data *data = priv; 631189251Ssam struct wpabuf *resp; 632189251Ssam const u8 *pos; 633189251Ssam size_t len; 634189251Ssam 635189251Ssam pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len); 636189251Ssam if (pos == NULL || len < 1) { 637189251Ssam ret->ignore = TRUE; 638189251Ssam return NULL; 639189251Ssam } 640189251Ssam 641189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos); 642189251Ssam 643189251Ssam ret->ignore = FALSE; 644189251Ssam ret->methodState = METHOD_MAY_CONT; 645189251Ssam ret->decision = DECISION_FAIL; 646189251Ssam ret->allowNotifications = FALSE; 647189251Ssam 648189251Ssam switch (*pos) { 649189251Ssam case EAP_GPSK_OPCODE_GPSK_1: 650189251Ssam resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData, 651189251Ssam pos + 1, len - 1); 652189251Ssam break; 653189251Ssam case EAP_GPSK_OPCODE_GPSK_3: 654189251Ssam resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData, 655189251Ssam pos + 1, len - 1); 656189251Ssam break; 657189251Ssam default: 658189251Ssam wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with " 659189251Ssam "unknown opcode %d", *pos); 660189251Ssam ret->ignore = TRUE; 661189251Ssam return NULL; 662189251Ssam } 663189251Ssam 664189251Ssam return resp; 665189251Ssam} 666189251Ssam 667189251Ssam 668189251Ssamstatic Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv) 669189251Ssam{ 670189251Ssam struct eap_gpsk_data *data = priv; 671189251Ssam return data->state == SUCCESS; 672189251Ssam} 673189251Ssam 674189251Ssam 675189251Ssamstatic u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) 676189251Ssam{ 677189251Ssam struct eap_gpsk_data *data = priv; 678189251Ssam u8 *key; 679189251Ssam 680189251Ssam if (data->state != SUCCESS) 681189251Ssam return NULL; 682189251Ssam 683189251Ssam key = os_malloc(EAP_MSK_LEN); 684189251Ssam if (key == NULL) 685189251Ssam return NULL; 686189251Ssam os_memcpy(key, data->msk, EAP_MSK_LEN); 687189251Ssam *len = EAP_MSK_LEN; 688189251Ssam 689189251Ssam return key; 690189251Ssam} 691189251Ssam 692189251Ssam 693189251Ssamstatic u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 694189251Ssam{ 695189251Ssam struct eap_gpsk_data *data = priv; 696189251Ssam u8 *key; 697189251Ssam 698189251Ssam if (data->state != SUCCESS) 699189251Ssam return NULL; 700189251Ssam 701189251Ssam key = os_malloc(EAP_EMSK_LEN); 702189251Ssam if (key == NULL) 703189251Ssam return NULL; 704189251Ssam os_memcpy(key, data->emsk, EAP_EMSK_LEN); 705189251Ssam *len = EAP_EMSK_LEN; 706189251Ssam 707189251Ssam return key; 708189251Ssam} 709189251Ssam 710189251Ssam 711189251Ssamint eap_peer_gpsk_register(void) 712189251Ssam{ 713189251Ssam struct eap_method *eap; 714189251Ssam int ret; 715189251Ssam 716189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 717189251Ssam EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); 718189251Ssam if (eap == NULL) 719189251Ssam return -1; 720189251Ssam 721189251Ssam eap->init = eap_gpsk_init; 722189251Ssam eap->deinit = eap_gpsk_deinit; 723189251Ssam eap->process = eap_gpsk_process; 724189251Ssam eap->isKeyAvailable = eap_gpsk_isKeyAvailable; 725189251Ssam eap->getKey = eap_gpsk_getKey; 726189251Ssam eap->get_emsk = eap_gpsk_get_emsk; 727189251Ssam 728189251Ssam ret = eap_peer_method_register(eap); 729189251Ssam if (ret) 730189251Ssam eap_peer_method_free(eap); 731189251Ssam return ret; 732189251Ssam} 733