xref: /freebsd/contrib/wpa/src/eap_peer/eap_aka.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
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];
34206b73d0SCy 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,
445*c1d255d3SCy Schubert 			      const struct wpabuf *msg1,
446*c1d255d3SCy Schubert 			      const struct wpabuf *msg2)
44739beb93cSSam Leffler {
448*c1d255d3SCy Schubert 	size_t len;
44939beb93cSSam Leffler 
450*c1d255d3SCy Schubert 	if (!msg1)
451*c1d255d3SCy Schubert 		return -1;
452*c1d255d3SCy Schubert 	len = wpabuf_len(msg1);
453*c1d255d3SCy Schubert 	if (msg2)
454*c1d255d3SCy Schubert 		len += wpabuf_len(msg2);
455*c1d255d3SCy Schubert 
456*c1d255d3SCy Schubert 	if (!data->id_msgs) {
457*c1d255d3SCy Schubert 		data->id_msgs = wpabuf_alloc(len);
458*c1d255d3SCy Schubert 		if (!data->id_msgs)
459*c1d255d3SCy Schubert 			return -1;
460*c1d255d3SCy Schubert 	} else if (wpabuf_resize(&data->id_msgs, len) < 0) {
461*c1d255d3SCy Schubert 		return -1;
46239beb93cSSam Leffler 	}
46339beb93cSSam Leffler 
464*c1d255d3SCy Schubert 	wpabuf_put_buf(data->id_msgs, msg1);
465*c1d255d3SCy Schubert 	if (msg2)
466*c1d255d3SCy Schubert 		wpabuf_put_buf(data->id_msgs, msg2);
46739beb93cSSam Leffler 
46839beb93cSSam Leffler 	return 0;
46939beb93cSSam Leffler }
47039beb93cSSam Leffler 
47139beb93cSSam Leffler 
47239beb93cSSam Leffler static void eap_aka_add_checkcode(struct eap_aka_data *data,
47339beb93cSSam Leffler 				  struct eap_sim_msg *msg)
47439beb93cSSam Leffler {
47539beb93cSSam Leffler 	const u8 *addr;
47639beb93cSSam Leffler 	size_t len;
47739beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
47839beb93cSSam Leffler 
47939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_CHECKCODE");
48039beb93cSSam Leffler 
48139beb93cSSam Leffler 	if (data->id_msgs == NULL) {
48239beb93cSSam Leffler 		/*
48339beb93cSSam Leffler 		 * No EAP-AKA/Identity packets were exchanged - send empty
48439beb93cSSam Leffler 		 * checkcode.
48539beb93cSSam Leffler 		 */
48639beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
48739beb93cSSam Leffler 		return;
48839beb93cSSam Leffler 	}
48939beb93cSSam Leffler 
49039beb93cSSam Leffler 	/* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
49139beb93cSSam Leffler 	addr = wpabuf_head(data->id_msgs);
49239beb93cSSam Leffler 	len = wpabuf_len(data->id_msgs);
49339beb93cSSam Leffler 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
49439beb93cSSam Leffler #ifdef EAP_AKA_PRIME
49539beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME)
49639beb93cSSam Leffler 		sha256_vector(1, &addr, &len, hash);
49739beb93cSSam Leffler 	else
49839beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
49939beb93cSSam Leffler 		sha1_vector(1, &addr, &len, hash);
50039beb93cSSam Leffler 
50139beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
50239beb93cSSam Leffler 			data->eap_method == EAP_TYPE_AKA_PRIME ?
50339beb93cSSam Leffler 			EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
50439beb93cSSam Leffler }
50539beb93cSSam Leffler 
50639beb93cSSam Leffler 
50739beb93cSSam Leffler static int eap_aka_verify_checkcode(struct eap_aka_data *data,
50839beb93cSSam Leffler 				    const u8 *checkcode, size_t checkcode_len)
50939beb93cSSam Leffler {
51039beb93cSSam Leffler 	const u8 *addr;
51139beb93cSSam Leffler 	size_t len;
51239beb93cSSam Leffler 	u8 hash[SHA256_MAC_LEN];
51339beb93cSSam Leffler 	size_t hash_len;
51439beb93cSSam Leffler 
51539beb93cSSam Leffler 	if (checkcode == NULL)
51639beb93cSSam Leffler 		return -1;
51739beb93cSSam Leffler 
51839beb93cSSam Leffler 	if (data->id_msgs == NULL) {
51939beb93cSSam Leffler 		if (checkcode_len != 0) {
52039beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
52139beb93cSSam Leffler 				   "indicates that AKA/Identity messages were "
52239beb93cSSam Leffler 				   "used, but they were not");
52339beb93cSSam Leffler 			return -1;
52439beb93cSSam Leffler 		}
52539beb93cSSam Leffler 		return 0;
52639beb93cSSam Leffler 	}
52739beb93cSSam Leffler 
52839beb93cSSam Leffler 	hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
52939beb93cSSam Leffler 		EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
53039beb93cSSam Leffler 
53139beb93cSSam Leffler 	if (checkcode_len != hash_len) {
53239beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
53339beb93cSSam Leffler 			   "indicates that AKA/Identity message were not "
53439beb93cSSam Leffler 			   "used, but they were");
53539beb93cSSam Leffler 		return -1;
53639beb93cSSam Leffler 	}
53739beb93cSSam Leffler 
53839beb93cSSam Leffler 	/* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
53939beb93cSSam Leffler 	addr = wpabuf_head(data->id_msgs);
54039beb93cSSam Leffler 	len = wpabuf_len(data->id_msgs);
54139beb93cSSam Leffler #ifdef EAP_AKA_PRIME
54239beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME)
54339beb93cSSam Leffler 		sha256_vector(1, &addr, &len, hash);
54439beb93cSSam Leffler 	else
54539beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
54639beb93cSSam Leffler 		sha1_vector(1, &addr, &len, hash);
54739beb93cSSam Leffler 
5485b9c547cSRui Paulo 	if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
54939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
55039beb93cSSam Leffler 		return -1;
55139beb93cSSam Leffler 	}
55239beb93cSSam Leffler 
55339beb93cSSam Leffler 	return 0;
55439beb93cSSam Leffler }
55539beb93cSSam Leffler 
55639beb93cSSam Leffler 
55739beb93cSSam Leffler static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id,
55839beb93cSSam Leffler 					    int err)
55939beb93cSSam Leffler {
56039beb93cSSam Leffler 	struct eap_sim_msg *msg;
56139beb93cSSam Leffler 
56239beb93cSSam Leffler 	eap_aka_state(data, FAILURE);
56339beb93cSSam Leffler 	data->num_id_req = 0;
56439beb93cSSam Leffler 	data->num_notification = 0;
56539beb93cSSam Leffler 
566f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Send Client-Error (error code %d)",
567f05cddf9SRui Paulo 		   err);
56839beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
56939beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_CLIENT_ERROR);
57039beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
5715b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
57239beb93cSSam Leffler }
57339beb93cSSam Leffler 
57439beb93cSSam Leffler 
57539beb93cSSam Leffler static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data,
57639beb93cSSam Leffler 						     u8 id)
57739beb93cSSam Leffler {
57839beb93cSSam Leffler 	struct eap_sim_msg *msg;
57939beb93cSSam Leffler 
58039beb93cSSam Leffler 	eap_aka_state(data, FAILURE);
58139beb93cSSam Leffler 	data->num_id_req = 0;
58239beb93cSSam Leffler 	data->num_notification = 0;
58339beb93cSSam Leffler 
58439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject "
58539beb93cSSam Leffler 		   "(id=%d)", id);
58639beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
58739beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT);
5885b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
58939beb93cSSam Leffler }
59039beb93cSSam Leffler 
59139beb93cSSam Leffler 
59239beb93cSSam Leffler static struct wpabuf * eap_aka_synchronization_failure(
59385732ac8SCy Schubert 	struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr)
59439beb93cSSam Leffler {
59539beb93cSSam Leffler 	struct eap_sim_msg *msg;
59639beb93cSSam Leffler 
59739beb93cSSam Leffler 	data->num_id_req = 0;
59839beb93cSSam Leffler 	data->num_notification = 0;
59939beb93cSSam Leffler 
60039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure "
60139beb93cSSam Leffler 		   "(id=%d)", id);
60239beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
60339beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE);
60439beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_AUTS");
60539beb93cSSam Leffler 	eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
60639beb93cSSam Leffler 			     EAP_AKA_AUTS_LEN);
60785732ac8SCy Schubert 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
60885732ac8SCy Schubert 		size_t i;
60985732ac8SCy Schubert 
61085732ac8SCy Schubert 		for (i = 0; i < attr->kdf_count; i++) {
61185732ac8SCy Schubert 			wpa_printf(MSG_DEBUG, "   AT_KDF");
61285732ac8SCy Schubert 			eap_sim_msg_add(msg, EAP_SIM_AT_KDF, attr->kdf[i],
61385732ac8SCy Schubert 					NULL, 0);
61485732ac8SCy Schubert 		}
61585732ac8SCy Schubert 	}
6165b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
61739beb93cSSam Leffler }
61839beb93cSSam Leffler 
61939beb93cSSam Leffler 
62039beb93cSSam Leffler static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
62139beb93cSSam Leffler 						 struct eap_aka_data *data,
62239beb93cSSam Leffler 						 u8 id,
62339beb93cSSam Leffler 						 enum eap_sim_id_req id_req)
62439beb93cSSam Leffler {
62539beb93cSSam Leffler 	const u8 *identity = NULL;
62639beb93cSSam Leffler 	size_t identity_len = 0;
62739beb93cSSam Leffler 	struct eap_sim_msg *msg;
62839beb93cSSam Leffler 
62939beb93cSSam Leffler 	data->reauth = 0;
63039beb93cSSam Leffler 	if (id_req == ANY_ID && data->reauth_id) {
63139beb93cSSam Leffler 		identity = data->reauth_id;
63239beb93cSSam Leffler 		identity_len = data->reauth_id_len;
63339beb93cSSam Leffler 		data->reauth = 1;
63439beb93cSSam Leffler 	} else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
635206b73d0SCy Schubert 		   data->pseudonym &&
636206b73d0SCy Schubert 		   !eap_sim_anonymous_username(data->pseudonym,
637206b73d0SCy Schubert 					       data->pseudonym_len)) {
63839beb93cSSam Leffler 		identity = data->pseudonym;
63939beb93cSSam Leffler 		identity_len = data->pseudonym_len;
640f05cddf9SRui Paulo 		eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID);
64139beb93cSSam Leffler 	} else if (id_req != NO_ID_REQ) {
64239beb93cSSam Leffler 		identity = eap_get_config_identity(sm, &identity_len);
64339beb93cSSam Leffler 		if (identity) {
644206b73d0SCy Schubert 			int ids = CLEAR_PSEUDONYM | CLEAR_REAUTH_ID;
645206b73d0SCy Schubert 
646206b73d0SCy Schubert 			if (data->pseudonym &&
647206b73d0SCy Schubert 			    eap_sim_anonymous_username(data->pseudonym,
648206b73d0SCy Schubert 						       data->pseudonym_len))
649206b73d0SCy Schubert 				ids &= ~CLEAR_PSEUDONYM;
650206b73d0SCy Schubert 			eap_aka_clear_identities(sm, data, ids);
65139beb93cSSam Leffler 		}
65239beb93cSSam Leffler 	}
65339beb93cSSam Leffler 	if (id_req != NO_ID_REQ)
654f05cddf9SRui Paulo 		eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
65539beb93cSSam Leffler 
65639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
65739beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
65839beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_IDENTITY);
65939beb93cSSam Leffler 
66039beb93cSSam Leffler 	if (identity) {
66139beb93cSSam Leffler 		wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
66239beb93cSSam Leffler 				  identity, identity_len);
66339beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
66439beb93cSSam Leffler 				identity, identity_len);
66539beb93cSSam Leffler 	}
66639beb93cSSam Leffler 
6675b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
66839beb93cSSam Leffler }
66939beb93cSSam Leffler 
67039beb93cSSam Leffler 
67139beb93cSSam Leffler static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data,
67239beb93cSSam Leffler 						  u8 id)
67339beb93cSSam Leffler {
67439beb93cSSam Leffler 	struct eap_sim_msg *msg;
67539beb93cSSam Leffler 
67639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id);
67739beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
67839beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_CHALLENGE);
67939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_RES");
68039beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8,
68139beb93cSSam Leffler 			data->res, data->res_len);
68239beb93cSSam Leffler 	eap_aka_add_checkcode(data, msg);
68339beb93cSSam Leffler 	if (data->use_result_ind) {
68439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
68539beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
68639beb93cSSam Leffler 	}
68739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_MAC");
68839beb93cSSam Leffler 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
6895b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, (u8 *) "",
6905b9c547cSRui Paulo 				  0);
69139beb93cSSam Leffler }
69239beb93cSSam Leffler 
69339beb93cSSam Leffler 
69439beb93cSSam Leffler static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data,
69539beb93cSSam Leffler 					       u8 id, int counter_too_small,
69639beb93cSSam Leffler 					       const u8 *nonce_s)
69739beb93cSSam Leffler {
69839beb93cSSam Leffler 	struct eap_sim_msg *msg;
69939beb93cSSam Leffler 	unsigned int counter;
70039beb93cSSam Leffler 
70139beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)",
70239beb93cSSam Leffler 		   id);
70339beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
70439beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_REAUTHENTICATION);
70539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_IV");
70639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
70739beb93cSSam Leffler 	eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
70839beb93cSSam Leffler 
70939beb93cSSam Leffler 	if (counter_too_small) {
71039beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   *AT_COUNTER_TOO_SMALL");
71139beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
71239beb93cSSam Leffler 		counter = data->counter_too_small;
71339beb93cSSam Leffler 	} else
71439beb93cSSam Leffler 		counter = data->counter;
71539beb93cSSam Leffler 
71639beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", counter);
71739beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
71839beb93cSSam Leffler 
71939beb93cSSam Leffler 	if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
72039beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
72139beb93cSSam Leffler 			   "AT_ENCR_DATA");
72239beb93cSSam Leffler 		eap_sim_msg_free(msg);
72339beb93cSSam Leffler 		return NULL;
72439beb93cSSam Leffler 	}
72539beb93cSSam Leffler 	eap_aka_add_checkcode(data, msg);
72639beb93cSSam Leffler 	if (data->use_result_ind) {
72739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
72839beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
72939beb93cSSam Leffler 	}
73039beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_MAC");
73139beb93cSSam Leffler 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
7325b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, nonce_s,
73339beb93cSSam Leffler 				  EAP_SIM_NONCE_S_LEN);
73439beb93cSSam Leffler }
73539beb93cSSam Leffler 
73639beb93cSSam Leffler 
73739beb93cSSam Leffler static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data,
73839beb93cSSam Leffler 						     u8 id, u16 notification)
73939beb93cSSam Leffler {
74039beb93cSSam Leffler 	struct eap_sim_msg *msg;
74139beb93cSSam Leffler 	u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
74239beb93cSSam Leffler 
74339beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id);
74439beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
74539beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_NOTIFICATION);
74639beb93cSSam Leffler 	if (k_aut && data->reauth) {
74739beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_IV");
74839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
74939beb93cSSam Leffler 		eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
75039beb93cSSam Leffler 					   EAP_SIM_AT_ENCR_DATA);
75139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", data->counter);
75239beb93cSSam Leffler 		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
75339beb93cSSam Leffler 				NULL, 0);
75439beb93cSSam Leffler 		if (eap_sim_msg_add_encr_end(msg, data->k_encr,
75539beb93cSSam Leffler 					     EAP_SIM_AT_PADDING)) {
75639beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
75739beb93cSSam Leffler 				   "AT_ENCR_DATA");
75839beb93cSSam Leffler 			eap_sim_msg_free(msg);
75939beb93cSSam Leffler 			return NULL;
76039beb93cSSam Leffler 		}
76139beb93cSSam Leffler 	}
76239beb93cSSam Leffler 	if (k_aut) {
76339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   AT_MAC");
76439beb93cSSam Leffler 		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
76539beb93cSSam Leffler 	}
7665b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, k_aut, (u8 *) "", 0);
76739beb93cSSam Leffler }
76839beb93cSSam Leffler 
76939beb93cSSam Leffler 
77039beb93cSSam Leffler static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm,
77139beb93cSSam Leffler 						struct eap_aka_data *data,
77239beb93cSSam Leffler 						u8 id,
77339beb93cSSam Leffler 						const struct wpabuf *reqData,
77439beb93cSSam Leffler 						struct eap_sim_attrs *attr)
77539beb93cSSam Leffler {
77639beb93cSSam Leffler 	int id_error;
77739beb93cSSam Leffler 	struct wpabuf *buf;
77839beb93cSSam Leffler 
77939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity");
78039beb93cSSam Leffler 
78139beb93cSSam Leffler 	id_error = 0;
78239beb93cSSam Leffler 	switch (attr->id_req) {
78339beb93cSSam Leffler 	case NO_ID_REQ:
78439beb93cSSam Leffler 		break;
78539beb93cSSam Leffler 	case ANY_ID:
78639beb93cSSam Leffler 		if (data->num_id_req > 0)
78739beb93cSSam Leffler 			id_error++;
78839beb93cSSam Leffler 		data->num_id_req++;
78939beb93cSSam Leffler 		break;
79039beb93cSSam Leffler 	case FULLAUTH_ID:
79139beb93cSSam Leffler 		if (data->num_id_req > 1)
79239beb93cSSam Leffler 			id_error++;
79339beb93cSSam Leffler 		data->num_id_req++;
79439beb93cSSam Leffler 		break;
79539beb93cSSam Leffler 	case PERMANENT_ID:
79639beb93cSSam Leffler 		if (data->num_id_req > 2)
79739beb93cSSam Leffler 			id_error++;
79839beb93cSSam Leffler 		data->num_id_req++;
79939beb93cSSam Leffler 		break;
80039beb93cSSam Leffler 	}
80139beb93cSSam Leffler 	if (id_error) {
80239beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests "
80339beb93cSSam Leffler 			   "used within one authentication");
80439beb93cSSam Leffler 		return eap_aka_client_error(data, id,
80539beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
80639beb93cSSam Leffler 	}
80739beb93cSSam Leffler 
80839beb93cSSam Leffler 	buf = eap_aka_response_identity(sm, data, id, attr->id_req);
80939beb93cSSam Leffler 
81039beb93cSSam Leffler 	if (data->prev_id != id) {
811*c1d255d3SCy Schubert 		if (eap_aka_add_id_msg(data, reqData, buf) < 0) {
812*c1d255d3SCy Schubert 			wpa_printf(MSG_INFO,
813*c1d255d3SCy Schubert 				   "EAP-AKA: Failed to store ID messages");
814*c1d255d3SCy Schubert 			wpabuf_free(buf);
815*c1d255d3SCy Schubert 			return eap_aka_client_error(
816*c1d255d3SCy Schubert 				data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
817*c1d255d3SCy Schubert 		}
81839beb93cSSam Leffler 		data->prev_id = id;
81939beb93cSSam Leffler 	}
82039beb93cSSam Leffler 
82139beb93cSSam Leffler 	return buf;
82239beb93cSSam Leffler }
82339beb93cSSam Leffler 
82439beb93cSSam Leffler 
82539beb93cSSam Leffler static int eap_aka_verify_mac(struct eap_aka_data *data,
82639beb93cSSam Leffler 			      const struct wpabuf *req,
82739beb93cSSam Leffler 			      const u8 *mac, const u8 *extra,
82839beb93cSSam Leffler 			      size_t extra_len)
82939beb93cSSam Leffler {
83039beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME)
83139beb93cSSam Leffler 		return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
83239beb93cSSam Leffler 						 extra_len);
83339beb93cSSam Leffler 	return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
83439beb93cSSam Leffler }
83539beb93cSSam Leffler 
83639beb93cSSam Leffler 
83739beb93cSSam Leffler #ifdef EAP_AKA_PRIME
83839beb93cSSam Leffler static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data,
83939beb93cSSam Leffler 						u8 id, u16 kdf)
84039beb93cSSam Leffler {
84139beb93cSSam Leffler 	struct eap_sim_msg *msg;
84239beb93cSSam Leffler 
84339beb93cSSam Leffler 	data->kdf_negotiation = 1;
84439beb93cSSam Leffler 	data->kdf = kdf;
84539beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF "
84639beb93cSSam Leffler 		   "select)", id);
84739beb93cSSam Leffler 	msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
84839beb93cSSam Leffler 			       EAP_AKA_SUBTYPE_CHALLENGE);
84939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "   AT_KDF");
85039beb93cSSam Leffler 	eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0);
8515b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
85239beb93cSSam Leffler }
85339beb93cSSam Leffler 
85439beb93cSSam Leffler 
85539beb93cSSam Leffler static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data,
85639beb93cSSam Leffler 					     u8 id, struct eap_sim_attrs *attr)
85739beb93cSSam Leffler {
85839beb93cSSam Leffler 	size_t i;
85939beb93cSSam Leffler 
86039beb93cSSam Leffler 	for (i = 0; i < attr->kdf_count; i++) {
86185732ac8SCy Schubert 		if (attr->kdf[i] == EAP_AKA_PRIME_KDF) {
86285732ac8SCy Schubert 			os_memcpy(data->last_kdf_attrs, attr->kdf,
86385732ac8SCy Schubert 				  sizeof(u16) * attr->kdf_count);
86485732ac8SCy Schubert 			data->last_kdf_count = attr->kdf_count;
86539beb93cSSam Leffler 			return eap_aka_prime_kdf_select(data, id,
86639beb93cSSam Leffler 							EAP_AKA_PRIME_KDF);
86739beb93cSSam Leffler 		}
86885732ac8SCy Schubert 	}
86939beb93cSSam Leffler 
87039beb93cSSam Leffler 	/* No matching KDF found - fail authentication as if AUTN had been
87139beb93cSSam Leffler 	 * incorrect */
87239beb93cSSam Leffler 	return eap_aka_authentication_reject(data, id);
87339beb93cSSam Leffler }
87439beb93cSSam Leffler 
87539beb93cSSam Leffler 
87639beb93cSSam Leffler static int eap_aka_prime_kdf_valid(struct eap_aka_data *data,
87739beb93cSSam Leffler 				   struct eap_sim_attrs *attr)
87839beb93cSSam Leffler {
87939beb93cSSam Leffler 	size_t i, j;
88039beb93cSSam Leffler 
88139beb93cSSam Leffler 	if (attr->kdf_count == 0)
88239beb93cSSam Leffler 		return 0;
88339beb93cSSam Leffler 
88439beb93cSSam Leffler 	/* The only allowed (and required) duplication of a KDF is the addition
88539beb93cSSam Leffler 	 * of the selected KDF into the beginning of the list. */
88639beb93cSSam Leffler 
88739beb93cSSam Leffler 	if (data->kdf_negotiation) {
88885732ac8SCy Schubert 		/* When the peer receives the new EAP-Request/AKA'-Challenge
88985732ac8SCy Schubert 		 * message, must check only requested change occurred in the
89085732ac8SCy Schubert 		 * list of AT_KDF attributes. If there are any other changes,
89185732ac8SCy Schubert 		 * the peer must behave like the case that AT_MAC had been
89285732ac8SCy Schubert 		 * incorrect and authentication is failed. These are defined in
89385732ac8SCy Schubert 		 * EAP-AKA' specification RFC 5448, Section 3.2. */
89439beb93cSSam Leffler 		if (attr->kdf[0] != data->kdf) {
89539beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
89639beb93cSSam Leffler 				   "accept the selected KDF");
89785732ac8SCy Schubert 			return -1;
89885732ac8SCy Schubert 		}
89985732ac8SCy Schubert 
90085732ac8SCy Schubert 		if (attr->kdf_count > EAP_AKA_PRIME_KDF_MAX ||
90185732ac8SCy Schubert 		    attr->kdf_count != data->last_kdf_count + 1) {
90285732ac8SCy Schubert 			wpa_printf(MSG_WARNING,
90385732ac8SCy Schubert 				   "EAP-AKA': The length of KDF attributes is wrong");
90485732ac8SCy Schubert 			return -1;
90539beb93cSSam Leffler 		}
90639beb93cSSam Leffler 
90739beb93cSSam Leffler 		for (i = 1; i < attr->kdf_count; i++) {
90885732ac8SCy Schubert 			if (attr->kdf[i] != data->last_kdf_attrs[i - 1]) {
90985732ac8SCy Schubert 				wpa_printf(MSG_WARNING,
91085732ac8SCy Schubert 					   "EAP-AKA': The KDF attributes except selected KDF are not same as original one");
91185732ac8SCy Schubert 				return -1;
91239beb93cSSam Leffler 			}
91339beb93cSSam Leffler 		}
91439beb93cSSam Leffler 	}
91539beb93cSSam Leffler 
91639beb93cSSam Leffler 	for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
91739beb93cSSam Leffler 		for (j = i + 1; j < attr->kdf_count; j++) {
91839beb93cSSam Leffler 			if (attr->kdf[i] == attr->kdf[j]) {
91939beb93cSSam Leffler 				wpa_printf(MSG_WARNING, "EAP-AKA': The server "
92039beb93cSSam Leffler 					   "included a duplicated KDF");
92139beb93cSSam Leffler 				return 0;
92239beb93cSSam Leffler 			}
92339beb93cSSam Leffler 		}
92439beb93cSSam Leffler 	}
92539beb93cSSam Leffler 
92639beb93cSSam Leffler 	return 1;
92739beb93cSSam Leffler }
92839beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
92939beb93cSSam Leffler 
93039beb93cSSam Leffler 
93139beb93cSSam Leffler static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
93239beb93cSSam Leffler 						 struct eap_aka_data *data,
93339beb93cSSam Leffler 						 u8 id,
93439beb93cSSam Leffler 						 const struct wpabuf *reqData,
93539beb93cSSam Leffler 						 struct eap_sim_attrs *attr)
93639beb93cSSam Leffler {
93739beb93cSSam Leffler 	const u8 *identity;
93839beb93cSSam Leffler 	size_t identity_len;
93939beb93cSSam Leffler 	int res;
94039beb93cSSam Leffler 	struct eap_sim_attrs eattr;
94139beb93cSSam Leffler 
94239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge");
94339beb93cSSam Leffler 
94439beb93cSSam Leffler 	if (attr->checkcode &&
94539beb93cSSam Leffler 	    eap_aka_verify_checkcode(data, attr->checkcode,
94639beb93cSSam Leffler 				     attr->checkcode_len)) {
94739beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
94839beb93cSSam Leffler 			   "message");
949206b73d0SCy Schubert #ifdef TEST_FUZZ
950206b73d0SCy Schubert 		wpa_printf(MSG_INFO,
951206b73d0SCy Schubert 			   "TEST: Ignore AT_CHECKCODE mismatch for fuzz testing");
952206b73d0SCy Schubert #else /* TEST_FUZZ */
95339beb93cSSam Leffler 		return eap_aka_client_error(data, id,
95439beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
955206b73d0SCy Schubert #endif /* TEST_FUZZ */
95639beb93cSSam Leffler 	}
95739beb93cSSam Leffler 
95839beb93cSSam Leffler #ifdef EAP_AKA_PRIME
95939beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
96039beb93cSSam Leffler 		if (!attr->kdf_input || attr->kdf_input_len == 0) {
96139beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message "
96239beb93cSSam Leffler 				   "did not include non-empty AT_KDF_INPUT");
96339beb93cSSam Leffler 			/* Fail authentication as if AUTN had been incorrect */
96439beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
96539beb93cSSam Leffler 		}
96639beb93cSSam Leffler 		os_free(data->network_name);
96785732ac8SCy Schubert 		data->network_name = os_memdup(attr->kdf_input,
96885732ac8SCy Schubert 					       attr->kdf_input_len);
96939beb93cSSam Leffler 		if (data->network_name == NULL) {
97039beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
97139beb93cSSam Leffler 				   "storing Network Name");
97239beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
97339beb93cSSam Leffler 		}
97439beb93cSSam Leffler 		data->network_name_len = attr->kdf_input_len;
97539beb93cSSam Leffler 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
97639beb93cSSam Leffler 				  "(AT_KDF_INPUT)",
97739beb93cSSam Leffler 				  data->network_name, data->network_name_len);
97839beb93cSSam Leffler 		/* TODO: check Network Name per 3GPP.33.402 */
97939beb93cSSam Leffler 
98085732ac8SCy Schubert 		res = eap_aka_prime_kdf_valid(data, attr);
98185732ac8SCy Schubert 		if (res == 0)
98239beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
98385732ac8SCy Schubert 		else if (res == -1)
98485732ac8SCy Schubert 			return eap_aka_client_error(
98585732ac8SCy Schubert 				data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
98639beb93cSSam Leffler 
98739beb93cSSam Leffler 		if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
98839beb93cSSam Leffler 			return eap_aka_prime_kdf_neg(data, id, attr);
98939beb93cSSam Leffler 
99039beb93cSSam Leffler 		data->kdf = EAP_AKA_PRIME_KDF;
99139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
99239beb93cSSam Leffler 	}
99339beb93cSSam Leffler 
99439beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA && attr->bidding) {
99539beb93cSSam Leffler 		u16 flags = WPA_GET_BE16(attr->bidding);
99639beb93cSSam Leffler 		if ((flags & EAP_AKA_BIDDING_FLAG_D) &&
99739beb93cSSam Leffler 		    eap_allowed_method(sm, EAP_VENDOR_IETF,
99839beb93cSSam Leffler 				       EAP_TYPE_AKA_PRIME)) {
99939beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from "
100039beb93cSSam Leffler 				   "AKA' to AKA detected");
100139beb93cSSam Leffler 			/* Fail authentication as if AUTN had been incorrect */
100239beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
100339beb93cSSam Leffler 		}
100439beb93cSSam Leffler 	}
100539beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
100639beb93cSSam Leffler 
100739beb93cSSam Leffler 	data->reauth = 0;
100839beb93cSSam Leffler 	if (!attr->mac || !attr->rand || !attr->autn) {
100939beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
101039beb93cSSam Leffler 			   "did not include%s%s%s",
101139beb93cSSam Leffler 			   !attr->mac ? " AT_MAC" : "",
101239beb93cSSam Leffler 			   !attr->rand ? " AT_RAND" : "",
101339beb93cSSam Leffler 			   !attr->autn ? " AT_AUTN" : "");
101439beb93cSSam Leffler 		return eap_aka_client_error(data, id,
101539beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
101639beb93cSSam Leffler 	}
101739beb93cSSam Leffler 	os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN);
101839beb93cSSam Leffler 	os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN);
101939beb93cSSam Leffler 
102039beb93cSSam Leffler 	res = eap_aka_umts_auth(sm, data);
102139beb93cSSam Leffler 	if (res == -1) {
102239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
102339beb93cSSam Leffler 			   "failed (AUTN)");
102439beb93cSSam Leffler 		return eap_aka_authentication_reject(data, id);
102539beb93cSSam Leffler 	} else if (res == -2) {
102639beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
102739beb93cSSam Leffler 			   "failed (AUTN seq# -> AUTS)");
102885732ac8SCy Schubert 		return eap_aka_synchronization_failure(data, id, attr);
10295b9c547cSRui Paulo 	} else if (res > 0) {
10305b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing");
10315b9c547cSRui Paulo 		return NULL;
103239beb93cSSam Leffler 	} else if (res) {
103339beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed");
103439beb93cSSam Leffler 		return eap_aka_client_error(data, id,
103539beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
103639beb93cSSam Leffler 	}
103739beb93cSSam Leffler #ifdef EAP_AKA_PRIME
103839beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
103939beb93cSSam Leffler 		/* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
104039beb93cSSam Leffler 		 * needed 6-octet SQN ^ AK for CK',IK' derivation */
104139beb93cSSam Leffler 		u16 amf = WPA_GET_BE16(data->autn + 6);
104239beb93cSSam Leffler 		if (!(amf & 0x8000)) {
104339beb93cSSam Leffler 			wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit "
104439beb93cSSam Leffler 				   "not set (AMF=0x%4x)", amf);
104539beb93cSSam Leffler 			return eap_aka_authentication_reject(data, id);
104639beb93cSSam Leffler 		}
104739beb93cSSam Leffler 		eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
104839beb93cSSam Leffler 						 data->autn,
104939beb93cSSam Leffler 						 data->network_name,
105039beb93cSSam Leffler 						 data->network_name_len);
105139beb93cSSam Leffler 	}
105239beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
105339beb93cSSam Leffler 	if (data->last_eap_identity) {
105439beb93cSSam Leffler 		identity = data->last_eap_identity;
105539beb93cSSam Leffler 		identity_len = data->last_eap_identity_len;
1056206b73d0SCy Schubert 	} else if (data->pseudonym &&
1057206b73d0SCy Schubert 		   !eap_sim_anonymous_username(data->pseudonym,
1058206b73d0SCy Schubert 					       data->pseudonym_len)) {
105939beb93cSSam Leffler 		identity = data->pseudonym;
106039beb93cSSam Leffler 		identity_len = data->pseudonym_len;
106185732ac8SCy Schubert 	} else {
106285732ac8SCy Schubert 		struct eap_peer_config *config;
106385732ac8SCy Schubert 
106485732ac8SCy Schubert 		config = eap_get_config(sm);
106585732ac8SCy Schubert 		if (config && config->imsi_identity) {
106685732ac8SCy Schubert 			identity = config->imsi_identity;
106785732ac8SCy Schubert 			identity_len = config->imsi_identity_len;
106885732ac8SCy Schubert 		} else {
106939beb93cSSam Leffler 			identity = eap_get_config_identity(sm, &identity_len);
107085732ac8SCy Schubert 		}
107185732ac8SCy Schubert 	}
107239beb93cSSam Leffler 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
107339beb93cSSam Leffler 			  "derivation", identity, identity_len);
107439beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
107539beb93cSSam Leffler 		eap_aka_prime_derive_keys(identity, identity_len, data->ik,
107639beb93cSSam Leffler 					  data->ck, data->k_encr, data->k_aut,
107739beb93cSSam Leffler 					  data->k_re, data->msk, data->emsk);
107839beb93cSSam Leffler 	} else {
107939beb93cSSam Leffler 		eap_aka_derive_mk(identity, identity_len, data->ik, data->ck,
108039beb93cSSam Leffler 				  data->mk);
108139beb93cSSam Leffler 		eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
108239beb93cSSam Leffler 				    data->msk, data->emsk);
108339beb93cSSam Leffler 	}
108439beb93cSSam Leffler 	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
108539beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
108639beb93cSSam Leffler 			   "used invalid AT_MAC");
1087206b73d0SCy Schubert #ifdef TEST_FUZZ
1088206b73d0SCy Schubert 		wpa_printf(MSG_INFO,
1089206b73d0SCy Schubert 			   "TEST: Ignore AT_MAC mismatch for fuzz testing");
1090206b73d0SCy Schubert #else /* TEST_FUZZ */
109139beb93cSSam Leffler 		return eap_aka_client_error(data, id,
109239beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
1093206b73d0SCy Schubert #endif /* TEST_FUZZ */
109439beb93cSSam Leffler 	}
109539beb93cSSam Leffler 
1096f05cddf9SRui Paulo 	/* Old reauthentication identity must not be used anymore. In
1097f05cddf9SRui Paulo 	 * other words, if no new identities are received, full
1098f05cddf9SRui Paulo 	 * authentication will be used on next reauthentication (using
1099f05cddf9SRui Paulo 	 * pseudonym identity or permanent identity). */
1100f05cddf9SRui Paulo 	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
110139beb93cSSam Leffler 
110239beb93cSSam Leffler 	if (attr->encr_data) {
110339beb93cSSam Leffler 		u8 *decrypted;
110439beb93cSSam Leffler 		decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
110539beb93cSSam Leffler 					       attr->encr_data_len, attr->iv,
110639beb93cSSam Leffler 					       &eattr, 0);
110739beb93cSSam Leffler 		if (decrypted == NULL) {
110839beb93cSSam Leffler 			return eap_aka_client_error(
110939beb93cSSam Leffler 				data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
111039beb93cSSam Leffler 		}
1111f05cddf9SRui Paulo 		eap_aka_learn_ids(sm, data, &eattr);
111239beb93cSSam Leffler 		os_free(decrypted);
111339beb93cSSam Leffler 	}
111439beb93cSSam Leffler 
111539beb93cSSam Leffler 	if (data->result_ind && attr->result_ind)
111639beb93cSSam Leffler 		data->use_result_ind = 1;
111739beb93cSSam Leffler 
11185b9c547cSRui Paulo 	if (data->state != FAILURE) {
111939beb93cSSam Leffler 		eap_aka_state(data, data->use_result_ind ?
112039beb93cSSam Leffler 			      RESULT_SUCCESS : SUCCESS);
112139beb93cSSam Leffler 	}
112239beb93cSSam Leffler 
112339beb93cSSam Leffler 	data->num_id_req = 0;
112439beb93cSSam Leffler 	data->num_notification = 0;
112539beb93cSSam Leffler 	/* RFC 4187 specifies that counter is initialized to one after
112639beb93cSSam Leffler 	 * fullauth, but initializing it to zero makes it easier to implement
112739beb93cSSam Leffler 	 * reauth verification. */
112839beb93cSSam Leffler 	data->counter = 0;
112939beb93cSSam Leffler 	return eap_aka_response_challenge(data, id);
113039beb93cSSam Leffler }
113139beb93cSSam Leffler 
113239beb93cSSam Leffler 
113339beb93cSSam Leffler static int eap_aka_process_notification_reauth(struct eap_aka_data *data,
113439beb93cSSam Leffler 					       struct eap_sim_attrs *attr)
113539beb93cSSam Leffler {
113639beb93cSSam Leffler 	struct eap_sim_attrs eattr;
113739beb93cSSam Leffler 	u8 *decrypted;
113839beb93cSSam Leffler 
113939beb93cSSam Leffler 	if (attr->encr_data == NULL || attr->iv == NULL) {
114039beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after "
114139beb93cSSam Leffler 			   "reauth did not include encrypted data");
114239beb93cSSam Leffler 		return -1;
114339beb93cSSam Leffler 	}
114439beb93cSSam Leffler 
114539beb93cSSam Leffler 	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
114639beb93cSSam Leffler 				       attr->encr_data_len, attr->iv, &eattr,
114739beb93cSSam Leffler 				       0);
114839beb93cSSam Leffler 	if (decrypted == NULL) {
114939beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
115039beb93cSSam Leffler 			   "data from notification message");
115139beb93cSSam Leffler 		return -1;
115239beb93cSSam Leffler 	}
115339beb93cSSam Leffler 
115439beb93cSSam Leffler 	if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
115539beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification "
115639beb93cSSam Leffler 			   "message does not match with counter in reauth "
115739beb93cSSam Leffler 			   "message");
115839beb93cSSam Leffler 		os_free(decrypted);
115939beb93cSSam Leffler 		return -1;
116039beb93cSSam Leffler 	}
116139beb93cSSam Leffler 
116239beb93cSSam Leffler 	os_free(decrypted);
116339beb93cSSam Leffler 	return 0;
116439beb93cSSam Leffler }
116539beb93cSSam Leffler 
116639beb93cSSam Leffler 
116739beb93cSSam Leffler static int eap_aka_process_notification_auth(struct eap_aka_data *data,
116839beb93cSSam Leffler 					     const struct wpabuf *reqData,
116939beb93cSSam Leffler 					     struct eap_sim_attrs *attr)
117039beb93cSSam Leffler {
117139beb93cSSam Leffler 	if (attr->mac == NULL) {
117239beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth "
117339beb93cSSam Leffler 			   "Notification message");
117439beb93cSSam Leffler 		return -1;
117539beb93cSSam Leffler 	}
117639beb93cSSam Leffler 
117739beb93cSSam Leffler 	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
117839beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Notification message "
117939beb93cSSam Leffler 			   "used invalid AT_MAC");
118039beb93cSSam Leffler 		return -1;
118139beb93cSSam Leffler 	}
118239beb93cSSam Leffler 
118339beb93cSSam Leffler 	if (data->reauth &&
118439beb93cSSam Leffler 	    eap_aka_process_notification_reauth(data, attr)) {
118539beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification "
118639beb93cSSam Leffler 			   "message after reauth");
118739beb93cSSam Leffler 		return -1;
118839beb93cSSam Leffler 	}
118939beb93cSSam Leffler 
119039beb93cSSam Leffler 	return 0;
119139beb93cSSam Leffler }
119239beb93cSSam Leffler 
119339beb93cSSam Leffler 
119439beb93cSSam Leffler static struct wpabuf * eap_aka_process_notification(
119539beb93cSSam Leffler 	struct eap_sm *sm, struct eap_aka_data *data, u8 id,
119639beb93cSSam Leffler 	const struct wpabuf *reqData, struct eap_sim_attrs *attr)
119739beb93cSSam Leffler {
119839beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification");
119939beb93cSSam Leffler 	if (data->num_notification > 0) {
120039beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: too many notification "
120139beb93cSSam Leffler 			   "rounds (only one allowed)");
120239beb93cSSam Leffler 		return eap_aka_client_error(data, id,
120339beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
120439beb93cSSam Leffler 	}
120539beb93cSSam Leffler 	data->num_notification++;
120639beb93cSSam Leffler 	if (attr->notification == -1) {
120739beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in "
120839beb93cSSam Leffler 			   "Notification message");
120939beb93cSSam Leffler 		return eap_aka_client_error(data, id,
121039beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
121139beb93cSSam Leffler 	}
121239beb93cSSam Leffler 
121339beb93cSSam Leffler 	if ((attr->notification & 0x4000) == 0 &&
121439beb93cSSam Leffler 	    eap_aka_process_notification_auth(data, reqData, attr)) {
121539beb93cSSam Leffler 		return eap_aka_client_error(data, id,
121639beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
121739beb93cSSam Leffler 	}
121839beb93cSSam Leffler 
121939beb93cSSam Leffler 	eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
122039beb93cSSam Leffler 	if (attr->notification >= 0 && attr->notification < 32768) {
122185732ac8SCy Schubert 		data->error_code = attr->notification;
122239beb93cSSam Leffler 		eap_aka_state(data, FAILURE);
122339beb93cSSam Leffler 	} else if (attr->notification == EAP_SIM_SUCCESS &&
122439beb93cSSam Leffler 		   data->state == RESULT_SUCCESS)
122539beb93cSSam Leffler 		eap_aka_state(data, SUCCESS);
122639beb93cSSam Leffler 	return eap_aka_response_notification(data, id, attr->notification);
122739beb93cSSam Leffler }
122839beb93cSSam Leffler 
122939beb93cSSam Leffler 
123039beb93cSSam Leffler static struct wpabuf * eap_aka_process_reauthentication(
123139beb93cSSam Leffler 	struct eap_sm *sm, struct eap_aka_data *data, u8 id,
123239beb93cSSam Leffler 	const struct wpabuf *reqData, struct eap_sim_attrs *attr)
123339beb93cSSam Leffler {
123439beb93cSSam Leffler 	struct eap_sim_attrs eattr;
123539beb93cSSam Leffler 	u8 *decrypted;
123639beb93cSSam Leffler 
123739beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication");
123839beb93cSSam Leffler 
123939beb93cSSam Leffler 	if (attr->checkcode &&
124039beb93cSSam Leffler 	    eap_aka_verify_checkcode(data, attr->checkcode,
124139beb93cSSam Leffler 				     attr->checkcode_len)) {
1242206b73d0SCy Schubert #ifdef TEST_FUZZ
1243206b73d0SCy Schubert 		wpa_printf(MSG_INFO,
1244206b73d0SCy Schubert 			   "TEST: Ignore AT_CHECKCODE mismatch for fuzz testing");
1245206b73d0SCy Schubert #else /* TEST_FUZZ */
124639beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
124739beb93cSSam Leffler 			   "message");
1248206b73d0SCy Schubert #endif /* TEST_FUZZ */
124939beb93cSSam Leffler 		return eap_aka_client_error(data, id,
125039beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
125139beb93cSSam Leffler 	}
125239beb93cSSam Leffler 
125339beb93cSSam Leffler 	if (data->reauth_id == NULL) {
125439beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying "
125539beb93cSSam Leffler 			   "reauthentication, but no reauth_id available");
125639beb93cSSam Leffler 		return eap_aka_client_error(data, id,
125739beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
125839beb93cSSam Leffler 	}
125939beb93cSSam Leffler 
126039beb93cSSam Leffler 	data->reauth = 1;
126139beb93cSSam Leffler 	if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
126239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
126339beb93cSSam Leffler 			   "did not have valid AT_MAC");
126439beb93cSSam Leffler 		return eap_aka_client_error(data, id,
126539beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
126639beb93cSSam Leffler 	}
126739beb93cSSam Leffler 
1268206b73d0SCy Schubert 	/* At this stage the received MAC has been verified. Use this MAC for
1269206b73d0SCy Schubert 	 * reauth Session-Id calculation if all other checks pass.
1270206b73d0SCy Schubert 	 * The peer does not use the local MAC but the received MAC in deriving
1271206b73d0SCy Schubert 	 * Session-Id. */
1272206b73d0SCy Schubert 	os_memcpy(data->reauth_mac, attr->mac, EAP_SIM_MAC_LEN);
1273206b73d0SCy Schubert 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Server MAC",
1274206b73d0SCy Schubert 		    data->reauth_mac, EAP_SIM_MAC_LEN);
1275206b73d0SCy Schubert 
127639beb93cSSam Leffler 	if (attr->encr_data == NULL || attr->iv == NULL) {
127739beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
127839beb93cSSam Leffler 			   "message did not include encrypted data");
127939beb93cSSam Leffler 		return eap_aka_client_error(data, id,
128039beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
128139beb93cSSam Leffler 	}
128239beb93cSSam Leffler 
128339beb93cSSam Leffler 	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
128439beb93cSSam Leffler 				       attr->encr_data_len, attr->iv, &eattr,
128539beb93cSSam Leffler 				       0);
128639beb93cSSam Leffler 	if (decrypted == NULL) {
128739beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
128839beb93cSSam Leffler 			   "data from reauthentication message");
128939beb93cSSam Leffler 		return eap_aka_client_error(data, id,
129039beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
129139beb93cSSam Leffler 	}
129239beb93cSSam Leffler 
129339beb93cSSam Leffler 	if (eattr.nonce_s == NULL || eattr.counter < 0) {
129439beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet",
129539beb93cSSam Leffler 			   !eattr.nonce_s ? " AT_NONCE_S" : "",
129639beb93cSSam Leffler 			   eattr.counter < 0 ? " AT_COUNTER" : "");
129739beb93cSSam Leffler 		os_free(decrypted);
129839beb93cSSam Leffler 		return eap_aka_client_error(data, id,
129939beb93cSSam Leffler 					    EAP_AKA_UNABLE_TO_PROCESS_PACKET);
130039beb93cSSam Leffler 	}
130139beb93cSSam Leffler 
130239beb93cSSam Leffler 	if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
130339beb93cSSam Leffler 		struct wpabuf *res;
130439beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter "
130539beb93cSSam Leffler 			   "(%d <= %d)", eattr.counter, data->counter);
130639beb93cSSam Leffler 		data->counter_too_small = eattr.counter;
130739beb93cSSam Leffler 
130839beb93cSSam Leffler 		/* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
130939beb93cSSam Leffler 		 * reauth_id must not be used to start a new reauthentication.
131039beb93cSSam Leffler 		 * However, since it was used in the last EAP-Response-Identity
131139beb93cSSam Leffler 		 * packet, it has to saved for the following fullauth to be
131239beb93cSSam Leffler 		 * used in MK derivation. */
131339beb93cSSam Leffler 		os_free(data->last_eap_identity);
131439beb93cSSam Leffler 		data->last_eap_identity = data->reauth_id;
131539beb93cSSam Leffler 		data->last_eap_identity_len = data->reauth_id_len;
131639beb93cSSam Leffler 		data->reauth_id = NULL;
131739beb93cSSam Leffler 		data->reauth_id_len = 0;
131839beb93cSSam Leffler 
131939beb93cSSam Leffler 		res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s);
132039beb93cSSam Leffler 		os_free(decrypted);
132139beb93cSSam Leffler 
132239beb93cSSam Leffler 		return res;
132339beb93cSSam Leffler 	}
132439beb93cSSam Leffler 	data->counter = eattr.counter;
132539beb93cSSam Leffler 
132639beb93cSSam Leffler 	os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
132739beb93cSSam Leffler 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S",
132839beb93cSSam Leffler 		    data->nonce_s, EAP_SIM_NONCE_S_LEN);
132939beb93cSSam Leffler 
133039beb93cSSam Leffler 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
133139beb93cSSam Leffler 		eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
133239beb93cSSam Leffler 						 data->reauth_id,
133339beb93cSSam Leffler 						 data->reauth_id_len,
133439beb93cSSam Leffler 						 data->nonce_s,
133539beb93cSSam Leffler 						 data->msk, data->emsk);
133639beb93cSSam Leffler 	} else {
133739beb93cSSam Leffler 		eap_sim_derive_keys_reauth(data->counter, data->reauth_id,
133839beb93cSSam Leffler 					   data->reauth_id_len,
133939beb93cSSam Leffler 					   data->nonce_s, data->mk,
134039beb93cSSam Leffler 					   data->msk, data->emsk);
134139beb93cSSam Leffler 	}
1342f05cddf9SRui Paulo 	eap_aka_clear_identities(sm, data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
1343f05cddf9SRui Paulo 	eap_aka_learn_ids(sm, data, &eattr);
134439beb93cSSam Leffler 
134539beb93cSSam Leffler 	if (data->result_ind && attr->result_ind)
134639beb93cSSam Leffler 		data->use_result_ind = 1;
134739beb93cSSam Leffler 
13485b9c547cSRui Paulo 	if (data->state != FAILURE) {
134939beb93cSSam Leffler 		eap_aka_state(data, data->use_result_ind ?
135039beb93cSSam Leffler 			      RESULT_SUCCESS : SUCCESS);
135139beb93cSSam Leffler 	}
135239beb93cSSam Leffler 
135339beb93cSSam Leffler 	data->num_id_req = 0;
135439beb93cSSam Leffler 	data->num_notification = 0;
135539beb93cSSam Leffler 	if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
135639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
135739beb93cSSam Leffler 			   "fast reauths performed - force fullauth");
1358f05cddf9SRui Paulo 		eap_aka_clear_identities(sm, data,
1359f05cddf9SRui Paulo 					 CLEAR_REAUTH_ID | CLEAR_EAP_ID);
136039beb93cSSam Leffler 	}
136139beb93cSSam Leffler 	os_free(decrypted);
136239beb93cSSam Leffler 	return eap_aka_response_reauth(data, id, 0, data->nonce_s);
136339beb93cSSam Leffler }
136439beb93cSSam Leffler 
136539beb93cSSam Leffler 
136639beb93cSSam Leffler static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv,
136739beb93cSSam Leffler 				       struct eap_method_ret *ret,
136839beb93cSSam Leffler 				       const struct wpabuf *reqData)
136939beb93cSSam Leffler {
137039beb93cSSam Leffler 	struct eap_aka_data *data = priv;
137139beb93cSSam Leffler 	const struct eap_hdr *req;
137239beb93cSSam Leffler 	u8 subtype, id;
137339beb93cSSam Leffler 	struct wpabuf *res;
137439beb93cSSam Leffler 	const u8 *pos;
137539beb93cSSam Leffler 	struct eap_sim_attrs attr;
137639beb93cSSam Leffler 	size_t len;
137739beb93cSSam Leffler 
137839beb93cSSam Leffler 	wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData);
137939beb93cSSam Leffler 	if (eap_get_config_identity(sm, &len) == NULL) {
138039beb93cSSam Leffler 		wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured");
138139beb93cSSam Leffler 		eap_sm_request_identity(sm);
1382*c1d255d3SCy Schubert 		ret->ignore = true;
138339beb93cSSam Leffler 		return NULL;
138439beb93cSSam Leffler 	}
138539beb93cSSam Leffler 
138639beb93cSSam Leffler 	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData,
138739beb93cSSam Leffler 			       &len);
1388325151a3SRui Paulo 	if (pos == NULL || len < 3) {
1389*c1d255d3SCy Schubert 		ret->ignore = true;
139039beb93cSSam Leffler 		return NULL;
139139beb93cSSam Leffler 	}
139239beb93cSSam Leffler 	req = wpabuf_head(reqData);
139339beb93cSSam Leffler 	id = req->identifier;
139439beb93cSSam Leffler 	len = be_to_host16(req->length);
139539beb93cSSam Leffler 
1396*c1d255d3SCy Schubert 	ret->ignore = false;
139739beb93cSSam Leffler 	ret->methodState = METHOD_MAY_CONT;
139839beb93cSSam Leffler 	ret->decision = DECISION_FAIL;
1399*c1d255d3SCy Schubert 	ret->allowNotifications = true;
140039beb93cSSam Leffler 
140139beb93cSSam Leffler 	subtype = *pos++;
140239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
140339beb93cSSam Leffler 	pos += 2; /* Reserved */
140439beb93cSSam Leffler 
140539beb93cSSam Leffler 	if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr,
140639beb93cSSam Leffler 			       data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
140739beb93cSSam Leffler 			       0)) {
140839beb93cSSam Leffler 		res = eap_aka_client_error(data, id,
140939beb93cSSam Leffler 					   EAP_AKA_UNABLE_TO_PROCESS_PACKET);
141039beb93cSSam Leffler 		goto done;
141139beb93cSSam Leffler 	}
141239beb93cSSam Leffler 
141339beb93cSSam Leffler 	switch (subtype) {
141439beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_IDENTITY:
141539beb93cSSam Leffler 		res = eap_aka_process_identity(sm, data, id, reqData, &attr);
141639beb93cSSam Leffler 		break;
141739beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_CHALLENGE:
141839beb93cSSam Leffler 		res = eap_aka_process_challenge(sm, data, id, reqData, &attr);
141939beb93cSSam Leffler 		break;
142039beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_NOTIFICATION:
142139beb93cSSam Leffler 		res = eap_aka_process_notification(sm, data, id, reqData,
142239beb93cSSam Leffler 						   &attr);
142339beb93cSSam Leffler 		break;
142439beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_REAUTHENTICATION:
142539beb93cSSam Leffler 		res = eap_aka_process_reauthentication(sm, data, id, reqData,
142639beb93cSSam Leffler 						       &attr);
142739beb93cSSam Leffler 		break;
142839beb93cSSam Leffler 	case EAP_AKA_SUBTYPE_CLIENT_ERROR:
142939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error");
143039beb93cSSam Leffler 		res = eap_aka_client_error(data, id,
143139beb93cSSam Leffler 					   EAP_AKA_UNABLE_TO_PROCESS_PACKET);
143239beb93cSSam Leffler 		break;
143339beb93cSSam Leffler 	default:
143439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype);
143539beb93cSSam Leffler 		res = eap_aka_client_error(data, id,
143639beb93cSSam Leffler 					   EAP_AKA_UNABLE_TO_PROCESS_PACKET);
143739beb93cSSam Leffler 		break;
143839beb93cSSam Leffler 	}
143939beb93cSSam Leffler 
144039beb93cSSam Leffler done:
144139beb93cSSam Leffler 	if (data->state == FAILURE) {
144239beb93cSSam Leffler 		ret->decision = DECISION_FAIL;
144339beb93cSSam Leffler 		ret->methodState = METHOD_DONE;
144439beb93cSSam Leffler 	} else if (data->state == SUCCESS) {
144539beb93cSSam Leffler 		ret->decision = data->use_result_ind ?
144639beb93cSSam Leffler 			DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
144739beb93cSSam Leffler 		/*
144839beb93cSSam Leffler 		 * It is possible for the server to reply with AKA
144939beb93cSSam Leffler 		 * Notification, so we must allow the method to continue and
145039beb93cSSam Leffler 		 * not only accept EAP-Success at this point.
145139beb93cSSam Leffler 		 */
145239beb93cSSam Leffler 		ret->methodState = data->use_result_ind ?
145339beb93cSSam Leffler 			METHOD_DONE : METHOD_MAY_CONT;
14545b9c547cSRui Paulo 	} else if (data->state == RESULT_SUCCESS)
145539beb93cSSam Leffler 		ret->methodState = METHOD_CONT;
145639beb93cSSam Leffler 
145739beb93cSSam Leffler 	if (ret->methodState == METHOD_DONE) {
1458*c1d255d3SCy Schubert 		ret->allowNotifications = false;
145939beb93cSSam Leffler 	}
146039beb93cSSam Leffler 
146139beb93cSSam Leffler 	return res;
146239beb93cSSam Leffler }
146339beb93cSSam Leffler 
146439beb93cSSam Leffler 
1465*c1d255d3SCy Schubert static bool eap_aka_has_reauth_data(struct eap_sm *sm, void *priv)
146639beb93cSSam Leffler {
146739beb93cSSam Leffler 	struct eap_aka_data *data = priv;
146839beb93cSSam Leffler 	return data->pseudonym || data->reauth_id;
146939beb93cSSam Leffler }
147039beb93cSSam Leffler 
147139beb93cSSam Leffler 
147239beb93cSSam Leffler static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
147339beb93cSSam Leffler {
147439beb93cSSam Leffler 	struct eap_aka_data *data = priv;
1475f05cddf9SRui Paulo 	eap_aka_clear_identities(sm, data, CLEAR_EAP_ID);
147639beb93cSSam Leffler 	data->prev_id = -1;
147739beb93cSSam Leffler 	wpabuf_free(data->id_msgs);
147839beb93cSSam Leffler 	data->id_msgs = NULL;
147939beb93cSSam Leffler 	data->use_result_ind = 0;
148039beb93cSSam Leffler 	data->kdf_negotiation = 0;
14815b9c547cSRui Paulo 	eap_aka_clear_keys(data, 1);
148239beb93cSSam Leffler }
148339beb93cSSam Leffler 
148439beb93cSSam Leffler 
148539beb93cSSam Leffler static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv)
148639beb93cSSam Leffler {
148739beb93cSSam Leffler 	struct eap_aka_data *data = priv;
148839beb93cSSam Leffler 	data->num_id_req = 0;
148939beb93cSSam Leffler 	data->num_notification = 0;
149039beb93cSSam Leffler 	eap_aka_state(data, CONTINUE);
149139beb93cSSam Leffler 	return priv;
149239beb93cSSam Leffler }
149339beb93cSSam Leffler 
149439beb93cSSam Leffler 
149539beb93cSSam Leffler static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv,
149639beb93cSSam Leffler 				       size_t *len)
149739beb93cSSam Leffler {
149839beb93cSSam Leffler 	struct eap_aka_data *data = priv;
149939beb93cSSam Leffler 
150039beb93cSSam Leffler 	if (data->reauth_id) {
150139beb93cSSam Leffler 		*len = data->reauth_id_len;
150239beb93cSSam Leffler 		return data->reauth_id;
150339beb93cSSam Leffler 	}
150439beb93cSSam Leffler 
150539beb93cSSam Leffler 	if (data->pseudonym) {
150639beb93cSSam Leffler 		*len = data->pseudonym_len;
150739beb93cSSam Leffler 		return data->pseudonym;
150839beb93cSSam Leffler 	}
150939beb93cSSam Leffler 
151039beb93cSSam Leffler 	return NULL;
151139beb93cSSam Leffler }
151239beb93cSSam Leffler 
151339beb93cSSam Leffler 
1514*c1d255d3SCy Schubert static bool eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv)
151539beb93cSSam Leffler {
151639beb93cSSam Leffler 	struct eap_aka_data *data = priv;
151739beb93cSSam Leffler 	return data->state == SUCCESS;
151839beb93cSSam Leffler }
151939beb93cSSam Leffler 
152039beb93cSSam Leffler 
152139beb93cSSam Leffler static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
152239beb93cSSam Leffler {
152339beb93cSSam Leffler 	struct eap_aka_data *data = priv;
152439beb93cSSam Leffler 	u8 *key;
152539beb93cSSam Leffler 
152639beb93cSSam Leffler 	if (data->state != SUCCESS)
152739beb93cSSam Leffler 		return NULL;
152839beb93cSSam Leffler 
152985732ac8SCy Schubert 	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
153039beb93cSSam Leffler 	if (key == NULL)
153139beb93cSSam Leffler 		return NULL;
153239beb93cSSam Leffler 
153339beb93cSSam Leffler 	*len = EAP_SIM_KEYING_DATA_LEN;
153439beb93cSSam Leffler 
153539beb93cSSam Leffler 	return key;
153639beb93cSSam Leffler }
153739beb93cSSam Leffler 
153839beb93cSSam Leffler 
15395b9c547cSRui Paulo static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
15405b9c547cSRui Paulo {
15415b9c547cSRui Paulo 	struct eap_aka_data *data = priv;
15425b9c547cSRui Paulo 	u8 *id;
15435b9c547cSRui Paulo 
15445b9c547cSRui Paulo 	if (data->state != SUCCESS)
15455b9c547cSRui Paulo 		return NULL;
15465b9c547cSRui Paulo 
1547206b73d0SCy Schubert 	if (!data->reauth)
15485b9c547cSRui Paulo 		*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
1549206b73d0SCy Schubert 	else
1550206b73d0SCy Schubert 		*len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
15515b9c547cSRui Paulo 	id = os_malloc(*len);
15525b9c547cSRui Paulo 	if (id == NULL)
15535b9c547cSRui Paulo 		return NULL;
15545b9c547cSRui Paulo 
15555b9c547cSRui Paulo 	id[0] = data->eap_method;
1556206b73d0SCy Schubert 	if (!data->reauth) {
15575b9c547cSRui Paulo 		os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
1558206b73d0SCy Schubert 		os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn,
1559206b73d0SCy Schubert 			  EAP_AKA_AUTN_LEN);
1560206b73d0SCy Schubert 	} else {
1561206b73d0SCy Schubert 		os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
1562206b73d0SCy Schubert 		os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
1563206b73d0SCy Schubert 			  EAP_SIM_MAC_LEN);
1564206b73d0SCy Schubert 	}
15655b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
15665b9c547cSRui Paulo 
15675b9c547cSRui Paulo 	return id;
15685b9c547cSRui Paulo }
15695b9c547cSRui Paulo 
15705b9c547cSRui Paulo 
157139beb93cSSam Leffler static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
157239beb93cSSam Leffler {
157339beb93cSSam Leffler 	struct eap_aka_data *data = priv;
157439beb93cSSam Leffler 	u8 *key;
157539beb93cSSam Leffler 
157639beb93cSSam Leffler 	if (data->state != SUCCESS)
157739beb93cSSam Leffler 		return NULL;
157839beb93cSSam Leffler 
157985732ac8SCy Schubert 	key = os_memdup(data->emsk, EAP_EMSK_LEN);
158039beb93cSSam Leffler 	if (key == NULL)
158139beb93cSSam Leffler 		return NULL;
158239beb93cSSam Leffler 
158339beb93cSSam Leffler 	*len = EAP_EMSK_LEN;
158439beb93cSSam Leffler 
158539beb93cSSam Leffler 	return key;
158639beb93cSSam Leffler }
158739beb93cSSam Leffler 
158839beb93cSSam Leffler 
158985732ac8SCy Schubert static int eap_aka_get_error_code(void *priv)
159085732ac8SCy Schubert {
159185732ac8SCy Schubert 	struct eap_aka_data *data = priv;
159285732ac8SCy Schubert 	int current_data_error;
159385732ac8SCy Schubert 
159485732ac8SCy Schubert 	if (!data)
159585732ac8SCy Schubert 		return NO_EAP_METHOD_ERROR;
159685732ac8SCy Schubert 
159785732ac8SCy Schubert 	current_data_error = data->error_code;
159885732ac8SCy Schubert 
159985732ac8SCy Schubert 	/* Now reset for next transaction */
160085732ac8SCy Schubert 	data->error_code = NO_EAP_METHOD_ERROR;
160185732ac8SCy Schubert 
160285732ac8SCy Schubert 	return current_data_error;
160385732ac8SCy Schubert }
160485732ac8SCy Schubert 
160585732ac8SCy Schubert 
160639beb93cSSam Leffler int eap_peer_aka_register(void)
160739beb93cSSam Leffler {
160839beb93cSSam Leffler 	struct eap_method *eap;
160939beb93cSSam Leffler 
161039beb93cSSam Leffler 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
161139beb93cSSam Leffler 				    EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
161239beb93cSSam Leffler 	if (eap == NULL)
161339beb93cSSam Leffler 		return -1;
161439beb93cSSam Leffler 
161539beb93cSSam Leffler 	eap->init = eap_aka_init;
161639beb93cSSam Leffler 	eap->deinit = eap_aka_deinit;
161739beb93cSSam Leffler 	eap->process = eap_aka_process;
161839beb93cSSam Leffler 	eap->isKeyAvailable = eap_aka_isKeyAvailable;
161939beb93cSSam Leffler 	eap->getKey = eap_aka_getKey;
16205b9c547cSRui Paulo 	eap->getSessionId = eap_aka_get_session_id;
162139beb93cSSam Leffler 	eap->has_reauth_data = eap_aka_has_reauth_data;
162239beb93cSSam Leffler 	eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
162339beb93cSSam Leffler 	eap->init_for_reauth = eap_aka_init_for_reauth;
162439beb93cSSam Leffler 	eap->get_identity = eap_aka_get_identity;
162539beb93cSSam Leffler 	eap->get_emsk = eap_aka_get_emsk;
162685732ac8SCy Schubert 	eap->get_error_code = eap_aka_get_error_code;
162739beb93cSSam Leffler 
1628780fb4a2SCy Schubert 	return eap_peer_method_register(eap);
162939beb93cSSam Leffler }
163039beb93cSSam Leffler 
163139beb93cSSam Leffler 
163239beb93cSSam Leffler #ifdef EAP_AKA_PRIME
163339beb93cSSam Leffler int eap_peer_aka_prime_register(void)
163439beb93cSSam Leffler {
163539beb93cSSam Leffler 	struct eap_method *eap;
163639beb93cSSam Leffler 
163739beb93cSSam Leffler 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
163839beb93cSSam Leffler 				    EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
163939beb93cSSam Leffler 				    "AKA'");
164039beb93cSSam Leffler 	if (eap == NULL)
164139beb93cSSam Leffler 		return -1;
164239beb93cSSam Leffler 
164339beb93cSSam Leffler 	eap->init = eap_aka_prime_init;
164439beb93cSSam Leffler 	eap->deinit = eap_aka_deinit;
164539beb93cSSam Leffler 	eap->process = eap_aka_process;
164639beb93cSSam Leffler 	eap->isKeyAvailable = eap_aka_isKeyAvailable;
164739beb93cSSam Leffler 	eap->getKey = eap_aka_getKey;
16485b9c547cSRui Paulo 	eap->getSessionId = eap_aka_get_session_id;
164939beb93cSSam Leffler 	eap->has_reauth_data = eap_aka_has_reauth_data;
165039beb93cSSam Leffler 	eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
165139beb93cSSam Leffler 	eap->init_for_reauth = eap_aka_init_for_reauth;
165239beb93cSSam Leffler 	eap->get_identity = eap_aka_get_identity;
165339beb93cSSam Leffler 	eap->get_emsk = eap_aka_get_emsk;
165485732ac8SCy Schubert 	eap->get_error_code = eap_aka_get_error_code;
165539beb93cSSam Leffler 
1656780fb4a2SCy Schubert 	return eap_peer_method_register(eap);
165739beb93cSSam Leffler }
165839beb93cSSam Leffler #endif /* EAP_AKA_PRIME */
1659