1189251Ssam/* 2252726Srpaulo * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448) 3252726Srpaulo * Copyright (c) 2004-2012, 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" 12189251Ssam#include "pcsc_funcs.h" 13214734Srpaulo#include "crypto/crypto.h" 14214734Srpaulo#include "crypto/sha1.h" 15214734Srpaulo#include "crypto/sha256.h" 16214734Srpaulo#include "crypto/milenage.h" 17189251Ssam#include "eap_common/eap_sim_common.h" 18214734Srpaulo#include "eap_config.h" 19214734Srpaulo#include "eap_i.h" 20189251Ssam 21189251Ssam 22189251Ssamstruct eap_aka_data { 23189251Ssam u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN]; 24189251Ssam size_t res_len; 25189251Ssam u8 nonce_s[EAP_SIM_NONCE_S_LEN]; 26189251Ssam u8 mk[EAP_SIM_MK_LEN]; 27189251Ssam u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN]; 28189251Ssam u8 k_encr[EAP_SIM_K_ENCR_LEN]; 29189251Ssam u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */ 30189251Ssam u8 msk[EAP_SIM_KEYING_DATA_LEN]; 31189251Ssam u8 emsk[EAP_EMSK_LEN]; 32189251Ssam u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN]; 33189251Ssam u8 auts[EAP_AKA_AUTS_LEN]; 34189251Ssam 35189251Ssam int num_id_req, num_notification; 36189251Ssam u8 *pseudonym; 37189251Ssam size_t pseudonym_len; 38189251Ssam u8 *reauth_id; 39189251Ssam size_t reauth_id_len; 40189251Ssam int reauth; 41189251Ssam unsigned int counter, counter_too_small; 42189251Ssam u8 *last_eap_identity; 43189251Ssam size_t last_eap_identity_len; 44189251Ssam enum { 45189251Ssam CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE 46189251Ssam } state; 47189251Ssam 48189251Ssam struct wpabuf *id_msgs; 49189251Ssam int prev_id; 50189251Ssam int result_ind, use_result_ind; 51189251Ssam u8 eap_method; 52189251Ssam u8 *network_name; 53189251Ssam size_t network_name_len; 54189251Ssam u16 kdf; 55189251Ssam int kdf_negotiation; 56189251Ssam}; 57189251Ssam 58189251Ssam 59189251Ssam#ifndef CONFIG_NO_STDOUT_DEBUG 60189251Ssamstatic const char * eap_aka_state_txt(int state) 61189251Ssam{ 62189251Ssam switch (state) { 63189251Ssam case CONTINUE: 64189251Ssam return "CONTINUE"; 65189251Ssam case RESULT_SUCCESS: 66189251Ssam return "RESULT_SUCCESS"; 67189251Ssam case RESULT_FAILURE: 68189251Ssam return "RESULT_FAILURE"; 69189251Ssam case SUCCESS: 70189251Ssam return "SUCCESS"; 71189251Ssam case FAILURE: 72189251Ssam return "FAILURE"; 73189251Ssam default: 74189251Ssam return "?"; 75189251Ssam } 76189251Ssam} 77189251Ssam#endif /* CONFIG_NO_STDOUT_DEBUG */ 78189251Ssam 79189251Ssam 80189251Ssamstatic void eap_aka_state(struct eap_aka_data *data, int state) 81189251Ssam{ 82189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", 83189251Ssam eap_aka_state_txt(data->state), 84189251Ssam eap_aka_state_txt(state)); 85189251Ssam data->state = state; 86189251Ssam} 87189251Ssam 88189251Ssam 89189251Ssamstatic void * eap_aka_init(struct eap_sm *sm) 90189251Ssam{ 91189251Ssam struct eap_aka_data *data; 92189251Ssam const char *phase1 = eap_get_config_phase1(sm); 93252726Srpaulo struct eap_peer_config *config = eap_get_config(sm); 94189251Ssam 95189251Ssam data = os_zalloc(sizeof(*data)); 96189251Ssam if (data == NULL) 97189251Ssam return NULL; 98189251Ssam 99189251Ssam data->eap_method = EAP_TYPE_AKA; 100189251Ssam 101189251Ssam eap_aka_state(data, CONTINUE); 102189251Ssam data->prev_id = -1; 103189251Ssam 104189251Ssam data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; 105189251Ssam 106252726Srpaulo if (config && config->anonymous_identity) { 107252726Srpaulo data->pseudonym = os_malloc(config->anonymous_identity_len); 108252726Srpaulo if (data->pseudonym) { 109252726Srpaulo os_memcpy(data->pseudonym, config->anonymous_identity, 110252726Srpaulo config->anonymous_identity_len); 111252726Srpaulo data->pseudonym_len = config->anonymous_identity_len; 112252726Srpaulo } 113252726Srpaulo } 114252726Srpaulo 115189251Ssam return data; 116189251Ssam} 117189251Ssam 118189251Ssam 119189251Ssam#ifdef EAP_AKA_PRIME 120189251Ssamstatic void * eap_aka_prime_init(struct eap_sm *sm) 121189251Ssam{ 122189251Ssam struct eap_aka_data *data = eap_aka_init(sm); 123189251Ssam if (data == NULL) 124189251Ssam return NULL; 125189251Ssam data->eap_method = EAP_TYPE_AKA_PRIME; 126189251Ssam return data; 127189251Ssam} 128189251Ssam#endif /* EAP_AKA_PRIME */ 129189251Ssam 130189251Ssam 131189251Ssamstatic void eap_aka_deinit(struct eap_sm *sm, void *priv) 132189251Ssam{ 133189251Ssam struct eap_aka_data *data = priv; 134189251Ssam if (data) { 135189251Ssam os_free(data->pseudonym); 136189251Ssam os_free(data->reauth_id); 137189251Ssam os_free(data->last_eap_identity); 138189251Ssam wpabuf_free(data->id_msgs); 139189251Ssam os_free(data->network_name); 140189251Ssam os_free(data); 141189251Ssam } 142189251Ssam} 143189251Ssam 144189251Ssam 145189251Ssamstatic int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data) 146189251Ssam{ 147189251Ssam struct eap_peer_config *conf; 148189251Ssam 149189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm"); 150189251Ssam 151189251Ssam conf = eap_get_config(sm); 152189251Ssam if (conf == NULL) 153189251Ssam return -1; 154189251Ssam if (conf->pcsc) { 155189251Ssam return scard_umts_auth(sm->scard_ctx, data->rand, 156189251Ssam data->autn, data->res, &data->res_len, 157189251Ssam data->ik, data->ck, data->auts); 158189251Ssam } 159189251Ssam 160189251Ssam#ifdef CONFIG_USIM_SIMULATOR 161189251Ssam if (conf->password) { 162189251Ssam u8 opc[16], k[16], sqn[6]; 163189251Ssam const char *pos; 164189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage " 165189251Ssam "implementation for UMTS authentication"); 166189251Ssam if (conf->password_len < 78) { 167189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage " 168189251Ssam "password"); 169189251Ssam return -1; 170189251Ssam } 171189251Ssam pos = (const char *) conf->password; 172189251Ssam if (hexstr2bin(pos, k, 16)) 173189251Ssam return -1; 174189251Ssam pos += 32; 175189251Ssam if (*pos != ':') 176189251Ssam return -1; 177189251Ssam pos++; 178189251Ssam 179189251Ssam if (hexstr2bin(pos, opc, 16)) 180189251Ssam return -1; 181189251Ssam pos += 32; 182189251Ssam if (*pos != ':') 183189251Ssam return -1; 184189251Ssam pos++; 185189251Ssam 186189251Ssam if (hexstr2bin(pos, sqn, 6)) 187189251Ssam return -1; 188189251Ssam 189189251Ssam return milenage_check(opc, k, sqn, data->rand, data->autn, 190189251Ssam data->ik, data->ck, 191189251Ssam data->res, &data->res_len, data->auts); 192189251Ssam } 193189251Ssam#endif /* CONFIG_USIM_SIMULATOR */ 194189251Ssam 195189251Ssam#ifdef CONFIG_USIM_HARDCODED 196189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for " 197189251Ssam "testing"); 198189251Ssam 199189251Ssam /* These hardcoded Kc and SRES values are used for testing. 200189251Ssam * Could consider making them configurable. */ 201189251Ssam os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN); 202189251Ssam data->res_len = EAP_AKA_RES_MAX_LEN; 203189251Ssam os_memset(data->ik, '3', EAP_AKA_IK_LEN); 204189251Ssam os_memset(data->ck, '4', EAP_AKA_CK_LEN); 205189251Ssam { 206189251Ssam u8 autn[EAP_AKA_AUTN_LEN]; 207189251Ssam os_memset(autn, '1', EAP_AKA_AUTN_LEN); 208189251Ssam if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) { 209189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match " 210189251Ssam "with expected value"); 211189251Ssam return -1; 212189251Ssam } 213189251Ssam } 214189251Ssam#if 0 215189251Ssam { 216189251Ssam static int test_resync = 1; 217189251Ssam if (test_resync) { 218189251Ssam /* Test Resynchronization */ 219189251Ssam test_resync = 0; 220189251Ssam return -2; 221189251Ssam } 222189251Ssam } 223189251Ssam#endif 224189251Ssam return 0; 225189251Ssam 226189251Ssam#else /* CONFIG_USIM_HARDCODED */ 227189251Ssam 228189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith " 229189251Ssam "enabled"); 230189251Ssam return -1; 231189251Ssam 232189251Ssam#endif /* CONFIG_USIM_HARDCODED */ 233189251Ssam} 234189251Ssam 235189251Ssam 236189251Ssam#define CLEAR_PSEUDONYM 0x01 237189251Ssam#define CLEAR_REAUTH_ID 0x02 238189251Ssam#define CLEAR_EAP_ID 0x04 239189251Ssam 240252726Srpaulostatic void eap_aka_clear_identities(struct eap_sm *sm, 241252726Srpaulo struct eap_aka_data *data, int id) 242189251Ssam{ 243252726Srpaulo if ((id & CLEAR_PSEUDONYM) && data->pseudonym) { 244252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym"); 245189251Ssam os_free(data->pseudonym); 246189251Ssam data->pseudonym = NULL; 247189251Ssam data->pseudonym_len = 0; 248252726Srpaulo eap_set_anon_id(sm, NULL, 0); 249189251Ssam } 250252726Srpaulo if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { 251252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); 252189251Ssam os_free(data->reauth_id); 253189251Ssam data->reauth_id = NULL; 254189251Ssam data->reauth_id_len = 0; 255189251Ssam } 256252726Srpaulo if ((id & CLEAR_EAP_ID) && data->last_eap_identity) { 257252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id"); 258189251Ssam os_free(data->last_eap_identity); 259189251Ssam data->last_eap_identity = NULL; 260189251Ssam data->last_eap_identity_len = 0; 261189251Ssam } 262189251Ssam} 263189251Ssam 264189251Ssam 265252726Srpaulostatic int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, 266189251Ssam struct eap_sim_attrs *attr) 267189251Ssam{ 268189251Ssam if (attr->next_pseudonym) { 269252726Srpaulo const u8 *identity = NULL; 270252726Srpaulo size_t identity_len = 0; 271252726Srpaulo const u8 *realm = NULL; 272252726Srpaulo size_t realm_len = 0; 273252726Srpaulo 274252726Srpaulo wpa_hexdump_ascii(MSG_DEBUG, 275252726Srpaulo "EAP-AKA: (encr) AT_NEXT_PSEUDONYM", 276252726Srpaulo attr->next_pseudonym, 277252726Srpaulo attr->next_pseudonym_len); 278189251Ssam os_free(data->pseudonym); 279252726Srpaulo /* Look for the realm of the permanent identity */ 280252726Srpaulo identity = eap_get_config_identity(sm, &identity_len); 281252726Srpaulo if (identity) { 282252726Srpaulo for (realm = identity, realm_len = identity_len; 283252726Srpaulo realm_len > 0; realm_len--, realm++) { 284252726Srpaulo if (*realm == '@') 285252726Srpaulo break; 286252726Srpaulo } 287252726Srpaulo } 288252726Srpaulo data->pseudonym = os_malloc(attr->next_pseudonym_len + 289252726Srpaulo realm_len); 290189251Ssam if (data->pseudonym == NULL) { 291189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " 292189251Ssam "next pseudonym"); 293252726Srpaulo data->pseudonym_len = 0; 294189251Ssam return -1; 295189251Ssam } 296189251Ssam os_memcpy(data->pseudonym, attr->next_pseudonym, 297189251Ssam attr->next_pseudonym_len); 298252726Srpaulo if (realm_len) { 299252726Srpaulo os_memcpy(data->pseudonym + attr->next_pseudonym_len, 300252726Srpaulo realm, realm_len); 301252726Srpaulo } 302252726Srpaulo data->pseudonym_len = attr->next_pseudonym_len + realm_len; 303252726Srpaulo eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); 304189251Ssam } 305189251Ssam 306189251Ssam if (attr->next_reauth_id) { 307189251Ssam os_free(data->reauth_id); 308189251Ssam data->reauth_id = os_malloc(attr->next_reauth_id_len); 309189251Ssam if (data->reauth_id == NULL) { 310189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " 311189251Ssam "next reauth_id"); 312252726Srpaulo data->reauth_id_len = 0; 313189251Ssam return -1; 314189251Ssam } 315189251Ssam os_memcpy(data->reauth_id, attr->next_reauth_id, 316189251Ssam attr->next_reauth_id_len); 317189251Ssam data->reauth_id_len = attr->next_reauth_id_len; 318189251Ssam wpa_hexdump_ascii(MSG_DEBUG, 319189251Ssam "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", 320189251Ssam data->reauth_id, 321189251Ssam data->reauth_id_len); 322189251Ssam } 323189251Ssam 324189251Ssam return 0; 325189251Ssam} 326189251Ssam 327189251Ssam 328189251Ssamstatic int eap_aka_add_id_msg(struct eap_aka_data *data, 329189251Ssam const struct wpabuf *msg) 330189251Ssam{ 331189251Ssam if (msg == NULL) 332189251Ssam return -1; 333189251Ssam 334189251Ssam if (data->id_msgs == NULL) { 335189251Ssam data->id_msgs = wpabuf_dup(msg); 336189251Ssam return data->id_msgs == NULL ? -1 : 0; 337189251Ssam } 338189251Ssam 339189251Ssam if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0) 340189251Ssam return -1; 341189251Ssam wpabuf_put_buf(data->id_msgs, msg); 342189251Ssam 343189251Ssam return 0; 344189251Ssam} 345189251Ssam 346189251Ssam 347189251Ssamstatic void eap_aka_add_checkcode(struct eap_aka_data *data, 348189251Ssam struct eap_sim_msg *msg) 349189251Ssam{ 350189251Ssam const u8 *addr; 351189251Ssam size_t len; 352189251Ssam u8 hash[SHA256_MAC_LEN]; 353189251Ssam 354189251Ssam wpa_printf(MSG_DEBUG, " AT_CHECKCODE"); 355189251Ssam 356189251Ssam if (data->id_msgs == NULL) { 357189251Ssam /* 358189251Ssam * No EAP-AKA/Identity packets were exchanged - send empty 359189251Ssam * checkcode. 360189251Ssam */ 361189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0); 362189251Ssam return; 363189251Ssam } 364189251Ssam 365189251Ssam /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ 366189251Ssam addr = wpabuf_head(data->id_msgs); 367189251Ssam len = wpabuf_len(data->id_msgs); 368189251Ssam wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len); 369189251Ssam#ifdef EAP_AKA_PRIME 370189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) 371189251Ssam sha256_vector(1, &addr, &len, hash); 372189251Ssam else 373189251Ssam#endif /* EAP_AKA_PRIME */ 374189251Ssam sha1_vector(1, &addr, &len, hash); 375189251Ssam 376189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash, 377189251Ssam data->eap_method == EAP_TYPE_AKA_PRIME ? 378189251Ssam EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN); 379189251Ssam} 380189251Ssam 381189251Ssam 382189251Ssamstatic int eap_aka_verify_checkcode(struct eap_aka_data *data, 383189251Ssam const u8 *checkcode, size_t checkcode_len) 384189251Ssam{ 385189251Ssam const u8 *addr; 386189251Ssam size_t len; 387189251Ssam u8 hash[SHA256_MAC_LEN]; 388189251Ssam size_t hash_len; 389189251Ssam 390189251Ssam if (checkcode == NULL) 391189251Ssam return -1; 392189251Ssam 393189251Ssam if (data->id_msgs == NULL) { 394189251Ssam if (checkcode_len != 0) { 395189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " 396189251Ssam "indicates that AKA/Identity messages were " 397189251Ssam "used, but they were not"); 398189251Ssam return -1; 399189251Ssam } 400189251Ssam return 0; 401189251Ssam } 402189251Ssam 403189251Ssam hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ? 404189251Ssam EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN; 405189251Ssam 406189251Ssam if (checkcode_len != hash_len) { 407189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server " 408189251Ssam "indicates that AKA/Identity message were not " 409189251Ssam "used, but they were"); 410189251Ssam return -1; 411189251Ssam } 412189251Ssam 413189251Ssam /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */ 414189251Ssam addr = wpabuf_head(data->id_msgs); 415189251Ssam len = wpabuf_len(data->id_msgs); 416189251Ssam#ifdef EAP_AKA_PRIME 417189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) 418189251Ssam sha256_vector(1, &addr, &len, hash); 419189251Ssam else 420189251Ssam#endif /* EAP_AKA_PRIME */ 421189251Ssam sha1_vector(1, &addr, &len, hash); 422189251Ssam 423189251Ssam if (os_memcmp(hash, checkcode, hash_len) != 0) { 424189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE"); 425189251Ssam return -1; 426189251Ssam } 427189251Ssam 428189251Ssam return 0; 429189251Ssam} 430189251Ssam 431189251Ssam 432189251Ssamstatic struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id, 433189251Ssam int err) 434189251Ssam{ 435189251Ssam struct eap_sim_msg *msg; 436189251Ssam 437189251Ssam eap_aka_state(data, FAILURE); 438189251Ssam data->num_id_req = 0; 439189251Ssam data->num_notification = 0; 440189251Ssam 441252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)", 442252726Srpaulo err); 443189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 444189251Ssam EAP_AKA_SUBTYPE_CLIENT_ERROR); 445189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0); 446189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 447189251Ssam} 448189251Ssam 449189251Ssam 450189251Ssamstatic struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, 451189251Ssam u8 id) 452189251Ssam{ 453189251Ssam struct eap_sim_msg *msg; 454189251Ssam 455189251Ssam eap_aka_state(data, FAILURE); 456189251Ssam data->num_id_req = 0; 457189251Ssam data->num_notification = 0; 458189251Ssam 459189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject " 460189251Ssam "(id=%d)", id); 461189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 462189251Ssam EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT); 463189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 464189251Ssam} 465189251Ssam 466189251Ssam 467189251Ssamstatic struct wpabuf * eap_aka_synchronization_failure( 468189251Ssam struct eap_aka_data *data, u8 id) 469189251Ssam{ 470189251Ssam struct eap_sim_msg *msg; 471189251Ssam 472189251Ssam data->num_id_req = 0; 473189251Ssam data->num_notification = 0; 474189251Ssam 475189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure " 476189251Ssam "(id=%d)", id); 477189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 478189251Ssam EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE); 479189251Ssam wpa_printf(MSG_DEBUG, " AT_AUTS"); 480189251Ssam eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, 481189251Ssam EAP_AKA_AUTS_LEN); 482189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 483189251Ssam} 484189251Ssam 485189251Ssam 486189251Ssamstatic struct wpabuf * eap_aka_response_identity(struct eap_sm *sm, 487189251Ssam struct eap_aka_data *data, 488189251Ssam u8 id, 489189251Ssam enum eap_sim_id_req id_req) 490189251Ssam{ 491189251Ssam const u8 *identity = NULL; 492189251Ssam size_t identity_len = 0; 493189251Ssam struct eap_sim_msg *msg; 494189251Ssam 495189251Ssam data->reauth = 0; 496189251Ssam if (id_req == ANY_ID && data->reauth_id) { 497189251Ssam identity = data->reauth_id; 498189251Ssam identity_len = data->reauth_id_len; 499189251Ssam data->reauth = 1; 500189251Ssam } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) && 501189251Ssam data->pseudonym) { 502189251Ssam identity = data->pseudonym; 503189251Ssam identity_len = data->pseudonym_len; 504252726Srpaulo eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID); 505189251Ssam } else if (id_req != NO_ID_REQ) { 506189251Ssam identity = eap_get_config_identity(sm, &identity_len); 507189251Ssam if (identity) { 508252726Srpaulo eap_aka_clear_identities(sm, data, CLEAR_PSEUDONYM | 509189251Ssam CLEAR_REAUTH_ID); 510189251Ssam } 511189251Ssam } 512189251Ssam if (id_req != NO_ID_REQ) 513252726Srpaulo eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); 514189251Ssam 515189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id); 516189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 517189251Ssam EAP_AKA_SUBTYPE_IDENTITY); 518189251Ssam 519189251Ssam if (identity) { 520189251Ssam wpa_hexdump_ascii(MSG_DEBUG, " AT_IDENTITY", 521189251Ssam identity, identity_len); 522189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len, 523189251Ssam identity, identity_len); 524189251Ssam } 525189251Ssam 526189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 527189251Ssam} 528189251Ssam 529189251Ssam 530189251Ssamstatic struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data, 531189251Ssam u8 id) 532189251Ssam{ 533189251Ssam struct eap_sim_msg *msg; 534189251Ssam 535189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id); 536189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 537189251Ssam EAP_AKA_SUBTYPE_CHALLENGE); 538189251Ssam wpa_printf(MSG_DEBUG, " AT_RES"); 539189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8, 540189251Ssam data->res, data->res_len); 541189251Ssam eap_aka_add_checkcode(data, msg); 542189251Ssam if (data->use_result_ind) { 543189251Ssam wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 544189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 545189251Ssam } 546189251Ssam wpa_printf(MSG_DEBUG, " AT_MAC"); 547189251Ssam eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 548189251Ssam return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0); 549189251Ssam} 550189251Ssam 551189251Ssam 552189251Ssamstatic struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data, 553189251Ssam u8 id, int counter_too_small, 554189251Ssam const u8 *nonce_s) 555189251Ssam{ 556189251Ssam struct eap_sim_msg *msg; 557189251Ssam unsigned int counter; 558189251Ssam 559189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)", 560189251Ssam id); 561189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 562189251Ssam EAP_AKA_SUBTYPE_REAUTHENTICATION); 563189251Ssam wpa_printf(MSG_DEBUG, " AT_IV"); 564189251Ssam wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 565189251Ssam eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); 566189251Ssam 567189251Ssam if (counter_too_small) { 568189251Ssam wpa_printf(MSG_DEBUG, " *AT_COUNTER_TOO_SMALL"); 569189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0); 570189251Ssam counter = data->counter_too_small; 571189251Ssam } else 572189251Ssam counter = data->counter; 573189251Ssam 574189251Ssam wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", counter); 575189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); 576189251Ssam 577189251Ssam if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { 578189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " 579189251Ssam "AT_ENCR_DATA"); 580189251Ssam eap_sim_msg_free(msg); 581189251Ssam return NULL; 582189251Ssam } 583189251Ssam eap_aka_add_checkcode(data, msg); 584189251Ssam if (data->use_result_ind) { 585189251Ssam wpa_printf(MSG_DEBUG, " AT_RESULT_IND"); 586189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); 587189251Ssam } 588189251Ssam wpa_printf(MSG_DEBUG, " AT_MAC"); 589189251Ssam eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 590189251Ssam return eap_sim_msg_finish(msg, data->k_aut, nonce_s, 591189251Ssam EAP_SIM_NONCE_S_LEN); 592189251Ssam} 593189251Ssam 594189251Ssam 595189251Ssamstatic struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data, 596189251Ssam u8 id, u16 notification) 597189251Ssam{ 598189251Ssam struct eap_sim_msg *msg; 599189251Ssam u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL; 600189251Ssam 601189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id); 602189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 603189251Ssam EAP_AKA_SUBTYPE_NOTIFICATION); 604189251Ssam if (k_aut && data->reauth) { 605189251Ssam wpa_printf(MSG_DEBUG, " AT_IV"); 606189251Ssam wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); 607189251Ssam eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, 608189251Ssam EAP_SIM_AT_ENCR_DATA); 609189251Ssam wpa_printf(MSG_DEBUG, " *AT_COUNTER %d", data->counter); 610189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter, 611189251Ssam NULL, 0); 612189251Ssam if (eap_sim_msg_add_encr_end(msg, data->k_encr, 613189251Ssam EAP_SIM_AT_PADDING)) { 614189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " 615189251Ssam "AT_ENCR_DATA"); 616189251Ssam eap_sim_msg_free(msg); 617189251Ssam return NULL; 618189251Ssam } 619189251Ssam } 620189251Ssam if (k_aut) { 621189251Ssam wpa_printf(MSG_DEBUG, " AT_MAC"); 622189251Ssam eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); 623189251Ssam } 624189251Ssam return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0); 625189251Ssam} 626189251Ssam 627189251Ssam 628189251Ssamstatic struct wpabuf * eap_aka_process_identity(struct eap_sm *sm, 629189251Ssam struct eap_aka_data *data, 630189251Ssam u8 id, 631189251Ssam const struct wpabuf *reqData, 632189251Ssam struct eap_sim_attrs *attr) 633189251Ssam{ 634189251Ssam int id_error; 635189251Ssam struct wpabuf *buf; 636189251Ssam 637189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity"); 638189251Ssam 639189251Ssam id_error = 0; 640189251Ssam switch (attr->id_req) { 641189251Ssam case NO_ID_REQ: 642189251Ssam break; 643189251Ssam case ANY_ID: 644189251Ssam if (data->num_id_req > 0) 645189251Ssam id_error++; 646189251Ssam data->num_id_req++; 647189251Ssam break; 648189251Ssam case FULLAUTH_ID: 649189251Ssam if (data->num_id_req > 1) 650189251Ssam id_error++; 651189251Ssam data->num_id_req++; 652189251Ssam break; 653189251Ssam case PERMANENT_ID: 654189251Ssam if (data->num_id_req > 2) 655189251Ssam id_error++; 656189251Ssam data->num_id_req++; 657189251Ssam break; 658189251Ssam } 659189251Ssam if (id_error) { 660189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests " 661189251Ssam "used within one authentication"); 662189251Ssam return eap_aka_client_error(data, id, 663189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 664189251Ssam } 665189251Ssam 666189251Ssam buf = eap_aka_response_identity(sm, data, id, attr->id_req); 667189251Ssam 668189251Ssam if (data->prev_id != id) { 669189251Ssam eap_aka_add_id_msg(data, reqData); 670189251Ssam eap_aka_add_id_msg(data, buf); 671189251Ssam data->prev_id = id; 672189251Ssam } 673189251Ssam 674189251Ssam return buf; 675189251Ssam} 676189251Ssam 677189251Ssam 678189251Ssamstatic int eap_aka_verify_mac(struct eap_aka_data *data, 679189251Ssam const struct wpabuf *req, 680189251Ssam const u8 *mac, const u8 *extra, 681189251Ssam size_t extra_len) 682189251Ssam{ 683189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) 684189251Ssam return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra, 685189251Ssam extra_len); 686189251Ssam return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len); 687189251Ssam} 688189251Ssam 689189251Ssam 690189251Ssam#ifdef EAP_AKA_PRIME 691189251Ssamstatic struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data, 692189251Ssam u8 id, u16 kdf) 693189251Ssam{ 694189251Ssam struct eap_sim_msg *msg; 695189251Ssam 696189251Ssam data->kdf_negotiation = 1; 697189251Ssam data->kdf = kdf; 698189251Ssam wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF " 699189251Ssam "select)", id); 700189251Ssam msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method, 701189251Ssam EAP_AKA_SUBTYPE_CHALLENGE); 702189251Ssam wpa_printf(MSG_DEBUG, " AT_KDF"); 703189251Ssam eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0); 704189251Ssam return eap_sim_msg_finish(msg, NULL, NULL, 0); 705189251Ssam} 706189251Ssam 707189251Ssam 708189251Ssamstatic struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, 709189251Ssam u8 id, struct eap_sim_attrs *attr) 710189251Ssam{ 711189251Ssam size_t i; 712189251Ssam 713189251Ssam for (i = 0; i < attr->kdf_count; i++) { 714189251Ssam if (attr->kdf[i] == EAP_AKA_PRIME_KDF) 715189251Ssam return eap_aka_prime_kdf_select(data, id, 716189251Ssam EAP_AKA_PRIME_KDF); 717189251Ssam } 718189251Ssam 719189251Ssam /* No matching KDF found - fail authentication as if AUTN had been 720189251Ssam * incorrect */ 721189251Ssam return eap_aka_authentication_reject(data, id); 722189251Ssam} 723189251Ssam 724189251Ssam 725189251Ssamstatic int eap_aka_prime_kdf_valid(struct eap_aka_data *data, 726189251Ssam struct eap_sim_attrs *attr) 727189251Ssam{ 728189251Ssam size_t i, j; 729189251Ssam 730189251Ssam if (attr->kdf_count == 0) 731189251Ssam return 0; 732189251Ssam 733189251Ssam /* The only allowed (and required) duplication of a KDF is the addition 734189251Ssam * of the selected KDF into the beginning of the list. */ 735189251Ssam 736189251Ssam if (data->kdf_negotiation) { 737189251Ssam if (attr->kdf[0] != data->kdf) { 738189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " 739189251Ssam "accept the selected KDF"); 740189251Ssam return 0; 741189251Ssam } 742189251Ssam 743189251Ssam for (i = 1; i < attr->kdf_count; i++) { 744189251Ssam if (attr->kdf[i] == data->kdf) 745189251Ssam break; 746189251Ssam } 747189251Ssam if (i == attr->kdf_count && 748189251Ssam attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { 749189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " 750189251Ssam "duplicate the selected KDF"); 751189251Ssam return 0; 752189251Ssam } 753189251Ssam 754189251Ssam /* TODO: should check that the list is identical to the one 755189251Ssam * used in the previous Challenge message apart from the added 756189251Ssam * entry in the beginning. */ 757189251Ssam } 758189251Ssam 759189251Ssam for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { 760189251Ssam for (j = i + 1; j < attr->kdf_count; j++) { 761189251Ssam if (attr->kdf[i] == attr->kdf[j]) { 762189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': The server " 763189251Ssam "included a duplicated KDF"); 764189251Ssam return 0; 765189251Ssam } 766189251Ssam } 767189251Ssam } 768189251Ssam 769189251Ssam return 1; 770189251Ssam} 771189251Ssam#endif /* EAP_AKA_PRIME */ 772189251Ssam 773189251Ssam 774189251Ssamstatic struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, 775189251Ssam struct eap_aka_data *data, 776189251Ssam u8 id, 777189251Ssam const struct wpabuf *reqData, 778189251Ssam struct eap_sim_attrs *attr) 779189251Ssam{ 780189251Ssam const u8 *identity; 781189251Ssam size_t identity_len; 782189251Ssam int res; 783189251Ssam struct eap_sim_attrs eattr; 784189251Ssam 785189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge"); 786189251Ssam 787189251Ssam if (attr->checkcode && 788189251Ssam eap_aka_verify_checkcode(data, attr->checkcode, 789189251Ssam attr->checkcode_len)) { 790189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " 791189251Ssam "message"); 792189251Ssam return eap_aka_client_error(data, id, 793189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 794189251Ssam } 795189251Ssam 796189251Ssam#ifdef EAP_AKA_PRIME 797189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) { 798189251Ssam if (!attr->kdf_input || attr->kdf_input_len == 0) { 799189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message " 800189251Ssam "did not include non-empty AT_KDF_INPUT"); 801189251Ssam /* Fail authentication as if AUTN had been incorrect */ 802189251Ssam return eap_aka_authentication_reject(data, id); 803189251Ssam } 804189251Ssam os_free(data->network_name); 805189251Ssam data->network_name = os_malloc(attr->kdf_input_len); 806189251Ssam if (data->network_name == NULL) { 807189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " 808189251Ssam "storing Network Name"); 809189251Ssam return eap_aka_authentication_reject(data, id); 810189251Ssam } 811189251Ssam os_memcpy(data->network_name, attr->kdf_input, 812189251Ssam attr->kdf_input_len); 813189251Ssam data->network_name_len = attr->kdf_input_len; 814189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " 815189251Ssam "(AT_KDF_INPUT)", 816189251Ssam data->network_name, data->network_name_len); 817189251Ssam /* TODO: check Network Name per 3GPP.33.402 */ 818189251Ssam 819189251Ssam if (!eap_aka_prime_kdf_valid(data, attr)) 820189251Ssam return eap_aka_authentication_reject(data, id); 821189251Ssam 822189251Ssam if (attr->kdf[0] != EAP_AKA_PRIME_KDF) 823189251Ssam return eap_aka_prime_kdf_neg(data, id, attr); 824189251Ssam 825189251Ssam data->kdf = EAP_AKA_PRIME_KDF; 826189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf); 827189251Ssam } 828189251Ssam 829189251Ssam if (data->eap_method == EAP_TYPE_AKA && attr->bidding) { 830189251Ssam u16 flags = WPA_GET_BE16(attr->bidding); 831189251Ssam if ((flags & EAP_AKA_BIDDING_FLAG_D) && 832189251Ssam eap_allowed_method(sm, EAP_VENDOR_IETF, 833189251Ssam EAP_TYPE_AKA_PRIME)) { 834189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from " 835189251Ssam "AKA' to AKA detected"); 836189251Ssam /* Fail authentication as if AUTN had been incorrect */ 837189251Ssam return eap_aka_authentication_reject(data, id); 838189251Ssam } 839189251Ssam } 840189251Ssam#endif /* EAP_AKA_PRIME */ 841189251Ssam 842189251Ssam data->reauth = 0; 843189251Ssam if (!attr->mac || !attr->rand || !attr->autn) { 844189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " 845189251Ssam "did not include%s%s%s", 846189251Ssam !attr->mac ? " AT_MAC" : "", 847189251Ssam !attr->rand ? " AT_RAND" : "", 848189251Ssam !attr->autn ? " AT_AUTN" : ""); 849189251Ssam return eap_aka_client_error(data, id, 850189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 851189251Ssam } 852189251Ssam os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN); 853189251Ssam os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN); 854189251Ssam 855189251Ssam res = eap_aka_umts_auth(sm, data); 856189251Ssam if (res == -1) { 857189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " 858189251Ssam "failed (AUTN)"); 859189251Ssam return eap_aka_authentication_reject(data, id); 860189251Ssam } else if (res == -2) { 861189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " 862189251Ssam "failed (AUTN seq# -> AUTS)"); 863189251Ssam return eap_aka_synchronization_failure(data, id); 864189251Ssam } else if (res) { 865189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed"); 866189251Ssam return eap_aka_client_error(data, id, 867189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 868189251Ssam } 869189251Ssam#ifdef EAP_AKA_PRIME 870189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) { 871189251Ssam /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the 872189251Ssam * needed 6-octet SQN ^ AK for CK',IK' derivation */ 873189251Ssam u16 amf = WPA_GET_BE16(data->autn + 6); 874189251Ssam if (!(amf & 0x8000)) { 875189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit " 876189251Ssam "not set (AMF=0x%4x)", amf); 877189251Ssam return eap_aka_authentication_reject(data, id); 878189251Ssam } 879189251Ssam eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik, 880189251Ssam data->autn, 881189251Ssam data->network_name, 882189251Ssam data->network_name_len); 883189251Ssam } 884189251Ssam#endif /* EAP_AKA_PRIME */ 885189251Ssam if (data->last_eap_identity) { 886189251Ssam identity = data->last_eap_identity; 887189251Ssam identity_len = data->last_eap_identity_len; 888189251Ssam } else if (data->pseudonym) { 889189251Ssam identity = data->pseudonym; 890189251Ssam identity_len = data->pseudonym_len; 891189251Ssam } else 892189251Ssam identity = eap_get_config_identity(sm, &identity_len); 893189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " 894189251Ssam "derivation", identity, identity_len); 895189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) { 896189251Ssam eap_aka_prime_derive_keys(identity, identity_len, data->ik, 897189251Ssam data->ck, data->k_encr, data->k_aut, 898189251Ssam data->k_re, data->msk, data->emsk); 899189251Ssam } else { 900189251Ssam eap_aka_derive_mk(identity, identity_len, data->ik, data->ck, 901189251Ssam data->mk); 902189251Ssam eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, 903189251Ssam data->msk, data->emsk); 904189251Ssam } 905189251Ssam if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { 906189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " 907189251Ssam "used invalid AT_MAC"); 908189251Ssam return eap_aka_client_error(data, id, 909189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 910189251Ssam } 911189251Ssam 912252726Srpaulo /* Old reauthentication identity must not be used anymore. In 913252726Srpaulo * other words, if no new identities are received, full 914252726Srpaulo * authentication will be used on next reauthentication (using 915252726Srpaulo * pseudonym identity or permanent identity). */ 916252726Srpaulo eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); 917189251Ssam 918189251Ssam if (attr->encr_data) { 919189251Ssam u8 *decrypted; 920189251Ssam decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 921189251Ssam attr->encr_data_len, attr->iv, 922189251Ssam &eattr, 0); 923189251Ssam if (decrypted == NULL) { 924189251Ssam return eap_aka_client_error( 925189251Ssam data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); 926189251Ssam } 927252726Srpaulo eap_aka_learn_ids(sm, data, &eattr); 928189251Ssam os_free(decrypted); 929189251Ssam } 930189251Ssam 931189251Ssam if (data->result_ind && attr->result_ind) 932189251Ssam data->use_result_ind = 1; 933189251Ssam 934189251Ssam if (data->state != FAILURE && data->state != RESULT_FAILURE) { 935189251Ssam eap_aka_state(data, data->use_result_ind ? 936189251Ssam RESULT_SUCCESS : SUCCESS); 937189251Ssam } 938189251Ssam 939189251Ssam data->num_id_req = 0; 940189251Ssam data->num_notification = 0; 941189251Ssam /* RFC 4187 specifies that counter is initialized to one after 942189251Ssam * fullauth, but initializing it to zero makes it easier to implement 943189251Ssam * reauth verification. */ 944189251Ssam data->counter = 0; 945189251Ssam return eap_aka_response_challenge(data, id); 946189251Ssam} 947189251Ssam 948189251Ssam 949189251Ssamstatic int eap_aka_process_notification_reauth(struct eap_aka_data *data, 950189251Ssam struct eap_sim_attrs *attr) 951189251Ssam{ 952189251Ssam struct eap_sim_attrs eattr; 953189251Ssam u8 *decrypted; 954189251Ssam 955189251Ssam if (attr->encr_data == NULL || attr->iv == NULL) { 956189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after " 957189251Ssam "reauth did not include encrypted data"); 958189251Ssam return -1; 959189251Ssam } 960189251Ssam 961189251Ssam decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 962189251Ssam attr->encr_data_len, attr->iv, &eattr, 963189251Ssam 0); 964189251Ssam if (decrypted == NULL) { 965189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " 966189251Ssam "data from notification message"); 967189251Ssam return -1; 968189251Ssam } 969189251Ssam 970189251Ssam if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) { 971189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification " 972189251Ssam "message does not match with counter in reauth " 973189251Ssam "message"); 974189251Ssam os_free(decrypted); 975189251Ssam return -1; 976189251Ssam } 977189251Ssam 978189251Ssam os_free(decrypted); 979189251Ssam return 0; 980189251Ssam} 981189251Ssam 982189251Ssam 983189251Ssamstatic int eap_aka_process_notification_auth(struct eap_aka_data *data, 984189251Ssam const struct wpabuf *reqData, 985189251Ssam struct eap_sim_attrs *attr) 986189251Ssam{ 987189251Ssam if (attr->mac == NULL) { 988189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth " 989189251Ssam "Notification message"); 990189251Ssam return -1; 991189251Ssam } 992189251Ssam 993189251Ssam if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { 994189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Notification message " 995189251Ssam "used invalid AT_MAC"); 996189251Ssam return -1; 997189251Ssam } 998189251Ssam 999189251Ssam if (data->reauth && 1000189251Ssam eap_aka_process_notification_reauth(data, attr)) { 1001189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification " 1002189251Ssam "message after reauth"); 1003189251Ssam return -1; 1004189251Ssam } 1005189251Ssam 1006189251Ssam return 0; 1007189251Ssam} 1008189251Ssam 1009189251Ssam 1010189251Ssamstatic struct wpabuf * eap_aka_process_notification( 1011189251Ssam struct eap_sm *sm, struct eap_aka_data *data, u8 id, 1012189251Ssam const struct wpabuf *reqData, struct eap_sim_attrs *attr) 1013189251Ssam{ 1014189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification"); 1015189251Ssam if (data->num_notification > 0) { 1016189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: too many notification " 1017189251Ssam "rounds (only one allowed)"); 1018189251Ssam return eap_aka_client_error(data, id, 1019189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1020189251Ssam } 1021189251Ssam data->num_notification++; 1022189251Ssam if (attr->notification == -1) { 1023189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in " 1024189251Ssam "Notification message"); 1025189251Ssam return eap_aka_client_error(data, id, 1026189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1027189251Ssam } 1028189251Ssam 1029189251Ssam if ((attr->notification & 0x4000) == 0 && 1030189251Ssam eap_aka_process_notification_auth(data, reqData, attr)) { 1031189251Ssam return eap_aka_client_error(data, id, 1032189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1033189251Ssam } 1034189251Ssam 1035189251Ssam eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); 1036189251Ssam if (attr->notification >= 0 && attr->notification < 32768) { 1037189251Ssam eap_aka_state(data, FAILURE); 1038189251Ssam } else if (attr->notification == EAP_SIM_SUCCESS && 1039189251Ssam data->state == RESULT_SUCCESS) 1040189251Ssam eap_aka_state(data, SUCCESS); 1041189251Ssam return eap_aka_response_notification(data, id, attr->notification); 1042189251Ssam} 1043189251Ssam 1044189251Ssam 1045189251Ssamstatic struct wpabuf * eap_aka_process_reauthentication( 1046189251Ssam struct eap_sm *sm, struct eap_aka_data *data, u8 id, 1047189251Ssam const struct wpabuf *reqData, struct eap_sim_attrs *attr) 1048189251Ssam{ 1049189251Ssam struct eap_sim_attrs eattr; 1050189251Ssam u8 *decrypted; 1051189251Ssam 1052189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication"); 1053189251Ssam 1054189251Ssam if (attr->checkcode && 1055189251Ssam eap_aka_verify_checkcode(data, attr->checkcode, 1056189251Ssam attr->checkcode_len)) { 1057189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the " 1058189251Ssam "message"); 1059189251Ssam return eap_aka_client_error(data, id, 1060189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1061189251Ssam } 1062189251Ssam 1063189251Ssam if (data->reauth_id == NULL) { 1064189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying " 1065189251Ssam "reauthentication, but no reauth_id available"); 1066189251Ssam return eap_aka_client_error(data, id, 1067189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1068189251Ssam } 1069189251Ssam 1070189251Ssam data->reauth = 1; 1071189251Ssam if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) { 1072189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " 1073189251Ssam "did not have valid AT_MAC"); 1074189251Ssam return eap_aka_client_error(data, id, 1075189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1076189251Ssam } 1077189251Ssam 1078189251Ssam if (attr->encr_data == NULL || attr->iv == NULL) { 1079189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " 1080189251Ssam "message did not include encrypted data"); 1081189251Ssam return eap_aka_client_error(data, id, 1082189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1083189251Ssam } 1084189251Ssam 1085189251Ssam decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, 1086189251Ssam attr->encr_data_len, attr->iv, &eattr, 1087189251Ssam 0); 1088189251Ssam if (decrypted == NULL) { 1089189251Ssam wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " 1090189251Ssam "data from reauthentication message"); 1091189251Ssam return eap_aka_client_error(data, id, 1092189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1093189251Ssam } 1094189251Ssam 1095189251Ssam if (eattr.nonce_s == NULL || eattr.counter < 0) { 1096189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet", 1097189251Ssam !eattr.nonce_s ? " AT_NONCE_S" : "", 1098189251Ssam eattr.counter < 0 ? " AT_COUNTER" : ""); 1099189251Ssam os_free(decrypted); 1100189251Ssam return eap_aka_client_error(data, id, 1101189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1102189251Ssam } 1103189251Ssam 1104189251Ssam if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) { 1105189251Ssam struct wpabuf *res; 1106189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter " 1107189251Ssam "(%d <= %d)", eattr.counter, data->counter); 1108189251Ssam data->counter_too_small = eattr.counter; 1109189251Ssam 1110189251Ssam /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current 1111189251Ssam * reauth_id must not be used to start a new reauthentication. 1112189251Ssam * However, since it was used in the last EAP-Response-Identity 1113189251Ssam * packet, it has to saved for the following fullauth to be 1114189251Ssam * used in MK derivation. */ 1115189251Ssam os_free(data->last_eap_identity); 1116189251Ssam data->last_eap_identity = data->reauth_id; 1117189251Ssam data->last_eap_identity_len = data->reauth_id_len; 1118189251Ssam data->reauth_id = NULL; 1119189251Ssam data->reauth_id_len = 0; 1120189251Ssam 1121189251Ssam res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s); 1122189251Ssam os_free(decrypted); 1123189251Ssam 1124189251Ssam return res; 1125189251Ssam } 1126189251Ssam data->counter = eattr.counter; 1127189251Ssam 1128189251Ssam os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN); 1129189251Ssam wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S", 1130189251Ssam data->nonce_s, EAP_SIM_NONCE_S_LEN); 1131189251Ssam 1132189251Ssam if (data->eap_method == EAP_TYPE_AKA_PRIME) { 1133189251Ssam eap_aka_prime_derive_keys_reauth(data->k_re, data->counter, 1134189251Ssam data->reauth_id, 1135189251Ssam data->reauth_id_len, 1136189251Ssam data->nonce_s, 1137189251Ssam data->msk, data->emsk); 1138189251Ssam } else { 1139189251Ssam eap_sim_derive_keys_reauth(data->counter, data->reauth_id, 1140189251Ssam data->reauth_id_len, 1141189251Ssam data->nonce_s, data->mk, 1142189251Ssam data->msk, data->emsk); 1143189251Ssam } 1144252726Srpaulo eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID); 1145252726Srpaulo eap_aka_learn_ids(sm, data, &eattr); 1146189251Ssam 1147189251Ssam if (data->result_ind && attr->result_ind) 1148189251Ssam data->use_result_ind = 1; 1149189251Ssam 1150189251Ssam if (data->state != FAILURE && data->state != RESULT_FAILURE) { 1151189251Ssam eap_aka_state(data, data->use_result_ind ? 1152189251Ssam RESULT_SUCCESS : SUCCESS); 1153189251Ssam } 1154189251Ssam 1155189251Ssam data->num_id_req = 0; 1156189251Ssam data->num_notification = 0; 1157189251Ssam if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) { 1158189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of " 1159189251Ssam "fast reauths performed - force fullauth"); 1160252726Srpaulo eap_aka_clear_identities(sm, data, 1161252726Srpaulo CLEAR_REAUTH_ID | CLEAR_EAP_ID); 1162189251Ssam } 1163189251Ssam os_free(decrypted); 1164189251Ssam return eap_aka_response_reauth(data, id, 0, data->nonce_s); 1165189251Ssam} 1166189251Ssam 1167189251Ssam 1168189251Ssamstatic struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv, 1169189251Ssam struct eap_method_ret *ret, 1170189251Ssam const struct wpabuf *reqData) 1171189251Ssam{ 1172189251Ssam struct eap_aka_data *data = priv; 1173189251Ssam const struct eap_hdr *req; 1174189251Ssam u8 subtype, id; 1175189251Ssam struct wpabuf *res; 1176189251Ssam const u8 *pos; 1177189251Ssam struct eap_sim_attrs attr; 1178189251Ssam size_t len; 1179189251Ssam 1180189251Ssam wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData); 1181189251Ssam if (eap_get_config_identity(sm, &len) == NULL) { 1182189251Ssam wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured"); 1183189251Ssam eap_sm_request_identity(sm); 1184189251Ssam ret->ignore = TRUE; 1185189251Ssam return NULL; 1186189251Ssam } 1187189251Ssam 1188189251Ssam pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData, 1189189251Ssam &len); 1190189251Ssam if (pos == NULL || len < 1) { 1191189251Ssam ret->ignore = TRUE; 1192189251Ssam return NULL; 1193189251Ssam } 1194189251Ssam req = wpabuf_head(reqData); 1195189251Ssam id = req->identifier; 1196189251Ssam len = be_to_host16(req->length); 1197189251Ssam 1198189251Ssam ret->ignore = FALSE; 1199189251Ssam ret->methodState = METHOD_MAY_CONT; 1200189251Ssam ret->decision = DECISION_FAIL; 1201189251Ssam ret->allowNotifications = TRUE; 1202189251Ssam 1203189251Ssam subtype = *pos++; 1204189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype); 1205189251Ssam pos += 2; /* Reserved */ 1206189251Ssam 1207189251Ssam if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 1208189251Ssam data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1, 1209189251Ssam 0)) { 1210189251Ssam res = eap_aka_client_error(data, id, 1211189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1212189251Ssam goto done; 1213189251Ssam } 1214189251Ssam 1215189251Ssam switch (subtype) { 1216189251Ssam case EAP_AKA_SUBTYPE_IDENTITY: 1217189251Ssam res = eap_aka_process_identity(sm, data, id, reqData, &attr); 1218189251Ssam break; 1219189251Ssam case EAP_AKA_SUBTYPE_CHALLENGE: 1220189251Ssam res = eap_aka_process_challenge(sm, data, id, reqData, &attr); 1221189251Ssam break; 1222189251Ssam case EAP_AKA_SUBTYPE_NOTIFICATION: 1223189251Ssam res = eap_aka_process_notification(sm, data, id, reqData, 1224189251Ssam &attr); 1225189251Ssam break; 1226189251Ssam case EAP_AKA_SUBTYPE_REAUTHENTICATION: 1227189251Ssam res = eap_aka_process_reauthentication(sm, data, id, reqData, 1228189251Ssam &attr); 1229189251Ssam break; 1230189251Ssam case EAP_AKA_SUBTYPE_CLIENT_ERROR: 1231189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error"); 1232189251Ssam res = eap_aka_client_error(data, id, 1233189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1234189251Ssam break; 1235189251Ssam default: 1236189251Ssam wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype); 1237189251Ssam res = eap_aka_client_error(data, id, 1238189251Ssam EAP_AKA_UNABLE_TO_PROCESS_PACKET); 1239189251Ssam break; 1240189251Ssam } 1241189251Ssam 1242189251Ssamdone: 1243189251Ssam if (data->state == FAILURE) { 1244189251Ssam ret->decision = DECISION_FAIL; 1245189251Ssam ret->methodState = METHOD_DONE; 1246189251Ssam } else if (data->state == SUCCESS) { 1247189251Ssam ret->decision = data->use_result_ind ? 1248189251Ssam DECISION_UNCOND_SUCC : DECISION_COND_SUCC; 1249189251Ssam /* 1250189251Ssam * It is possible for the server to reply with AKA 1251189251Ssam * Notification, so we must allow the method to continue and 1252189251Ssam * not only accept EAP-Success at this point. 1253189251Ssam */ 1254189251Ssam ret->methodState = data->use_result_ind ? 1255189251Ssam METHOD_DONE : METHOD_MAY_CONT; 1256189251Ssam } else if (data->state == RESULT_FAILURE) 1257189251Ssam ret->methodState = METHOD_CONT; 1258189251Ssam else if (data->state == RESULT_SUCCESS) 1259189251Ssam ret->methodState = METHOD_CONT; 1260189251Ssam 1261189251Ssam if (ret->methodState == METHOD_DONE) { 1262189251Ssam ret->allowNotifications = FALSE; 1263189251Ssam } 1264189251Ssam 1265189251Ssam return res; 1266189251Ssam} 1267189251Ssam 1268189251Ssam 1269189251Ssamstatic Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv) 1270189251Ssam{ 1271189251Ssam struct eap_aka_data *data = priv; 1272189251Ssam return data->pseudonym || data->reauth_id; 1273189251Ssam} 1274189251Ssam 1275189251Ssam 1276189251Ssamstatic void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv) 1277189251Ssam{ 1278189251Ssam struct eap_aka_data *data = priv; 1279252726Srpaulo eap_aka_clear_identities(sm, data, CLEAR_EAP_ID); 1280189251Ssam data->prev_id = -1; 1281189251Ssam wpabuf_free(data->id_msgs); 1282189251Ssam data->id_msgs = NULL; 1283189251Ssam data->use_result_ind = 0; 1284189251Ssam data->kdf_negotiation = 0; 1285189251Ssam} 1286189251Ssam 1287189251Ssam 1288189251Ssamstatic void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv) 1289189251Ssam{ 1290189251Ssam struct eap_aka_data *data = priv; 1291189251Ssam data->num_id_req = 0; 1292189251Ssam data->num_notification = 0; 1293189251Ssam eap_aka_state(data, CONTINUE); 1294189251Ssam return priv; 1295189251Ssam} 1296189251Ssam 1297189251Ssam 1298189251Ssamstatic const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv, 1299189251Ssam size_t *len) 1300189251Ssam{ 1301189251Ssam struct eap_aka_data *data = priv; 1302189251Ssam 1303189251Ssam if (data->reauth_id) { 1304189251Ssam *len = data->reauth_id_len; 1305189251Ssam return data->reauth_id; 1306189251Ssam } 1307189251Ssam 1308189251Ssam if (data->pseudonym) { 1309189251Ssam *len = data->pseudonym_len; 1310189251Ssam return data->pseudonym; 1311189251Ssam } 1312189251Ssam 1313189251Ssam return NULL; 1314189251Ssam} 1315189251Ssam 1316189251Ssam 1317189251Ssamstatic Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv) 1318189251Ssam{ 1319189251Ssam struct eap_aka_data *data = priv; 1320189251Ssam return data->state == SUCCESS; 1321189251Ssam} 1322189251Ssam 1323189251Ssam 1324189251Ssamstatic u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) 1325189251Ssam{ 1326189251Ssam struct eap_aka_data *data = priv; 1327189251Ssam u8 *key; 1328189251Ssam 1329189251Ssam if (data->state != SUCCESS) 1330189251Ssam return NULL; 1331189251Ssam 1332189251Ssam key = os_malloc(EAP_SIM_KEYING_DATA_LEN); 1333189251Ssam if (key == NULL) 1334189251Ssam return NULL; 1335189251Ssam 1336189251Ssam *len = EAP_SIM_KEYING_DATA_LEN; 1337189251Ssam os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); 1338189251Ssam 1339189251Ssam return key; 1340189251Ssam} 1341189251Ssam 1342189251Ssam 1343189251Ssamstatic u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) 1344189251Ssam{ 1345189251Ssam struct eap_aka_data *data = priv; 1346189251Ssam u8 *key; 1347189251Ssam 1348189251Ssam if (data->state != SUCCESS) 1349189251Ssam return NULL; 1350189251Ssam 1351189251Ssam key = os_malloc(EAP_EMSK_LEN); 1352189251Ssam if (key == NULL) 1353189251Ssam return NULL; 1354189251Ssam 1355189251Ssam *len = EAP_EMSK_LEN; 1356189251Ssam os_memcpy(key, data->emsk, EAP_EMSK_LEN); 1357189251Ssam 1358189251Ssam return key; 1359189251Ssam} 1360189251Ssam 1361189251Ssam 1362189251Ssamint eap_peer_aka_register(void) 1363189251Ssam{ 1364189251Ssam struct eap_method *eap; 1365189251Ssam int ret; 1366189251Ssam 1367189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1368189251Ssam EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); 1369189251Ssam if (eap == NULL) 1370189251Ssam return -1; 1371189251Ssam 1372189251Ssam eap->init = eap_aka_init; 1373189251Ssam eap->deinit = eap_aka_deinit; 1374189251Ssam eap->process = eap_aka_process; 1375189251Ssam eap->isKeyAvailable = eap_aka_isKeyAvailable; 1376189251Ssam eap->getKey = eap_aka_getKey; 1377189251Ssam eap->has_reauth_data = eap_aka_has_reauth_data; 1378189251Ssam eap->deinit_for_reauth = eap_aka_deinit_for_reauth; 1379189251Ssam eap->init_for_reauth = eap_aka_init_for_reauth; 1380189251Ssam eap->get_identity = eap_aka_get_identity; 1381189251Ssam eap->get_emsk = eap_aka_get_emsk; 1382189251Ssam 1383189251Ssam ret = eap_peer_method_register(eap); 1384189251Ssam if (ret) 1385189251Ssam eap_peer_method_free(eap); 1386189251Ssam return ret; 1387189251Ssam} 1388189251Ssam 1389189251Ssam 1390189251Ssam#ifdef EAP_AKA_PRIME 1391189251Ssamint eap_peer_aka_prime_register(void) 1392189251Ssam{ 1393189251Ssam struct eap_method *eap; 1394189251Ssam int ret; 1395189251Ssam 1396189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 1397189251Ssam EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME, 1398189251Ssam "AKA'"); 1399189251Ssam if (eap == NULL) 1400189251Ssam return -1; 1401189251Ssam 1402189251Ssam eap->init = eap_aka_prime_init; 1403189251Ssam eap->deinit = eap_aka_deinit; 1404189251Ssam eap->process = eap_aka_process; 1405189251Ssam eap->isKeyAvailable = eap_aka_isKeyAvailable; 1406189251Ssam eap->getKey = eap_aka_getKey; 1407189251Ssam eap->has_reauth_data = eap_aka_has_reauth_data; 1408189251Ssam eap->deinit_for_reauth = eap_aka_deinit_for_reauth; 1409189251Ssam eap->init_for_reauth = eap_aka_init_for_reauth; 1410189251Ssam eap->get_identity = eap_aka_get_identity; 1411189251Ssam eap->get_emsk = eap_aka_get_emsk; 1412189251Ssam 1413189251Ssam ret = eap_peer_method_register(eap); 1414189251Ssam if (ret) 1415189251Ssam eap_peer_method_free(eap); 1416189251Ssam 1417189251Ssam return ret; 1418189251Ssam} 1419189251Ssam#endif /* EAP_AKA_PRIME */ 1420