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