xref: /freebsd/contrib/wpa/src/eap_peer/eap_aka.c (revision 206b73d0429edb7c49b612537544e677fa568e83)
139beb93cSSam Leffler /*
2f05cddf9SRui Paulo  * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
3f05cddf9SRui Paulo  * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "pcsc_funcs.h"
13e28a4053SRui Paulo #include "crypto/crypto.h"
14e28a4053SRui Paulo #include "crypto/sha1.h"
15e28a4053SRui Paulo #include "crypto/sha256.h"
16e28a4053SRui Paulo #include "crypto/milenage.h"
1739beb93cSSam Leffler #include "eap_common/eap_sim_common.h"
18e28a4053SRui Paulo #include "eap_config.h"
19e28a4053SRui Paulo #include "eap_i.h"
2039beb93cSSam Leffler 
2139beb93cSSam Leffler 
2239beb93cSSam Leffler struct eap_aka_data {
2339beb93cSSam Leffler 	u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN];
2439beb93cSSam Leffler 	size_t res_len;
2539beb93cSSam Leffler 	u8 nonce_s[EAP_SIM_NONCE_S_LEN];
2639beb93cSSam Leffler 	u8 mk[EAP_SIM_MK_LEN];
2739beb93cSSam Leffler 	u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
2839beb93cSSam Leffler 	u8 k_encr[EAP_SIM_K_ENCR_LEN];
2939beb93cSSam Leffler 	u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */
3039beb93cSSam Leffler 	u8 msk[EAP_SIM_KEYING_DATA_LEN];
3139beb93cSSam Leffler 	u8 emsk[EAP_EMSK_LEN];
3239beb93cSSam Leffler 	u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN];
3339beb93cSSam Leffler 	u8 auts[EAP_AKA_AUTS_LEN];
34*206b73d0SCy Schubert 	u8 reauth_mac[EAP_SIM_MAC_LEN];
3539beb93cSSam Leffler 
3639beb93cSSam Leffler 	int num_id_req, num_notification;
3739beb93cSSam Leffler 	u8 *pseudonym;
3839beb93cSSam Leffler 	size_t pseudonym_len;
3939beb93cSSam Leffler 	u8 *reauth_id;
4039beb93cSSam Leffler 	size_t reauth_id_len;
4139beb93cSSam Leffler 	int reauth;
4239beb93cSSam Leffler 	unsigned int counter, counter_too_small;
4339beb93cSSam Leffler 	u8 *last_eap_identity;
4439beb93cSSam Leffler 	size_t last_eap_identity_len;
4539beb93cSSam Leffler 	enum {
465b9c547cSRui Paulo 		CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
4739beb93cSSam Leffler 	} state;
4839beb93cSSam Leffler 
4939beb93cSSam Leffler 	struct wpabuf *id_msgs;
5039beb93cSSam Leffler 	int prev_id;
5139beb93cSSam Leffler 	int result_ind, use_result_ind;
5285732ac8SCy Schubert 	int use_pseudonym;
5339beb93cSSam Leffler 	u8 eap_method;
5439beb93cSSam Leffler 	u8 *network_name;
5539beb93cSSam Leffler 	size_t network_name_len;
5639beb93cSSam Leffler 	u16 kdf;
5739beb93cSSam Leffler 	int kdf_negotiation;
5885732ac8SCy Schubert 	u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX];
5985732ac8SCy Schubert 	size_t last_kdf_count;
6085732ac8SCy Schubert 	int error_code;
6139beb93cSSam Leffler };
6239beb93cSSam Leffler 
6339beb93cSSam Leffler 
6439beb93cSSam Leffler #ifndef CONFIG_NO_STDOUT_DEBUG
6539beb93cSSam Leffler static const char * eap_aka_state_txt(int state)
6639beb93cSSam Leffler {
6739beb93cSSam Leffler 	switch (state) {
6839beb93cSSam Leffler 	case CONTINUE:
6939beb93cSSam Leffler 		return "CONTINUE";
7039beb93cSSam Leffler 	case RESULT_SUCCESS:
7139beb93cSSam Leffler 		return "RESULT_SUCCESS";
7239beb93cSSam Leffler 	case SUCCESS:
7339beb93cSSam Leffler 		return "SUCCESS";
7439beb93cSSam Leffler 	case FAILURE:
7539beb93cSSam Leffler 		return "FAILURE";
7639beb93cSSam Leffler 	default:
7739beb93cSSam Leffler 		return "?";
7839beb93cSSam Leffler 	}
7939beb93cSSam Leffler }
8039beb93cSSam Leffler #endif /* CONFIG_NO_STDOUT_DEBUG */
8139beb93cSSam Leffler 
8239beb93cSSam Leffler 
8339beb93cSSam Leffler static void eap_aka_state(struct eap_aka_data *data, int state)
8439beb93cSSam Leffler {
8539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
8639beb93cSSam Leffler 		   eap_aka_state_txt(data->state),
8739beb93cSSam Leffler 		   eap_aka_state_txt(state));
8839beb93cSSam Leffler 	data->state = state;
8939beb93cSSam Leffler }
9039beb93cSSam Leffler 
9139beb93cSSam Leffler 
9239beb93cSSam Leffler static void * eap_aka_init(struct eap_sm *sm)
9339beb93cSSam Leffler {
9439beb93cSSam Leffler 	struct eap_aka_data *data;
9539beb93cSSam Leffler 	const char *phase1 = eap_get_config_phase1(sm);
96f05cddf9SRui Paulo 	struct eap_peer_config *config = eap_get_config(sm);
9739beb93cSSam Leffler 
9839beb93cSSam Leffler 	data = os_zalloc(sizeof(*data));
9939beb93cSSam Leffler 	if (data == NULL)
10039beb93cSSam Leffler 		return NULL;
10139beb93cSSam Leffler 
10239beb93cSSam Leffler 	data->eap_method = EAP_TYPE_AKA;
10339beb93cSSam Leffler 
10485732ac8SCy Schubert 	/* Zero is a valid error code, so we need to initialize */
10585732ac8SCy Schubert 	data->error_code = NO_EAP_METHOD_ERROR;
10685732ac8SCy Schubert 
10739beb93cSSam Leffler 	eap_aka_state(data, CONTINUE);
10839beb93cSSam Leffler 	data->prev_id = -1;
10939beb93cSSam Leffler 
11039beb93cSSam Leffler 	data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
11139beb93cSSam Leffler 
11285732ac8SCy Schubert 	data->use_pseudonym = !sm->init_phase2;
11385732ac8SCy Schubert 	if (config && config->anonymous_identity && data->use_pseudonym) {
114f05cddf9SRui Paulo 		data->pseudonym = os_malloc(config->anonymous_identity_len);
115f05cddf9SRui Paulo 		if (data->pseudonym) {
116f05cddf9SRui Paulo 			os_memcpy(data->pseudonym, config->anonymous_identity,
117f05cddf9SRui Paulo 				  config->anonymous_identity_len);
118f05cddf9SRui Paulo 			data->pseudonym_len = config->anonymous_identity_len;
119f05cddf9SRui Paulo 		}
120f05cddf9SRui Paulo 	}
121f05cddf9SRui Paulo 
12239beb93cSSam Leffler 	return data;
12339beb93cSSam Leffler }
12439beb93cSSam Leffler 
12539beb93cSSam Leffler 
12639beb93cSSam Leffler #ifdef EAP_AKA_PRIME
12739beb93cSSam Leffler static void * eap_aka_prime_init(struct eap_sm *sm)
12839beb93cSSam Leffler {
12939beb93cSSam Leffler 	struct eap_aka_data *data = eap_aka_init(sm);
13039beb93cSSam Leffler 	if (data == NULL)
13139beb93cSSam Leffler 		return NULL;
13239beb93cSSam Leffler 	data->eap_method = EAP_TYPE_AKA_PRIME;
13339beb93cSSam Leffler 	return data;
13439beb93cSSam Leffler }
13539beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
13639beb93cSSam Leffler 
13739beb93cSSam Leffler 
1385b9c547cSRui Paulo static void eap_aka_clear_keys(struct eap_aka_data *data, int reauth)
1395b9c547cSRui Paulo {
1405b9c547cSRui Paulo 	if (!reauth) {
1415b9c547cSRui Paulo 		os_memset(data->mk, 0, EAP_SIM_MK_LEN);
1425b9c547cSRui Paulo 		os_memset(data->k_aut, 0, EAP_AKA_PRIME_K_AUT_LEN);
1435b9c547cSRui Paulo 		os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN);
1445b9c547cSRui Paulo 		os_memset(data->k_re, 0, EAP_AKA_PRIME_K_RE_LEN);
1455b9c547cSRui Paulo 	}
1465b9c547cSRui Paulo 	os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN);
1475b9c547cSRui Paulo 	os_memset(data->emsk, 0, EAP_EMSK_LEN);
1485b9c547cSRui Paulo 	os_memset(data->autn, 0, EAP_AKA_AUTN_LEN);
1495b9c547cSRui Paulo 	os_memset(data->auts, 0, EAP_AKA_AUTS_LEN);
1505b9c547cSRui Paulo }
1515b9c547cSRui Paulo 
1525b9c547cSRui Paulo 
15339beb93cSSam Leffler static void eap_aka_deinit(struct eap_sm *sm, void *priv)
15439beb93cSSam Leffler {
15539beb93cSSam Leffler 	struct eap_aka_data *data = priv;
15639beb93cSSam Leffler 	if (data) {
15739beb93cSSam Leffler 		os_free(data->pseudonym);
15839beb93cSSam Leffler 		os_free(data->reauth_id);
15939beb93cSSam Leffler 		os_free(data->last_eap_identity);
16039beb93cSSam Leffler 		wpabuf_free(data->id_msgs);
16139beb93cSSam Leffler 		os_free(data->network_name);
1625b9c547cSRui Paulo 		eap_aka_clear_keys(data, 0);
16339beb93cSSam Leffler 		os_free(data);
16439beb93cSSam Leffler 	}
16539beb93cSSam Leffler }
16639beb93cSSam Leffler 
16739beb93cSSam Leffler 
1685b9c547cSRui Paulo static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data)
1695b9c547cSRui Paulo {
1705b9c547cSRui Paulo 	char req[200], *pos, *end;
1715b9c547cSRui Paulo 
1725b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing");
1735b9c547cSRui Paulo 	pos = req;
1745b9c547cSRui Paulo 	end = pos + sizeof(req);
1755b9c547cSRui Paulo 	pos += os_snprintf(pos, end - pos, "UMTS-AUTH");
1765b9c547cSRui Paulo 	pos += os_snprintf(pos, end - pos, ":");
1775b9c547cSRui Paulo 	pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN);
1785b9c547cSRui Paulo 	pos += os_snprintf(pos, end - pos, ":");
1795b9c547cSRui Paulo 	wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN);
1805b9c547cSRui Paulo 
1815b9c547cSRui Paulo 	eap_sm_request_sim(sm, req);
1825b9c547cSRui Paulo 	return 1;
1835b9c547cSRui Paulo }
1845b9c547cSRui Paulo 
1855b9c547cSRui Paulo 
1865b9c547cSRui Paulo static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data,
1875b9c547cSRui Paulo 				  struct eap_peer_config *conf)
1885b9c547cSRui Paulo {
1895b9c547cSRui Paulo 	char *resp, *pos;
1905b9c547cSRui Paulo 
1915b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG,
1925b9c547cSRui Paulo 		   "EAP-AKA: Use result from external USIM processing");
1935b9c547cSRui Paulo 
1945b9c547cSRui Paulo 	resp = conf->external_sim_resp;
1955b9c547cSRui Paulo 	conf->external_sim_resp = NULL;
1965b9c547cSRui Paulo 
1975b9c547cSRui Paulo 	if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) {
1985b9c547cSRui Paulo 		pos = resp + 10;
1995b9c547cSRui Paulo 		if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0)
2005b9c547cSRui Paulo 			goto invalid;
2015b9c547cSRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts,
2025b9c547cSRui Paulo 				EAP_AKA_AUTS_LEN);
2035b9c547cSRui Paulo 		os_free(resp);
2045b9c547cSRui Paulo 		return -2;
2055b9c547cSRui Paulo 	}
2065b9c547cSRui Paulo 
2075b9c547cSRui Paulo 	if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) {
2085b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response");
2095b9c547cSRui Paulo 		os_free(resp);
2105b9c547cSRui Paulo 		return -1;
2115b9c547cSRui Paulo 	}
2125b9c547cSRui Paulo 
2135b9c547cSRui Paulo 	pos = resp + 10;
2145b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN);
2155b9c547cSRui Paulo 
2165b9c547cSRui Paulo 	if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0)
2175b9c547cSRui Paulo 		goto invalid;
2185b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN);
2195b9c547cSRui Paulo 	pos += EAP_AKA_IK_LEN * 2;
2205b9c547cSRui Paulo 	if (*pos != ':')
2215b9c547cSRui Paulo 		goto invalid;
2225b9c547cSRui Paulo 	pos++;
2235b9c547cSRui Paulo 
2245b9c547cSRui Paulo 	if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0)
2255b9c547cSRui Paulo 		goto invalid;
2265b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN);
2275b9c547cSRui Paulo 	pos += EAP_AKA_CK_LEN * 2;
2285b9c547cSRui Paulo 	if (*pos != ':')
2295b9c547cSRui Paulo 		goto invalid;
2305b9c547cSRui Paulo 	pos++;
2315b9c547cSRui Paulo 
2325b9c547cSRui Paulo 	data->res_len = os_strlen(pos) / 2;
2335b9c547cSRui Paulo 	if (data->res_len > EAP_AKA_RES_MAX_LEN) {
2345b9c547cSRui Paulo 		data->res_len = 0;
2355b9c547cSRui Paulo 		goto invalid;
2365b9c547cSRui Paulo 	}
2375b9c547cSRui Paulo 	if (hexstr2bin(pos, data->res, data->res_len) < 0)
2385b9c547cSRui Paulo 		goto invalid;
2395b9c547cSRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len);
2405b9c547cSRui Paulo 
2415b9c547cSRui Paulo 	os_free(resp);
2425b9c547cSRui Paulo 	return 0;
2435b9c547cSRui Paulo 
2445b9c547cSRui Paulo invalid:
2455b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response");
2465b9c547cSRui Paulo 	os_free(resp);
2475b9c547cSRui Paulo 	return -1;
2485b9c547cSRui Paulo }
2495b9c547cSRui Paulo 
2505b9c547cSRui Paulo 
25139beb93cSSam Leffler static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
25239beb93cSSam Leffler {
25339beb93cSSam Leffler 	struct eap_peer_config *conf;
25439beb93cSSam Leffler 
25539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm");
25639beb93cSSam Leffler 
25739beb93cSSam Leffler 	conf = eap_get_config(sm);
25839beb93cSSam Leffler 	if (conf == NULL)
25939beb93cSSam Leffler 		return -1;
2605b9c547cSRui Paulo 
2615b9c547cSRui Paulo 	if (sm->external_sim) {
2625b9c547cSRui Paulo 		if (conf->external_sim_resp)
2635b9c547cSRui Paulo 			return eap_aka_ext_sim_result(sm, data, conf);
2645b9c547cSRui Paulo 		else
2655b9c547cSRui Paulo 			return eap_aka_ext_sim_req(sm, data);
2665b9c547cSRui Paulo 	}
2675b9c547cSRui Paulo 
26839beb93cSSam Leffler 	if (conf->pcsc) {
26939beb93cSSam Leffler 		return scard_umts_auth(sm->scard_ctx, data->rand,
27039beb93cSSam Leffler 				       data->autn, data->res, &data->res_len,
27139beb93cSSam Leffler 				       data->ik, data->ck, data->auts);
27239beb93cSSam Leffler 	}
27339beb93cSSam Leffler 
27439beb93cSSam Leffler #ifdef CONFIG_USIM_SIMULATOR
27539beb93cSSam Leffler 	if (conf->password) {
27639beb93cSSam Leffler 		u8 opc[16], k[16], sqn[6];
27739beb93cSSam Leffler 		const char *pos;
27839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage "
27939beb93cSSam Leffler 			   "implementation for UMTS authentication");
28039beb93cSSam Leffler 		if (conf->password_len < 78) {
28139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage "
28239beb93cSSam Leffler 				   "password");
28339beb93cSSam Leffler 			return -1;
28439beb93cSSam Leffler 		}
28539beb93cSSam Leffler 		pos = (const char *) conf->password;
28639beb93cSSam Leffler 		if (hexstr2bin(pos, k, 16))
28739beb93cSSam Leffler 			return -1;
28839beb93cSSam Leffler 		pos += 32;
28939beb93cSSam Leffler 		if (*pos != ':')
29039beb93cSSam Leffler 			return -1;
29139beb93cSSam Leffler 		pos++;
29239beb93cSSam Leffler 
29339beb93cSSam Leffler 		if (hexstr2bin(pos, opc, 16))
29439beb93cSSam Leffler 			return -1;
29539beb93cSSam Leffler 		pos += 32;
29639beb93cSSam Leffler 		if (*pos != ':')
29739beb93cSSam Leffler 			return -1;
29839beb93cSSam Leffler 		pos++;
29939beb93cSSam Leffler 
30039beb93cSSam Leffler 		if (hexstr2bin(pos, sqn, 6))
30139beb93cSSam Leffler 			return -1;
30239beb93cSSam Leffler 
30339beb93cSSam Leffler 		return milenage_check(opc, k, sqn, data->rand, data->autn,
30439beb93cSSam Leffler 				      data->ik, data->ck,
30539beb93cSSam Leffler 				      data->res, &data->res_len, data->auts);
30639beb93cSSam Leffler 	}
30739beb93cSSam Leffler #endif /* CONFIG_USIM_SIMULATOR */
30839beb93cSSam Leffler 
30939beb93cSSam Leffler #ifdef CONFIG_USIM_HARDCODED
31039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for "
31139beb93cSSam Leffler 		   "testing");
31239beb93cSSam Leffler 
31339beb93cSSam Leffler 	/* These hardcoded Kc and SRES values are used for testing.
31439beb93cSSam Leffler 	 * Could consider making them configurable. */
31539beb93cSSam Leffler 	os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN);
31639beb93cSSam Leffler 	data->res_len = EAP_AKA_RES_MAX_LEN;
31739beb93cSSam Leffler 	os_memset(data->ik, '3', EAP_AKA_IK_LEN);
31839beb93cSSam Leffler 	os_memset(data->ck, '4', EAP_AKA_CK_LEN);
31939beb93cSSam Leffler 	{
32039beb93cSSam Leffler 		u8 autn[EAP_AKA_AUTN_LEN];
32139beb93cSSam Leffler 		os_memset(autn, '1', EAP_AKA_AUTN_LEN);
3225b9c547cSRui Paulo 		if (os_memcmp_const(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) {
32339beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match "
32439beb93cSSam Leffler 				   "with expected value");
32539beb93cSSam Leffler 			return -1;
32639beb93cSSam Leffler 		}
32739beb93cSSam Leffler 	}
32839beb93cSSam Leffler #if 0
32939beb93cSSam Leffler 	{
33039beb93cSSam Leffler 		static int test_resync = 1;
33139beb93cSSam Leffler 		if (test_resync) {
33239beb93cSSam Leffler 			/* Test Resynchronization */
33339beb93cSSam Leffler 			test_resync = 0;
33439beb93cSSam Leffler 			return -2;
33539beb93cSSam Leffler 		}
33639beb93cSSam Leffler 	}
33739beb93cSSam Leffler #endif
33839beb93cSSam Leffler 	return 0;
33939beb93cSSam Leffler 
34039beb93cSSam Leffler #else /* CONFIG_USIM_HARDCODED */
34139beb93cSSam Leffler 
3425b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorithm "
34339beb93cSSam Leffler 		   "enabled");
34439beb93cSSam Leffler 	return -1;
34539beb93cSSam Leffler 
34639beb93cSSam Leffler #endif /* CONFIG_USIM_HARDCODED */
34739beb93cSSam Leffler }
34839beb93cSSam Leffler 
34939beb93cSSam Leffler 
35039beb93cSSam Leffler #define CLEAR_PSEUDONYM	0x01
35139beb93cSSam Leffler #define CLEAR_REAUTH_ID	0x02
35239beb93cSSam Leffler #define CLEAR_EAP_ID	0x04
35339beb93cSSam Leffler 
354f05cddf9SRui Paulo static void eap_aka_clear_identities(struct eap_sm *sm,
355f05cddf9SRui Paulo 				     struct eap_aka_data *data, int id)
35639beb93cSSam Leffler {
357f05cddf9SRui Paulo 	if ((id & CLEAR_PSEUDONYM) && data->pseudonym) {
358f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old pseudonym");
35939beb93cSSam Leffler 		os_free(data->pseudonym);
36039beb93cSSam Leffler 		data->pseudonym = NULL;
36139beb93cSSam Leffler 		data->pseudonym_len = 0;
36285732ac8SCy Schubert 		if (data->use_pseudonym)
363f05cddf9SRui Paulo 			eap_set_anon_id(sm, NULL, 0);
36439beb93cSSam Leffler 	}
365f05cddf9SRui Paulo 	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
366f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
36739beb93cSSam Leffler 		os_free(data->reauth_id);
36839beb93cSSam Leffler 		data->reauth_id = NULL;
36939beb93cSSam Leffler 		data->reauth_id_len = 0;
37039beb93cSSam Leffler 	}
371f05cddf9SRui Paulo 	if ((id & CLEAR_EAP_ID) && data->last_eap_identity) {
372f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old eap_id");
37339beb93cSSam Leffler 		os_free(data->last_eap_identity);
37439beb93cSSam Leffler 		data->last_eap_identity = NULL;
37539beb93cSSam Leffler 		data->last_eap_identity_len = 0;
37639beb93cSSam Leffler 	}
37739beb93cSSam Leffler }
37839beb93cSSam Leffler 
37939beb93cSSam Leffler 
380f05cddf9SRui Paulo static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data,
38139beb93cSSam Leffler 			     struct eap_sim_attrs *attr)
38239beb93cSSam Leffler {
38339beb93cSSam Leffler 	if (attr->next_pseudonym) {
384f05cddf9SRui Paulo 		const u8 *identity = NULL;
385f05cddf9SRui Paulo 		size_t identity_len = 0;
386f05cddf9SRui Paulo 		const u8 *realm = NULL;
387f05cddf9SRui Paulo 		size_t realm_len = 0;
388f05cddf9SRui Paulo 
389f05cddf9SRui Paulo 		wpa_hexdump_ascii(MSG_DEBUG,
390f05cddf9SRui Paulo 				  "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
391f05cddf9SRui Paulo 				  attr->next_pseudonym,
392f05cddf9SRui Paulo 				  attr->next_pseudonym_len);
39339beb93cSSam Leffler 		os_free(data->pseudonym);
394f05cddf9SRui Paulo 		/* Look for the realm of the permanent identity */
395f05cddf9SRui Paulo 		identity = eap_get_config_identity(sm, &identity_len);
396f05cddf9SRui Paulo 		if (identity) {
397f05cddf9SRui Paulo 			for (realm = identity, realm_len = identity_len;
398f05cddf9SRui Paulo 			     realm_len > 0; realm_len--, realm++) {
399f05cddf9SRui Paulo 				if (*realm == '@')
400f05cddf9SRui Paulo 					break;
401f05cddf9SRui Paulo 			}
402f05cddf9SRui Paulo 		}
403f05cddf9SRui Paulo 		data->pseudonym = os_malloc(attr->next_pseudonym_len +
404f05cddf9SRui Paulo 					    realm_len);
40539beb93cSSam Leffler 		if (data->pseudonym == NULL) {
40639beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
40739beb93cSSam Leffler 				   "next pseudonym");
408f05cddf9SRui Paulo 			data->pseudonym_len = 0;
40939beb93cSSam Leffler 			return -1;
41039beb93cSSam Leffler 		}
41139beb93cSSam Leffler 		os_memcpy(data->pseudonym, attr->next_pseudonym,
41239beb93cSSam Leffler 			  attr->next_pseudonym_len);
413f05cddf9SRui Paulo 		if (realm_len) {
414f05cddf9SRui Paulo 			os_memcpy(data->pseudonym + attr->next_pseudonym_len,
415f05cddf9SRui Paulo 				  realm, realm_len);
416f05cddf9SRui Paulo 		}
417f05cddf9SRui Paulo 		data->pseudonym_len = attr->next_pseudonym_len + realm_len;
41885732ac8SCy Schubert 		if (data->use_pseudonym)
41985732ac8SCy Schubert 			eap_set_anon_id(sm, data->pseudonym,
42085732ac8SCy Schubert 					data->pseudonym_len);
42139beb93cSSam Leffler 	}
42239beb93cSSam Leffler 
42339beb93cSSam Leffler 	if (attr->next_reauth_id) {
42439beb93cSSam Leffler 		os_free(data->reauth_id);
42585732ac8SCy Schubert 		data->reauth_id = os_memdup(attr->next_reauth_id,
42685732ac8SCy Schubert 					    attr->next_reauth_id_len);
42739beb93cSSam Leffler 		if (data->reauth_id == NULL) {
42839beb93cSSam Leffler 			wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
42939beb93cSSam Leffler 				   "next reauth_id");
430f05cddf9SRui Paulo 			data->reauth_id_len = 0;
43139beb93cSSam Leffler 			return -1;
43239beb93cSSam Leffler 		}
43339beb93cSSam Leffler 		data->reauth_id_len = attr->next_reauth_id_len;
43439beb93cSSam Leffler 		wpa_hexdump_ascii(MSG_DEBUG,
43539beb93cSSam Leffler 				  "EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
43639beb93cSSam Leffler 				  data->reauth_id,
43739beb93cSSam Leffler 				  data->reauth_id_len);
43839beb93cSSam Leffler 	}
43939beb93cSSam Leffler 
44039beb93cSSam Leffler 	return 0;
44139beb93cSSam Leffler }
44239beb93cSSam Leffler 
44339beb93cSSam Leffler 
44439beb93cSSam Leffler static int eap_aka_add_id_msg(struct eap_aka_data *data,
44539beb93cSSam Leffler 			      const struct wpabuf *msg)
44639beb93cSSam Leffler {
44739beb93cSSam Leffler 	if (msg == NULL)
44839beb93cSSam Leffler 		return -1;
44939beb93cSSam Leffler 
45039beb93cSSam Leffler 	if (data->id_msgs == NULL) {
45139beb93cSSam Leffler 		data->id_msgs = wpabuf_dup(msg);
45239beb93cSSam Leffler 		return data->id_msgs == NULL ? -1 : 0;
45339beb93cSSam Leffler 	}
45439beb93cSSam Leffler 
45539beb93cSSam Leffler 	if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
45639beb93cSSam Leffler 		return -1;
45739beb93cSSam Leffler 	wpabuf_put_buf(data->id_msgs, msg);
45839beb93cSSam Leffler 
45939beb93cSSam Leffler 	return 0;
46039beb93cSSam Leffler }
46139beb93cSSam Leffler 
46239beb93cSSam Leffler 
46339beb93cSSam Leffler static void eap_aka_add_checkcode(struct eap_aka_data *data,
46439beb93cSSam Leffler 				  struct eap_sim_msg *msg)
46539beb93cSSam Leffler {
46639beb93cSSam Leffler 	const u8 *addr;
46739beb93cSSam Leffler 	size_t len;
46839beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
46939beb93cSSam Leffler 
47039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_CHECKCODE");
47139beb93cSSam Leffler 
47239beb93cSSam Leffler 	if (data->id_msgs == NULL) {
47339beb93cSSam Leffler 		/*
47439beb93cSSam Leffler 		 * No EAP-AKA/Identity packets were exchanged - send empty
47539beb93cSSam Leffler 		 * checkcode.
47639beb93cSSam Leffler 		 */
47739beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
47839beb93cSSam Leffler 		return;
47939beb93cSSam Leffler 	}
48039beb93cSSam Leffler 
48139beb93cSSam Leffler 	/* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
48239beb93cSSam Leffler 	addr = wpabuf_head(data->id_msgs);
48339beb93cSSam Leffler 	len = wpabuf_len(data->id_msgs);
48439beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
48539beb93cSSam Leffler #ifdef EAP_AKA_PRIME
48639beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME)
48739beb93cSSam Leffler 		sha256_vector(1, &addr, &len, hash);
48839beb93cSSam Leffler 	else
48939beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
49039beb93cSSam Leffler 		sha1_vector(1, &addr, &len, hash);
49139beb93cSSam Leffler 
49239beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
49339beb93cSSam Leffler 			data->eap_method == EAP_TYPE_AKA_PRIME ?
49439beb93cSSam Leffler 			EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
49539beb93cSSam Leffler }
49639beb93cSSam Leffler 
49739beb93cSSam Leffler 
49839beb93cSSam Leffler static int eap_aka_verify_checkcode(struct eap_aka_data *data,
49939beb93cSSam Leffler 				    const u8 *checkcode, size_t checkcode_len)
50039beb93cSSam Leffler {
50139beb93cSSam Leffler 	const u8 *addr;
50239beb93cSSam Leffler 	size_t len;
50339beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
50439beb93cSSam Leffler 	size_t hash_len;
50539beb93cSSam Leffler 
50639beb93cSSam Leffler 	if (checkcode == NULL)
50739beb93cSSam Leffler 		return -1;
50839beb93cSSam Leffler 
50939beb93cSSam Leffler 	if (data->id_msgs == NULL) {
51039beb93cSSam Leffler 		if (checkcode_len != 0) {
51139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
51239beb93cSSam Leffler 				   "indicates that AKA/Identity messages were "
51339beb93cSSam Leffler 				   "used, but they were not");
51439beb93cSSam Leffler 			return -1;
51539beb93cSSam Leffler 		}
51639beb93cSSam Leffler 		return 0;
51739beb93cSSam Leffler 	}
51839beb93cSSam Leffler 
51939beb93cSSam Leffler 	hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
52039beb93cSSam Leffler 		EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
52139beb93cSSam Leffler 
52239beb93cSSam Leffler 	if (checkcode_len != hash_len) {
52339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
52439beb93cSSam Leffler 			   "indicates that AKA/Identity message were not "
52539beb93cSSam Leffler 			   "used, but they were");
52639beb93cSSam Leffler 		return -1;
52739beb93cSSam Leffler 	}
52839beb93cSSam Leffler 
52939beb93cSSam Leffler 	/* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
53039beb93cSSam Leffler 	addr = wpabuf_head(data->id_msgs);
53139beb93cSSam Leffler 	len = wpabuf_len(data->id_msgs);
53239beb93cSSam Leffler #ifdef EAP_AKA_PRIME
53339beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME)
53439beb93cSSam Leffler 		sha256_vector(1, &addr, &len, hash);
53539beb93cSSam Leffler 	else
53639beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
53739beb93cSSam Leffler 		sha1_vector(1, &addr, &len, hash);
53839beb93cSSam Leffler 
5395b9c547cSRui Paulo 	if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
54039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
54139beb93cSSam Leffler 		return -1;
54239beb93cSSam Leffler 	}
54339beb93cSSam Leffler 
54439beb93cSSam Leffler 	return 0;
54539beb93cSSam Leffler }
54639beb93cSSam Leffler 
54739beb93cSSam Leffler 
54839beb93cSSam Leffler static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id,
54939beb93cSSam Leffler 					    int err)
55039beb93cSSam Leffler {
55139beb93cSSam Leffler 	struct eap_sim_msg *msg;
55239beb93cSSam Leffler 
55339beb93cSSam Leffler 	eap_aka_state(data, FAILURE);
55439beb93cSSam Leffler 	data->num_id_req = 0;
55539beb93cSSam Leffler 	data->num_notification = 0;
55639beb93cSSam Leffler 
557f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)",
558f05cddf9SRui Paulo 		   err);
55939beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
56039beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_CLIENT_ERROR);
56139beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
5625b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
56339beb93cSSam Leffler }
56439beb93cSSam Leffler 
56539beb93cSSam Leffler 
56639beb93cSSam Leffler static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data,
56739beb93cSSam Leffler 						     u8 id)
56839beb93cSSam Leffler {
56939beb93cSSam Leffler 	struct eap_sim_msg *msg;
57039beb93cSSam Leffler 
57139beb93cSSam Leffler 	eap_aka_state(data, FAILURE);
57239beb93cSSam Leffler 	data->num_id_req = 0;
57339beb93cSSam Leffler 	data->num_notification = 0;
57439beb93cSSam Leffler 
57539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject "
57639beb93cSSam Leffler 		   "(id=%d)", id);
57739beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
57839beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT);
5795b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
58039beb93cSSam Leffler }
58139beb93cSSam Leffler 
58239beb93cSSam Leffler 
58339beb93cSSam Leffler static struct wpabuf * eap_aka_synchronization_failure(
58485732ac8SCy Schubert 	struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr)
58539beb93cSSam Leffler {
58639beb93cSSam Leffler 	struct eap_sim_msg *msg;
58739beb93cSSam Leffler 
58839beb93cSSam Leffler 	data->num_id_req = 0;
58939beb93cSSam Leffler 	data->num_notification = 0;
59039beb93cSSam Leffler 
59139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure "
59239beb93cSSam Leffler 		   "(id=%d)", id);
59339beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
59439beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE);
59539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_AUTS");
59639beb93cSSam Leffler 	eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
59739beb93cSSam Leffler 			     EAP_AKA_AUTS_LEN);
59885732ac8SCy Schubert 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
59985732ac8SCy Schubert 		size_t i;
60085732ac8SCy Schubert 
60185732ac8SCy Schubert 		for (i = 0; i < attr->kdf_count; i++) {
60285732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "   AT_KDF");
60385732ac8SCy Schubert 			eap_sim_msg_add(msg, EAP_SIM_AT_KDF, attr->kdf[i],
60485732ac8SCy Schubert 					NULL, 0);
60585732ac8SCy Schubert 		}
60685732ac8SCy Schubert 	}
6075b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
60839beb93cSSam Leffler }
60939beb93cSSam Leffler 
61039beb93cSSam Leffler 
61139beb93cSSam Leffler static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
61239beb93cSSam Leffler 						 struct eap_aka_data *data,
61339beb93cSSam Leffler 						 u8 id,
61439beb93cSSam Leffler 						 enum eap_sim_id_req id_req)
61539beb93cSSam Leffler {
61639beb93cSSam Leffler 	const u8 *identity = NULL;
61739beb93cSSam Leffler 	size_t identity_len = 0;
61839beb93cSSam Leffler 	struct eap_sim_msg *msg;
61939beb93cSSam Leffler 
62039beb93cSSam Leffler 	data->reauth = 0;
62139beb93cSSam Leffler 	if (id_req == ANY_ID && data->reauth_id) {
62239beb93cSSam Leffler 		identity = data->reauth_id;
62339beb93cSSam Leffler 		identity_len = data->reauth_id_len;
62439beb93cSSam Leffler 		data->reauth = 1;
62539beb93cSSam Leffler 	} else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
626*206b73d0SCy Schubert 		   data->pseudonym &&
627*206b73d0SCy Schubert 		   !eap_sim_anonymous_username(data->pseudonym,
628*206b73d0SCy Schubert 					       data->pseudonym_len)) {
62939beb93cSSam Leffler 		identity = data->pseudonym;
63039beb93cSSam Leffler 		identity_len = data->pseudonym_len;
631f05cddf9SRui Paulo 		eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
63239beb93cSSam Leffler 	} else if (id_req != NO_ID_REQ) {
63339beb93cSSam Leffler 		identity = eap_get_config_identity(sm, &identity_len);
63439beb93cSSam Leffler 		if (identity) {
635*206b73d0SCy Schubert 			int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID;
636*206b73d0SCy Schubert 
637*206b73d0SCy Schubert 			if (data->pseudonym &&
638*206b73d0SCy Schubert 			    eap_sim_anonymous_username(data->pseudonym,
639*206b73d0SCy Schubert 						       data->pseudonym_len))
640*206b73d0SCy Schubert 				ids &= ~CLEAR_PSEUDONYM;
641*206b73d0SCy Schubert 			eap_aka_clear_identities(sm, data, ids);
64239beb93cSSam Leffler 		}
64339beb93cSSam Leffler 	}
64439beb93cSSam Leffler 	if (id_req != NO_ID_REQ)
645f05cddf9SRui Paulo 		eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
64639beb93cSSam Leffler 
64739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
64839beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
64939beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_IDENTITY);
65039beb93cSSam Leffler 
65139beb93cSSam Leffler 	if (identity) {
65239beb93cSSam Leffler 		wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
65339beb93cSSam Leffler 				  identity, identity_len);
65439beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
65539beb93cSSam Leffler 				identity, identity_len);
65639beb93cSSam Leffler 	}
65739beb93cSSam Leffler 
6585b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
65939beb93cSSam Leffler }
66039beb93cSSam Leffler 
66139beb93cSSam Leffler 
66239beb93cSSam Leffler static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data,
66339beb93cSSam Leffler 						  u8 id)
66439beb93cSSam Leffler {
66539beb93cSSam Leffler 	struct eap_sim_msg *msg;
66639beb93cSSam Leffler 
66739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id);
66839beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
66939beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_CHALLENGE);
67039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_RES");
67139beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8,
67239beb93cSSam Leffler 			data->res, data->res_len);
67339beb93cSSam Leffler 	eap_aka_add_checkcode(data, msg);
67439beb93cSSam Leffler 	if (data->use_result_ind) {
67539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
67639beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
67739beb93cSSam Leffler 	}
67839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_MAC");
67939beb93cSSam Leffler 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
6805b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, (u8 *) "",
6815b9c547cSRui Paulo 				  0);
68239beb93cSSam Leffler }
68339beb93cSSam Leffler 
68439beb93cSSam Leffler 
68539beb93cSSam Leffler static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data,
68639beb93cSSam Leffler 					       u8 id, int counter_too_small,
68739beb93cSSam Leffler 					       const u8 *nonce_s)
68839beb93cSSam Leffler {
68939beb93cSSam Leffler 	struct eap_sim_msg *msg;
69039beb93cSSam Leffler 	unsigned int counter;
69139beb93cSSam Leffler 
69239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)",
69339beb93cSSam Leffler 		   id);
69439beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
69539beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_REAUTHENTICATION);
69639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_IV");
69739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
69839beb93cSSam Leffler 	eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
69939beb93cSSam Leffler 
70039beb93cSSam Leffler 	if (counter_too_small) {
70139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   *AT_COUNTER_TOO_SMALL");
70239beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
70339beb93cSSam Leffler 		counter = data->counter_too_small;
70439beb93cSSam Leffler 	} else
70539beb93cSSam Leffler 		counter = data->counter;
70639beb93cSSam Leffler 
70739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", counter);
70839beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
70939beb93cSSam Leffler 
71039beb93cSSam Leffler 	if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
71139beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
71239beb93cSSam Leffler 			   "AT_ENCR_DATA");
71339beb93cSSam Leffler 		eap_sim_msg_free(msg);
71439beb93cSSam Leffler 		return NULL;
71539beb93cSSam Leffler 	}
71639beb93cSSam Leffler 	eap_aka_add_checkcode(data, msg);
71739beb93cSSam Leffler 	if (data->use_result_ind) {
71839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
71939beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
72039beb93cSSam Leffler 	}
72139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_MAC");
72239beb93cSSam Leffler 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
7235b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, nonce_s,
72439beb93cSSam Leffler 				  EAP_SIM_NONCE_S_LEN);
72539beb93cSSam Leffler }
72639beb93cSSam Leffler 
72739beb93cSSam Leffler 
72839beb93cSSam Leffler static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data,
72939beb93cSSam Leffler 						     u8 id, u16 notification)
73039beb93cSSam Leffler {
73139beb93cSSam Leffler 	struct eap_sim_msg *msg;
73239beb93cSSam Leffler 	u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
73339beb93cSSam Leffler 
73439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id);
73539beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
73639beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_NOTIFICATION);
73739beb93cSSam Leffler 	if (k_aut && data->reauth) {
73839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_IV");
73939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
74039beb93cSSam Leffler 		eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
74139beb93cSSam Leffler 					   EAP_SIM_AT_ENCR_DATA);
74239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", data->counter);
74339beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
74439beb93cSSam Leffler 				NULL, 0);
74539beb93cSSam Leffler 		if (eap_sim_msg_add_encr_end(msg, data->k_encr,
74639beb93cSSam Leffler 					     EAP_SIM_AT_PADDING)) {
74739beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
74839beb93cSSam Leffler 				   "AT_ENCR_DATA");
74939beb93cSSam Leffler 			eap_sim_msg_free(msg);
75039beb93cSSam Leffler 			return NULL;
75139beb93cSSam Leffler 		}
75239beb93cSSam Leffler 	}
75339beb93cSSam Leffler 	if (k_aut) {
75439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_MAC");
75539beb93cSSam Leffler 		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
75639beb93cSSam Leffler 	}
7575b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, k_aut, (u8 *) "", 0);
75839beb93cSSam Leffler }
75939beb93cSSam Leffler 
76039beb93cSSam Leffler 
76139beb93cSSam Leffler static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm,
76239beb93cSSam Leffler 						struct eap_aka_data *data,
76339beb93cSSam Leffler 						u8 id,
76439beb93cSSam Leffler 						const struct wpabuf *reqData,
76539beb93cSSam Leffler 						struct eap_sim_attrs *attr)
76639beb93cSSam Leffler {
76739beb93cSSam Leffler 	int id_error;
76839beb93cSSam Leffler 	struct wpabuf *buf;
76939beb93cSSam Leffler 
77039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity");
77139beb93cSSam Leffler 
77239beb93cSSam Leffler 	id_error = 0;
77339beb93cSSam Leffler 	switch (attr->id_req) {
77439beb93cSSam Leffler 	case NO_ID_REQ:
77539beb93cSSam Leffler 		break;
77639beb93cSSam Leffler 	case ANY_ID:
77739beb93cSSam Leffler 		if (data->num_id_req > 0)
77839beb93cSSam Leffler 			id_error++;
77939beb93cSSam Leffler 		data->num_id_req++;
78039beb93cSSam Leffler 		break;
78139beb93cSSam Leffler 	case FULLAUTH_ID:
78239beb93cSSam Leffler 		if (data->num_id_req > 1)
78339beb93cSSam Leffler 			id_error++;
78439beb93cSSam Leffler 		data->num_id_req++;
78539beb93cSSam Leffler 		break;
78639beb93cSSam Leffler 	case PERMANENT_ID:
78739beb93cSSam Leffler 		if (data->num_id_req > 2)
78839beb93cSSam Leffler 			id_error++;
78939beb93cSSam Leffler 		data->num_id_req++;
79039beb93cSSam Leffler 		break;
79139beb93cSSam Leffler 	}
79239beb93cSSam Leffler 	if (id_error) {
79339beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests "
79439beb93cSSam Leffler 			   "used within one authentication");
79539beb93cSSam Leffler 		return eap_aka_client_error(data, id,
79639beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
79739beb93cSSam Leffler 	}
79839beb93cSSam Leffler 
79939beb93cSSam Leffler 	buf = eap_aka_response_identity(sm, data, id, attr->id_req);
80039beb93cSSam Leffler 
80139beb93cSSam Leffler 	if (data->prev_id != id) {
80239beb93cSSam Leffler 		eap_aka_add_id_msg(data, reqData);
80339beb93cSSam Leffler 		eap_aka_add_id_msg(data, buf);
80439beb93cSSam Leffler 		data->prev_id = id;
80539beb93cSSam Leffler 	}
80639beb93cSSam Leffler 
80739beb93cSSam Leffler 	return buf;
80839beb93cSSam Leffler }
80939beb93cSSam Leffler 
81039beb93cSSam Leffler 
81139beb93cSSam Leffler static int eap_aka_verify_mac(struct eap_aka_data *data,
81239beb93cSSam Leffler 			      const struct wpabuf *req,
81339beb93cSSam Leffler 			      const u8 *mac, const u8 *extra,
81439beb93cSSam Leffler 			      size_t extra_len)
81539beb93cSSam Leffler {
81639beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME)
81739beb93cSSam Leffler 		return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
81839beb93cSSam Leffler 						 extra_len);
81939beb93cSSam Leffler 	return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
82039beb93cSSam Leffler }
82139beb93cSSam Leffler 
82239beb93cSSam Leffler 
82339beb93cSSam Leffler #ifdef EAP_AKA_PRIME
82439beb93cSSam Leffler static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data,
82539beb93cSSam Leffler 						u8 id, u16 kdf)
82639beb93cSSam Leffler {
82739beb93cSSam Leffler 	struct eap_sim_msg *msg;
82839beb93cSSam Leffler 
82939beb93cSSam Leffler 	data->kdf_negotiation = 1;
83039beb93cSSam Leffler 	data->kdf = kdf;
83139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF "
83239beb93cSSam Leffler 		   "select)", id);
83339beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
83439beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_CHALLENGE);
83539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_KDF");
83639beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0);
8375b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
83839beb93cSSam Leffler }
83939beb93cSSam Leffler 
84039beb93cSSam Leffler 
84139beb93cSSam Leffler static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data,
84239beb93cSSam Leffler 					     u8 id, struct eap_sim_attrs *attr)
84339beb93cSSam Leffler {
84439beb93cSSam Leffler 	size_t i;
84539beb93cSSam Leffler 
84639beb93cSSam Leffler 	for (i = 0; i < attr->kdf_count; i++) {
84785732ac8SCy Schubert 		if (attr->kdf[i] == EAP_AKA_PRIME_KDF) {
84885732ac8SCy Schubert 			os_memcpy(data->last_kdf_attrs, attr->kdf,
84985732ac8SCy Schubert 				  sizeof(u16) * attr->kdf_count);
85085732ac8SCy Schubert 			data->last_kdf_count = attr->kdf_count;
85139beb93cSSam Leffler 			return eap_aka_prime_kdf_select(data, id,
85239beb93cSSam Leffler 							EAP_AKA_PRIME_KDF);
85339beb93cSSam Leffler 		}
85485732ac8SCy Schubert 	}
85539beb93cSSam Leffler 
85639beb93cSSam Leffler 	/* No matching KDF found - fail authentication as if AUTN had been
85739beb93cSSam Leffler 	 * incorrect */
85839beb93cSSam Leffler 	return eap_aka_authentication_reject(data, id);
85939beb93cSSam Leffler }
86039beb93cSSam Leffler 
86139beb93cSSam Leffler 
86239beb93cSSam Leffler static int eap_aka_prime_kdf_valid(struct eap_aka_data *data,
86339beb93cSSam Leffler 				   struct eap_sim_attrs *attr)
86439beb93cSSam Leffler {
86539beb93cSSam Leffler 	size_t i, j;
86639beb93cSSam Leffler 
86739beb93cSSam Leffler 	if (attr->kdf_count == 0)
86839beb93cSSam Leffler 		return 0;
86939beb93cSSam Leffler 
87039beb93cSSam Leffler 	/* The only allowed (and required) duplication of a KDF is the addition
87139beb93cSSam Leffler 	 * of the selected KDF into the beginning of the list. */
87239beb93cSSam Leffler 
87339beb93cSSam Leffler 	if (data->kdf_negotiation) {
87485732ac8SCy Schubert 		/* When the peer receives the new EAP-Request/AKA'-Challenge
87585732ac8SCy Schubert 		 * message, must check only requested change occurred in the
87685732ac8SCy Schubert 		 * list of AT_KDF attributes. If there are any other changes,
87785732ac8SCy Schubert 		 * the peer must behave like the case that AT_MAC had been
87885732ac8SCy Schubert 		 * incorrect and authentication is failed. These are defined in
87985732ac8SCy Schubert 		 * EAP-AKA' specification RFC 5448, Section 3.2. */
88039beb93cSSam Leffler 		if (attr->kdf[0] != data->kdf) {
88139beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
88239beb93cSSam Leffler 				   "accept the selected KDF");
88385732ac8SCy Schubert 			return -1;
88485732ac8SCy Schubert 		}
88585732ac8SCy Schubert 
88685732ac8SCy Schubert 		if (attr->kdf_count > EAP_AKA_PRIME_KDF_MAX ||
88785732ac8SCy Schubert 		    attr->kdf_count != data->last_kdf_count + 1) {
88885732ac8SCy Schubert 			wpa_printf(MSG_WARNING,
88985732ac8SCy Schubert 				   "EAP-AKA': The length of KDF attributes is wrong");
89085732ac8SCy Schubert 			return -1;
89139beb93cSSam Leffler 		}
89239beb93cSSam Leffler 
89339beb93cSSam Leffler 		for (i = 1; i < attr->kdf_count; i++) {
89485732ac8SCy Schubert 			if (attr->kdf[i] != data->last_kdf_attrs[i - 1]) {
89585732ac8SCy Schubert 				wpa_printf(MSG_WARNING,
89685732ac8SCy Schubert 					   "EAP-AKA': The KDF attributes except selected KDF are not same as original one");
89785732ac8SCy Schubert 				return -1;
89839beb93cSSam Leffler 			}
89939beb93cSSam Leffler 		}
90039beb93cSSam Leffler 	}
90139beb93cSSam Leffler 
90239beb93cSSam Leffler 	for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
90339beb93cSSam Leffler 		for (j = i + 1; j < attr->kdf_count; j++) {
90439beb93cSSam Leffler 			if (attr->kdf[i] == attr->kdf[j]) {
90539beb93cSSam Leffler 				wpa_printf(MSG_WARNING, "EAP-AKA': The server "
90639beb93cSSam Leffler 					   "included a duplicated KDF");
90739beb93cSSam Leffler 				return 0;
90839beb93cSSam Leffler 			}
90939beb93cSSam Leffler 		}
91039beb93cSSam Leffler 	}
91139beb93cSSam Leffler 
91239beb93cSSam Leffler 	return 1;
91339beb93cSSam Leffler }
91439beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
91539beb93cSSam Leffler 
91639beb93cSSam Leffler 
91739beb93cSSam Leffler static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
91839beb93cSSam Leffler 						 struct eap_aka_data *data,
91939beb93cSSam Leffler 						 u8 id,
92039beb93cSSam Leffler 						 const struct wpabuf *reqData,
92139beb93cSSam Leffler 						 struct eap_sim_attrs *attr)
92239beb93cSSam Leffler {
92339beb93cSSam Leffler 	const u8 *identity;
92439beb93cSSam Leffler 	size_t identity_len;
92539beb93cSSam Leffler 	int res;
92639beb93cSSam Leffler 	struct eap_sim_attrs eattr;
92739beb93cSSam Leffler 
92839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge");
92939beb93cSSam Leffler 
93039beb93cSSam Leffler 	if (attr->checkcode &&
93139beb93cSSam Leffler 	    eap_aka_verify_checkcode(data, attr->checkcode,
93239beb93cSSam Leffler 				     attr->checkcode_len)) {
93339beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
93439beb93cSSam Leffler 			   "message");
935*206b73d0SCy Schubert #ifdef TEST_FUZZ
936*206b73d0SCy Schubert 		wpa_printf(MSG_INFO,
937*206b73d0SCy Schubert 			   "TEST: Ignore AT_CHECKCODE mismatch for fuzz testing");
938*206b73d0SCy Schubert #else /* TEST_FUZZ */
93939beb93cSSam Leffler 		return eap_aka_client_error(data, id,
94039beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
941*206b73d0SCy Schubert #endif /* TEST_FUZZ */
94239beb93cSSam Leffler 	}
94339beb93cSSam Leffler 
94439beb93cSSam Leffler #ifdef EAP_AKA_PRIME
94539beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
94639beb93cSSam Leffler 		if (!attr->kdf_input || attr->kdf_input_len == 0) {
94739beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message "
94839beb93cSSam Leffler 				   "did not include non-empty AT_KDF_INPUT");
94939beb93cSSam Leffler 			/* Fail authentication as if AUTN had been incorrect */
95039beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
95139beb93cSSam Leffler 		}
95239beb93cSSam Leffler 		os_free(data->network_name);
95385732ac8SCy Schubert 		data->network_name = os_memdup(attr->kdf_input,
95485732ac8SCy Schubert 					       attr->kdf_input_len);
95539beb93cSSam Leffler 		if (data->network_name == NULL) {
95639beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
95739beb93cSSam Leffler 				   "storing Network Name");
95839beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
95939beb93cSSam Leffler 		}
96039beb93cSSam Leffler 		data->network_name_len = attr->kdf_input_len;
96139beb93cSSam Leffler 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
96239beb93cSSam Leffler 				  "(AT_KDF_INPUT)",
96339beb93cSSam Leffler 				  data->network_name, data->network_name_len);
96439beb93cSSam Leffler 		/* TODO: check Network Name per 3GPP.33.402 */
96539beb93cSSam Leffler 
96685732ac8SCy Schubert 		res = eap_aka_prime_kdf_valid(data, attr);
96785732ac8SCy Schubert 		if (res == 0)
96839beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
96985732ac8SCy Schubert 		else if (res == -1)
97085732ac8SCy Schubert 			return eap_aka_client_error(
97185732ac8SCy Schubert 				data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
97239beb93cSSam Leffler 
97339beb93cSSam Leffler 		if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
97439beb93cSSam Leffler 			return eap_aka_prime_kdf_neg(data, id, attr);
97539beb93cSSam Leffler 
97639beb93cSSam Leffler 		data->kdf = EAP_AKA_PRIME_KDF;
97739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
97839beb93cSSam Leffler 	}
97939beb93cSSam Leffler 
98039beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA && attr->bidding) {
98139beb93cSSam Leffler 		u16 flags = WPA_GET_BE16(attr->bidding);
98239beb93cSSam Leffler 		if ((flags & EAP_AKA_BIDDING_FLAG_D) &&
98339beb93cSSam Leffler 		    eap_allowed_method(sm, EAP_VENDOR_IETF,
98439beb93cSSam Leffler 				       EAP_TYPE_AKA_PRIME)) {
98539beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from "
98639beb93cSSam Leffler 				   "AKA' to AKA detected");
98739beb93cSSam Leffler 			/* Fail authentication as if AUTN had been incorrect */
98839beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
98939beb93cSSam Leffler 		}
99039beb93cSSam Leffler 	}
99139beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
99239beb93cSSam Leffler 
99339beb93cSSam Leffler 	data->reauth = 0;
99439beb93cSSam Leffler 	if (!attr->mac || !attr->rand || !attr->autn) {
99539beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
99639beb93cSSam Leffler 			   "did not include%s%s%s",
99739beb93cSSam Leffler 			   !attr->mac ? " AT_MAC" : "",
99839beb93cSSam Leffler 			   !attr->rand ? " AT_RAND" : "",
99939beb93cSSam Leffler 			   !attr->autn ? " AT_AUTN" : "");
100039beb93cSSam Leffler 		return eap_aka_client_error(data, id,
100139beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
100239beb93cSSam Leffler 	}
100339beb93cSSam Leffler 	os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN);
100439beb93cSSam Leffler 	os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN);
100539beb93cSSam Leffler 
100639beb93cSSam Leffler 	res = eap_aka_umts_auth(sm, data);
100739beb93cSSam Leffler 	if (res == -1) {
100839beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
100939beb93cSSam Leffler 			   "failed (AUTN)");
101039beb93cSSam Leffler 		return eap_aka_authentication_reject(data, id);
101139beb93cSSam Leffler 	} else if (res == -2) {
101239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
101339beb93cSSam Leffler 			   "failed (AUTN seq# -> AUTS)");
101485732ac8SCy Schubert 		return eap_aka_synchronization_failure(data, id, attr);
10155b9c547cSRui Paulo 	} else if (res > 0) {
10165b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing");
10175b9c547cSRui Paulo 		return NULL;
101839beb93cSSam Leffler 	} else if (res) {
101939beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed");
102039beb93cSSam Leffler 		return eap_aka_client_error(data, id,
102139beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
102239beb93cSSam Leffler 	}
102339beb93cSSam Leffler #ifdef EAP_AKA_PRIME
102439beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
102539beb93cSSam Leffler 		/* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
102639beb93cSSam Leffler 		 * needed 6-octet SQN ^ AK for CK',IK' derivation */
102739beb93cSSam Leffler 		u16 amf = WPA_GET_BE16(data->autn + 6);
102839beb93cSSam Leffler 		if (!(amf & 0x8000)) {
102939beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit "
103039beb93cSSam Leffler 				   "not set (AMF=0x%4x)", amf);
103139beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
103239beb93cSSam Leffler 		}
103339beb93cSSam Leffler 		eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
103439beb93cSSam Leffler 						 data->autn,
103539beb93cSSam Leffler 						 data->network_name,
103639beb93cSSam Leffler 						 data->network_name_len);
103739beb93cSSam Leffler 	}
103839beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
103939beb93cSSam Leffler 	if (data->last_eap_identity) {
104039beb93cSSam Leffler 		identity = data->last_eap_identity;
104139beb93cSSam Leffler 		identity_len = data->last_eap_identity_len;
1042*206b73d0SCy Schubert 	} else if (data->pseudonym &&
1043*206b73d0SCy Schubert 		   !eap_sim_anonymous_username(data->pseudonym,
1044*206b73d0SCy Schubert 					       data->pseudonym_len)) {
104539beb93cSSam Leffler 		identity = data->pseudonym;
104639beb93cSSam Leffler 		identity_len = data->pseudonym_len;
104785732ac8SCy Schubert 	} else {
104885732ac8SCy Schubert 		struct eap_peer_config *config;
104985732ac8SCy Schubert 
105085732ac8SCy Schubert 		config = eap_get_config(sm);
105185732ac8SCy Schubert 		if (config && config->imsi_identity) {
105285732ac8SCy Schubert 			identity = config->imsi_identity;
105385732ac8SCy Schubert 			identity_len = config->imsi_identity_len;
105485732ac8SCy Schubert 		} else {
105539beb93cSSam Leffler 			identity = eap_get_config_identity(sm, &identity_len);
105685732ac8SCy Schubert 		}
105785732ac8SCy Schubert 	}
105839beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
105939beb93cSSam Leffler 			  "derivation", identity, identity_len);
106039beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
106139beb93cSSam Leffler 		eap_aka_prime_derive_keys(identity, identity_len, data->ik,
106239beb93cSSam Leffler 					  data->ck, data->k_encr, data->k_aut,
106339beb93cSSam Leffler 					  data->k_re, data->msk, data->emsk);
106439beb93cSSam Leffler 	} else {
106539beb93cSSam Leffler 		eap_aka_derive_mk(identity, identity_len, data->ik, data->ck,
106639beb93cSSam Leffler 				  data->mk);
106739beb93cSSam Leffler 		eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
106839beb93cSSam Leffler 				    data->msk, data->emsk);
106939beb93cSSam Leffler 	}
107039beb93cSSam Leffler 	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
107139beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
107239beb93cSSam Leffler 			   "used invalid AT_MAC");
1073*206b73d0SCy Schubert #ifdef TEST_FUZZ
1074*206b73d0SCy Schubert 		wpa_printf(MSG_INFO,
1075*206b73d0SCy Schubert 			   "TEST: Ignore AT_MAC mismatch for fuzz testing");
1076*206b73d0SCy Schubert #else /* TEST_FUZZ */
107739beb93cSSam Leffler 		return eap_aka_client_error(data, id,
107839beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
1079*206b73d0SCy Schubert #endif /* TEST_FUZZ */
108039beb93cSSam Leffler 	}
108139beb93cSSam Leffler 
1082f05cddf9SRui Paulo 	/* Old reauthentication identity must not be used anymore. In
1083f05cddf9SRui Paulo 	 * other words, if no new identities are received, full
1084f05cddf9SRui Paulo 	 * authentication will be used on next reauthentication (using
1085f05cddf9SRui Paulo 	 * pseudonym identity or permanent identity). */
1086f05cddf9SRui Paulo 	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
108739beb93cSSam Leffler 
108839beb93cSSam Leffler 	if (attr->encr_data) {
108939beb93cSSam Leffler 		u8 *decrypted;
109039beb93cSSam Leffler 		decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
109139beb93cSSam Leffler 					       attr->encr_data_len, attr->iv,
109239beb93cSSam Leffler 					       &eattr, 0);
109339beb93cSSam Leffler 		if (decrypted == NULL) {
109439beb93cSSam Leffler 			return eap_aka_client_error(
109539beb93cSSam Leffler 				data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
109639beb93cSSam Leffler 		}
1097f05cddf9SRui Paulo 		eap_aka_learn_ids(sm, data, &eattr);
109839beb93cSSam Leffler 		os_free(decrypted);
109939beb93cSSam Leffler 	}
110039beb93cSSam Leffler 
110139beb93cSSam Leffler 	if (data->result_ind && attr->result_ind)
110239beb93cSSam Leffler 		data->use_result_ind = 1;
110339beb93cSSam Leffler 
11045b9c547cSRui Paulo 	if (data->state != FAILURE) {
110539beb93cSSam Leffler 		eap_aka_state(data, data->use_result_ind ?
110639beb93cSSam Leffler 			      RESULT_SUCCESS : SUCCESS);
110739beb93cSSam Leffler 	}
110839beb93cSSam Leffler 
110939beb93cSSam Leffler 	data->num_id_req = 0;
111039beb93cSSam Leffler 	data->num_notification = 0;
111139beb93cSSam Leffler 	/* RFC 4187 specifies that counter is initialized to one after
111239beb93cSSam Leffler 	 * fullauth, but initializing it to zero makes it easier to implement
111339beb93cSSam Leffler 	 * reauth verification. */
111439beb93cSSam Leffler 	data->counter = 0;
111539beb93cSSam Leffler 	return eap_aka_response_challenge(data, id);
111639beb93cSSam Leffler }
111739beb93cSSam Leffler 
111839beb93cSSam Leffler 
111939beb93cSSam Leffler static int eap_aka_process_notification_reauth(struct eap_aka_data *data,
112039beb93cSSam Leffler 					       struct eap_sim_attrs *attr)
112139beb93cSSam Leffler {
112239beb93cSSam Leffler 	struct eap_sim_attrs eattr;
112339beb93cSSam Leffler 	u8 *decrypted;
112439beb93cSSam Leffler 
112539beb93cSSam Leffler 	if (attr->encr_data == NULL || attr->iv == NULL) {
112639beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after "
112739beb93cSSam Leffler 			   "reauth did not include encrypted data");
112839beb93cSSam Leffler 		return -1;
112939beb93cSSam Leffler 	}
113039beb93cSSam Leffler 
113139beb93cSSam Leffler 	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
113239beb93cSSam Leffler 				       attr->encr_data_len, attr->iv, &eattr,
113339beb93cSSam Leffler 				       0);
113439beb93cSSam Leffler 	if (decrypted == NULL) {
113539beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
113639beb93cSSam Leffler 			   "data from notification message");
113739beb93cSSam Leffler 		return -1;
113839beb93cSSam Leffler 	}
113939beb93cSSam Leffler 
114039beb93cSSam Leffler 	if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
114139beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification "
114239beb93cSSam Leffler 			   "message does not match with counter in reauth "
114339beb93cSSam Leffler 			   "message");
114439beb93cSSam Leffler 		os_free(decrypted);
114539beb93cSSam Leffler 		return -1;
114639beb93cSSam Leffler 	}
114739beb93cSSam Leffler 
114839beb93cSSam Leffler 	os_free(decrypted);
114939beb93cSSam Leffler 	return 0;
115039beb93cSSam Leffler }
115139beb93cSSam Leffler 
115239beb93cSSam Leffler 
115339beb93cSSam Leffler static int eap_aka_process_notification_auth(struct eap_aka_data *data,
115439beb93cSSam Leffler 					     const struct wpabuf *reqData,
115539beb93cSSam Leffler 					     struct eap_sim_attrs *attr)
115639beb93cSSam Leffler {
115739beb93cSSam Leffler 	if (attr->mac == NULL) {
115839beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth "
115939beb93cSSam Leffler 			   "Notification message");
116039beb93cSSam Leffler 		return -1;
116139beb93cSSam Leffler 	}
116239beb93cSSam Leffler 
116339beb93cSSam Leffler 	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
116439beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Notification message "
116539beb93cSSam Leffler 			   "used invalid AT_MAC");
116639beb93cSSam Leffler 		return -1;
116739beb93cSSam Leffler 	}
116839beb93cSSam Leffler 
116939beb93cSSam Leffler 	if (data->reauth &&
117039beb93cSSam Leffler 	    eap_aka_process_notification_reauth(data, attr)) {
117139beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification "
117239beb93cSSam Leffler 			   "message after reauth");
117339beb93cSSam Leffler 		return -1;
117439beb93cSSam Leffler 	}
117539beb93cSSam Leffler 
117639beb93cSSam Leffler 	return 0;
117739beb93cSSam Leffler }
117839beb93cSSam Leffler 
117939beb93cSSam Leffler 
118039beb93cSSam Leffler static struct wpabuf * eap_aka_process_notification(
118139beb93cSSam Leffler 	struct eap_sm *sm, struct eap_aka_data *data, u8 id,
118239beb93cSSam Leffler 	const struct wpabuf *reqData, struct eap_sim_attrs *attr)
118339beb93cSSam Leffler {
118439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification");
118539beb93cSSam Leffler 	if (data->num_notification > 0) {
118639beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: too many notification "
118739beb93cSSam Leffler 			   "rounds (only one allowed)");
118839beb93cSSam Leffler 		return eap_aka_client_error(data, id,
118939beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
119039beb93cSSam Leffler 	}
119139beb93cSSam Leffler 	data->num_notification++;
119239beb93cSSam Leffler 	if (attr->notification == -1) {
119339beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in "
119439beb93cSSam Leffler 			   "Notification message");
119539beb93cSSam Leffler 		return eap_aka_client_error(data, id,
119639beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
119739beb93cSSam Leffler 	}
119839beb93cSSam Leffler 
119939beb93cSSam Leffler 	if ((attr->notification & 0x4000) == 0 &&
120039beb93cSSam Leffler 	    eap_aka_process_notification_auth(data, reqData, attr)) {
120139beb93cSSam Leffler 		return eap_aka_client_error(data, id,
120239beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
120339beb93cSSam Leffler 	}
120439beb93cSSam Leffler 
120539beb93cSSam Leffler 	eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
120639beb93cSSam Leffler 	if (attr->notification >= 0 && attr->notification < 32768) {
120785732ac8SCy Schubert 		data->error_code = attr->notification;
120839beb93cSSam Leffler 		eap_aka_state(data, FAILURE);
120939beb93cSSam Leffler 	} else if (attr->notification == EAP_SIM_SUCCESS &&
121039beb93cSSam Leffler 		   data->state == RESULT_SUCCESS)
121139beb93cSSam Leffler 		eap_aka_state(data, SUCCESS);
121239beb93cSSam Leffler 	return eap_aka_response_notification(data, id, attr->notification);
121339beb93cSSam Leffler }
121439beb93cSSam Leffler 
121539beb93cSSam Leffler 
121639beb93cSSam Leffler static struct wpabuf * eap_aka_process_reauthentication(
121739beb93cSSam Leffler 	struct eap_sm *sm, struct eap_aka_data *data, u8 id,
121839beb93cSSam Leffler 	const struct wpabuf *reqData, struct eap_sim_attrs *attr)
121939beb93cSSam Leffler {
122039beb93cSSam Leffler 	struct eap_sim_attrs eattr;
122139beb93cSSam Leffler 	u8 *decrypted;
122239beb93cSSam Leffler 
122339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication");
122439beb93cSSam Leffler 
122539beb93cSSam Leffler 	if (attr->checkcode &&
122639beb93cSSam Leffler 	    eap_aka_verify_checkcode(data, attr->checkcode,
122739beb93cSSam Leffler 				     attr->checkcode_len)) {
1228*206b73d0SCy Schubert #ifdef TEST_FUZZ
1229*206b73d0SCy Schubert 		wpa_printf(MSG_INFO,
1230*206b73d0SCy Schubert 			   "TEST: Ignore AT_CHECKCODE mismatch for fuzz testing");
1231*206b73d0SCy Schubert #else /* TEST_FUZZ */
123239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
123339beb93cSSam Leffler 			   "message");
1234*206b73d0SCy Schubert #endif /* TEST_FUZZ */
123539beb93cSSam Leffler 		return eap_aka_client_error(data, id,
123639beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
123739beb93cSSam Leffler 	}
123839beb93cSSam Leffler 
123939beb93cSSam Leffler 	if (data->reauth_id == NULL) {
124039beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying "
124139beb93cSSam Leffler 			   "reauthentication, but no reauth_id available");
124239beb93cSSam Leffler 		return eap_aka_client_error(data, id,
124339beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
124439beb93cSSam Leffler 	}
124539beb93cSSam Leffler 
124639beb93cSSam Leffler 	data->reauth = 1;
124739beb93cSSam Leffler 	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
124839beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
124939beb93cSSam Leffler 			   "did not have valid AT_MAC");
125039beb93cSSam Leffler 		return eap_aka_client_error(data, id,
125139beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
125239beb93cSSam Leffler 	}
125339beb93cSSam Leffler 
1254*206b73d0SCy Schubert 	/* At this stage the received MAC has been verified. Use this MAC for
1255*206b73d0SCy Schubert 	 * reauth Session-Id calculation if all other checks pass.
1256*206b73d0SCy Schubert 	 * The peer does not use the local MAC but the received MAC in deriving
1257*206b73d0SCy Schubert 	 * Session-Id. */
1258*206b73d0SCy Schubert 	os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN);
1259*206b73d0SCy Schubert 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Server MAC",
1260*206b73d0SCy Schubert 		    data->reauth_mac, EAP_SIM_MAC_LEN);
1261*206b73d0SCy Schubert 
126239beb93cSSam Leffler 	if (attr->encr_data == NULL || attr->iv == NULL) {
126339beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
126439beb93cSSam Leffler 			   "message did not include encrypted data");
126539beb93cSSam Leffler 		return eap_aka_client_error(data, id,
126639beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
126739beb93cSSam Leffler 	}
126839beb93cSSam Leffler 
126939beb93cSSam Leffler 	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
127039beb93cSSam Leffler 				       attr->encr_data_len, attr->iv, &eattr,
127139beb93cSSam Leffler 				       0);
127239beb93cSSam Leffler 	if (decrypted == NULL) {
127339beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
127439beb93cSSam Leffler 			   "data from reauthentication message");
127539beb93cSSam Leffler 		return eap_aka_client_error(data, id,
127639beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
127739beb93cSSam Leffler 	}
127839beb93cSSam Leffler 
127939beb93cSSam Leffler 	if (eattr.nonce_s == NULL || eattr.counter < 0) {
128039beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet",
128139beb93cSSam Leffler 			   !eattr.nonce_s ? " AT_NONCE_S" : "",
128239beb93cSSam Leffler 			   eattr.counter < 0 ? " AT_COUNTER" : "");
128339beb93cSSam Leffler 		os_free(decrypted);
128439beb93cSSam Leffler 		return eap_aka_client_error(data, id,
128539beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
128639beb93cSSam Leffler 	}
128739beb93cSSam Leffler 
128839beb93cSSam Leffler 	if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
128939beb93cSSam Leffler 		struct wpabuf *res;
129039beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter "
129139beb93cSSam Leffler 			   "(%d <= %d)", eattr.counter, data->counter);
129239beb93cSSam Leffler 		data->counter_too_small = eattr.counter;
129339beb93cSSam Leffler 
129439beb93cSSam Leffler 		/* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
129539beb93cSSam Leffler 		 * reauth_id must not be used to start a new reauthentication.
129639beb93cSSam Leffler 		 * However, since it was used in the last EAP-Response-Identity
129739beb93cSSam Leffler 		 * packet, it has to saved for the following fullauth to be
129839beb93cSSam Leffler 		 * used in MK derivation. */
129939beb93cSSam Leffler 		os_free(data->last_eap_identity);
130039beb93cSSam Leffler 		data->last_eap_identity = data->reauth_id;
130139beb93cSSam Leffler 		data->last_eap_identity_len = data->reauth_id_len;
130239beb93cSSam Leffler 		data->reauth_id = NULL;
130339beb93cSSam Leffler 		data->reauth_id_len = 0;
130439beb93cSSam Leffler 
130539beb93cSSam Leffler 		res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s);
130639beb93cSSam Leffler 		os_free(decrypted);
130739beb93cSSam Leffler 
130839beb93cSSam Leffler 		return res;
130939beb93cSSam Leffler 	}
131039beb93cSSam Leffler 	data->counter = eattr.counter;
131139beb93cSSam Leffler 
131239beb93cSSam Leffler 	os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
131339beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S",
131439beb93cSSam Leffler 		    data->nonce_s, EAP_SIM_NONCE_S_LEN);
131539beb93cSSam Leffler 
131639beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
131739beb93cSSam Leffler 		eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
131839beb93cSSam Leffler 						 data->reauth_id,
131939beb93cSSam Leffler 						 data->reauth_id_len,
132039beb93cSSam Leffler 						 data->nonce_s,
132139beb93cSSam Leffler 						 data->msk, data->emsk);
132239beb93cSSam Leffler 	} else {
132339beb93cSSam Leffler 		eap_sim_derive_keys_reauth(data->counter, data->reauth_id,
132439beb93cSSam Leffler 					   data->reauth_id_len,
132539beb93cSSam Leffler 					   data->nonce_s, data->mk,
132639beb93cSSam Leffler 					   data->msk, data->emsk);
132739beb93cSSam Leffler 	}
1328f05cddf9SRui Paulo 	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
1329f05cddf9SRui Paulo 	eap_aka_learn_ids(sm, data, &eattr);
133039beb93cSSam Leffler 
133139beb93cSSam Leffler 	if (data->result_ind && attr->result_ind)
133239beb93cSSam Leffler 		data->use_result_ind = 1;
133339beb93cSSam Leffler 
13345b9c547cSRui Paulo 	if (data->state != FAILURE) {
133539beb93cSSam Leffler 		eap_aka_state(data, data->use_result_ind ?
133639beb93cSSam Leffler 			      RESULT_SUCCESS : SUCCESS);
133739beb93cSSam Leffler 	}
133839beb93cSSam Leffler 
133939beb93cSSam Leffler 	data->num_id_req = 0;
134039beb93cSSam Leffler 	data->num_notification = 0;
134139beb93cSSam Leffler 	if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
134239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
134339beb93cSSam Leffler 			   "fast reauths performed - force fullauth");
1344f05cddf9SRui Paulo 		eap_aka_clear_identities(sm, data,
1345f05cddf9SRui Paulo 					 CLEAR_REAUTH_ID | CLEAR_EAP_ID);
134639beb93cSSam Leffler 	}
134739beb93cSSam Leffler 	os_free(decrypted);
134839beb93cSSam Leffler 	return eap_aka_response_reauth(data, id, 0, data->nonce_s);
134939beb93cSSam Leffler }
135039beb93cSSam Leffler 
135139beb93cSSam Leffler 
135239beb93cSSam Leffler static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv,
135339beb93cSSam Leffler 				       struct eap_method_ret *ret,
135439beb93cSSam Leffler 				       const struct wpabuf *reqData)
135539beb93cSSam Leffler {
135639beb93cSSam Leffler 	struct eap_aka_data *data = priv;
135739beb93cSSam Leffler 	const struct eap_hdr *req;
135839beb93cSSam Leffler 	u8 subtype, id;
135939beb93cSSam Leffler 	struct wpabuf *res;
136039beb93cSSam Leffler 	const u8 *pos;
136139beb93cSSam Leffler 	struct eap_sim_attrs attr;
136239beb93cSSam Leffler 	size_t len;
136339beb93cSSam Leffler 
136439beb93cSSam Leffler 	wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData);
136539beb93cSSam Leffler 	if (eap_get_config_identity(sm, &len) == NULL) {
136639beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured");
136739beb93cSSam Leffler 		eap_sm_request_identity(sm);
136839beb93cSSam Leffler 		ret->ignore = TRUE;
136939beb93cSSam Leffler 		return NULL;
137039beb93cSSam Leffler 	}
137139beb93cSSam Leffler 
137239beb93cSSam Leffler 	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData,
137339beb93cSSam Leffler 			       &len);
1374325151a3SRui Paulo 	if (pos == NULL || len < 3) {
137539beb93cSSam Leffler 		ret->ignore = TRUE;
137639beb93cSSam Leffler 		return NULL;
137739beb93cSSam Leffler 	}
137839beb93cSSam Leffler 	req = wpabuf_head(reqData);
137939beb93cSSam Leffler 	id = req->identifier;
138039beb93cSSam Leffler 	len = be_to_host16(req->length);
138139beb93cSSam Leffler 
138239beb93cSSam Leffler 	ret->ignore = FALSE;
138339beb93cSSam Leffler 	ret->methodState = METHOD_MAY_CONT;
138439beb93cSSam Leffler 	ret->decision = DECISION_FAIL;
138539beb93cSSam Leffler 	ret->allowNotifications = TRUE;
138639beb93cSSam Leffler 
138739beb93cSSam Leffler 	subtype = *pos++;
138839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
138939beb93cSSam Leffler 	pos += 2; /* Reserved */
139039beb93cSSam Leffler 
139139beb93cSSam Leffler 	if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr,
139239beb93cSSam Leffler 			       data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
139339beb93cSSam Leffler 			       0)) {
139439beb93cSSam Leffler 		res = eap_aka_client_error(data, id,
139539beb93cSSam Leffler 					   EAP_AKA_UNABLE_TO_PROCESS_PACKET);
139639beb93cSSam Leffler 		goto done;
139739beb93cSSam Leffler 	}
139839beb93cSSam Leffler 
139939beb93cSSam Leffler 	switch (subtype) {
140039beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_IDENTITY:
140139beb93cSSam Leffler 		res = eap_aka_process_identity(sm, data, id, reqData, &attr);
140239beb93cSSam Leffler 		break;
140339beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_CHALLENGE:
140439beb93cSSam Leffler 		res = eap_aka_process_challenge(sm, data, id, reqData, &attr);
140539beb93cSSam Leffler 		break;
140639beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_NOTIFICATION:
140739beb93cSSam Leffler 		res = eap_aka_process_notification(sm, data, id, reqData,
140839beb93cSSam Leffler 						   &attr);
140939beb93cSSam Leffler 		break;
141039beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_REAUTHENTICATION:
141139beb93cSSam Leffler 		res = eap_aka_process_reauthentication(sm, data, id, reqData,
141239beb93cSSam Leffler 						       &attr);
141339beb93cSSam Leffler 		break;
141439beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_CLIENT_ERROR:
141539beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error");
141639beb93cSSam Leffler 		res = eap_aka_client_error(data, id,
141739beb93cSSam Leffler 					   EAP_AKA_UNABLE_TO_PROCESS_PACKET);
141839beb93cSSam Leffler 		break;
141939beb93cSSam Leffler 	default:
142039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype);
142139beb93cSSam Leffler 		res = eap_aka_client_error(data, id,
142239beb93cSSam Leffler 					   EAP_AKA_UNABLE_TO_PROCESS_PACKET);
142339beb93cSSam Leffler 		break;
142439beb93cSSam Leffler 	}
142539beb93cSSam Leffler 
142639beb93cSSam Leffler done:
142739beb93cSSam Leffler 	if (data->state == FAILURE) {
142839beb93cSSam Leffler 		ret->decision = DECISION_FAIL;
142939beb93cSSam Leffler 		ret->methodState = METHOD_DONE;
143039beb93cSSam Leffler 	} else if (data->state == SUCCESS) {
143139beb93cSSam Leffler 		ret->decision = data->use_result_ind ?
143239beb93cSSam Leffler 			DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
143339beb93cSSam Leffler 		/*
143439beb93cSSam Leffler 		 * It is possible for the server to reply with AKA
143539beb93cSSam Leffler 		 * Notification, so we must allow the method to continue and
143639beb93cSSam Leffler 		 * not only accept EAP-Success at this point.
143739beb93cSSam Leffler 		 */
143839beb93cSSam Leffler 		ret->methodState = data->use_result_ind ?
143939beb93cSSam Leffler 			METHOD_DONE : METHOD_MAY_CONT;
14405b9c547cSRui Paulo 	} else if (data->state == RESULT_SUCCESS)
144139beb93cSSam Leffler 		ret->methodState = METHOD_CONT;
144239beb93cSSam Leffler 
144339beb93cSSam Leffler 	if (ret->methodState == METHOD_DONE) {
144439beb93cSSam Leffler 		ret->allowNotifications = FALSE;
144539beb93cSSam Leffler 	}
144639beb93cSSam Leffler 
144739beb93cSSam Leffler 	return res;
144839beb93cSSam Leffler }
144939beb93cSSam Leffler 
145039beb93cSSam Leffler 
145139beb93cSSam Leffler static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv)
145239beb93cSSam Leffler {
145339beb93cSSam Leffler 	struct eap_aka_data *data = priv;
145439beb93cSSam Leffler 	return data->pseudonym || data->reauth_id;
145539beb93cSSam Leffler }
145639beb93cSSam Leffler 
145739beb93cSSam Leffler 
145839beb93cSSam Leffler static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
145939beb93cSSam Leffler {
146039beb93cSSam Leffler 	struct eap_aka_data *data = priv;
1461f05cddf9SRui Paulo 	eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
146239beb93cSSam Leffler 	data->prev_id = -1;
146339beb93cSSam Leffler 	wpabuf_free(data->id_msgs);
146439beb93cSSam Leffler 	data->id_msgs = NULL;
146539beb93cSSam Leffler 	data->use_result_ind = 0;
146639beb93cSSam Leffler 	data->kdf_negotiation = 0;
14675b9c547cSRui Paulo 	eap_aka_clear_keys(data, 1);
146839beb93cSSam Leffler }
146939beb93cSSam Leffler 
147039beb93cSSam Leffler 
147139beb93cSSam Leffler static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv)
147239beb93cSSam Leffler {
147339beb93cSSam Leffler 	struct eap_aka_data *data = priv;
147439beb93cSSam Leffler 	data->num_id_req = 0;
147539beb93cSSam Leffler 	data->num_notification = 0;
147639beb93cSSam Leffler 	eap_aka_state(data, CONTINUE);
147739beb93cSSam Leffler 	return priv;
147839beb93cSSam Leffler }
147939beb93cSSam Leffler 
148039beb93cSSam Leffler 
148139beb93cSSam Leffler static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv,
148239beb93cSSam Leffler 				       size_t *len)
148339beb93cSSam Leffler {
148439beb93cSSam Leffler 	struct eap_aka_data *data = priv;
148539beb93cSSam Leffler 
148639beb93cSSam Leffler 	if (data->reauth_id) {
148739beb93cSSam Leffler 		*len = data->reauth_id_len;
148839beb93cSSam Leffler 		return data->reauth_id;
148939beb93cSSam Leffler 	}
149039beb93cSSam Leffler 
149139beb93cSSam Leffler 	if (data->pseudonym) {
149239beb93cSSam Leffler 		*len = data->pseudonym_len;
149339beb93cSSam Leffler 		return data->pseudonym;
149439beb93cSSam Leffler 	}
149539beb93cSSam Leffler 
149639beb93cSSam Leffler 	return NULL;
149739beb93cSSam Leffler }
149839beb93cSSam Leffler 
149939beb93cSSam Leffler 
150039beb93cSSam Leffler static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv)
150139beb93cSSam Leffler {
150239beb93cSSam Leffler 	struct eap_aka_data *data = priv;
150339beb93cSSam Leffler 	return data->state == SUCCESS;
150439beb93cSSam Leffler }
150539beb93cSSam Leffler 
150639beb93cSSam Leffler 
150739beb93cSSam Leffler static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
150839beb93cSSam Leffler {
150939beb93cSSam Leffler 	struct eap_aka_data *data = priv;
151039beb93cSSam Leffler 	u8 *key;
151139beb93cSSam Leffler 
151239beb93cSSam Leffler 	if (data->state != SUCCESS)
151339beb93cSSam Leffler 		return NULL;
151439beb93cSSam Leffler 
151585732ac8SCy Schubert 	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
151639beb93cSSam Leffler 	if (key == NULL)
151739beb93cSSam Leffler 		return NULL;
151839beb93cSSam Leffler 
151939beb93cSSam Leffler 	*len = EAP_SIM_KEYING_DATA_LEN;
152039beb93cSSam Leffler 
152139beb93cSSam Leffler 	return key;
152239beb93cSSam Leffler }
152339beb93cSSam Leffler 
152439beb93cSSam Leffler 
15255b9c547cSRui Paulo static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
15265b9c547cSRui Paulo {
15275b9c547cSRui Paulo 	struct eap_aka_data *data = priv;
15285b9c547cSRui Paulo 	u8 *id;
15295b9c547cSRui Paulo 
15305b9c547cSRui Paulo 	if (data->state != SUCCESS)
15315b9c547cSRui Paulo 		return NULL;
15325b9c547cSRui Paulo 
1533*206b73d0SCy Schubert 	if (!data->reauth)
15345b9c547cSRui Paulo 		*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
1535*206b73d0SCy Schubert 	else
1536*206b73d0SCy Schubert 		*len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
15375b9c547cSRui Paulo 	id = os_malloc(*len);
15385b9c547cSRui Paulo 	if (id == NULL)
15395b9c547cSRui Paulo 		return NULL;
15405b9c547cSRui Paulo 
15415b9c547cSRui Paulo 	id[0] = data->eap_method;
1542*206b73d0SCy Schubert 	if (!data->reauth) {
15435b9c547cSRui Paulo 		os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
1544*206b73d0SCy Schubert 		os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn,
1545*206b73d0SCy Schubert 			  EAP_AKA_AUTN_LEN);
1546*206b73d0SCy Schubert 	} else {
1547*206b73d0SCy Schubert 		os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
1548*206b73d0SCy Schubert 		os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
1549*206b73d0SCy Schubert 			  EAP_SIM_MAC_LEN);
1550*206b73d0SCy Schubert 	}
15515b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
15525b9c547cSRui Paulo 
15535b9c547cSRui Paulo 	return id;
15545b9c547cSRui Paulo }
15555b9c547cSRui Paulo 
15565b9c547cSRui Paulo 
155739beb93cSSam Leffler static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
155839beb93cSSam Leffler {
155939beb93cSSam Leffler 	struct eap_aka_data *data = priv;
156039beb93cSSam Leffler 	u8 *key;
156139beb93cSSam Leffler 
156239beb93cSSam Leffler 	if (data->state != SUCCESS)
156339beb93cSSam Leffler 		return NULL;
156439beb93cSSam Leffler 
156585732ac8SCy Schubert 	key = os_memdup(data->emsk, EAP_EMSK_LEN);
156639beb93cSSam Leffler 	if (key == NULL)
156739beb93cSSam Leffler 		return NULL;
156839beb93cSSam Leffler 
156939beb93cSSam Leffler 	*len = EAP_EMSK_LEN;
157039beb93cSSam Leffler 
157139beb93cSSam Leffler 	return key;
157239beb93cSSam Leffler }
157339beb93cSSam Leffler 
157439beb93cSSam Leffler 
157585732ac8SCy Schubert static int eap_aka_get_error_code(void *priv)
157685732ac8SCy Schubert {
157785732ac8SCy Schubert 	struct eap_aka_data *data = priv;
157885732ac8SCy Schubert 	int current_data_error;
157985732ac8SCy Schubert 
158085732ac8SCy Schubert 	if (!data)
158185732ac8SCy Schubert 		return NO_EAP_METHOD_ERROR;
158285732ac8SCy Schubert 
158385732ac8SCy Schubert 	current_data_error = data->error_code;
158485732ac8SCy Schubert 
158585732ac8SCy Schubert 	/* Now reset for next transaction */
158685732ac8SCy Schubert 	data->error_code = NO_EAP_METHOD_ERROR;
158785732ac8SCy Schubert 
158885732ac8SCy Schubert 	return current_data_error;
158985732ac8SCy Schubert }
159085732ac8SCy Schubert 
159185732ac8SCy Schubert 
159239beb93cSSam Leffler int eap_peer_aka_register(void)
159339beb93cSSam Leffler {
159439beb93cSSam Leffler 	struct eap_method *eap;
159539beb93cSSam Leffler 
159639beb93cSSam Leffler 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
159739beb93cSSam Leffler 				    EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
159839beb93cSSam Leffler 	if (eap == NULL)
159939beb93cSSam Leffler 		return -1;
160039beb93cSSam Leffler 
160139beb93cSSam Leffler 	eap->init = eap_aka_init;
160239beb93cSSam Leffler 	eap->deinit = eap_aka_deinit;
160339beb93cSSam Leffler 	eap->process = eap_aka_process;
160439beb93cSSam Leffler 	eap->isKeyAvailable = eap_aka_isKeyAvailable;
160539beb93cSSam Leffler 	eap->getKey = eap_aka_getKey;
16065b9c547cSRui Paulo 	eap->getSessionId = eap_aka_get_session_id;
160739beb93cSSam Leffler 	eap->has_reauth_data = eap_aka_has_reauth_data;
160839beb93cSSam Leffler 	eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
160939beb93cSSam Leffler 	eap->init_for_reauth = eap_aka_init_for_reauth;
161039beb93cSSam Leffler 	eap->get_identity = eap_aka_get_identity;
161139beb93cSSam Leffler 	eap->get_emsk = eap_aka_get_emsk;
161285732ac8SCy Schubert 	eap->get_error_code = eap_aka_get_error_code;
161339beb93cSSam Leffler 
1614780fb4a2SCy Schubert 	return eap_peer_method_register(eap);
161539beb93cSSam Leffler }
161639beb93cSSam Leffler 
161739beb93cSSam Leffler 
161839beb93cSSam Leffler #ifdef EAP_AKA_PRIME
161939beb93cSSam Leffler int eap_peer_aka_prime_register(void)
162039beb93cSSam Leffler {
162139beb93cSSam Leffler 	struct eap_method *eap;
162239beb93cSSam Leffler 
162339beb93cSSam Leffler 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
162439beb93cSSam Leffler 				    EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
162539beb93cSSam Leffler 				    "AKA'");
162639beb93cSSam Leffler 	if (eap == NULL)
162739beb93cSSam Leffler 		return -1;
162839beb93cSSam Leffler 
162939beb93cSSam Leffler 	eap->init = eap_aka_prime_init;
163039beb93cSSam Leffler 	eap->deinit = eap_aka_deinit;
163139beb93cSSam Leffler 	eap->process = eap_aka_process;
163239beb93cSSam Leffler 	eap->isKeyAvailable = eap_aka_isKeyAvailable;
163339beb93cSSam Leffler 	eap->getKey = eap_aka_getKey;
16345b9c547cSRui Paulo 	eap->getSessionId = eap_aka_get_session_id;
163539beb93cSSam Leffler 	eap->has_reauth_data = eap_aka_has_reauth_data;
163639beb93cSSam Leffler 	eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
163739beb93cSSam Leffler 	eap->init_for_reauth = eap_aka_init_for_reauth;
163839beb93cSSam Leffler 	eap->get_identity = eap_aka_get_identity;
163939beb93cSSam Leffler 	eap->get_emsk = eap_aka_get_emsk;
164085732ac8SCy Schubert 	eap->get_error_code = eap_aka_get_error_code;
164139beb93cSSam Leffler 
1642780fb4a2SCy Schubert 	return eap_peer_method_register(eap);
164339beb93cSSam Leffler }
164439beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
1645