xref: /freebsd/contrib/wpa/src/eap_server/eap_server_aka.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1e28a4053SRui Paulo /*
2f05cddf9SRui Paulo  * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
3f05cddf9SRui Paulo  * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
4e28a4053SRui Paulo  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
7e28a4053SRui Paulo  */
8e28a4053SRui Paulo 
9e28a4053SRui Paulo #include "includes.h"
10e28a4053SRui Paulo 
11e28a4053SRui Paulo #include "common.h"
12*a90b9d01SCy Schubert #include "utils/base64.h"
13e28a4053SRui Paulo #include "crypto/sha256.h"
14e28a4053SRui Paulo #include "crypto/crypto.h"
15f05cddf9SRui Paulo #include "crypto/random.h"
16e28a4053SRui Paulo #include "eap_common/eap_sim_common.h"
17e28a4053SRui Paulo #include "eap_server/eap_i.h"
18e28a4053SRui Paulo #include "eap_server/eap_sim_db.h"
19e28a4053SRui Paulo 
20e28a4053SRui Paulo 
21e28a4053SRui Paulo struct eap_aka_data {
22e28a4053SRui Paulo 	u8 mk[EAP_SIM_MK_LEN];
23e28a4053SRui Paulo 	u8 nonce_s[EAP_SIM_NONCE_S_LEN];
24e28a4053SRui Paulo 	u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
25e28a4053SRui Paulo 	u8 k_encr[EAP_SIM_K_ENCR_LEN];
26e28a4053SRui Paulo 	u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */
27e28a4053SRui Paulo 	u8 msk[EAP_SIM_KEYING_DATA_LEN];
28e28a4053SRui Paulo 	u8 emsk[EAP_EMSK_LEN];
29e28a4053SRui Paulo 	u8 rand[EAP_AKA_RAND_LEN];
30e28a4053SRui Paulo 	u8 autn[EAP_AKA_AUTN_LEN];
31e28a4053SRui Paulo 	u8 ck[EAP_AKA_CK_LEN];
32e28a4053SRui Paulo 	u8 ik[EAP_AKA_IK_LEN];
33e28a4053SRui Paulo 	u8 res[EAP_AKA_RES_MAX_LEN];
34206b73d0SCy Schubert 	u8 reauth_mac[EAP_SIM_MAC_LEN];
35e28a4053SRui Paulo 	size_t res_len;
36e28a4053SRui Paulo 	enum {
37e28a4053SRui Paulo 		IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
38e28a4053SRui Paulo 	} state;
39e28a4053SRui Paulo 	char *next_pseudonym;
40e28a4053SRui Paulo 	char *next_reauth_id;
41e28a4053SRui Paulo 	u16 counter;
42e28a4053SRui Paulo 	struct eap_sim_reauth *reauth;
43e28a4053SRui Paulo 	int auts_reported; /* whether the current AUTS has been reported to the
44e28a4053SRui Paulo 			    * eap_sim_db */
45e28a4053SRui Paulo 	u16 notification;
46e28a4053SRui Paulo 	int use_result_ind;
47e28a4053SRui Paulo 
48e28a4053SRui Paulo 	struct wpabuf *id_msgs;
49e28a4053SRui Paulo 	int pending_id;
50e28a4053SRui Paulo 	u8 eap_method;
51e28a4053SRui Paulo 	u8 *network_name;
52e28a4053SRui Paulo 	size_t network_name_len;
53e28a4053SRui Paulo 	u16 kdf;
54f05cddf9SRui Paulo 	int identity_round;
55f05cddf9SRui Paulo 	char permanent[20]; /* Permanent username */
56e28a4053SRui Paulo };
57e28a4053SRui Paulo 
58e28a4053SRui Paulo 
59f05cddf9SRui Paulo static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data);
60e28a4053SRui Paulo 
61e28a4053SRui Paulo 
eap_aka_state_txt(int state)62e28a4053SRui Paulo static const char * eap_aka_state_txt(int state)
63e28a4053SRui Paulo {
64e28a4053SRui Paulo 	switch (state) {
65e28a4053SRui Paulo 	case IDENTITY:
66e28a4053SRui Paulo 		return "IDENTITY";
67e28a4053SRui Paulo 	case CHALLENGE:
68e28a4053SRui Paulo 		return "CHALLENGE";
69e28a4053SRui Paulo 	case REAUTH:
70e28a4053SRui Paulo 		return "REAUTH";
71e28a4053SRui Paulo 	case SUCCESS:
72e28a4053SRui Paulo 		return "SUCCESS";
73e28a4053SRui Paulo 	case FAILURE:
74e28a4053SRui Paulo 		return "FAILURE";
75e28a4053SRui Paulo 	case NOTIFICATION:
76e28a4053SRui Paulo 		return "NOTIFICATION";
77e28a4053SRui Paulo 	default:
78e28a4053SRui Paulo 		return "Unknown?!";
79e28a4053SRui Paulo 	}
80e28a4053SRui Paulo }
81e28a4053SRui Paulo 
82e28a4053SRui Paulo 
eap_aka_state(struct eap_aka_data * data,int state)83e28a4053SRui Paulo static void eap_aka_state(struct eap_aka_data *data, int state)
84e28a4053SRui Paulo {
85e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
86e28a4053SRui Paulo 		   eap_aka_state_txt(data->state),
87e28a4053SRui Paulo 		   eap_aka_state_txt(state));
88e28a4053SRui Paulo 	data->state = state;
89e28a4053SRui Paulo }
90e28a4053SRui Paulo 
91e28a4053SRui Paulo 
eap_aka_check_identity_reauth(struct eap_sm * sm,struct eap_aka_data * data,const char * username)92f05cddf9SRui Paulo static int eap_aka_check_identity_reauth(struct eap_sm *sm,
93f05cddf9SRui Paulo 					 struct eap_aka_data *data,
94f05cddf9SRui Paulo 					 const char *username)
95f05cddf9SRui Paulo {
96f05cddf9SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME &&
97f05cddf9SRui Paulo 	    username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX)
98f05cddf9SRui Paulo 		return 0;
99f05cddf9SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA &&
100f05cddf9SRui Paulo 	    username[0] != EAP_AKA_REAUTH_ID_PREFIX)
101f05cddf9SRui Paulo 		return 0;
102f05cddf9SRui Paulo 
103f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username);
104c1d255d3SCy Schubert 	data->reauth = eap_sim_db_get_reauth_entry(sm->cfg->eap_sim_db_priv,
105f05cddf9SRui Paulo 						   username);
106f05cddf9SRui Paulo 	if (data->reauth == NULL) {
107f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - "
108f05cddf9SRui Paulo 			   "request full auth identity");
109f05cddf9SRui Paulo 		/* Remain in IDENTITY state for another round */
110f05cddf9SRui Paulo 		return 0;
111f05cddf9SRui Paulo 	}
112f05cddf9SRui Paulo 
113*a90b9d01SCy Schubert 	if (data->reauth->counter > sm->cfg->eap_sim_aka_fast_reauth_limit) {
114*a90b9d01SCy Schubert 		wpa_printf(MSG_DEBUG,
115*a90b9d01SCy Schubert 			   "EAP-AKA: Too many fast re-authentication attemps - fall back to full authentication");
116*a90b9d01SCy Schubert 		if (sm->cfg->eap_sim_id & 0x04) {
117*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
118*a90b9d01SCy Schubert 				   "EAP-AKA: Permanent identity recognized - skip AKA-Identity exchange");
119*a90b9d01SCy Schubert 			os_strlcpy(data->permanent, data->reauth->permanent,
120*a90b9d01SCy Schubert 				   sizeof(data->permanent));
121*a90b9d01SCy Schubert 			os_strlcpy(sm->sim_aka_permanent,
122*a90b9d01SCy Schubert 				   data->reauth->permanent,
123*a90b9d01SCy Schubert 				   sizeof(sm->sim_aka_permanent));
124*a90b9d01SCy Schubert 			eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv,
125*a90b9d01SCy Schubert 						 data->reauth);
126*a90b9d01SCy Schubert 			data->reauth = NULL;
127*a90b9d01SCy Schubert 			eap_aka_fullauth(sm, data);
128*a90b9d01SCy Schubert 			return 1;
129*a90b9d01SCy Schubert 		}
130*a90b9d01SCy Schubert 		return 0;
131*a90b9d01SCy Schubert 	}
132*a90b9d01SCy Schubert 
133*a90b9d01SCy Schubert 	wpa_printf(MSG_DEBUG,
134*a90b9d01SCy Schubert 		   "EAP-AKA: Using fast re-authentication (counter=%d)",
135*a90b9d01SCy Schubert 		   data->reauth->counter);
136f05cddf9SRui Paulo 	os_strlcpy(data->permanent, data->reauth->permanent,
137f05cddf9SRui Paulo 		   sizeof(data->permanent));
138f05cddf9SRui Paulo 	data->counter = data->reauth->counter;
139f05cddf9SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
140f05cddf9SRui Paulo 		os_memcpy(data->k_encr, data->reauth->k_encr,
141f05cddf9SRui Paulo 			  EAP_SIM_K_ENCR_LEN);
142f05cddf9SRui Paulo 		os_memcpy(data->k_aut, data->reauth->k_aut,
143f05cddf9SRui Paulo 			  EAP_AKA_PRIME_K_AUT_LEN);
144f05cddf9SRui Paulo 		os_memcpy(data->k_re, data->reauth->k_re,
145f05cddf9SRui Paulo 			  EAP_AKA_PRIME_K_RE_LEN);
146f05cddf9SRui Paulo 	} else {
147f05cddf9SRui Paulo 		os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
148f05cddf9SRui Paulo 	}
149f05cddf9SRui Paulo 
150f05cddf9SRui Paulo 	eap_aka_state(data, REAUTH);
151f05cddf9SRui Paulo 	return 1;
152f05cddf9SRui Paulo }
153f05cddf9SRui Paulo 
154f05cddf9SRui Paulo 
eap_aka_check_identity(struct eap_sm * sm,struct eap_aka_data * data)155f05cddf9SRui Paulo static void eap_aka_check_identity(struct eap_sm *sm,
156f05cddf9SRui Paulo 				   struct eap_aka_data *data)
157f05cddf9SRui Paulo {
158f05cddf9SRui Paulo 	char *username;
159*a90b9d01SCy Schubert 	const u8 *identity = sm->identity;
160*a90b9d01SCy Schubert 	size_t identity_len = sm->identity_len;
161*a90b9d01SCy Schubert 
162*a90b9d01SCy Schubert 	if (sm->sim_aka_permanent[0]) {
163*a90b9d01SCy Schubert 		identity = (const u8 *) sm->sim_aka_permanent;
164*a90b9d01SCy Schubert 		identity_len = os_strlen(sm->sim_aka_permanent);
165*a90b9d01SCy Schubert 	}
166f05cddf9SRui Paulo 
167f05cddf9SRui Paulo 	/* Check if we already know the identity from EAP-Response/Identity */
168f05cddf9SRui Paulo 
169*a90b9d01SCy Schubert 	username = sim_get_username(identity, identity_len);
170f05cddf9SRui Paulo 	if (username == NULL)
171f05cddf9SRui Paulo 		return;
172f05cddf9SRui Paulo 
173f05cddf9SRui Paulo 	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
174f05cddf9SRui Paulo 		os_free(username);
175f05cddf9SRui Paulo 		/*
176f05cddf9SRui Paulo 		 * Since re-auth username was recognized, skip AKA/Identity
177f05cddf9SRui Paulo 		 * exchange.
178f05cddf9SRui Paulo 		 */
179f05cddf9SRui Paulo 		return;
180f05cddf9SRui Paulo 	}
181f05cddf9SRui Paulo 
182*a90b9d01SCy Schubert 	if (sm->sim_aka_permanent[0] && data->state == IDENTITY) {
183*a90b9d01SCy Schubert 		/* Skip AKA/Identity exchange since the permanent identity
184*a90b9d01SCy Schubert 		 * was recognized. */
185*a90b9d01SCy Schubert 		os_free(username);
186*a90b9d01SCy Schubert 		os_strlcpy(data->permanent, sm->sim_aka_permanent,
187*a90b9d01SCy Schubert 			   sizeof(data->permanent));
188*a90b9d01SCy Schubert 		eap_aka_fullauth(sm, data);
189*a90b9d01SCy Schubert 		return;
190*a90b9d01SCy Schubert 	}
191*a90b9d01SCy Schubert 
192f05cddf9SRui Paulo 	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
193f05cddf9SRui Paulo 	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
194f05cddf9SRui Paulo 	    (data->eap_method == EAP_TYPE_AKA &&
195f05cddf9SRui Paulo 	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
196f05cddf9SRui Paulo 		const char *permanent;
197f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
198f05cddf9SRui Paulo 			   username);
199f05cddf9SRui Paulo 		permanent = eap_sim_db_get_permanent(
200c1d255d3SCy Schubert 			sm->cfg->eap_sim_db_priv, username);
201f05cddf9SRui Paulo 		if (permanent == NULL) {
202f05cddf9SRui Paulo 			os_free(username);
203f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
204f05cddf9SRui Paulo 				   "identity - request permanent identity");
205f05cddf9SRui Paulo 			/* Remain in IDENTITY state for another round */
206f05cddf9SRui Paulo 			return;
207f05cddf9SRui Paulo 		}
208f05cddf9SRui Paulo 		os_strlcpy(data->permanent, permanent,
209f05cddf9SRui Paulo 			   sizeof(data->permanent));
210f05cddf9SRui Paulo 		/*
211f05cddf9SRui Paulo 		 * Since pseudonym username was recognized, skip AKA/Identity
212f05cddf9SRui Paulo 		 * exchange.
213f05cddf9SRui Paulo 		 */
214f05cddf9SRui Paulo 		eap_aka_fullauth(sm, data);
215f05cddf9SRui Paulo 	}
216f05cddf9SRui Paulo 
217f05cddf9SRui Paulo 	os_free(username);
218f05cddf9SRui Paulo }
219f05cddf9SRui Paulo 
220f05cddf9SRui Paulo 
eap_aka_init(struct eap_sm * sm)221e28a4053SRui Paulo static void * eap_aka_init(struct eap_sm *sm)
222e28a4053SRui Paulo {
223e28a4053SRui Paulo 	struct eap_aka_data *data;
224e28a4053SRui Paulo 
225c1d255d3SCy Schubert 	if (!sm->cfg->eap_sim_db_priv) {
226e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
227e28a4053SRui Paulo 		return NULL;
228e28a4053SRui Paulo 	}
229e28a4053SRui Paulo 
230e28a4053SRui Paulo 	data = os_zalloc(sizeof(*data));
231e28a4053SRui Paulo 	if (data == NULL)
232e28a4053SRui Paulo 		return NULL;
233e28a4053SRui Paulo 
234e28a4053SRui Paulo 	data->eap_method = EAP_TYPE_AKA;
235e28a4053SRui Paulo 
236e28a4053SRui Paulo 	data->state = IDENTITY;
237e28a4053SRui Paulo 	data->pending_id = -1;
238f05cddf9SRui Paulo 	eap_aka_check_identity(sm, data);
239e28a4053SRui Paulo 
240e28a4053SRui Paulo 	return data;
241e28a4053SRui Paulo }
242e28a4053SRui Paulo 
243e28a4053SRui Paulo 
244e28a4053SRui Paulo #ifdef EAP_SERVER_AKA_PRIME
eap_aka_prime_init(struct eap_sm * sm)245e28a4053SRui Paulo static void * eap_aka_prime_init(struct eap_sm *sm)
246e28a4053SRui Paulo {
247e28a4053SRui Paulo 	struct eap_aka_data *data;
248e28a4053SRui Paulo 	/* TODO: make ANID configurable; see 3GPP TS 24.302 */
249e28a4053SRui Paulo 	char *network_name = "WLAN";
250e28a4053SRui Paulo 
251c1d255d3SCy Schubert 	if (sm->cfg->eap_sim_db_priv == NULL) {
252e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
253e28a4053SRui Paulo 		return NULL;
254e28a4053SRui Paulo 	}
255e28a4053SRui Paulo 
256e28a4053SRui Paulo 	data = os_zalloc(sizeof(*data));
257e28a4053SRui Paulo 	if (data == NULL)
258e28a4053SRui Paulo 		return NULL;
259e28a4053SRui Paulo 
260e28a4053SRui Paulo 	data->eap_method = EAP_TYPE_AKA_PRIME;
261f05cddf9SRui Paulo 	data->network_name = (u8 *) os_strdup(network_name);
262e28a4053SRui Paulo 	if (data->network_name == NULL) {
263e28a4053SRui Paulo 		os_free(data);
264e28a4053SRui Paulo 		return NULL;
265e28a4053SRui Paulo 	}
266e28a4053SRui Paulo 
267e28a4053SRui Paulo 	data->network_name_len = os_strlen(network_name);
268e28a4053SRui Paulo 
269e28a4053SRui Paulo 	data->state = IDENTITY;
270e28a4053SRui Paulo 	data->pending_id = -1;
271f05cddf9SRui Paulo 	eap_aka_check_identity(sm, data);
272e28a4053SRui Paulo 
273e28a4053SRui Paulo 	return data;
274e28a4053SRui Paulo }
275e28a4053SRui Paulo #endif /* EAP_SERVER_AKA_PRIME */
276e28a4053SRui Paulo 
277e28a4053SRui Paulo 
eap_aka_reset(struct eap_sm * sm,void * priv)278e28a4053SRui Paulo static void eap_aka_reset(struct eap_sm *sm, void *priv)
279e28a4053SRui Paulo {
280e28a4053SRui Paulo 	struct eap_aka_data *data = priv;
281e28a4053SRui Paulo 	os_free(data->next_pseudonym);
282e28a4053SRui Paulo 	os_free(data->next_reauth_id);
283e28a4053SRui Paulo 	wpabuf_free(data->id_msgs);
284e28a4053SRui Paulo 	os_free(data->network_name);
2855b9c547cSRui Paulo 	bin_clear_free(data, sizeof(*data));
286e28a4053SRui Paulo }
287e28a4053SRui Paulo 
288e28a4053SRui Paulo 
eap_aka_add_id_msg(struct eap_aka_data * data,const struct wpabuf * msg)289e28a4053SRui Paulo static int eap_aka_add_id_msg(struct eap_aka_data *data,
290e28a4053SRui Paulo 			      const struct wpabuf *msg)
291e28a4053SRui Paulo {
292e28a4053SRui Paulo 	if (msg == NULL)
293e28a4053SRui Paulo 		return -1;
294e28a4053SRui Paulo 
295e28a4053SRui Paulo 	if (data->id_msgs == NULL) {
296e28a4053SRui Paulo 		data->id_msgs = wpabuf_dup(msg);
297e28a4053SRui Paulo 		return data->id_msgs == NULL ? -1 : 0;
298e28a4053SRui Paulo 	}
299e28a4053SRui Paulo 
300e28a4053SRui Paulo 	if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
301e28a4053SRui Paulo 		return -1;
302e28a4053SRui Paulo 	wpabuf_put_buf(data->id_msgs, msg);
303e28a4053SRui Paulo 
304e28a4053SRui Paulo 	return 0;
305e28a4053SRui Paulo }
306e28a4053SRui Paulo 
307e28a4053SRui Paulo 
eap_aka_add_checkcode(struct eap_aka_data * data,struct eap_sim_msg * msg)308e28a4053SRui Paulo static void eap_aka_add_checkcode(struct eap_aka_data *data,
309e28a4053SRui Paulo 				  struct eap_sim_msg *msg)
310e28a4053SRui Paulo {
311e28a4053SRui Paulo 	const u8 *addr;
312e28a4053SRui Paulo 	size_t len;
313e28a4053SRui Paulo 	u8 hash[SHA256_MAC_LEN];
314e28a4053SRui Paulo 
315e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "   AT_CHECKCODE");
316e28a4053SRui Paulo 
317e28a4053SRui Paulo 	if (data->id_msgs == NULL) {
318e28a4053SRui Paulo 		/*
319e28a4053SRui Paulo 		 * No EAP-AKA/Identity packets were exchanged - send empty
320e28a4053SRui Paulo 		 * checkcode.
321e28a4053SRui Paulo 		 */
322e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
323e28a4053SRui Paulo 		return;
324e28a4053SRui Paulo 	}
325e28a4053SRui Paulo 
326e28a4053SRui Paulo 	/* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
327e28a4053SRui Paulo 	addr = wpabuf_head(data->id_msgs);
328e28a4053SRui Paulo 	len = wpabuf_len(data->id_msgs);
329e28a4053SRui Paulo 	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
330e28a4053SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME)
331e28a4053SRui Paulo 		sha256_vector(1, &addr, &len, hash);
332e28a4053SRui Paulo 	else
333e28a4053SRui Paulo 		sha1_vector(1, &addr, &len, hash);
334e28a4053SRui Paulo 
335e28a4053SRui Paulo 	eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
336e28a4053SRui Paulo 			data->eap_method == EAP_TYPE_AKA_PRIME ?
337e28a4053SRui Paulo 			EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
338e28a4053SRui Paulo }
339e28a4053SRui Paulo 
340e28a4053SRui Paulo 
eap_aka_verify_checkcode(struct eap_aka_data * data,const u8 * checkcode,size_t checkcode_len)341e28a4053SRui Paulo static int eap_aka_verify_checkcode(struct eap_aka_data *data,
342e28a4053SRui Paulo 				    const u8 *checkcode, size_t checkcode_len)
343e28a4053SRui Paulo {
344e28a4053SRui Paulo 	const u8 *addr;
345e28a4053SRui Paulo 	size_t len;
346e28a4053SRui Paulo 	u8 hash[SHA256_MAC_LEN];
347e28a4053SRui Paulo 	size_t hash_len;
348e28a4053SRui Paulo 
349e28a4053SRui Paulo 	if (checkcode == NULL)
350e28a4053SRui Paulo 		return -1;
351e28a4053SRui Paulo 
352e28a4053SRui Paulo 	if (data->id_msgs == NULL) {
353e28a4053SRui Paulo 		if (checkcode_len != 0) {
354e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer "
355e28a4053SRui Paulo 				   "indicates that AKA/Identity messages were "
356e28a4053SRui Paulo 				   "used, but they were not");
357e28a4053SRui Paulo 			return -1;
358e28a4053SRui Paulo 		}
359e28a4053SRui Paulo 		return 0;
360e28a4053SRui Paulo 	}
361e28a4053SRui Paulo 
362e28a4053SRui Paulo 	hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
363e28a4053SRui Paulo 		EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
364e28a4053SRui Paulo 
365e28a4053SRui Paulo 	if (checkcode_len != hash_len) {
366e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates "
367e28a4053SRui Paulo 			   "that AKA/Identity message were not used, but they "
368e28a4053SRui Paulo 			   "were");
369e28a4053SRui Paulo 		return -1;
370e28a4053SRui Paulo 	}
371e28a4053SRui Paulo 
372e28a4053SRui Paulo 	/* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
373e28a4053SRui Paulo 	addr = wpabuf_head(data->id_msgs);
374e28a4053SRui Paulo 	len = wpabuf_len(data->id_msgs);
375e28a4053SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME)
376e28a4053SRui Paulo 		sha256_vector(1, &addr, &len, hash);
377e28a4053SRui Paulo 	else
378e28a4053SRui Paulo 		sha1_vector(1, &addr, &len, hash);
379e28a4053SRui Paulo 
3805b9c547cSRui Paulo 	if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
381e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
382e28a4053SRui Paulo 		return -1;
383e28a4053SRui Paulo 	}
384e28a4053SRui Paulo 
385e28a4053SRui Paulo 	return 0;
386e28a4053SRui Paulo }
387e28a4053SRui Paulo 
388e28a4053SRui Paulo 
eap_aka_build_identity(struct eap_sm * sm,struct eap_aka_data * data,u8 id)389e28a4053SRui Paulo static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm,
390e28a4053SRui Paulo 					      struct eap_aka_data *data, u8 id)
391e28a4053SRui Paulo {
392e28a4053SRui Paulo 	struct eap_sim_msg *msg;
393e28a4053SRui Paulo 	struct wpabuf *buf;
394e28a4053SRui Paulo 
395e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity");
396e28a4053SRui Paulo 	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
397e28a4053SRui Paulo 			       EAP_AKA_SUBTYPE_IDENTITY);
398f05cddf9SRui Paulo 	data->identity_round++;
399f05cddf9SRui Paulo 	if (data->identity_round == 1) {
400e28a4053SRui Paulo 		/*
401e28a4053SRui Paulo 		 * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is
402e28a4053SRui Paulo 		 * ignored and the AKA/Identity is used to request the
403e28a4053SRui Paulo 		 * identity.
404e28a4053SRui Paulo 		 */
405e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
406e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
407f05cddf9SRui Paulo 	} else if (data->identity_round > 3) {
408f05cddf9SRui Paulo 		/* Cannot use more than three rounds of Identity messages */
409f05cddf9SRui Paulo 		eap_sim_msg_free(msg);
410f05cddf9SRui Paulo 		return NULL;
411f05cddf9SRui Paulo 	} else if (sm->identity && sm->identity_len > 0 &&
412f05cddf9SRui Paulo 		   (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
413f05cddf9SRui Paulo 		    sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) {
414f05cddf9SRui Paulo 		/* Reauth id may have expired - try fullauth */
415f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
416f05cddf9SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
417f05cddf9SRui Paulo 	} else {
418f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
419f05cddf9SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
420e28a4053SRui Paulo 	}
4215b9c547cSRui Paulo 	buf = eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
422e28a4053SRui Paulo 	if (eap_aka_add_id_msg(data, buf) < 0) {
423e28a4053SRui Paulo 		wpabuf_free(buf);
424e28a4053SRui Paulo 		return NULL;
425e28a4053SRui Paulo 	}
426e28a4053SRui Paulo 	data->pending_id = id;
427e28a4053SRui Paulo 	return buf;
428e28a4053SRui Paulo }
429e28a4053SRui Paulo 
430e28a4053SRui Paulo 
eap_aka_build_encr(struct eap_sm * sm,struct eap_aka_data * data,struct eap_sim_msg * msg,u16 counter,const u8 * nonce_s)431e28a4053SRui Paulo static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data,
432e28a4053SRui Paulo 			      struct eap_sim_msg *msg, u16 counter,
433e28a4053SRui Paulo 			      const u8 *nonce_s)
434e28a4053SRui Paulo {
435e28a4053SRui Paulo 	os_free(data->next_pseudonym);
436c1d255d3SCy Schubert 	if (!(sm->cfg->eap_sim_id & 0x01)) {
437206b73d0SCy Schubert 		/* Use of pseudonyms disabled in configuration */
438206b73d0SCy Schubert 		data->next_pseudonym = NULL;
439206b73d0SCy Schubert 	} else if (!nonce_s) {
440e28a4053SRui Paulo 		data->next_pseudonym =
441f05cddf9SRui Paulo 			eap_sim_db_get_next_pseudonym(
442c1d255d3SCy Schubert 				sm->cfg->eap_sim_db_priv,
443f05cddf9SRui Paulo 				data->eap_method == EAP_TYPE_AKA_PRIME ?
444f05cddf9SRui Paulo 				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
445f05cddf9SRui Paulo 	} else {
446f05cddf9SRui Paulo 		/* Do not update pseudonym during re-authentication */
447f05cddf9SRui Paulo 		data->next_pseudonym = NULL;
448f05cddf9SRui Paulo 	}
449e28a4053SRui Paulo 	os_free(data->next_reauth_id);
450c1d255d3SCy Schubert 	if (!(sm->cfg->eap_sim_id & 0x02)) {
451206b73d0SCy Schubert 		/* Use of fast reauth disabled in configuration */
452206b73d0SCy Schubert 		data->next_reauth_id = NULL;
453206b73d0SCy Schubert 	} else if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
454e28a4053SRui Paulo 		data->next_reauth_id =
455f05cddf9SRui Paulo 			eap_sim_db_get_next_reauth_id(
456c1d255d3SCy Schubert 				sm->cfg->eap_sim_db_priv,
457f05cddf9SRui Paulo 				data->eap_method == EAP_TYPE_AKA_PRIME ?
458f05cddf9SRui Paulo 				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
459e28a4053SRui Paulo 	} else {
460e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication "
461e28a4053SRui Paulo 			   "count exceeded - force full authentication");
462e28a4053SRui Paulo 		data->next_reauth_id = NULL;
463e28a4053SRui Paulo 	}
464e28a4053SRui Paulo 
465e28a4053SRui Paulo 	if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
466e28a4053SRui Paulo 	    counter == 0 && nonce_s == NULL)
467e28a4053SRui Paulo 		return 0;
468e28a4053SRui Paulo 
469e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "   AT_IV");
470e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
471e28a4053SRui Paulo 	eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
472e28a4053SRui Paulo 
473e28a4053SRui Paulo 	if (counter > 0) {
474e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)", counter);
475e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
476e28a4053SRui Paulo 	}
477e28a4053SRui Paulo 
478e28a4053SRui Paulo 	if (nonce_s) {
479e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   *AT_NONCE_S");
480e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
481e28a4053SRui Paulo 				EAP_SIM_NONCE_S_LEN);
482e28a4053SRui Paulo 	}
483e28a4053SRui Paulo 
484e28a4053SRui Paulo 	if (data->next_pseudonym) {
485e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   *AT_NEXT_PSEUDONYM (%s)",
486e28a4053SRui Paulo 			   data->next_pseudonym);
487e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
488e28a4053SRui Paulo 				os_strlen(data->next_pseudonym),
489e28a4053SRui Paulo 				(u8 *) data->next_pseudonym,
490e28a4053SRui Paulo 				os_strlen(data->next_pseudonym));
491e28a4053SRui Paulo 	}
492e28a4053SRui Paulo 
493e28a4053SRui Paulo 	if (data->next_reauth_id) {
494e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   *AT_NEXT_REAUTH_ID (%s)",
495e28a4053SRui Paulo 			   data->next_reauth_id);
496e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
497e28a4053SRui Paulo 				os_strlen(data->next_reauth_id),
498e28a4053SRui Paulo 				(u8 *) data->next_reauth_id,
499e28a4053SRui Paulo 				os_strlen(data->next_reauth_id));
500e28a4053SRui Paulo 	}
501e28a4053SRui Paulo 
502e28a4053SRui Paulo 	if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
503e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
504e28a4053SRui Paulo 			   "AT_ENCR_DATA");
505e28a4053SRui Paulo 		return -1;
506e28a4053SRui Paulo 	}
507e28a4053SRui Paulo 
508e28a4053SRui Paulo 	return 0;
509e28a4053SRui Paulo }
510e28a4053SRui Paulo 
511e28a4053SRui Paulo 
eap_aka_build_challenge(struct eap_sm * sm,struct eap_aka_data * data,u8 id)512e28a4053SRui Paulo static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm,
513e28a4053SRui Paulo 					       struct eap_aka_data *data,
514e28a4053SRui Paulo 					       u8 id)
515e28a4053SRui Paulo {
516e28a4053SRui Paulo 	struct eap_sim_msg *msg;
517e28a4053SRui Paulo 
518e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge");
519e28a4053SRui Paulo 	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
520e28a4053SRui Paulo 			       EAP_AKA_SUBTYPE_CHALLENGE);
521e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "   AT_RAND");
522e28a4053SRui Paulo 	eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN);
523e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "   AT_AUTN");
524e28a4053SRui Paulo 	eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN);
525e28a4053SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
526e28a4053SRui Paulo 		if (data->kdf) {
527e28a4053SRui Paulo 			/* Add the selected KDF into the beginning */
528e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "   AT_KDF");
529e28a4053SRui Paulo 			eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf,
530e28a4053SRui Paulo 					NULL, 0);
531e28a4053SRui Paulo 		}
532e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   AT_KDF");
533e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF,
534e28a4053SRui Paulo 				NULL, 0);
535e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   AT_KDF_INPUT");
536e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT,
537e28a4053SRui Paulo 				data->network_name_len,
538e28a4053SRui Paulo 				data->network_name, data->network_name_len);
539e28a4053SRui Paulo 	}
540e28a4053SRui Paulo 
541e28a4053SRui Paulo 	if (eap_aka_build_encr(sm, data, msg, 0, NULL)) {
542e28a4053SRui Paulo 		eap_sim_msg_free(msg);
543e28a4053SRui Paulo 		return NULL;
544e28a4053SRui Paulo 	}
545e28a4053SRui Paulo 
546e28a4053SRui Paulo 	eap_aka_add_checkcode(data, msg);
547e28a4053SRui Paulo 
548c1d255d3SCy Schubert 	if (sm->cfg->eap_sim_aka_result_ind) {
549e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
550e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
551e28a4053SRui Paulo 	}
552e28a4053SRui Paulo 
553e28a4053SRui Paulo #ifdef EAP_SERVER_AKA_PRIME
554e28a4053SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA) {
555e28a4053SRui Paulo 		u16 flags = 0;
556e28a4053SRui Paulo 		int i;
557e28a4053SRui Paulo 		int aka_prime_preferred = 0;
558e28a4053SRui Paulo 
559e28a4053SRui Paulo 		i = 0;
560e28a4053SRui Paulo 		while (sm->user && i < EAP_MAX_METHODS &&
561e28a4053SRui Paulo 		       (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
562e28a4053SRui Paulo 			sm->user->methods[i].method != EAP_TYPE_NONE)) {
563e28a4053SRui Paulo 			if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) {
564e28a4053SRui Paulo 				if (sm->user->methods[i].method ==
565e28a4053SRui Paulo 				    EAP_TYPE_AKA)
566e28a4053SRui Paulo 					break;
567e28a4053SRui Paulo 				if (sm->user->methods[i].method ==
568e28a4053SRui Paulo 				    EAP_TYPE_AKA_PRIME) {
569e28a4053SRui Paulo 					aka_prime_preferred = 1;
570e28a4053SRui Paulo 					break;
571e28a4053SRui Paulo 				}
572e28a4053SRui Paulo 			}
573e28a4053SRui Paulo 			i++;
574e28a4053SRui Paulo 		}
575e28a4053SRui Paulo 
576e28a4053SRui Paulo 		if (aka_prime_preferred)
577e28a4053SRui Paulo 			flags |= EAP_AKA_BIDDING_FLAG_D;
578e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0);
579e28a4053SRui Paulo 	}
580e28a4053SRui Paulo #endif /* EAP_SERVER_AKA_PRIME */
581e28a4053SRui Paulo 
582e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "   AT_MAC");
583e28a4053SRui Paulo 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
5845b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
585e28a4053SRui Paulo }
586e28a4053SRui Paulo 
587e28a4053SRui Paulo 
eap_aka_build_reauth(struct eap_sm * sm,struct eap_aka_data * data,u8 id)588e28a4053SRui Paulo static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm,
589e28a4053SRui Paulo 					    struct eap_aka_data *data, u8 id)
590e28a4053SRui Paulo {
591e28a4053SRui Paulo 	struct eap_sim_msg *msg;
592206b73d0SCy Schubert 	struct wpabuf *buf;
593e28a4053SRui Paulo 
594e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
595e28a4053SRui Paulo 
596f05cddf9SRui Paulo 	if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
597e28a4053SRui Paulo 		return NULL;
598e28a4053SRui Paulo 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S",
599e28a4053SRui Paulo 			data->nonce_s, EAP_SIM_NONCE_S_LEN);
600e28a4053SRui Paulo 
601e28a4053SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
602e28a4053SRui Paulo 		eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
603e28a4053SRui Paulo 						 sm->identity,
604e28a4053SRui Paulo 						 sm->identity_len,
605e28a4053SRui Paulo 						 data->nonce_s,
606e28a4053SRui Paulo 						 data->msk, data->emsk);
607e28a4053SRui Paulo 	} else {
608e28a4053SRui Paulo 		eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
609e28a4053SRui Paulo 				    data->msk, data->emsk);
610e28a4053SRui Paulo 		eap_sim_derive_keys_reauth(data->counter, sm->identity,
611e28a4053SRui Paulo 					   sm->identity_len, data->nonce_s,
612e28a4053SRui Paulo 					   data->mk, data->msk, data->emsk);
613e28a4053SRui Paulo 	}
614e28a4053SRui Paulo 
615e28a4053SRui Paulo 	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
616e28a4053SRui Paulo 			       EAP_AKA_SUBTYPE_REAUTHENTICATION);
617e28a4053SRui Paulo 
618e28a4053SRui Paulo 	if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
619e28a4053SRui Paulo 		eap_sim_msg_free(msg);
620e28a4053SRui Paulo 		return NULL;
621e28a4053SRui Paulo 	}
622e28a4053SRui Paulo 
623e28a4053SRui Paulo 	eap_aka_add_checkcode(data, msg);
624e28a4053SRui Paulo 
625c1d255d3SCy Schubert 	if (sm->cfg->eap_sim_aka_result_ind) {
626e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
627e28a4053SRui Paulo 		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
628e28a4053SRui Paulo 	}
629e28a4053SRui Paulo 
630e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "   AT_MAC");
631e28a4053SRui Paulo 	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
632206b73d0SCy Schubert 	buf = eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
633206b73d0SCy Schubert 
634206b73d0SCy Schubert 	/* Remember this MAC before sending it to the peer. This MAC is used for
635206b73d0SCy Schubert 	 * Session-Id calculation after receiving response from the peer and
636206b73d0SCy Schubert 	 * after all other checks pass. */
637206b73d0SCy Schubert 	os_memcpy(data->reauth_mac,
638206b73d0SCy Schubert 		  wpabuf_head_u8(buf) + wpabuf_len(buf) - EAP_SIM_MAC_LEN,
639206b73d0SCy Schubert 		  EAP_SIM_MAC_LEN);
640206b73d0SCy Schubert 
641206b73d0SCy Schubert 	return buf;
642e28a4053SRui Paulo }
643e28a4053SRui Paulo 
644e28a4053SRui Paulo 
eap_aka_build_notification(struct eap_sm * sm,struct eap_aka_data * data,u8 id)645e28a4053SRui Paulo static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm,
646e28a4053SRui Paulo 						  struct eap_aka_data *data,
647e28a4053SRui Paulo 						  u8 id)
648e28a4053SRui Paulo {
649e28a4053SRui Paulo 	struct eap_sim_msg *msg;
650e28a4053SRui Paulo 
651e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification");
652e28a4053SRui Paulo 	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
653e28a4053SRui Paulo 			       EAP_AKA_SUBTYPE_NOTIFICATION);
654e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "   AT_NOTIFICATION (%d)", data->notification);
655e28a4053SRui Paulo 	eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
656e28a4053SRui Paulo 			NULL, 0);
657e28a4053SRui Paulo 	if (data->use_result_ind) {
658e28a4053SRui Paulo 		if (data->reauth) {
659e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "   AT_IV");
660e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
661e28a4053SRui Paulo 			eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
662e28a4053SRui Paulo 						   EAP_SIM_AT_ENCR_DATA);
663e28a4053SRui Paulo 			wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)",
664e28a4053SRui Paulo 				   data->counter);
665e28a4053SRui Paulo 			eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
666e28a4053SRui Paulo 					NULL, 0);
667e28a4053SRui Paulo 
668e28a4053SRui Paulo 			if (eap_sim_msg_add_encr_end(msg, data->k_encr,
669e28a4053SRui Paulo 						     EAP_SIM_AT_PADDING)) {
670e28a4053SRui Paulo 				wpa_printf(MSG_WARNING, "EAP-AKA: Failed to "
671e28a4053SRui Paulo 					   "encrypt AT_ENCR_DATA");
672e28a4053SRui Paulo 				eap_sim_msg_free(msg);
673e28a4053SRui Paulo 				return NULL;
674e28a4053SRui Paulo 			}
675e28a4053SRui Paulo 		}
676e28a4053SRui Paulo 
677e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "   AT_MAC");
678e28a4053SRui Paulo 		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
679e28a4053SRui Paulo 	}
6805b9c547cSRui Paulo 	return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
681e28a4053SRui Paulo }
682e28a4053SRui Paulo 
683e28a4053SRui Paulo 
eap_aka_buildReq(struct eap_sm * sm,void * priv,u8 id)684e28a4053SRui Paulo static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id)
685e28a4053SRui Paulo {
686e28a4053SRui Paulo 	struct eap_aka_data *data = priv;
687e28a4053SRui Paulo 
688e28a4053SRui Paulo 	data->auts_reported = 0;
689e28a4053SRui Paulo 	switch (data->state) {
690e28a4053SRui Paulo 	case IDENTITY:
691e28a4053SRui Paulo 		return eap_aka_build_identity(sm, data, id);
692e28a4053SRui Paulo 	case CHALLENGE:
693e28a4053SRui Paulo 		return eap_aka_build_challenge(sm, data, id);
694e28a4053SRui Paulo 	case REAUTH:
695e28a4053SRui Paulo 		return eap_aka_build_reauth(sm, data, id);
696e28a4053SRui Paulo 	case NOTIFICATION:
697e28a4053SRui Paulo 		return eap_aka_build_notification(sm, data, id);
698e28a4053SRui Paulo 	default:
699e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in "
700e28a4053SRui Paulo 			   "buildReq", data->state);
701e28a4053SRui Paulo 		break;
702e28a4053SRui Paulo 	}
703e28a4053SRui Paulo 	return NULL;
704e28a4053SRui Paulo }
705e28a4053SRui Paulo 
706e28a4053SRui Paulo 
eap_aka_check(struct eap_sm * sm,void * priv,struct wpabuf * respData)707c1d255d3SCy Schubert static bool eap_aka_check(struct eap_sm *sm, void *priv,
708e28a4053SRui Paulo 			  struct wpabuf *respData)
709e28a4053SRui Paulo {
710e28a4053SRui Paulo 	struct eap_aka_data *data = priv;
711e28a4053SRui Paulo 	const u8 *pos;
712e28a4053SRui Paulo 	size_t len;
713e28a4053SRui Paulo 
714e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData,
715e28a4053SRui Paulo 			       &len);
716e28a4053SRui Paulo 	if (pos == NULL || len < 3) {
717e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame");
718c1d255d3SCy Schubert 		return true;
719e28a4053SRui Paulo 	}
720e28a4053SRui Paulo 
721c1d255d3SCy Schubert 	return false;
722e28a4053SRui Paulo }
723e28a4053SRui Paulo 
724e28a4053SRui Paulo 
eap_aka_subtype_ok(struct eap_aka_data * data,u8 subtype)725c1d255d3SCy Schubert static bool eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype)
726e28a4053SRui Paulo {
727e28a4053SRui Paulo 	if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR ||
728e28a4053SRui Paulo 	    subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT)
729c1d255d3SCy Schubert 		return false;
730e28a4053SRui Paulo 
731e28a4053SRui Paulo 	switch (data->state) {
732e28a4053SRui Paulo 	case IDENTITY:
733e28a4053SRui Paulo 		if (subtype != EAP_AKA_SUBTYPE_IDENTITY) {
734e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
735e28a4053SRui Paulo 				   "subtype %d", subtype);
736c1d255d3SCy Schubert 			return true;
737e28a4053SRui Paulo 		}
738e28a4053SRui Paulo 		break;
739e28a4053SRui Paulo 	case CHALLENGE:
740e28a4053SRui Paulo 		if (subtype != EAP_AKA_SUBTYPE_CHALLENGE &&
741e28a4053SRui Paulo 		    subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) {
742e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
743e28a4053SRui Paulo 				   "subtype %d", subtype);
744c1d255d3SCy Schubert 			return true;
745e28a4053SRui Paulo 		}
746e28a4053SRui Paulo 		break;
747e28a4053SRui Paulo 	case REAUTH:
748e28a4053SRui Paulo 		if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) {
749e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
750e28a4053SRui Paulo 				   "subtype %d", subtype);
751c1d255d3SCy Schubert 			return true;
752e28a4053SRui Paulo 		}
753e28a4053SRui Paulo 		break;
754e28a4053SRui Paulo 	case NOTIFICATION:
755e28a4053SRui Paulo 		if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) {
756e28a4053SRui Paulo 			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
757e28a4053SRui Paulo 				   "subtype %d", subtype);
758c1d255d3SCy Schubert 			return true;
759e28a4053SRui Paulo 		}
760e28a4053SRui Paulo 		break;
761e28a4053SRui Paulo 	default:
762e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for "
763e28a4053SRui Paulo 			   "processing a response", data->state);
764c1d255d3SCy Schubert 		return true;
765e28a4053SRui Paulo 	}
766e28a4053SRui Paulo 
767c1d255d3SCy Schubert 	return false;
768e28a4053SRui Paulo }
769e28a4053SRui Paulo 
770e28a4053SRui Paulo 
eap_aka_determine_identity(struct eap_sm * sm,struct eap_aka_data * data)771e28a4053SRui Paulo static void eap_aka_determine_identity(struct eap_sm *sm,
772f05cddf9SRui Paulo 				       struct eap_aka_data *data)
773e28a4053SRui Paulo {
774f05cddf9SRui Paulo 	char *username;
775f05cddf9SRui Paulo 
776f05cddf9SRui Paulo 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity",
777f05cddf9SRui Paulo 			  sm->identity, sm->identity_len);
778f05cddf9SRui Paulo 
779f05cddf9SRui Paulo 	username = sim_get_username(sm->identity, sm->identity_len);
780*a90b9d01SCy Schubert 	if (!username)
781*a90b9d01SCy Schubert 		goto fail;
782f05cddf9SRui Paulo 
783f05cddf9SRui Paulo 	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
784f05cddf9SRui Paulo 		os_free(username);
785f05cddf9SRui Paulo 		return;
786f05cddf9SRui Paulo 	}
787f05cddf9SRui Paulo 
788f05cddf9SRui Paulo 	if (((data->eap_method == EAP_TYPE_AKA_PRIME &&
789f05cddf9SRui Paulo 	      username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) ||
790f05cddf9SRui Paulo 	     (data->eap_method == EAP_TYPE_AKA &&
791f05cddf9SRui Paulo 	      username[0] == EAP_AKA_REAUTH_ID_PREFIX)) &&
792f05cddf9SRui Paulo 	    data->identity_round == 1) {
793f05cddf9SRui Paulo 		/* Remain in IDENTITY state for another round to request full
794f05cddf9SRui Paulo 		 * auth identity since we did not recognize reauth id */
795f05cddf9SRui Paulo 		os_free(username);
796f05cddf9SRui Paulo 		return;
797f05cddf9SRui Paulo 	}
798f05cddf9SRui Paulo 
799f05cddf9SRui Paulo 	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
800f05cddf9SRui Paulo 	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
801f05cddf9SRui Paulo 	    (data->eap_method == EAP_TYPE_AKA &&
802f05cddf9SRui Paulo 	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
803f05cddf9SRui Paulo 		const char *permanent;
804f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
805f05cddf9SRui Paulo 			   username);
806f05cddf9SRui Paulo 		permanent = eap_sim_db_get_permanent(
807c1d255d3SCy Schubert 			sm->cfg->eap_sim_db_priv, username);
808f05cddf9SRui Paulo 		os_free(username);
809f05cddf9SRui Paulo 		if (permanent == NULL) {
810f05cddf9SRui Paulo 			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
811f05cddf9SRui Paulo 				   "identity - request permanent identity");
812f05cddf9SRui Paulo 			/* Remain in IDENTITY state for another round */
813f05cddf9SRui Paulo 			return;
814f05cddf9SRui Paulo 		}
815f05cddf9SRui Paulo 		os_strlcpy(data->permanent, permanent,
816f05cddf9SRui Paulo 			   sizeof(data->permanent));
817f05cddf9SRui Paulo 	} else if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
818f05cddf9SRui Paulo 		    username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) ||
819f05cddf9SRui Paulo 		   (data->eap_method == EAP_TYPE_AKA &&
820f05cddf9SRui Paulo 		    username[0] == EAP_AKA_PERMANENT_PREFIX)) {
821f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'",
822f05cddf9SRui Paulo 			   username);
823f05cddf9SRui Paulo 		os_strlcpy(data->permanent, username, sizeof(data->permanent));
824f05cddf9SRui Paulo 		os_free(username);
825*a90b9d01SCy Schubert #ifdef CRYPTO_RSA_OAEP_SHA256
826*a90b9d01SCy Schubert 	} else if (sm->identity_len > 1 && sm->identity[0] == '\0') {
827*a90b9d01SCy Schubert 		char *enc_id, *pos, *end;
828*a90b9d01SCy Schubert 		size_t enc_id_len;
829*a90b9d01SCy Schubert 		u8 *decoded_id;
830*a90b9d01SCy Schubert 		size_t decoded_id_len;
831*a90b9d01SCy Schubert 		struct wpabuf *enc, *dec;
832*a90b9d01SCy Schubert 		u8 *new_id;
833*a90b9d01SCy Schubert 
834*a90b9d01SCy Schubert 		os_free(username);
835*a90b9d01SCy Schubert 		if (!sm->cfg->imsi_privacy_key) {
836*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
837*a90b9d01SCy Schubert 				   "EAP-AKA: Received encrypted identity, but no IMSI privacy key configured to decrypt it");
838*a90b9d01SCy Schubert 			goto fail;
839*a90b9d01SCy Schubert 		}
840*a90b9d01SCy Schubert 
841*a90b9d01SCy Schubert 		enc_id = (char *) &sm->identity[1];
842*a90b9d01SCy Schubert 		end = (char *) &sm->identity[sm->identity_len];
843*a90b9d01SCy Schubert 		for (pos = enc_id; pos < end; pos++) {
844*a90b9d01SCy Schubert 			if (*pos == ',')
845*a90b9d01SCy Schubert 				break;
846*a90b9d01SCy Schubert 		}
847*a90b9d01SCy Schubert 		enc_id_len = pos - enc_id;
848*a90b9d01SCy Schubert 
849*a90b9d01SCy Schubert 		wpa_hexdump_ascii(MSG_DEBUG,
850*a90b9d01SCy Schubert 				  "EAP-AKA: Encrypted permanent identity",
851*a90b9d01SCy Schubert 				  enc_id, enc_id_len);
852*a90b9d01SCy Schubert 		decoded_id = base64_decode(enc_id, enc_id_len, &decoded_id_len);
853*a90b9d01SCy Schubert 		if (!decoded_id) {
854*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
855*a90b9d01SCy Schubert 				   "EAP-AKA: Could not base64 decode encrypted identity");
856*a90b9d01SCy Schubert 			goto fail;
857*a90b9d01SCy Schubert 		}
858*a90b9d01SCy Schubert 		wpa_hexdump(MSG_DEBUG,
859*a90b9d01SCy Schubert 			    "EAP-AKA: Decoded encrypted permanent identity",
860*a90b9d01SCy Schubert 			    decoded_id, decoded_id_len);
861*a90b9d01SCy Schubert 		enc = wpabuf_alloc_copy(decoded_id, decoded_id_len);
862*a90b9d01SCy Schubert 		os_free(decoded_id);
863*a90b9d01SCy Schubert 		if (!enc)
864*a90b9d01SCy Schubert 			goto fail;
865*a90b9d01SCy Schubert 		dec = crypto_rsa_oaep_sha256_decrypt(sm->cfg->imsi_privacy_key,
866*a90b9d01SCy Schubert 						     enc);
867*a90b9d01SCy Schubert 		wpabuf_free(enc);
868*a90b9d01SCy Schubert 		if (!dec) {
869*a90b9d01SCy Schubert 			wpa_printf(MSG_DEBUG,
870*a90b9d01SCy Schubert 				   "EAP-AKA: Failed to decrypt encrypted identity");
871*a90b9d01SCy Schubert 			goto fail;
872*a90b9d01SCy Schubert 		}
873*a90b9d01SCy Schubert 		wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Decrypted permanent identity",
874*a90b9d01SCy Schubert 				  wpabuf_head(dec), wpabuf_len(dec));
875*a90b9d01SCy Schubert 		username = sim_get_username(wpabuf_head(dec), wpabuf_len(dec));
876*a90b9d01SCy Schubert 		if (!username) {
877*a90b9d01SCy Schubert 			wpabuf_free(dec);
878*a90b9d01SCy Schubert 			goto fail;
879*a90b9d01SCy Schubert 		}
880*a90b9d01SCy Schubert 		new_id = os_memdup(wpabuf_head(dec), wpabuf_len(dec));
881*a90b9d01SCy Schubert 		if (!new_id) {
882*a90b9d01SCy Schubert 			wpabuf_free(dec);
883*a90b9d01SCy Schubert 			goto fail;
884*a90b9d01SCy Schubert 		}
885*a90b9d01SCy Schubert 		os_free(sm->identity);
886*a90b9d01SCy Schubert 		sm->identity = new_id;
887*a90b9d01SCy Schubert 		sm->identity_len = wpabuf_len(dec);
888*a90b9d01SCy Schubert 		wpabuf_free(dec);
889*a90b9d01SCy Schubert 		os_strlcpy(data->permanent, username, sizeof(data->permanent));
890*a90b9d01SCy Schubert 		os_free(username);
891*a90b9d01SCy Schubert #endif /* CRYPTO_RSA_OAEP_SHA256 */
892f05cddf9SRui Paulo 	} else {
893f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'",
894f05cddf9SRui Paulo 			   username);
895f05cddf9SRui Paulo 		os_free(username);
896*a90b9d01SCy Schubert 		goto fail;
897f05cddf9SRui Paulo 		return;
898f05cddf9SRui Paulo 	}
899f05cddf9SRui Paulo 
900f05cddf9SRui Paulo 	eap_aka_fullauth(sm, data);
901*a90b9d01SCy Schubert 	return;
902*a90b9d01SCy Schubert 
903*a90b9d01SCy Schubert fail:
904*a90b9d01SCy Schubert 	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
905*a90b9d01SCy Schubert 	eap_aka_state(data, NOTIFICATION);
906f05cddf9SRui Paulo }
907f05cddf9SRui Paulo 
908f05cddf9SRui Paulo 
eap_aka_fullauth(struct eap_sm * sm,struct eap_aka_data * data)909f05cddf9SRui Paulo static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data)
910f05cddf9SRui Paulo {
911e28a4053SRui Paulo 	size_t identity_len;
912e28a4053SRui Paulo 	int res;
913e28a4053SRui Paulo 
914c1d255d3SCy Schubert 	res = eap_sim_db_get_aka_auth(sm->cfg->eap_sim_db_priv, data->permanent,
915f05cddf9SRui Paulo 				      data->rand, data->autn, data->ik,
916f05cddf9SRui Paulo 				      data->ck, data->res, &data->res_len, sm);
917e28a4053SRui Paulo 	if (res == EAP_SIM_DB_PENDING) {
918e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
919e28a4053SRui Paulo 			   "not yet available - pending request");
920e28a4053SRui Paulo 		sm->method_pending = METHOD_PENDING_WAIT;
921e28a4053SRui Paulo 		return;
922e28a4053SRui Paulo 	}
923e28a4053SRui Paulo 
9244bc52338SCy Schubert 	if (data->permanent[0] == EAP_AKA_PERMANENT_PREFIX ||
9254bc52338SCy Schubert 	    data->permanent[0] == EAP_AKA_PRIME_PERMANENT_PREFIX)
9264bc52338SCy Schubert 		os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi));
9274bc52338SCy Schubert 
928e28a4053SRui Paulo #ifdef EAP_SERVER_AKA_PRIME
929e28a4053SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
930e28a4053SRui Paulo 		/* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
931e28a4053SRui Paulo 		 * needed 6-octet SQN ^AK for CK',IK' derivation */
932e28a4053SRui Paulo 		eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
933e28a4053SRui Paulo 						 data->autn,
934e28a4053SRui Paulo 						 data->network_name,
935e28a4053SRui Paulo 						 data->network_name_len);
936e28a4053SRui Paulo 	}
937e28a4053SRui Paulo #endif /* EAP_SERVER_AKA_PRIME */
938e28a4053SRui Paulo 
939e28a4053SRui Paulo 	data->reauth = NULL;
940e28a4053SRui Paulo 	data->counter = 0; /* reset re-auth counter since this is full auth */
941e28a4053SRui Paulo 
942e28a4053SRui Paulo 	if (res != 0) {
943e28a4053SRui Paulo 		wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA "
944e28a4053SRui Paulo 			   "authentication data for the peer");
945e28a4053SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
946e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
947e28a4053SRui Paulo 		return;
948e28a4053SRui Paulo 	}
949e28a4053SRui Paulo 	if (sm->method_pending == METHOD_PENDING_WAIT) {
950e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
951e28a4053SRui Paulo 			   "available - abort pending wait");
952e28a4053SRui Paulo 		sm->method_pending = METHOD_PENDING_NONE;
953e28a4053SRui Paulo 	}
954e28a4053SRui Paulo 
955e28a4053SRui Paulo 	identity_len = sm->identity_len;
956e28a4053SRui Paulo 	while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
957e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null "
958e28a4053SRui Paulo 			   "character from identity");
959e28a4053SRui Paulo 		identity_len--;
960e28a4053SRui Paulo 	}
961e28a4053SRui Paulo 	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation",
962e28a4053SRui Paulo 			  sm->identity, identity_len);
963e28a4053SRui Paulo 
964e28a4053SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
965f05cddf9SRui Paulo 		eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik,
966e28a4053SRui Paulo 					  data->ck, data->k_encr, data->k_aut,
967e28a4053SRui Paulo 					  data->k_re, data->msk, data->emsk);
968e28a4053SRui Paulo 	} else {
969e28a4053SRui Paulo 		eap_aka_derive_mk(sm->identity, identity_len, data->ik,
970e28a4053SRui Paulo 				  data->ck, data->mk);
971e28a4053SRui Paulo 		eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
972e28a4053SRui Paulo 				    data->msk, data->emsk);
973e28a4053SRui Paulo 	}
974e28a4053SRui Paulo 
975e28a4053SRui Paulo 	eap_aka_state(data, CHALLENGE);
976e28a4053SRui Paulo }
977e28a4053SRui Paulo 
978e28a4053SRui Paulo 
eap_aka_process_identity(struct eap_sm * sm,struct eap_aka_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)979e28a4053SRui Paulo static void eap_aka_process_identity(struct eap_sm *sm,
980e28a4053SRui Paulo 				     struct eap_aka_data *data,
981e28a4053SRui Paulo 				     struct wpabuf *respData,
982e28a4053SRui Paulo 				     struct eap_sim_attrs *attr)
983e28a4053SRui Paulo {
984f05cddf9SRui Paulo 	u8 *new_identity;
985f05cddf9SRui Paulo 
986e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity");
987e28a4053SRui Paulo 
988e28a4053SRui Paulo 	if (attr->mac || attr->iv || attr->encr_data) {
989e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute "
990e28a4053SRui Paulo 			   "received in EAP-Response/AKA-Identity");
991e28a4053SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
992e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
993e28a4053SRui Paulo 		return;
994e28a4053SRui Paulo 	}
995e28a4053SRui Paulo 
996f05cddf9SRui Paulo 	/*
997f05cddf9SRui Paulo 	 * We always request identity with AKA/Identity, so the peer is
998f05cddf9SRui Paulo 	 * required to have replied with one.
999f05cddf9SRui Paulo 	 */
1000f05cddf9SRui Paulo 	if (!attr->identity || attr->identity_len == 0) {
1001f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any "
1002f05cddf9SRui Paulo 			   "identity");
1003f05cddf9SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1004f05cddf9SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1005f05cddf9SRui Paulo 		return;
1006e28a4053SRui Paulo 	}
1007e28a4053SRui Paulo 
1008f05cddf9SRui Paulo 	new_identity = os_malloc(attr->identity_len);
1009f05cddf9SRui Paulo 	if (new_identity == NULL) {
1010f05cddf9SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1011f05cddf9SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1012f05cddf9SRui Paulo 		return;
1013f05cddf9SRui Paulo 	}
1014f05cddf9SRui Paulo 	os_free(sm->identity);
1015f05cddf9SRui Paulo 	sm->identity = new_identity;
1016f05cddf9SRui Paulo 	os_memcpy(sm->identity, attr->identity, attr->identity_len);
1017f05cddf9SRui Paulo 	sm->identity_len = attr->identity_len;
1018f05cddf9SRui Paulo 
1019f05cddf9SRui Paulo 	eap_aka_determine_identity(sm, data);
1020e28a4053SRui Paulo 	if (eap_get_id(respData) == data->pending_id) {
1021e28a4053SRui Paulo 		data->pending_id = -1;
1022e28a4053SRui Paulo 		eap_aka_add_id_msg(data, respData);
1023e28a4053SRui Paulo 	}
1024e28a4053SRui Paulo }
1025e28a4053SRui Paulo 
1026e28a4053SRui Paulo 
eap_aka_verify_mac(struct eap_aka_data * data,const struct wpabuf * req,const u8 * mac,const u8 * extra,size_t extra_len)1027e28a4053SRui Paulo static int eap_aka_verify_mac(struct eap_aka_data *data,
1028e28a4053SRui Paulo 			      const struct wpabuf *req,
1029e28a4053SRui Paulo 			      const u8 *mac, const u8 *extra,
1030e28a4053SRui Paulo 			      size_t extra_len)
1031e28a4053SRui Paulo {
1032e28a4053SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME)
1033e28a4053SRui Paulo 		return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
1034e28a4053SRui Paulo 						 extra_len);
1035e28a4053SRui Paulo 	return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
1036e28a4053SRui Paulo }
1037e28a4053SRui Paulo 
1038e28a4053SRui Paulo 
eap_aka_process_challenge(struct eap_sm * sm,struct eap_aka_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)1039e28a4053SRui Paulo static void eap_aka_process_challenge(struct eap_sm *sm,
1040e28a4053SRui Paulo 				      struct eap_aka_data *data,
1041e28a4053SRui Paulo 				      struct wpabuf *respData,
1042e28a4053SRui Paulo 				      struct eap_sim_attrs *attr)
1043e28a4053SRui Paulo {
1044e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
1045e28a4053SRui Paulo 
1046e28a4053SRui Paulo #ifdef EAP_SERVER_AKA_PRIME
1047e28a4053SRui Paulo #if 0
1048e28a4053SRui Paulo 	/* KDF negotiation; to be enabled only after more than one KDF is
1049e28a4053SRui Paulo 	 * supported */
1050e28a4053SRui Paulo 	if (data->eap_method == EAP_TYPE_AKA_PRIME &&
1051e28a4053SRui Paulo 	    attr->kdf_count == 1 && attr->mac == NULL) {
1052e28a4053SRui Paulo 		if (attr->kdf[0] != EAP_AKA_PRIME_KDF) {
1053e28a4053SRui Paulo 			wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected "
1054e28a4053SRui Paulo 				   "unknown KDF");
1055e28a4053SRui Paulo 			data->notification =
1056e28a4053SRui Paulo 				EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1057e28a4053SRui Paulo 			eap_aka_state(data, NOTIFICATION);
1058e28a4053SRui Paulo 			return;
1059e28a4053SRui Paulo 		}
1060e28a4053SRui Paulo 
1061e28a4053SRui Paulo 		data->kdf = attr->kdf[0];
1062e28a4053SRui Paulo 
1063e28a4053SRui Paulo 		/* Allow negotiation to continue with the selected KDF by
1064e28a4053SRui Paulo 		 * sending another Challenge message */
1065e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
1066e28a4053SRui Paulo 		return;
1067e28a4053SRui Paulo 	}
1068e28a4053SRui Paulo #endif
1069e28a4053SRui Paulo #endif /* EAP_SERVER_AKA_PRIME */
1070e28a4053SRui Paulo 
1071e28a4053SRui Paulo 	if (attr->checkcode &&
1072e28a4053SRui Paulo 	    eap_aka_verify_checkcode(data, attr->checkcode,
1073e28a4053SRui Paulo 				     attr->checkcode_len)) {
1074e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
1075e28a4053SRui Paulo 			   "message");
1076e28a4053SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1077e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1078e28a4053SRui Paulo 		return;
1079e28a4053SRui Paulo 	}
1080e28a4053SRui Paulo 	if (attr->mac == NULL ||
1081e28a4053SRui Paulo 	    eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) {
1082e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
1083e28a4053SRui Paulo 			   "did not include valid AT_MAC");
1084e28a4053SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1085e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1086e28a4053SRui Paulo 		return;
1087e28a4053SRui Paulo 	}
1088e28a4053SRui Paulo 
1089e28a4053SRui Paulo 	/*
1090e28a4053SRui Paulo 	 * AT_RES is padded, so verify that there is enough room for RES and
1091e28a4053SRui Paulo 	 * that the RES length in bits matches with the expected RES.
1092e28a4053SRui Paulo 	 */
1093e28a4053SRui Paulo 	if (attr->res == NULL || attr->res_len < data->res_len ||
1094e28a4053SRui Paulo 	    attr->res_len_bits != data->res_len * 8 ||
10955b9c547cSRui Paulo 	    os_memcmp_const(attr->res, data->res, data->res_len) != 0) {
1096e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not "
1097e28a4053SRui Paulo 			   "include valid AT_RES (attr len=%lu, res len=%lu "
1098e28a4053SRui Paulo 			   "bits, expected %lu bits)",
1099e28a4053SRui Paulo 			   (unsigned long) attr->res_len,
1100e28a4053SRui Paulo 			   (unsigned long) attr->res_len_bits,
1101e28a4053SRui Paulo 			   (unsigned long) data->res_len * 8);
1102e28a4053SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1103e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1104e28a4053SRui Paulo 		return;
1105e28a4053SRui Paulo 	}
1106e28a4053SRui Paulo 
1107e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the "
1108e28a4053SRui Paulo 		   "correct AT_MAC");
1109c1d255d3SCy Schubert 	if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) {
1110e28a4053SRui Paulo 		data->use_result_ind = 1;
1111e28a4053SRui Paulo 		data->notification = EAP_SIM_SUCCESS;
1112e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1113e28a4053SRui Paulo 	} else
1114e28a4053SRui Paulo 		eap_aka_state(data, SUCCESS);
1115e28a4053SRui Paulo 
1116e28a4053SRui Paulo 	if (data->next_pseudonym) {
1117c1d255d3SCy Schubert 		eap_sim_db_add_pseudonym(sm->cfg->eap_sim_db_priv,
1118c1d255d3SCy Schubert 					 data->permanent,
1119e28a4053SRui Paulo 					 data->next_pseudonym);
1120e28a4053SRui Paulo 		data->next_pseudonym = NULL;
1121e28a4053SRui Paulo 	}
1122e28a4053SRui Paulo 	if (data->next_reauth_id) {
1123e28a4053SRui Paulo 		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
1124e28a4053SRui Paulo #ifdef EAP_SERVER_AKA_PRIME
1125c1d255d3SCy Schubert 			eap_sim_db_add_reauth_prime(sm->cfg->eap_sim_db_priv,
1126f05cddf9SRui Paulo 						    data->permanent,
1127e28a4053SRui Paulo 						    data->next_reauth_id,
1128e28a4053SRui Paulo 						    data->counter + 1,
1129e28a4053SRui Paulo 						    data->k_encr, data->k_aut,
1130e28a4053SRui Paulo 						    data->k_re);
1131e28a4053SRui Paulo #endif /* EAP_SERVER_AKA_PRIME */
1132e28a4053SRui Paulo 		} else {
1133c1d255d3SCy Schubert 			eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv,
1134f05cddf9SRui Paulo 					      data->permanent,
1135e28a4053SRui Paulo 					      data->next_reauth_id,
1136e28a4053SRui Paulo 					      data->counter + 1,
1137e28a4053SRui Paulo 					      data->mk);
1138e28a4053SRui Paulo 		}
1139e28a4053SRui Paulo 		data->next_reauth_id = NULL;
1140e28a4053SRui Paulo 	}
1141e28a4053SRui Paulo }
1142e28a4053SRui Paulo 
1143e28a4053SRui Paulo 
eap_aka_process_sync_failure(struct eap_sm * sm,struct eap_aka_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)1144e28a4053SRui Paulo static void eap_aka_process_sync_failure(struct eap_sm *sm,
1145e28a4053SRui Paulo 					 struct eap_aka_data *data,
1146e28a4053SRui Paulo 					 struct wpabuf *respData,
1147e28a4053SRui Paulo 					 struct eap_sim_attrs *attr)
1148e28a4053SRui Paulo {
1149e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure");
1150e28a4053SRui Paulo 
1151e28a4053SRui Paulo 	if (attr->auts == NULL) {
1152e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure "
1153e28a4053SRui Paulo 			   "message did not include valid AT_AUTS");
1154e28a4053SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1155e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1156e28a4053SRui Paulo 		return;
1157e28a4053SRui Paulo 	}
1158e28a4053SRui Paulo 
1159e28a4053SRui Paulo 	/* Avoid re-reporting AUTS when processing pending EAP packet by
1160e28a4053SRui Paulo 	 * maintaining a local flag stating whether this AUTS has already been
1161e28a4053SRui Paulo 	 * reported. */
1162e28a4053SRui Paulo 	if (!data->auts_reported &&
1163c1d255d3SCy Schubert 	    eap_sim_db_resynchronize(sm->cfg->eap_sim_db_priv, data->permanent,
1164f05cddf9SRui Paulo 				     attr->auts, data->rand)) {
1165e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed");
1166e28a4053SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1167e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1168e28a4053SRui Paulo 		return;
1169e28a4053SRui Paulo 	}
1170e28a4053SRui Paulo 	data->auts_reported = 1;
1171e28a4053SRui Paulo 
1172f05cddf9SRui Paulo 	/* Remain in CHALLENGE state to re-try after resynchronization */
11735b9c547cSRui Paulo 	eap_aka_fullauth(sm, data);
1174e28a4053SRui Paulo }
1175e28a4053SRui Paulo 
1176e28a4053SRui Paulo 
eap_aka_process_reauth(struct eap_sm * sm,struct eap_aka_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)1177e28a4053SRui Paulo static void eap_aka_process_reauth(struct eap_sm *sm,
1178e28a4053SRui Paulo 				   struct eap_aka_data *data,
1179e28a4053SRui Paulo 				   struct wpabuf *respData,
1180e28a4053SRui Paulo 				   struct eap_sim_attrs *attr)
1181e28a4053SRui Paulo {
1182e28a4053SRui Paulo 	struct eap_sim_attrs eattr;
1183e28a4053SRui Paulo 	u8 *decrypted = NULL;
1184e28a4053SRui Paulo 
1185e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication");
1186e28a4053SRui Paulo 
1187e28a4053SRui Paulo 	if (attr->mac == NULL ||
1188e28a4053SRui Paulo 	    eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s,
1189e28a4053SRui Paulo 			       EAP_SIM_NONCE_S_LEN)) {
1190e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
1191e28a4053SRui Paulo 			   "did not include valid AT_MAC");
1192e28a4053SRui Paulo 		goto fail;
1193e28a4053SRui Paulo 	}
1194e28a4053SRui Paulo 
1195e28a4053SRui Paulo 	if (attr->encr_data == NULL || attr->iv == NULL) {
1196e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
1197e28a4053SRui Paulo 			   "message did not include encrypted data");
1198e28a4053SRui Paulo 		goto fail;
1199e28a4053SRui Paulo 	}
1200e28a4053SRui Paulo 
1201e28a4053SRui Paulo 	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
1202e28a4053SRui Paulo 				       attr->encr_data_len, attr->iv, &eattr,
1203e28a4053SRui Paulo 				       0);
1204e28a4053SRui Paulo 	if (decrypted == NULL) {
1205e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
1206e28a4053SRui Paulo 			   "data from reauthentication message");
1207e28a4053SRui Paulo 		goto fail;
1208e28a4053SRui Paulo 	}
1209e28a4053SRui Paulo 
1210e28a4053SRui Paulo 	if (eattr.counter != data->counter) {
1211e28a4053SRui Paulo 		wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
1212e28a4053SRui Paulo 			   "used incorrect counter %u, expected %u",
1213e28a4053SRui Paulo 			   eattr.counter, data->counter);
1214e28a4053SRui Paulo 		goto fail;
1215e28a4053SRui Paulo 	}
1216e28a4053SRui Paulo 	os_free(decrypted);
1217e28a4053SRui Paulo 	decrypted = NULL;
1218e28a4053SRui Paulo 
1219e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes "
1220e28a4053SRui Paulo 		   "the correct AT_MAC");
1221e28a4053SRui Paulo 
1222e28a4053SRui Paulo 	if (eattr.counter_too_small) {
1223e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
1224e28a4053SRui Paulo 			   "included AT_COUNTER_TOO_SMALL - starting full "
1225e28a4053SRui Paulo 			   "authentication");
1226f05cddf9SRui Paulo 		eap_aka_fullauth(sm, data);
1227e28a4053SRui Paulo 		return;
1228e28a4053SRui Paulo 	}
1229e28a4053SRui Paulo 
1230c1d255d3SCy Schubert 	if (sm->cfg->eap_sim_aka_result_ind && attr->result_ind) {
1231e28a4053SRui Paulo 		data->use_result_ind = 1;
1232e28a4053SRui Paulo 		data->notification = EAP_SIM_SUCCESS;
1233e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1234e28a4053SRui Paulo 	} else
1235e28a4053SRui Paulo 		eap_aka_state(data, SUCCESS);
1236e28a4053SRui Paulo 
1237e28a4053SRui Paulo 	if (data->next_reauth_id) {
1238e28a4053SRui Paulo 		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
1239e28a4053SRui Paulo #ifdef EAP_SERVER_AKA_PRIME
1240c1d255d3SCy Schubert 			eap_sim_db_add_reauth_prime(sm->cfg->eap_sim_db_priv,
1241f05cddf9SRui Paulo 						    data->permanent,
1242e28a4053SRui Paulo 						    data->next_reauth_id,
1243e28a4053SRui Paulo 						    data->counter + 1,
1244e28a4053SRui Paulo 						    data->k_encr, data->k_aut,
1245e28a4053SRui Paulo 						    data->k_re);
1246e28a4053SRui Paulo #endif /* EAP_SERVER_AKA_PRIME */
1247e28a4053SRui Paulo 		} else {
1248c1d255d3SCy Schubert 			eap_sim_db_add_reauth(sm->cfg->eap_sim_db_priv,
1249f05cddf9SRui Paulo 					      data->permanent,
1250e28a4053SRui Paulo 					      data->next_reauth_id,
1251e28a4053SRui Paulo 					      data->counter + 1,
1252e28a4053SRui Paulo 					      data->mk);
1253e28a4053SRui Paulo 		}
1254e28a4053SRui Paulo 		data->next_reauth_id = NULL;
1255e28a4053SRui Paulo 	} else {
1256c1d255d3SCy Schubert 		eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv,
1257c1d255d3SCy Schubert 					 data->reauth);
1258e28a4053SRui Paulo 		data->reauth = NULL;
1259e28a4053SRui Paulo 	}
1260e28a4053SRui Paulo 
1261e28a4053SRui Paulo 	return;
1262e28a4053SRui Paulo 
1263e28a4053SRui Paulo fail:
1264e28a4053SRui Paulo 	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1265e28a4053SRui Paulo 	eap_aka_state(data, NOTIFICATION);
1266c1d255d3SCy Schubert 	eap_sim_db_remove_reauth(sm->cfg->eap_sim_db_priv, data->reauth);
1267e28a4053SRui Paulo 	data->reauth = NULL;
1268e28a4053SRui Paulo 	os_free(decrypted);
1269e28a4053SRui Paulo }
1270e28a4053SRui Paulo 
1271e28a4053SRui Paulo 
eap_aka_process_client_error(struct eap_sm * sm,struct eap_aka_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)1272e28a4053SRui Paulo static void eap_aka_process_client_error(struct eap_sm *sm,
1273e28a4053SRui Paulo 					 struct eap_aka_data *data,
1274e28a4053SRui Paulo 					 struct wpabuf *respData,
1275e28a4053SRui Paulo 					 struct eap_sim_attrs *attr)
1276e28a4053SRui Paulo {
1277e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d",
1278e28a4053SRui Paulo 		   attr->client_error_code);
1279e28a4053SRui Paulo 	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
1280e28a4053SRui Paulo 		eap_aka_state(data, SUCCESS);
1281e28a4053SRui Paulo 	else
1282e28a4053SRui Paulo 		eap_aka_state(data, FAILURE);
1283e28a4053SRui Paulo }
1284e28a4053SRui Paulo 
1285e28a4053SRui Paulo 
eap_aka_process_authentication_reject(struct eap_sm * sm,struct eap_aka_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)1286e28a4053SRui Paulo static void eap_aka_process_authentication_reject(
1287e28a4053SRui Paulo 	struct eap_sm *sm, struct eap_aka_data *data,
1288e28a4053SRui Paulo 	struct wpabuf *respData, struct eap_sim_attrs *attr)
1289e28a4053SRui Paulo {
1290e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication");
1291e28a4053SRui Paulo 	eap_aka_state(data, FAILURE);
1292e28a4053SRui Paulo }
1293e28a4053SRui Paulo 
1294e28a4053SRui Paulo 
eap_aka_process_notification(struct eap_sm * sm,struct eap_aka_data * data,struct wpabuf * respData,struct eap_sim_attrs * attr)1295e28a4053SRui Paulo static void eap_aka_process_notification(struct eap_sm *sm,
1296e28a4053SRui Paulo 					 struct eap_aka_data *data,
1297e28a4053SRui Paulo 					 struct wpabuf *respData,
1298e28a4053SRui Paulo 					 struct eap_sim_attrs *attr)
1299e28a4053SRui Paulo {
1300e28a4053SRui Paulo 	wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification");
1301e28a4053SRui Paulo 	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
1302e28a4053SRui Paulo 		eap_aka_state(data, SUCCESS);
1303e28a4053SRui Paulo 	else
1304e28a4053SRui Paulo 		eap_aka_state(data, FAILURE);
1305e28a4053SRui Paulo }
1306e28a4053SRui Paulo 
1307e28a4053SRui Paulo 
eap_aka_process(struct eap_sm * sm,void * priv,struct wpabuf * respData)1308e28a4053SRui Paulo static void eap_aka_process(struct eap_sm *sm, void *priv,
1309e28a4053SRui Paulo 			    struct wpabuf *respData)
1310e28a4053SRui Paulo {
1311e28a4053SRui Paulo 	struct eap_aka_data *data = priv;
1312e28a4053SRui Paulo 	const u8 *pos, *end;
1313e28a4053SRui Paulo 	u8 subtype;
1314e28a4053SRui Paulo 	size_t len;
1315e28a4053SRui Paulo 	struct eap_sim_attrs attr;
1316e28a4053SRui Paulo 
1317e28a4053SRui Paulo 	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData,
1318e28a4053SRui Paulo 			       &len);
1319e28a4053SRui Paulo 	if (pos == NULL || len < 3)
1320e28a4053SRui Paulo 		return;
1321e28a4053SRui Paulo 
1322e28a4053SRui Paulo 	end = pos + len;
1323e28a4053SRui Paulo 	subtype = *pos;
1324e28a4053SRui Paulo 	pos += 3;
1325e28a4053SRui Paulo 
1326e28a4053SRui Paulo 	if (eap_aka_subtype_ok(data, subtype)) {
1327e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected "
1328e28a4053SRui Paulo 			   "EAP-AKA Subtype in EAP Response");
1329e28a4053SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1330e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1331e28a4053SRui Paulo 		return;
1332e28a4053SRui Paulo 	}
1333e28a4053SRui Paulo 
1334e28a4053SRui Paulo 	if (eap_sim_parse_attr(pos, end, &attr,
1335e28a4053SRui Paulo 			       data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
1336e28a4053SRui Paulo 			       0)) {
1337e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes");
1338e28a4053SRui Paulo 		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1339e28a4053SRui Paulo 		eap_aka_state(data, NOTIFICATION);
1340e28a4053SRui Paulo 		return;
1341e28a4053SRui Paulo 	}
1342e28a4053SRui Paulo 
1343e28a4053SRui Paulo 	if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) {
1344e28a4053SRui Paulo 		eap_aka_process_client_error(sm, data, respData, &attr);
1345e28a4053SRui Paulo 		return;
1346e28a4053SRui Paulo 	}
1347e28a4053SRui Paulo 
1348e28a4053SRui Paulo 	if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) {
1349e28a4053SRui Paulo 		eap_aka_process_authentication_reject(sm, data, respData,
1350e28a4053SRui Paulo 						      &attr);
1351e28a4053SRui Paulo 		return;
1352e28a4053SRui Paulo 	}
1353e28a4053SRui Paulo 
1354e28a4053SRui Paulo 	switch (data->state) {
1355e28a4053SRui Paulo 	case IDENTITY:
1356e28a4053SRui Paulo 		eap_aka_process_identity(sm, data, respData, &attr);
1357e28a4053SRui Paulo 		break;
1358e28a4053SRui Paulo 	case CHALLENGE:
1359e28a4053SRui Paulo 		if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) {
1360e28a4053SRui Paulo 			eap_aka_process_sync_failure(sm, data, respData,
1361e28a4053SRui Paulo 						     &attr);
1362e28a4053SRui Paulo 		} else {
1363e28a4053SRui Paulo 			eap_aka_process_challenge(sm, data, respData, &attr);
1364e28a4053SRui Paulo 		}
1365e28a4053SRui Paulo 		break;
1366e28a4053SRui Paulo 	case REAUTH:
1367e28a4053SRui Paulo 		eap_aka_process_reauth(sm, data, respData, &attr);
1368e28a4053SRui Paulo 		break;
1369e28a4053SRui Paulo 	case NOTIFICATION:
1370e28a4053SRui Paulo 		eap_aka_process_notification(sm, data, respData, &attr);
1371e28a4053SRui Paulo 		break;
1372e28a4053SRui Paulo 	default:
1373e28a4053SRui Paulo 		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in "
1374e28a4053SRui Paulo 			   "process", data->state);
1375e28a4053SRui Paulo 		break;
1376e28a4053SRui Paulo 	}
1377e28a4053SRui Paulo }
1378e28a4053SRui Paulo 
1379e28a4053SRui Paulo 
eap_aka_isDone(struct eap_sm * sm,void * priv)1380c1d255d3SCy Schubert static bool eap_aka_isDone(struct eap_sm *sm, void *priv)
1381e28a4053SRui Paulo {
1382e28a4053SRui Paulo 	struct eap_aka_data *data = priv;
1383e28a4053SRui Paulo 	return data->state == SUCCESS || data->state == FAILURE;
1384e28a4053SRui Paulo }
1385e28a4053SRui Paulo 
1386e28a4053SRui Paulo 
eap_aka_getKey(struct eap_sm * sm,void * priv,size_t * len)1387e28a4053SRui Paulo static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
1388e28a4053SRui Paulo {
1389e28a4053SRui Paulo 	struct eap_aka_data *data = priv;
1390e28a4053SRui Paulo 	u8 *key;
1391e28a4053SRui Paulo 
1392e28a4053SRui Paulo 	if (data->state != SUCCESS)
1393e28a4053SRui Paulo 		return NULL;
1394e28a4053SRui Paulo 
139585732ac8SCy Schubert 	key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN);
1396e28a4053SRui Paulo 	if (key == NULL)
1397e28a4053SRui Paulo 		return NULL;
1398e28a4053SRui Paulo 	*len = EAP_SIM_KEYING_DATA_LEN;
1399e28a4053SRui Paulo 	return key;
1400e28a4053SRui Paulo }
1401e28a4053SRui Paulo 
1402e28a4053SRui Paulo 
eap_aka_get_emsk(struct eap_sm * sm,void * priv,size_t * len)1403e28a4053SRui Paulo static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
1404e28a4053SRui Paulo {
1405e28a4053SRui Paulo 	struct eap_aka_data *data = priv;
1406e28a4053SRui Paulo 	u8 *key;
1407e28a4053SRui Paulo 
1408e28a4053SRui Paulo 	if (data->state != SUCCESS)
1409e28a4053SRui Paulo 		return NULL;
1410e28a4053SRui Paulo 
141185732ac8SCy Schubert 	key = os_memdup(data->emsk, EAP_EMSK_LEN);
1412e28a4053SRui Paulo 	if (key == NULL)
1413e28a4053SRui Paulo 		return NULL;
1414e28a4053SRui Paulo 	*len = EAP_EMSK_LEN;
1415e28a4053SRui Paulo 	return key;
1416e28a4053SRui Paulo }
1417e28a4053SRui Paulo 
1418e28a4053SRui Paulo 
eap_aka_isSuccess(struct eap_sm * sm,void * priv)1419c1d255d3SCy Schubert static bool eap_aka_isSuccess(struct eap_sm *sm, void *priv)
1420e28a4053SRui Paulo {
1421e28a4053SRui Paulo 	struct eap_aka_data *data = priv;
1422e28a4053SRui Paulo 	return data->state == SUCCESS;
1423e28a4053SRui Paulo }
1424e28a4053SRui Paulo 
1425e28a4053SRui Paulo 
eap_aka_get_session_id(struct eap_sm * sm,void * priv,size_t * len)14265b9c547cSRui Paulo static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
14275b9c547cSRui Paulo {
14285b9c547cSRui Paulo 	struct eap_aka_data *data = priv;
14295b9c547cSRui Paulo 	u8 *id;
14305b9c547cSRui Paulo 
14315b9c547cSRui Paulo 	if (data->state != SUCCESS)
14325b9c547cSRui Paulo 		return NULL;
14335b9c547cSRui Paulo 
1434206b73d0SCy Schubert 	if (!data->reauth)
14355b9c547cSRui Paulo 		*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
1436206b73d0SCy Schubert 	else
1437206b73d0SCy Schubert 		*len = 1 + EAP_SIM_NONCE_S_LEN + EAP_SIM_MAC_LEN;
14385b9c547cSRui Paulo 	id = os_malloc(*len);
14395b9c547cSRui Paulo 	if (id == NULL)
14405b9c547cSRui Paulo 		return NULL;
14415b9c547cSRui Paulo 
14425b9c547cSRui Paulo 	id[0] = data->eap_method;
1443206b73d0SCy Schubert 	if (!data->reauth) {
14445b9c547cSRui Paulo 		os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
1445206b73d0SCy Schubert 		os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn,
1446206b73d0SCy Schubert 			  EAP_AKA_AUTN_LEN);
1447206b73d0SCy Schubert 	} else {
1448206b73d0SCy Schubert 		os_memcpy(id + 1, data->nonce_s, EAP_SIM_NONCE_S_LEN);
1449206b73d0SCy Schubert 		os_memcpy(id + 1 + EAP_SIM_NONCE_S_LEN, data->reauth_mac,
1450206b73d0SCy Schubert 			  EAP_SIM_MAC_LEN);
1451206b73d0SCy Schubert 	}
14525b9c547cSRui Paulo 	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
14535b9c547cSRui Paulo 
14545b9c547cSRui Paulo 	return id;
14555b9c547cSRui Paulo }
14565b9c547cSRui Paulo 
14575b9c547cSRui Paulo 
eap_server_aka_register(void)1458e28a4053SRui Paulo int eap_server_aka_register(void)
1459e28a4053SRui Paulo {
1460e28a4053SRui Paulo 	struct eap_method *eap;
1461e28a4053SRui Paulo 
1462e28a4053SRui Paulo 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
1463e28a4053SRui Paulo 				      EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
1464e28a4053SRui Paulo 	if (eap == NULL)
1465e28a4053SRui Paulo 		return -1;
1466e28a4053SRui Paulo 
1467e28a4053SRui Paulo 	eap->init = eap_aka_init;
1468e28a4053SRui Paulo 	eap->reset = eap_aka_reset;
1469e28a4053SRui Paulo 	eap->buildReq = eap_aka_buildReq;
1470e28a4053SRui Paulo 	eap->check = eap_aka_check;
1471e28a4053SRui Paulo 	eap->process = eap_aka_process;
1472e28a4053SRui Paulo 	eap->isDone = eap_aka_isDone;
1473e28a4053SRui Paulo 	eap->getKey = eap_aka_getKey;
1474e28a4053SRui Paulo 	eap->isSuccess = eap_aka_isSuccess;
1475e28a4053SRui Paulo 	eap->get_emsk = eap_aka_get_emsk;
14765b9c547cSRui Paulo 	eap->getSessionId = eap_aka_get_session_id;
1477e28a4053SRui Paulo 
1478780fb4a2SCy Schubert 	return eap_server_method_register(eap);
1479e28a4053SRui Paulo }
1480e28a4053SRui Paulo 
1481e28a4053SRui Paulo 
1482e28a4053SRui Paulo #ifdef EAP_SERVER_AKA_PRIME
eap_server_aka_prime_register(void)1483e28a4053SRui Paulo int eap_server_aka_prime_register(void)
1484e28a4053SRui Paulo {
1485e28a4053SRui Paulo 	struct eap_method *eap;
1486e28a4053SRui Paulo 
1487e28a4053SRui Paulo 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
1488e28a4053SRui Paulo 				      EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
1489e28a4053SRui Paulo 				      "AKA'");
1490e28a4053SRui Paulo 	if (eap == NULL)
1491e28a4053SRui Paulo 		return -1;
1492e28a4053SRui Paulo 
1493e28a4053SRui Paulo 	eap->init = eap_aka_prime_init;
1494e28a4053SRui Paulo 	eap->reset = eap_aka_reset;
1495e28a4053SRui Paulo 	eap->buildReq = eap_aka_buildReq;
1496e28a4053SRui Paulo 	eap->check = eap_aka_check;
1497e28a4053SRui Paulo 	eap->process = eap_aka_process;
1498e28a4053SRui Paulo 	eap->isDone = eap_aka_isDone;
1499e28a4053SRui Paulo 	eap->getKey = eap_aka_getKey;
1500e28a4053SRui Paulo 	eap->isSuccess = eap_aka_isSuccess;
1501e28a4053SRui Paulo 	eap->get_emsk = eap_aka_get_emsk;
15025b9c547cSRui Paulo 	eap->getSessionId = eap_aka_get_session_id;
1503e28a4053SRui Paulo 
1504780fb4a2SCy Schubert 	return eap_server_method_register(eap);
1505e28a4053SRui Paulo }
1506e28a4053SRui Paulo #endif /* EAP_SERVER_AKA_PRIME */
1507