1214501Srpaulo/*
2252726Srpaulo * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (RFC 5448)
3252726Srpaulo * Copyright (c) 2005-2012, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "includes.h"
10214501Srpaulo
11214501Srpaulo#include "common.h"
12214501Srpaulo#include "crypto/sha256.h"
13214501Srpaulo#include "crypto/crypto.h"
14252726Srpaulo#include "crypto/random.h"
15214501Srpaulo#include "eap_common/eap_sim_common.h"
16214501Srpaulo#include "eap_server/eap_i.h"
17214501Srpaulo#include "eap_server/eap_sim_db.h"
18214501Srpaulo
19214501Srpaulo
20214501Srpaulostruct eap_aka_data {
21214501Srpaulo	u8 mk[EAP_SIM_MK_LEN];
22214501Srpaulo	u8 nonce_s[EAP_SIM_NONCE_S_LEN];
23214501Srpaulo	u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
24214501Srpaulo	u8 k_encr[EAP_SIM_K_ENCR_LEN];
25214501Srpaulo	u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */
26214501Srpaulo	u8 msk[EAP_SIM_KEYING_DATA_LEN];
27214501Srpaulo	u8 emsk[EAP_EMSK_LEN];
28214501Srpaulo	u8 rand[EAP_AKA_RAND_LEN];
29214501Srpaulo	u8 autn[EAP_AKA_AUTN_LEN];
30214501Srpaulo	u8 ck[EAP_AKA_CK_LEN];
31214501Srpaulo	u8 ik[EAP_AKA_IK_LEN];
32214501Srpaulo	u8 res[EAP_AKA_RES_MAX_LEN];
33214501Srpaulo	size_t res_len;
34214501Srpaulo	enum {
35214501Srpaulo		IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
36214501Srpaulo	} state;
37214501Srpaulo	char *next_pseudonym;
38214501Srpaulo	char *next_reauth_id;
39214501Srpaulo	u16 counter;
40214501Srpaulo	struct eap_sim_reauth *reauth;
41214501Srpaulo	int auts_reported; /* whether the current AUTS has been reported to the
42214501Srpaulo			    * eap_sim_db */
43214501Srpaulo	u16 notification;
44214501Srpaulo	int use_result_ind;
45214501Srpaulo
46214501Srpaulo	struct wpabuf *id_msgs;
47214501Srpaulo	int pending_id;
48214501Srpaulo	u8 eap_method;
49214501Srpaulo	u8 *network_name;
50214501Srpaulo	size_t network_name_len;
51214501Srpaulo	u16 kdf;
52252726Srpaulo	int identity_round;
53252726Srpaulo	char permanent[20]; /* Permanent username */
54214501Srpaulo};
55214501Srpaulo
56214501Srpaulo
57252726Srpaulostatic void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data);
58214501Srpaulo
59214501Srpaulo
60214501Srpaulostatic const char * eap_aka_state_txt(int state)
61214501Srpaulo{
62214501Srpaulo	switch (state) {
63214501Srpaulo	case IDENTITY:
64214501Srpaulo		return "IDENTITY";
65214501Srpaulo	case CHALLENGE:
66214501Srpaulo		return "CHALLENGE";
67214501Srpaulo	case REAUTH:
68214501Srpaulo		return "REAUTH";
69214501Srpaulo	case SUCCESS:
70214501Srpaulo		return "SUCCESS";
71214501Srpaulo	case FAILURE:
72214501Srpaulo		return "FAILURE";
73214501Srpaulo	case NOTIFICATION:
74214501Srpaulo		return "NOTIFICATION";
75214501Srpaulo	default:
76214501Srpaulo		return "Unknown?!";
77214501Srpaulo	}
78214501Srpaulo}
79214501Srpaulo
80214501Srpaulo
81214501Srpaulostatic void eap_aka_state(struct eap_aka_data *data, int state)
82214501Srpaulo{
83214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
84214501Srpaulo		   eap_aka_state_txt(data->state),
85214501Srpaulo		   eap_aka_state_txt(state));
86214501Srpaulo	data->state = state;
87214501Srpaulo}
88214501Srpaulo
89214501Srpaulo
90252726Srpaulostatic int eap_aka_check_identity_reauth(struct eap_sm *sm,
91252726Srpaulo					 struct eap_aka_data *data,
92252726Srpaulo					 const char *username)
93252726Srpaulo{
94252726Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME &&
95252726Srpaulo	    username[0] != EAP_AKA_PRIME_REAUTH_ID_PREFIX)
96252726Srpaulo		return 0;
97252726Srpaulo	if (data->eap_method == EAP_TYPE_AKA &&
98252726Srpaulo	    username[0] != EAP_AKA_REAUTH_ID_PREFIX)
99252726Srpaulo		return 0;
100252726Srpaulo
101252726Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth username '%s'", username);
102252726Srpaulo	data->reauth = eap_sim_db_get_reauth_entry(sm->eap_sim_db_priv,
103252726Srpaulo						   username);
104252726Srpaulo	if (data->reauth == NULL) {
105252726Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown reauth identity - "
106252726Srpaulo			   "request full auth identity");
107252726Srpaulo		/* Remain in IDENTITY state for another round */
108252726Srpaulo		return 0;
109252726Srpaulo	}
110252726Srpaulo
111252726Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast re-authentication");
112252726Srpaulo	os_strlcpy(data->permanent, data->reauth->permanent,
113252726Srpaulo		   sizeof(data->permanent));
114252726Srpaulo	data->counter = data->reauth->counter;
115252726Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
116252726Srpaulo		os_memcpy(data->k_encr, data->reauth->k_encr,
117252726Srpaulo			  EAP_SIM_K_ENCR_LEN);
118252726Srpaulo		os_memcpy(data->k_aut, data->reauth->k_aut,
119252726Srpaulo			  EAP_AKA_PRIME_K_AUT_LEN);
120252726Srpaulo		os_memcpy(data->k_re, data->reauth->k_re,
121252726Srpaulo			  EAP_AKA_PRIME_K_RE_LEN);
122252726Srpaulo	} else {
123252726Srpaulo		os_memcpy(data->mk, data->reauth->mk, EAP_SIM_MK_LEN);
124252726Srpaulo	}
125252726Srpaulo
126252726Srpaulo	eap_aka_state(data, REAUTH);
127252726Srpaulo	return 1;
128252726Srpaulo}
129252726Srpaulo
130252726Srpaulo
131252726Srpaulostatic void eap_aka_check_identity(struct eap_sm *sm,
132252726Srpaulo				   struct eap_aka_data *data)
133252726Srpaulo{
134252726Srpaulo	char *username;
135252726Srpaulo
136252726Srpaulo	/* Check if we already know the identity from EAP-Response/Identity */
137252726Srpaulo
138252726Srpaulo	username = sim_get_username(sm->identity, sm->identity_len);
139252726Srpaulo	if (username == NULL)
140252726Srpaulo		return;
141252726Srpaulo
142252726Srpaulo	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
143252726Srpaulo		os_free(username);
144252726Srpaulo		/*
145252726Srpaulo		 * Since re-auth username was recognized, skip AKA/Identity
146252726Srpaulo		 * exchange.
147252726Srpaulo		 */
148252726Srpaulo		return;
149252726Srpaulo	}
150252726Srpaulo
151252726Srpaulo	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
152252726Srpaulo	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
153252726Srpaulo	    (data->eap_method == EAP_TYPE_AKA &&
154252726Srpaulo	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
155252726Srpaulo		const char *permanent;
156252726Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
157252726Srpaulo			   username);
158252726Srpaulo		permanent = eap_sim_db_get_permanent(
159252726Srpaulo			sm->eap_sim_db_priv, username);
160252726Srpaulo		if (permanent == NULL) {
161252726Srpaulo			os_free(username);
162252726Srpaulo			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
163252726Srpaulo				   "identity - request permanent identity");
164252726Srpaulo			/* Remain in IDENTITY state for another round */
165252726Srpaulo			return;
166252726Srpaulo		}
167252726Srpaulo		os_strlcpy(data->permanent, permanent,
168252726Srpaulo			   sizeof(data->permanent));
169252726Srpaulo		/*
170252726Srpaulo		 * Since pseudonym username was recognized, skip AKA/Identity
171252726Srpaulo		 * exchange.
172252726Srpaulo		 */
173252726Srpaulo		eap_aka_fullauth(sm, data);
174252726Srpaulo	}
175252726Srpaulo
176252726Srpaulo	os_free(username);
177252726Srpaulo}
178252726Srpaulo
179252726Srpaulo
180214501Srpaulostatic void * eap_aka_init(struct eap_sm *sm)
181214501Srpaulo{
182214501Srpaulo	struct eap_aka_data *data;
183214501Srpaulo
184214501Srpaulo	if (sm->eap_sim_db_priv == NULL) {
185214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
186214501Srpaulo		return NULL;
187214501Srpaulo	}
188214501Srpaulo
189214501Srpaulo	data = os_zalloc(sizeof(*data));
190214501Srpaulo	if (data == NULL)
191214501Srpaulo		return NULL;
192214501Srpaulo
193214501Srpaulo	data->eap_method = EAP_TYPE_AKA;
194214501Srpaulo
195214501Srpaulo	data->state = IDENTITY;
196214501Srpaulo	data->pending_id = -1;
197252726Srpaulo	eap_aka_check_identity(sm, data);
198214501Srpaulo
199214501Srpaulo	return data;
200214501Srpaulo}
201214501Srpaulo
202214501Srpaulo
203214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME
204214501Srpaulostatic void * eap_aka_prime_init(struct eap_sm *sm)
205214501Srpaulo{
206214501Srpaulo	struct eap_aka_data *data;
207214501Srpaulo	/* TODO: make ANID configurable; see 3GPP TS 24.302 */
208214501Srpaulo	char *network_name = "WLAN";
209214501Srpaulo
210214501Srpaulo	if (sm->eap_sim_db_priv == NULL) {
211214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
212214501Srpaulo		return NULL;
213214501Srpaulo	}
214214501Srpaulo
215214501Srpaulo	data = os_zalloc(sizeof(*data));
216214501Srpaulo	if (data == NULL)
217214501Srpaulo		return NULL;
218214501Srpaulo
219214501Srpaulo	data->eap_method = EAP_TYPE_AKA_PRIME;
220252726Srpaulo	data->network_name = (u8 *) os_strdup(network_name);
221214501Srpaulo	if (data->network_name == NULL) {
222214501Srpaulo		os_free(data);
223214501Srpaulo		return NULL;
224214501Srpaulo	}
225214501Srpaulo
226214501Srpaulo	data->network_name_len = os_strlen(network_name);
227214501Srpaulo
228214501Srpaulo	data->state = IDENTITY;
229214501Srpaulo	data->pending_id = -1;
230252726Srpaulo	eap_aka_check_identity(sm, data);
231214501Srpaulo
232214501Srpaulo	return data;
233214501Srpaulo}
234214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */
235214501Srpaulo
236214501Srpaulo
237214501Srpaulostatic void eap_aka_reset(struct eap_sm *sm, void *priv)
238214501Srpaulo{
239214501Srpaulo	struct eap_aka_data *data = priv;
240214501Srpaulo	os_free(data->next_pseudonym);
241214501Srpaulo	os_free(data->next_reauth_id);
242214501Srpaulo	wpabuf_free(data->id_msgs);
243214501Srpaulo	os_free(data->network_name);
244214501Srpaulo	os_free(data);
245214501Srpaulo}
246214501Srpaulo
247214501Srpaulo
248214501Srpaulostatic int eap_aka_add_id_msg(struct eap_aka_data *data,
249214501Srpaulo			      const struct wpabuf *msg)
250214501Srpaulo{
251214501Srpaulo	if (msg == NULL)
252214501Srpaulo		return -1;
253214501Srpaulo
254214501Srpaulo	if (data->id_msgs == NULL) {
255214501Srpaulo		data->id_msgs = wpabuf_dup(msg);
256214501Srpaulo		return data->id_msgs == NULL ? -1 : 0;
257214501Srpaulo	}
258214501Srpaulo
259214501Srpaulo	if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
260214501Srpaulo		return -1;
261214501Srpaulo	wpabuf_put_buf(data->id_msgs, msg);
262214501Srpaulo
263214501Srpaulo	return 0;
264214501Srpaulo}
265214501Srpaulo
266214501Srpaulo
267214501Srpaulostatic void eap_aka_add_checkcode(struct eap_aka_data *data,
268214501Srpaulo				  struct eap_sim_msg *msg)
269214501Srpaulo{
270214501Srpaulo	const u8 *addr;
271214501Srpaulo	size_t len;
272214501Srpaulo	u8 hash[SHA256_MAC_LEN];
273214501Srpaulo
274214501Srpaulo	wpa_printf(MSG_DEBUG, "   AT_CHECKCODE");
275214501Srpaulo
276214501Srpaulo	if (data->id_msgs == NULL) {
277214501Srpaulo		/*
278214501Srpaulo		 * No EAP-AKA/Identity packets were exchanged - send empty
279214501Srpaulo		 * checkcode.
280214501Srpaulo		 */
281214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
282214501Srpaulo		return;
283214501Srpaulo	}
284214501Srpaulo
285214501Srpaulo	/* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
286214501Srpaulo	addr = wpabuf_head(data->id_msgs);
287214501Srpaulo	len = wpabuf_len(data->id_msgs);
288214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
289214501Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME)
290214501Srpaulo		sha256_vector(1, &addr, &len, hash);
291214501Srpaulo	else
292214501Srpaulo		sha1_vector(1, &addr, &len, hash);
293214501Srpaulo
294214501Srpaulo	eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
295214501Srpaulo			data->eap_method == EAP_TYPE_AKA_PRIME ?
296214501Srpaulo			EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
297214501Srpaulo}
298214501Srpaulo
299214501Srpaulo
300214501Srpaulostatic int eap_aka_verify_checkcode(struct eap_aka_data *data,
301214501Srpaulo				    const u8 *checkcode, size_t checkcode_len)
302214501Srpaulo{
303214501Srpaulo	const u8 *addr;
304214501Srpaulo	size_t len;
305214501Srpaulo	u8 hash[SHA256_MAC_LEN];
306214501Srpaulo	size_t hash_len;
307214501Srpaulo
308214501Srpaulo	if (checkcode == NULL)
309214501Srpaulo		return -1;
310214501Srpaulo
311214501Srpaulo	if (data->id_msgs == NULL) {
312214501Srpaulo		if (checkcode_len != 0) {
313214501Srpaulo			wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer "
314214501Srpaulo				   "indicates that AKA/Identity messages were "
315214501Srpaulo				   "used, but they were not");
316214501Srpaulo			return -1;
317214501Srpaulo		}
318214501Srpaulo		return 0;
319214501Srpaulo	}
320214501Srpaulo
321214501Srpaulo	hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
322214501Srpaulo		EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
323214501Srpaulo
324214501Srpaulo	if (checkcode_len != hash_len) {
325214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates "
326214501Srpaulo			   "that AKA/Identity message were not used, but they "
327214501Srpaulo			   "were");
328214501Srpaulo		return -1;
329214501Srpaulo	}
330214501Srpaulo
331214501Srpaulo	/* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
332214501Srpaulo	addr = wpabuf_head(data->id_msgs);
333214501Srpaulo	len = wpabuf_len(data->id_msgs);
334214501Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME)
335214501Srpaulo		sha256_vector(1, &addr, &len, hash);
336214501Srpaulo	else
337214501Srpaulo		sha1_vector(1, &addr, &len, hash);
338214501Srpaulo
339214501Srpaulo	if (os_memcmp(hash, checkcode, hash_len) != 0) {
340214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
341214501Srpaulo		return -1;
342214501Srpaulo	}
343214501Srpaulo
344214501Srpaulo	return 0;
345214501Srpaulo}
346214501Srpaulo
347214501Srpaulo
348214501Srpaulostatic struct wpabuf * eap_aka_build_identity(struct eap_sm *sm,
349214501Srpaulo					      struct eap_aka_data *data, u8 id)
350214501Srpaulo{
351214501Srpaulo	struct eap_sim_msg *msg;
352214501Srpaulo	struct wpabuf *buf;
353214501Srpaulo
354214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity");
355214501Srpaulo	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
356214501Srpaulo			       EAP_AKA_SUBTYPE_IDENTITY);
357252726Srpaulo	data->identity_round++;
358252726Srpaulo	if (data->identity_round == 1) {
359214501Srpaulo		/*
360214501Srpaulo		 * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is
361214501Srpaulo		 * ignored and the AKA/Identity is used to request the
362214501Srpaulo		 * identity.
363214501Srpaulo		 */
364214501Srpaulo		wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
365214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
366252726Srpaulo	} else if (data->identity_round > 3) {
367252726Srpaulo		/* Cannot use more than three rounds of Identity messages */
368252726Srpaulo		eap_sim_msg_free(msg);
369252726Srpaulo		return NULL;
370252726Srpaulo	} else if (sm->identity && sm->identity_len > 0 &&
371252726Srpaulo		   (sm->identity[0] == EAP_AKA_REAUTH_ID_PREFIX ||
372252726Srpaulo		    sm->identity[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX)) {
373252726Srpaulo		/* Reauth id may have expired - try fullauth */
374252726Srpaulo		wpa_printf(MSG_DEBUG, "   AT_FULLAUTH_ID_REQ");
375252726Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_FULLAUTH_ID_REQ, 0, NULL, 0);
376252726Srpaulo	} else {
377252726Srpaulo		wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
378252726Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
379214501Srpaulo	}
380214501Srpaulo	buf = eap_sim_msg_finish(msg, NULL, NULL, 0);
381214501Srpaulo	if (eap_aka_add_id_msg(data, buf) < 0) {
382214501Srpaulo		wpabuf_free(buf);
383214501Srpaulo		return NULL;
384214501Srpaulo	}
385214501Srpaulo	data->pending_id = id;
386214501Srpaulo	return buf;
387214501Srpaulo}
388214501Srpaulo
389214501Srpaulo
390214501Srpaulostatic int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data,
391214501Srpaulo			      struct eap_sim_msg *msg, u16 counter,
392214501Srpaulo			      const u8 *nonce_s)
393214501Srpaulo{
394214501Srpaulo	os_free(data->next_pseudonym);
395252726Srpaulo	if (nonce_s == NULL) {
396252726Srpaulo		data->next_pseudonym =
397252726Srpaulo			eap_sim_db_get_next_pseudonym(
398252726Srpaulo				sm->eap_sim_db_priv,
399252726Srpaulo				data->eap_method == EAP_TYPE_AKA_PRIME ?
400252726Srpaulo				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
401252726Srpaulo	} else {
402252726Srpaulo		/* Do not update pseudonym during re-authentication */
403252726Srpaulo		data->next_pseudonym = NULL;
404252726Srpaulo	}
405214501Srpaulo	os_free(data->next_reauth_id);
406214501Srpaulo	if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
407214501Srpaulo		data->next_reauth_id =
408252726Srpaulo			eap_sim_db_get_next_reauth_id(
409252726Srpaulo				sm->eap_sim_db_priv,
410252726Srpaulo				data->eap_method == EAP_TYPE_AKA_PRIME ?
411252726Srpaulo				EAP_SIM_DB_AKA_PRIME : EAP_SIM_DB_AKA);
412214501Srpaulo	} else {
413214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication "
414214501Srpaulo			   "count exceeded - force full authentication");
415214501Srpaulo		data->next_reauth_id = NULL;
416214501Srpaulo	}
417214501Srpaulo
418214501Srpaulo	if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
419214501Srpaulo	    counter == 0 && nonce_s == NULL)
420214501Srpaulo		return 0;
421214501Srpaulo
422214501Srpaulo	wpa_printf(MSG_DEBUG, "   AT_IV");
423214501Srpaulo	wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
424214501Srpaulo	eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
425214501Srpaulo
426214501Srpaulo	if (counter > 0) {
427214501Srpaulo		wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)", counter);
428214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
429214501Srpaulo	}
430214501Srpaulo
431214501Srpaulo	if (nonce_s) {
432214501Srpaulo		wpa_printf(MSG_DEBUG, "   *AT_NONCE_S");
433214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
434214501Srpaulo				EAP_SIM_NONCE_S_LEN);
435214501Srpaulo	}
436214501Srpaulo
437214501Srpaulo	if (data->next_pseudonym) {
438214501Srpaulo		wpa_printf(MSG_DEBUG, "   *AT_NEXT_PSEUDONYM (%s)",
439214501Srpaulo			   data->next_pseudonym);
440214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
441214501Srpaulo				os_strlen(data->next_pseudonym),
442214501Srpaulo				(u8 *) data->next_pseudonym,
443214501Srpaulo				os_strlen(data->next_pseudonym));
444214501Srpaulo	}
445214501Srpaulo
446214501Srpaulo	if (data->next_reauth_id) {
447214501Srpaulo		wpa_printf(MSG_DEBUG, "   *AT_NEXT_REAUTH_ID (%s)",
448214501Srpaulo			   data->next_reauth_id);
449214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
450214501Srpaulo				os_strlen(data->next_reauth_id),
451214501Srpaulo				(u8 *) data->next_reauth_id,
452214501Srpaulo				os_strlen(data->next_reauth_id));
453214501Srpaulo	}
454214501Srpaulo
455214501Srpaulo	if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
456214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
457214501Srpaulo			   "AT_ENCR_DATA");
458214501Srpaulo		return -1;
459214501Srpaulo	}
460214501Srpaulo
461214501Srpaulo	return 0;
462214501Srpaulo}
463214501Srpaulo
464214501Srpaulo
465214501Srpaulostatic struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm,
466214501Srpaulo					       struct eap_aka_data *data,
467214501Srpaulo					       u8 id)
468214501Srpaulo{
469214501Srpaulo	struct eap_sim_msg *msg;
470214501Srpaulo
471214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge");
472214501Srpaulo	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
473214501Srpaulo			       EAP_AKA_SUBTYPE_CHALLENGE);
474214501Srpaulo	wpa_printf(MSG_DEBUG, "   AT_RAND");
475214501Srpaulo	eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN);
476214501Srpaulo	wpa_printf(MSG_DEBUG, "   AT_AUTN");
477214501Srpaulo	eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN);
478214501Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
479214501Srpaulo		if (data->kdf) {
480214501Srpaulo			/* Add the selected KDF into the beginning */
481214501Srpaulo			wpa_printf(MSG_DEBUG, "   AT_KDF");
482214501Srpaulo			eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf,
483214501Srpaulo					NULL, 0);
484214501Srpaulo		}
485214501Srpaulo		wpa_printf(MSG_DEBUG, "   AT_KDF");
486214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF,
487214501Srpaulo				NULL, 0);
488214501Srpaulo		wpa_printf(MSG_DEBUG, "   AT_KDF_INPUT");
489214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT,
490214501Srpaulo				data->network_name_len,
491214501Srpaulo				data->network_name, data->network_name_len);
492214501Srpaulo	}
493214501Srpaulo
494214501Srpaulo	if (eap_aka_build_encr(sm, data, msg, 0, NULL)) {
495214501Srpaulo		eap_sim_msg_free(msg);
496214501Srpaulo		return NULL;
497214501Srpaulo	}
498214501Srpaulo
499214501Srpaulo	eap_aka_add_checkcode(data, msg);
500214501Srpaulo
501214501Srpaulo	if (sm->eap_sim_aka_result_ind) {
502214501Srpaulo		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
503214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
504214501Srpaulo	}
505214501Srpaulo
506214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME
507214501Srpaulo	if (data->eap_method == EAP_TYPE_AKA) {
508214501Srpaulo		u16 flags = 0;
509214501Srpaulo		int i;
510214501Srpaulo		int aka_prime_preferred = 0;
511214501Srpaulo
512214501Srpaulo		i = 0;
513214501Srpaulo		while (sm->user && i < EAP_MAX_METHODS &&
514214501Srpaulo		       (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
515214501Srpaulo			sm->user->methods[i].method != EAP_TYPE_NONE)) {
516214501Srpaulo			if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) {
517214501Srpaulo				if (sm->user->methods[i].method ==
518214501Srpaulo				    EAP_TYPE_AKA)
519214501Srpaulo					break;
520214501Srpaulo				if (sm->user->methods[i].method ==
521214501Srpaulo				    EAP_TYPE_AKA_PRIME) {
522214501Srpaulo					aka_prime_preferred = 1;
523214501Srpaulo					break;
524214501Srpaulo				}
525214501Srpaulo			}
526214501Srpaulo			i++;
527214501Srpaulo		}
528214501Srpaulo
529214501Srpaulo		if (aka_prime_preferred)
530214501Srpaulo			flags |= EAP_AKA_BIDDING_FLAG_D;
531214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0);
532214501Srpaulo	}
533214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */
534214501Srpaulo
535214501Srpaulo	wpa_printf(MSG_DEBUG, "   AT_MAC");
536214501Srpaulo	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
537214501Srpaulo	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
538214501Srpaulo}
539214501Srpaulo
540214501Srpaulo
541214501Srpaulostatic struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm,
542214501Srpaulo					    struct eap_aka_data *data, u8 id)
543214501Srpaulo{
544214501Srpaulo	struct eap_sim_msg *msg;
545214501Srpaulo
546214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
547214501Srpaulo
548252726Srpaulo	if (random_get_bytes(data->nonce_s, EAP_SIM_NONCE_S_LEN))
549214501Srpaulo		return NULL;
550214501Srpaulo	wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S",
551214501Srpaulo			data->nonce_s, EAP_SIM_NONCE_S_LEN);
552214501Srpaulo
553214501Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
554214501Srpaulo		eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
555214501Srpaulo						 sm->identity,
556214501Srpaulo						 sm->identity_len,
557214501Srpaulo						 data->nonce_s,
558214501Srpaulo						 data->msk, data->emsk);
559214501Srpaulo	} else {
560214501Srpaulo		eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
561214501Srpaulo				    data->msk, data->emsk);
562214501Srpaulo		eap_sim_derive_keys_reauth(data->counter, sm->identity,
563214501Srpaulo					   sm->identity_len, data->nonce_s,
564214501Srpaulo					   data->mk, data->msk, data->emsk);
565214501Srpaulo	}
566214501Srpaulo
567214501Srpaulo	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
568214501Srpaulo			       EAP_AKA_SUBTYPE_REAUTHENTICATION);
569214501Srpaulo
570214501Srpaulo	if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
571214501Srpaulo		eap_sim_msg_free(msg);
572214501Srpaulo		return NULL;
573214501Srpaulo	}
574214501Srpaulo
575214501Srpaulo	eap_aka_add_checkcode(data, msg);
576214501Srpaulo
577214501Srpaulo	if (sm->eap_sim_aka_result_ind) {
578214501Srpaulo		wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
579214501Srpaulo		eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
580214501Srpaulo	}
581214501Srpaulo
582214501Srpaulo	wpa_printf(MSG_DEBUG, "   AT_MAC");
583214501Srpaulo	eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
584214501Srpaulo	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
585214501Srpaulo}
586214501Srpaulo
587214501Srpaulo
588214501Srpaulostatic struct wpabuf * eap_aka_build_notification(struct eap_sm *sm,
589214501Srpaulo						  struct eap_aka_data *data,
590214501Srpaulo						  u8 id)
591214501Srpaulo{
592214501Srpaulo	struct eap_sim_msg *msg;
593214501Srpaulo
594214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification");
595214501Srpaulo	msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
596214501Srpaulo			       EAP_AKA_SUBTYPE_NOTIFICATION);
597214501Srpaulo	wpa_printf(MSG_DEBUG, "   AT_NOTIFICATION (%d)", data->notification);
598214501Srpaulo	eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
599214501Srpaulo			NULL, 0);
600214501Srpaulo	if (data->use_result_ind) {
601214501Srpaulo		if (data->reauth) {
602214501Srpaulo			wpa_printf(MSG_DEBUG, "   AT_IV");
603214501Srpaulo			wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
604214501Srpaulo			eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
605214501Srpaulo						   EAP_SIM_AT_ENCR_DATA);
606214501Srpaulo			wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)",
607214501Srpaulo				   data->counter);
608214501Srpaulo			eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
609214501Srpaulo					NULL, 0);
610214501Srpaulo
611214501Srpaulo			if (eap_sim_msg_add_encr_end(msg, data->k_encr,
612214501Srpaulo						     EAP_SIM_AT_PADDING)) {
613214501Srpaulo				wpa_printf(MSG_WARNING, "EAP-AKA: Failed to "
614214501Srpaulo					   "encrypt AT_ENCR_DATA");
615214501Srpaulo				eap_sim_msg_free(msg);
616214501Srpaulo				return NULL;
617214501Srpaulo			}
618214501Srpaulo		}
619214501Srpaulo
620214501Srpaulo		wpa_printf(MSG_DEBUG, "   AT_MAC");
621214501Srpaulo		eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
622214501Srpaulo	}
623214501Srpaulo	return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
624214501Srpaulo}
625214501Srpaulo
626214501Srpaulo
627214501Srpaulostatic struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id)
628214501Srpaulo{
629214501Srpaulo	struct eap_aka_data *data = priv;
630214501Srpaulo
631214501Srpaulo	data->auts_reported = 0;
632214501Srpaulo	switch (data->state) {
633214501Srpaulo	case IDENTITY:
634214501Srpaulo		return eap_aka_build_identity(sm, data, id);
635214501Srpaulo	case CHALLENGE:
636214501Srpaulo		return eap_aka_build_challenge(sm, data, id);
637214501Srpaulo	case REAUTH:
638214501Srpaulo		return eap_aka_build_reauth(sm, data, id);
639214501Srpaulo	case NOTIFICATION:
640214501Srpaulo		return eap_aka_build_notification(sm, data, id);
641214501Srpaulo	default:
642214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in "
643214501Srpaulo			   "buildReq", data->state);
644214501Srpaulo		break;
645214501Srpaulo	}
646214501Srpaulo	return NULL;
647214501Srpaulo}
648214501Srpaulo
649214501Srpaulo
650214501Srpaulostatic Boolean eap_aka_check(struct eap_sm *sm, void *priv,
651214501Srpaulo			     struct wpabuf *respData)
652214501Srpaulo{
653214501Srpaulo	struct eap_aka_data *data = priv;
654214501Srpaulo	const u8 *pos;
655214501Srpaulo	size_t len;
656214501Srpaulo
657214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData,
658214501Srpaulo			       &len);
659214501Srpaulo	if (pos == NULL || len < 3) {
660214501Srpaulo		wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame");
661214501Srpaulo		return TRUE;
662214501Srpaulo	}
663214501Srpaulo
664214501Srpaulo	return FALSE;
665214501Srpaulo}
666214501Srpaulo
667214501Srpaulo
668214501Srpaulostatic Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype)
669214501Srpaulo{
670214501Srpaulo	if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR ||
671214501Srpaulo	    subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT)
672214501Srpaulo		return FALSE;
673214501Srpaulo
674214501Srpaulo	switch (data->state) {
675214501Srpaulo	case IDENTITY:
676214501Srpaulo		if (subtype != EAP_AKA_SUBTYPE_IDENTITY) {
677214501Srpaulo			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
678214501Srpaulo				   "subtype %d", subtype);
679214501Srpaulo			return TRUE;
680214501Srpaulo		}
681214501Srpaulo		break;
682214501Srpaulo	case CHALLENGE:
683214501Srpaulo		if (subtype != EAP_AKA_SUBTYPE_CHALLENGE &&
684214501Srpaulo		    subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) {
685214501Srpaulo			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
686214501Srpaulo				   "subtype %d", subtype);
687214501Srpaulo			return TRUE;
688214501Srpaulo		}
689214501Srpaulo		break;
690214501Srpaulo	case REAUTH:
691214501Srpaulo		if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) {
692214501Srpaulo			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
693214501Srpaulo				   "subtype %d", subtype);
694214501Srpaulo			return TRUE;
695214501Srpaulo		}
696214501Srpaulo		break;
697214501Srpaulo	case NOTIFICATION:
698214501Srpaulo		if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) {
699214501Srpaulo			wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
700214501Srpaulo				   "subtype %d", subtype);
701214501Srpaulo			return TRUE;
702214501Srpaulo		}
703214501Srpaulo		break;
704214501Srpaulo	default:
705214501Srpaulo		wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for "
706214501Srpaulo			   "processing a response", data->state);
707214501Srpaulo		return TRUE;
708214501Srpaulo	}
709214501Srpaulo
710214501Srpaulo	return FALSE;
711214501Srpaulo}
712214501Srpaulo
713214501Srpaulo
714214501Srpaulostatic void eap_aka_determine_identity(struct eap_sm *sm,
715252726Srpaulo				       struct eap_aka_data *data)
716214501Srpaulo{
717252726Srpaulo	char *username;
718214501Srpaulo
719252726Srpaulo	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity",
720252726Srpaulo			  sm->identity, sm->identity_len);
721214501Srpaulo
722252726Srpaulo	username = sim_get_username(sm->identity, sm->identity_len);
723252726Srpaulo	if (username == NULL) {
724252726Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
725252726Srpaulo		eap_aka_state(data, NOTIFICATION);
726252726Srpaulo		return;
727214501Srpaulo	}
728214501Srpaulo
729252726Srpaulo	if (eap_aka_check_identity_reauth(sm, data, username) > 0) {
730252726Srpaulo		os_free(username);
731252726Srpaulo		return;
732214501Srpaulo	}
733214501Srpaulo
734252726Srpaulo	if (((data->eap_method == EAP_TYPE_AKA_PRIME &&
735252726Srpaulo	      username[0] == EAP_AKA_PRIME_REAUTH_ID_PREFIX) ||
736252726Srpaulo	     (data->eap_method == EAP_TYPE_AKA &&
737252726Srpaulo	      username[0] == EAP_AKA_REAUTH_ID_PREFIX)) &&
738252726Srpaulo	    data->identity_round == 1) {
739252726Srpaulo		/* Remain in IDENTITY state for another round to request full
740252726Srpaulo		 * auth identity since we did not recognize reauth id */
741252726Srpaulo		os_free(username);
742252726Srpaulo		return;
743252726Srpaulo	}
744214501Srpaulo
745252726Srpaulo	if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
746252726Srpaulo	     username[0] == EAP_AKA_PRIME_PSEUDONYM_PREFIX) ||
747252726Srpaulo	    (data->eap_method == EAP_TYPE_AKA &&
748252726Srpaulo	     username[0] == EAP_AKA_PSEUDONYM_PREFIX)) {
749252726Srpaulo		const char *permanent;
750252726Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Pseudonym username '%s'",
751252726Srpaulo			   username);
752252726Srpaulo		permanent = eap_sim_db_get_permanent(
753252726Srpaulo			sm->eap_sim_db_priv, username);
754252726Srpaulo		os_free(username);
755252726Srpaulo		if (permanent == NULL) {
756252726Srpaulo			wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown pseudonym "
757252726Srpaulo				   "identity - request permanent identity");
758252726Srpaulo			/* Remain in IDENTITY state for another round */
759252726Srpaulo			return;
760252726Srpaulo		}
761252726Srpaulo		os_strlcpy(data->permanent, permanent,
762252726Srpaulo			   sizeof(data->permanent));
763252726Srpaulo	} else if ((data->eap_method == EAP_TYPE_AKA_PRIME &&
764252726Srpaulo		    username[0] == EAP_AKA_PRIME_PERMANENT_PREFIX) ||
765252726Srpaulo		   (data->eap_method == EAP_TYPE_AKA &&
766252726Srpaulo		    username[0] == EAP_AKA_PERMANENT_PREFIX)) {
767252726Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent username '%s'",
768252726Srpaulo			   username);
769252726Srpaulo		os_strlcpy(data->permanent, username, sizeof(data->permanent));
770252726Srpaulo		os_free(username);
771252726Srpaulo	} else {
772252726Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized username '%s'",
773252726Srpaulo			   username);
774252726Srpaulo		os_free(username);
775252726Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
776252726Srpaulo		eap_aka_state(data, NOTIFICATION);
777214501Srpaulo		return;
778214501Srpaulo	}
779214501Srpaulo
780252726Srpaulo	eap_aka_fullauth(sm, data);
781252726Srpaulo}
782252726Srpaulo
783252726Srpaulo
784252726Srpaulostatic void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data)
785252726Srpaulo{
786252726Srpaulo	size_t identity_len;
787252726Srpaulo	int res;
788252726Srpaulo
789252726Srpaulo	res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, data->permanent,
790252726Srpaulo				      data->rand, data->autn, data->ik,
791252726Srpaulo				      data->ck, data->res, &data->res_len, sm);
792214501Srpaulo	if (res == EAP_SIM_DB_PENDING) {
793214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
794214501Srpaulo			   "not yet available - pending request");
795214501Srpaulo		sm->method_pending = METHOD_PENDING_WAIT;
796214501Srpaulo		return;
797214501Srpaulo	}
798214501Srpaulo
799214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME
800214501Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
801214501Srpaulo		/* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
802214501Srpaulo		 * needed 6-octet SQN ^AK for CK',IK' derivation */
803214501Srpaulo		eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
804214501Srpaulo						 data->autn,
805214501Srpaulo						 data->network_name,
806214501Srpaulo						 data->network_name_len);
807214501Srpaulo	}
808214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */
809214501Srpaulo
810214501Srpaulo	data->reauth = NULL;
811214501Srpaulo	data->counter = 0; /* reset re-auth counter since this is full auth */
812214501Srpaulo
813214501Srpaulo	if (res != 0) {
814214501Srpaulo		wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA "
815214501Srpaulo			   "authentication data for the peer");
816214501Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
817214501Srpaulo		eap_aka_state(data, NOTIFICATION);
818214501Srpaulo		return;
819214501Srpaulo	}
820214501Srpaulo	if (sm->method_pending == METHOD_PENDING_WAIT) {
821214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
822214501Srpaulo			   "available - abort pending wait");
823214501Srpaulo		sm->method_pending = METHOD_PENDING_NONE;
824214501Srpaulo	}
825214501Srpaulo
826214501Srpaulo	identity_len = sm->identity_len;
827214501Srpaulo	while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
828214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null "
829214501Srpaulo			   "character from identity");
830214501Srpaulo		identity_len--;
831214501Srpaulo	}
832214501Srpaulo	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation",
833214501Srpaulo			  sm->identity, identity_len);
834214501Srpaulo
835214501Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME) {
836252726Srpaulo		eap_aka_prime_derive_keys(sm->identity, identity_len, data->ik,
837214501Srpaulo					  data->ck, data->k_encr, data->k_aut,
838214501Srpaulo					  data->k_re, data->msk, data->emsk);
839214501Srpaulo	} else {
840214501Srpaulo		eap_aka_derive_mk(sm->identity, identity_len, data->ik,
841214501Srpaulo				  data->ck, data->mk);
842214501Srpaulo		eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
843214501Srpaulo				    data->msk, data->emsk);
844214501Srpaulo	}
845214501Srpaulo
846214501Srpaulo	eap_aka_state(data, CHALLENGE);
847214501Srpaulo}
848214501Srpaulo
849214501Srpaulo
850214501Srpaulostatic void eap_aka_process_identity(struct eap_sm *sm,
851214501Srpaulo				     struct eap_aka_data *data,
852214501Srpaulo				     struct wpabuf *respData,
853214501Srpaulo				     struct eap_sim_attrs *attr)
854214501Srpaulo{
855252726Srpaulo	u8 *new_identity;
856252726Srpaulo
857214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity");
858214501Srpaulo
859214501Srpaulo	if (attr->mac || attr->iv || attr->encr_data) {
860214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute "
861214501Srpaulo			   "received in EAP-Response/AKA-Identity");
862214501Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
863214501Srpaulo		eap_aka_state(data, NOTIFICATION);
864214501Srpaulo		return;
865214501Srpaulo	}
866214501Srpaulo
867252726Srpaulo	/*
868252726Srpaulo	 * We always request identity with AKA/Identity, so the peer is
869252726Srpaulo	 * required to have replied with one.
870252726Srpaulo	 */
871252726Srpaulo	if (!attr->identity || attr->identity_len == 0) {
872252726Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Peer did not provide any "
873252726Srpaulo			   "identity");
874252726Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
875252726Srpaulo		eap_aka_state(data, NOTIFICATION);
876252726Srpaulo		return;
877214501Srpaulo	}
878214501Srpaulo
879252726Srpaulo	new_identity = os_malloc(attr->identity_len);
880252726Srpaulo	if (new_identity == NULL) {
881252726Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
882252726Srpaulo		eap_aka_state(data, NOTIFICATION);
883252726Srpaulo		return;
884252726Srpaulo	}
885252726Srpaulo	os_free(sm->identity);
886252726Srpaulo	sm->identity = new_identity;
887252726Srpaulo	os_memcpy(sm->identity, attr->identity, attr->identity_len);
888252726Srpaulo	sm->identity_len = attr->identity_len;
889252726Srpaulo
890252726Srpaulo	eap_aka_determine_identity(sm, data);
891214501Srpaulo	if (eap_get_id(respData) == data->pending_id) {
892214501Srpaulo		data->pending_id = -1;
893214501Srpaulo		eap_aka_add_id_msg(data, respData);
894214501Srpaulo	}
895214501Srpaulo}
896214501Srpaulo
897214501Srpaulo
898214501Srpaulostatic int eap_aka_verify_mac(struct eap_aka_data *data,
899214501Srpaulo			      const struct wpabuf *req,
900214501Srpaulo			      const u8 *mac, const u8 *extra,
901214501Srpaulo			      size_t extra_len)
902214501Srpaulo{
903214501Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME)
904214501Srpaulo		return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
905214501Srpaulo						 extra_len);
906214501Srpaulo	return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
907214501Srpaulo}
908214501Srpaulo
909214501Srpaulo
910214501Srpaulostatic void eap_aka_process_challenge(struct eap_sm *sm,
911214501Srpaulo				      struct eap_aka_data *data,
912214501Srpaulo				      struct wpabuf *respData,
913214501Srpaulo				      struct eap_sim_attrs *attr)
914214501Srpaulo{
915214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
916214501Srpaulo
917214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME
918214501Srpaulo#if 0
919214501Srpaulo	/* KDF negotiation; to be enabled only after more than one KDF is
920214501Srpaulo	 * supported */
921214501Srpaulo	if (data->eap_method == EAP_TYPE_AKA_PRIME &&
922214501Srpaulo	    attr->kdf_count == 1 && attr->mac == NULL) {
923214501Srpaulo		if (attr->kdf[0] != EAP_AKA_PRIME_KDF) {
924214501Srpaulo			wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected "
925214501Srpaulo				   "unknown KDF");
926214501Srpaulo			data->notification =
927214501Srpaulo				EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
928214501Srpaulo			eap_aka_state(data, NOTIFICATION);
929214501Srpaulo			return;
930214501Srpaulo		}
931214501Srpaulo
932214501Srpaulo		data->kdf = attr->kdf[0];
933214501Srpaulo
934214501Srpaulo		/* Allow negotiation to continue with the selected KDF by
935214501Srpaulo		 * sending another Challenge message */
936214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
937214501Srpaulo		return;
938214501Srpaulo	}
939214501Srpaulo#endif
940214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */
941214501Srpaulo
942214501Srpaulo	if (attr->checkcode &&
943214501Srpaulo	    eap_aka_verify_checkcode(data, attr->checkcode,
944214501Srpaulo				     attr->checkcode_len)) {
945214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
946214501Srpaulo			   "message");
947214501Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
948214501Srpaulo		eap_aka_state(data, NOTIFICATION);
949214501Srpaulo		return;
950214501Srpaulo	}
951214501Srpaulo	if (attr->mac == NULL ||
952214501Srpaulo	    eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) {
953214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
954214501Srpaulo			   "did not include valid AT_MAC");
955214501Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
956214501Srpaulo		eap_aka_state(data, NOTIFICATION);
957214501Srpaulo		return;
958214501Srpaulo	}
959214501Srpaulo
960214501Srpaulo	/*
961214501Srpaulo	 * AT_RES is padded, so verify that there is enough room for RES and
962214501Srpaulo	 * that the RES length in bits matches with the expected RES.
963214501Srpaulo	 */
964214501Srpaulo	if (attr->res == NULL || attr->res_len < data->res_len ||
965214501Srpaulo	    attr->res_len_bits != data->res_len * 8 ||
966214501Srpaulo	    os_memcmp(attr->res, data->res, data->res_len) != 0) {
967214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not "
968214501Srpaulo			   "include valid AT_RES (attr len=%lu, res len=%lu "
969214501Srpaulo			   "bits, expected %lu bits)",
970214501Srpaulo			   (unsigned long) attr->res_len,
971214501Srpaulo			   (unsigned long) attr->res_len_bits,
972214501Srpaulo			   (unsigned long) data->res_len * 8);
973214501Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
974214501Srpaulo		eap_aka_state(data, NOTIFICATION);
975214501Srpaulo		return;
976214501Srpaulo	}
977214501Srpaulo
978214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the "
979214501Srpaulo		   "correct AT_MAC");
980214501Srpaulo	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
981214501Srpaulo		data->use_result_ind = 1;
982214501Srpaulo		data->notification = EAP_SIM_SUCCESS;
983214501Srpaulo		eap_aka_state(data, NOTIFICATION);
984214501Srpaulo	} else
985214501Srpaulo		eap_aka_state(data, SUCCESS);
986214501Srpaulo
987214501Srpaulo	if (data->next_pseudonym) {
988252726Srpaulo		eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, data->permanent,
989214501Srpaulo					 data->next_pseudonym);
990214501Srpaulo		data->next_pseudonym = NULL;
991214501Srpaulo	}
992214501Srpaulo	if (data->next_reauth_id) {
993214501Srpaulo		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
994214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME
995214501Srpaulo			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
996252726Srpaulo						    data->permanent,
997214501Srpaulo						    data->next_reauth_id,
998214501Srpaulo						    data->counter + 1,
999214501Srpaulo						    data->k_encr, data->k_aut,
1000214501Srpaulo						    data->k_re);
1001214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */
1002214501Srpaulo		} else {
1003252726Srpaulo			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
1004252726Srpaulo					      data->permanent,
1005214501Srpaulo					      data->next_reauth_id,
1006214501Srpaulo					      data->counter + 1,
1007214501Srpaulo					      data->mk);
1008214501Srpaulo		}
1009214501Srpaulo		data->next_reauth_id = NULL;
1010214501Srpaulo	}
1011214501Srpaulo}
1012214501Srpaulo
1013214501Srpaulo
1014214501Srpaulostatic void eap_aka_process_sync_failure(struct eap_sm *sm,
1015214501Srpaulo					 struct eap_aka_data *data,
1016214501Srpaulo					 struct wpabuf *respData,
1017214501Srpaulo					 struct eap_sim_attrs *attr)
1018214501Srpaulo{
1019214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure");
1020214501Srpaulo
1021214501Srpaulo	if (attr->auts == NULL) {
1022214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure "
1023214501Srpaulo			   "message did not include valid AT_AUTS");
1024214501Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1025214501Srpaulo		eap_aka_state(data, NOTIFICATION);
1026214501Srpaulo		return;
1027214501Srpaulo	}
1028214501Srpaulo
1029214501Srpaulo	/* Avoid re-reporting AUTS when processing pending EAP packet by
1030214501Srpaulo	 * maintaining a local flag stating whether this AUTS has already been
1031214501Srpaulo	 * reported. */
1032214501Srpaulo	if (!data->auts_reported &&
1033252726Srpaulo	    eap_sim_db_resynchronize(sm->eap_sim_db_priv, data->permanent,
1034252726Srpaulo				     attr->auts, data->rand)) {
1035214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed");
1036214501Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1037214501Srpaulo		eap_aka_state(data, NOTIFICATION);
1038214501Srpaulo		return;
1039214501Srpaulo	}
1040214501Srpaulo	data->auts_reported = 1;
1041214501Srpaulo
1042252726Srpaulo	/* Remain in CHALLENGE state to re-try after resynchronization */
1043214501Srpaulo}
1044214501Srpaulo
1045214501Srpaulo
1046214501Srpaulostatic void eap_aka_process_reauth(struct eap_sm *sm,
1047214501Srpaulo				   struct eap_aka_data *data,
1048214501Srpaulo				   struct wpabuf *respData,
1049214501Srpaulo				   struct eap_sim_attrs *attr)
1050214501Srpaulo{
1051214501Srpaulo	struct eap_sim_attrs eattr;
1052214501Srpaulo	u8 *decrypted = NULL;
1053214501Srpaulo
1054214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication");
1055214501Srpaulo
1056214501Srpaulo	if (attr->mac == NULL ||
1057214501Srpaulo	    eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s,
1058214501Srpaulo			       EAP_SIM_NONCE_S_LEN)) {
1059214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
1060214501Srpaulo			   "did not include valid AT_MAC");
1061214501Srpaulo		goto fail;
1062214501Srpaulo	}
1063214501Srpaulo
1064214501Srpaulo	if (attr->encr_data == NULL || attr->iv == NULL) {
1065214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
1066214501Srpaulo			   "message did not include encrypted data");
1067214501Srpaulo		goto fail;
1068214501Srpaulo	}
1069214501Srpaulo
1070214501Srpaulo	decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
1071214501Srpaulo				       attr->encr_data_len, attr->iv, &eattr,
1072214501Srpaulo				       0);
1073214501Srpaulo	if (decrypted == NULL) {
1074214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
1075214501Srpaulo			   "data from reauthentication message");
1076214501Srpaulo		goto fail;
1077214501Srpaulo	}
1078214501Srpaulo
1079214501Srpaulo	if (eattr.counter != data->counter) {
1080214501Srpaulo		wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
1081214501Srpaulo			   "used incorrect counter %u, expected %u",
1082214501Srpaulo			   eattr.counter, data->counter);
1083214501Srpaulo		goto fail;
1084214501Srpaulo	}
1085214501Srpaulo	os_free(decrypted);
1086214501Srpaulo	decrypted = NULL;
1087214501Srpaulo
1088214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes "
1089214501Srpaulo		   "the correct AT_MAC");
1090214501Srpaulo
1091214501Srpaulo	if (eattr.counter_too_small) {
1092214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
1093214501Srpaulo			   "included AT_COUNTER_TOO_SMALL - starting full "
1094214501Srpaulo			   "authentication");
1095252726Srpaulo		eap_aka_fullauth(sm, data);
1096214501Srpaulo		return;
1097214501Srpaulo	}
1098214501Srpaulo
1099214501Srpaulo	if (sm->eap_sim_aka_result_ind && attr->result_ind) {
1100214501Srpaulo		data->use_result_ind = 1;
1101214501Srpaulo		data->notification = EAP_SIM_SUCCESS;
1102214501Srpaulo		eap_aka_state(data, NOTIFICATION);
1103214501Srpaulo	} else
1104214501Srpaulo		eap_aka_state(data, SUCCESS);
1105214501Srpaulo
1106214501Srpaulo	if (data->next_reauth_id) {
1107214501Srpaulo		if (data->eap_method == EAP_TYPE_AKA_PRIME) {
1108214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME
1109214501Srpaulo			eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
1110252726Srpaulo						    data->permanent,
1111214501Srpaulo						    data->next_reauth_id,
1112214501Srpaulo						    data->counter + 1,
1113214501Srpaulo						    data->k_encr, data->k_aut,
1114214501Srpaulo						    data->k_re);
1115214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */
1116214501Srpaulo		} else {
1117252726Srpaulo			eap_sim_db_add_reauth(sm->eap_sim_db_priv,
1118252726Srpaulo					      data->permanent,
1119214501Srpaulo					      data->next_reauth_id,
1120214501Srpaulo					      data->counter + 1,
1121214501Srpaulo					      data->mk);
1122214501Srpaulo		}
1123214501Srpaulo		data->next_reauth_id = NULL;
1124214501Srpaulo	} else {
1125214501Srpaulo		eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
1126214501Srpaulo		data->reauth = NULL;
1127214501Srpaulo	}
1128214501Srpaulo
1129214501Srpaulo	return;
1130214501Srpaulo
1131214501Srpaulofail:
1132214501Srpaulo	data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1133214501Srpaulo	eap_aka_state(data, NOTIFICATION);
1134214501Srpaulo	eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
1135214501Srpaulo	data->reauth = NULL;
1136214501Srpaulo	os_free(decrypted);
1137214501Srpaulo}
1138214501Srpaulo
1139214501Srpaulo
1140214501Srpaulostatic void eap_aka_process_client_error(struct eap_sm *sm,
1141214501Srpaulo					 struct eap_aka_data *data,
1142214501Srpaulo					 struct wpabuf *respData,
1143214501Srpaulo					 struct eap_sim_attrs *attr)
1144214501Srpaulo{
1145214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d",
1146214501Srpaulo		   attr->client_error_code);
1147214501Srpaulo	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
1148214501Srpaulo		eap_aka_state(data, SUCCESS);
1149214501Srpaulo	else
1150214501Srpaulo		eap_aka_state(data, FAILURE);
1151214501Srpaulo}
1152214501Srpaulo
1153214501Srpaulo
1154214501Srpaulostatic void eap_aka_process_authentication_reject(
1155214501Srpaulo	struct eap_sm *sm, struct eap_aka_data *data,
1156214501Srpaulo	struct wpabuf *respData, struct eap_sim_attrs *attr)
1157214501Srpaulo{
1158214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication");
1159214501Srpaulo	eap_aka_state(data, FAILURE);
1160214501Srpaulo}
1161214501Srpaulo
1162214501Srpaulo
1163214501Srpaulostatic void eap_aka_process_notification(struct eap_sm *sm,
1164214501Srpaulo					 struct eap_aka_data *data,
1165214501Srpaulo					 struct wpabuf *respData,
1166214501Srpaulo					 struct eap_sim_attrs *attr)
1167214501Srpaulo{
1168214501Srpaulo	wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification");
1169214501Srpaulo	if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
1170214501Srpaulo		eap_aka_state(data, SUCCESS);
1171214501Srpaulo	else
1172214501Srpaulo		eap_aka_state(data, FAILURE);
1173214501Srpaulo}
1174214501Srpaulo
1175214501Srpaulo
1176214501Srpaulostatic void eap_aka_process(struct eap_sm *sm, void *priv,
1177214501Srpaulo			    struct wpabuf *respData)
1178214501Srpaulo{
1179214501Srpaulo	struct eap_aka_data *data = priv;
1180214501Srpaulo	const u8 *pos, *end;
1181214501Srpaulo	u8 subtype;
1182214501Srpaulo	size_t len;
1183214501Srpaulo	struct eap_sim_attrs attr;
1184214501Srpaulo
1185214501Srpaulo	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData,
1186214501Srpaulo			       &len);
1187214501Srpaulo	if (pos == NULL || len < 3)
1188214501Srpaulo		return;
1189214501Srpaulo
1190214501Srpaulo	end = pos + len;
1191214501Srpaulo	subtype = *pos;
1192214501Srpaulo	pos += 3;
1193214501Srpaulo
1194214501Srpaulo	if (eap_aka_subtype_ok(data, subtype)) {
1195214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected "
1196214501Srpaulo			   "EAP-AKA Subtype in EAP Response");
1197214501Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1198214501Srpaulo		eap_aka_state(data, NOTIFICATION);
1199214501Srpaulo		return;
1200214501Srpaulo	}
1201214501Srpaulo
1202214501Srpaulo	if (eap_sim_parse_attr(pos, end, &attr,
1203214501Srpaulo			       data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
1204214501Srpaulo			       0)) {
1205214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes");
1206214501Srpaulo		data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
1207214501Srpaulo		eap_aka_state(data, NOTIFICATION);
1208214501Srpaulo		return;
1209214501Srpaulo	}
1210214501Srpaulo
1211214501Srpaulo	if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) {
1212214501Srpaulo		eap_aka_process_client_error(sm, data, respData, &attr);
1213214501Srpaulo		return;
1214214501Srpaulo	}
1215214501Srpaulo
1216214501Srpaulo	if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) {
1217214501Srpaulo		eap_aka_process_authentication_reject(sm, data, respData,
1218214501Srpaulo						      &attr);
1219214501Srpaulo		return;
1220214501Srpaulo	}
1221214501Srpaulo
1222214501Srpaulo	switch (data->state) {
1223214501Srpaulo	case IDENTITY:
1224214501Srpaulo		eap_aka_process_identity(sm, data, respData, &attr);
1225214501Srpaulo		break;
1226214501Srpaulo	case CHALLENGE:
1227214501Srpaulo		if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) {
1228214501Srpaulo			eap_aka_process_sync_failure(sm, data, respData,
1229214501Srpaulo						     &attr);
1230214501Srpaulo		} else {
1231214501Srpaulo			eap_aka_process_challenge(sm, data, respData, &attr);
1232214501Srpaulo		}
1233214501Srpaulo		break;
1234214501Srpaulo	case REAUTH:
1235214501Srpaulo		eap_aka_process_reauth(sm, data, respData, &attr);
1236214501Srpaulo		break;
1237214501Srpaulo	case NOTIFICATION:
1238214501Srpaulo		eap_aka_process_notification(sm, data, respData, &attr);
1239214501Srpaulo		break;
1240214501Srpaulo	default:
1241214501Srpaulo		wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in "
1242214501Srpaulo			   "process", data->state);
1243214501Srpaulo		break;
1244214501Srpaulo	}
1245214501Srpaulo}
1246214501Srpaulo
1247214501Srpaulo
1248214501Srpaulostatic Boolean eap_aka_isDone(struct eap_sm *sm, void *priv)
1249214501Srpaulo{
1250214501Srpaulo	struct eap_aka_data *data = priv;
1251214501Srpaulo	return data->state == SUCCESS || data->state == FAILURE;
1252214501Srpaulo}
1253214501Srpaulo
1254214501Srpaulo
1255214501Srpaulostatic u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
1256214501Srpaulo{
1257214501Srpaulo	struct eap_aka_data *data = priv;
1258214501Srpaulo	u8 *key;
1259214501Srpaulo
1260214501Srpaulo	if (data->state != SUCCESS)
1261214501Srpaulo		return NULL;
1262214501Srpaulo
1263214501Srpaulo	key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
1264214501Srpaulo	if (key == NULL)
1265214501Srpaulo		return NULL;
1266214501Srpaulo	os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
1267214501Srpaulo	*len = EAP_SIM_KEYING_DATA_LEN;
1268214501Srpaulo	return key;
1269214501Srpaulo}
1270214501Srpaulo
1271214501Srpaulo
1272214501Srpaulostatic u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
1273214501Srpaulo{
1274214501Srpaulo	struct eap_aka_data *data = priv;
1275214501Srpaulo	u8 *key;
1276214501Srpaulo
1277214501Srpaulo	if (data->state != SUCCESS)
1278214501Srpaulo		return NULL;
1279214501Srpaulo
1280214501Srpaulo	key = os_malloc(EAP_EMSK_LEN);
1281214501Srpaulo	if (key == NULL)
1282214501Srpaulo		return NULL;
1283214501Srpaulo	os_memcpy(key, data->emsk, EAP_EMSK_LEN);
1284214501Srpaulo	*len = EAP_EMSK_LEN;
1285214501Srpaulo	return key;
1286214501Srpaulo}
1287214501Srpaulo
1288214501Srpaulo
1289214501Srpaulostatic Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv)
1290214501Srpaulo{
1291214501Srpaulo	struct eap_aka_data *data = priv;
1292214501Srpaulo	return data->state == SUCCESS;
1293214501Srpaulo}
1294214501Srpaulo
1295214501Srpaulo
1296214501Srpauloint eap_server_aka_register(void)
1297214501Srpaulo{
1298214501Srpaulo	struct eap_method *eap;
1299214501Srpaulo	int ret;
1300214501Srpaulo
1301214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
1302214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
1303214501Srpaulo	if (eap == NULL)
1304214501Srpaulo		return -1;
1305214501Srpaulo
1306214501Srpaulo	eap->init = eap_aka_init;
1307214501Srpaulo	eap->reset = eap_aka_reset;
1308214501Srpaulo	eap->buildReq = eap_aka_buildReq;
1309214501Srpaulo	eap->check = eap_aka_check;
1310214501Srpaulo	eap->process = eap_aka_process;
1311214501Srpaulo	eap->isDone = eap_aka_isDone;
1312214501Srpaulo	eap->getKey = eap_aka_getKey;
1313214501Srpaulo	eap->isSuccess = eap_aka_isSuccess;
1314214501Srpaulo	eap->get_emsk = eap_aka_get_emsk;
1315214501Srpaulo
1316214501Srpaulo	ret = eap_server_method_register(eap);
1317214501Srpaulo	if (ret)
1318214501Srpaulo		eap_server_method_free(eap);
1319214501Srpaulo	return ret;
1320214501Srpaulo}
1321214501Srpaulo
1322214501Srpaulo
1323214501Srpaulo#ifdef EAP_SERVER_AKA_PRIME
1324214501Srpauloint eap_server_aka_prime_register(void)
1325214501Srpaulo{
1326214501Srpaulo	struct eap_method *eap;
1327214501Srpaulo	int ret;
1328214501Srpaulo
1329214501Srpaulo	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
1330214501Srpaulo				      EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
1331214501Srpaulo				      "AKA'");
1332214501Srpaulo	if (eap == NULL)
1333214501Srpaulo		return -1;
1334214501Srpaulo
1335214501Srpaulo	eap->init = eap_aka_prime_init;
1336214501Srpaulo	eap->reset = eap_aka_reset;
1337214501Srpaulo	eap->buildReq = eap_aka_buildReq;
1338214501Srpaulo	eap->check = eap_aka_check;
1339214501Srpaulo	eap->process = eap_aka_process;
1340214501Srpaulo	eap->isDone = eap_aka_isDone;
1341214501Srpaulo	eap->getKey = eap_aka_getKey;
1342214501Srpaulo	eap->isSuccess = eap_aka_isSuccess;
1343214501Srpaulo	eap->get_emsk = eap_aka_get_emsk;
1344214501Srpaulo
1345214501Srpaulo	ret = eap_server_method_register(eap);
1346214501Srpaulo	if (ret)
1347214501Srpaulo		eap_server_method_free(eap);
1348214501Srpaulo
1349214501Srpaulo	return ret;
1350214501Srpaulo}
1351214501Srpaulo#endif /* EAP_SERVER_AKA_PRIME */
1352