1189251Ssam/*
2189251Ssam * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
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"
18214734Srpaulo#include "wpabuf.h"
19214734Srpaulo#include "crypto/aes_wrap.h"
20214734Srpaulo#include "crypto/crypto.h"
21214734Srpaulo#include "crypto/sha1.h"
22214734Srpaulo#include "crypto/sha256.h"
23189251Ssam#include "eap_common/eap_defs.h"
24189251Ssam#include "eap_common/eap_sim_common.h"
25189251Ssam
26189251Ssam
27189251Ssamstatic int eap_sim_prf(const u8 *key, u8 *x, size_t xlen)
28189251Ssam{
29189251Ssam	return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen);
30189251Ssam}
31189251Ssam
32189251Ssam
33189251Ssamvoid eap_sim_derive_mk(const u8 *identity, size_t identity_len,
34189251Ssam		       const u8 *nonce_mt, u16 selected_version,
35189251Ssam		       const u8 *ver_list, size_t ver_list_len,
36189251Ssam		       int num_chal, const u8 *kc, u8 *mk)
37189251Ssam{
38189251Ssam	u8 sel_ver[2];
39189251Ssam	const unsigned char *addr[5];
40189251Ssam	size_t len[5];
41189251Ssam
42189251Ssam	addr[0] = identity;
43189251Ssam	len[0] = identity_len;
44189251Ssam	addr[1] = kc;
45189251Ssam	len[1] = num_chal * EAP_SIM_KC_LEN;
46189251Ssam	addr[2] = nonce_mt;
47189251Ssam	len[2] = EAP_SIM_NONCE_MT_LEN;
48189251Ssam	addr[3] = ver_list;
49189251Ssam	len[3] = ver_list_len;
50189251Ssam	addr[4] = sel_ver;
51189251Ssam	len[4] = 2;
52189251Ssam
53189251Ssam	WPA_PUT_BE16(sel_ver, selected_version);
54189251Ssam
55189251Ssam	/* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
56189251Ssam	sha1_vector(5, addr, len, mk);
57189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
58189251Ssam}
59189251Ssam
60189251Ssam
61189251Ssamvoid eap_aka_derive_mk(const u8 *identity, size_t identity_len,
62189251Ssam		       const u8 *ik, const u8 *ck, u8 *mk)
63189251Ssam{
64189251Ssam	const u8 *addr[3];
65189251Ssam	size_t len[3];
66189251Ssam
67189251Ssam	addr[0] = identity;
68189251Ssam	len[0] = identity_len;
69189251Ssam	addr[1] = ik;
70189251Ssam	len[1] = EAP_AKA_IK_LEN;
71189251Ssam	addr[2] = ck;
72189251Ssam	len[2] = EAP_AKA_CK_LEN;
73189251Ssam
74189251Ssam	/* MK = SHA1(Identity|IK|CK) */
75189251Ssam	sha1_vector(3, addr, len, mk);
76189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN);
77189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN);
78189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN);
79189251Ssam}
80189251Ssam
81189251Ssam
82189251Ssamint eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk)
83189251Ssam{
84189251Ssam	u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN +
85189251Ssam	       EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos;
86189251Ssam	if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) {
87189251Ssam		wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
88189251Ssam		return -1;
89189251Ssam	}
90189251Ssam	pos = buf;
91189251Ssam	os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
92189251Ssam	pos += EAP_SIM_K_ENCR_LEN;
93189251Ssam	os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN);
94189251Ssam	pos += EAP_SIM_K_AUT_LEN;
95189251Ssam	os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN);
96189251Ssam	pos += EAP_SIM_KEYING_DATA_LEN;
97189251Ssam	os_memcpy(emsk, pos, EAP_EMSK_LEN);
98189251Ssam
99189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr",
100189251Ssam			k_encr, EAP_SIM_K_ENCR_LEN);
101189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut",
102189251Ssam			k_aut, EAP_SIM_K_AUT_LEN);
103189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
104189251Ssam			msk, EAP_SIM_KEYING_DATA_LEN);
105189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
106189251Ssam	os_memset(buf, 0, sizeof(buf));
107189251Ssam
108189251Ssam	return 0;
109189251Ssam}
110189251Ssam
111189251Ssam
112189251Ssamint eap_sim_derive_keys_reauth(u16 _counter,
113189251Ssam			       const u8 *identity, size_t identity_len,
114189251Ssam			       const u8 *nonce_s, const u8 *mk, u8 *msk,
115189251Ssam			       u8 *emsk)
116189251Ssam{
117189251Ssam	u8 xkey[SHA1_MAC_LEN];
118189251Ssam	u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32];
119189251Ssam	u8 counter[2];
120189251Ssam	const u8 *addr[4];
121189251Ssam	size_t len[4];
122189251Ssam
123189251Ssam	while (identity_len > 0 && identity[identity_len - 1] == 0) {
124189251Ssam		wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null "
125189251Ssam			   "character from the end of identity");
126189251Ssam		identity_len--;
127189251Ssam	}
128189251Ssam	addr[0] = identity;
129189251Ssam	len[0] = identity_len;
130189251Ssam	addr[1] = counter;
131189251Ssam	len[1] = 2;
132189251Ssam	addr[2] = nonce_s;
133189251Ssam	len[2] = EAP_SIM_NONCE_S_LEN;
134189251Ssam	addr[3] = mk;
135189251Ssam	len[3] = EAP_SIM_MK_LEN;
136189251Ssam
137189251Ssam	WPA_PUT_BE16(counter, _counter);
138189251Ssam
139189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth");
140189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
141189251Ssam			  identity, identity_len);
142189251Ssam	wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2);
143189251Ssam	wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s,
144189251Ssam		    EAP_SIM_NONCE_S_LEN);
145189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
146189251Ssam
147189251Ssam	/* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */
148189251Ssam	sha1_vector(4, addr, len, xkey);
149189251Ssam	wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN);
150189251Ssam
151189251Ssam	if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) {
152189251Ssam		wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
153189251Ssam		return -1;
154189251Ssam	}
155189251Ssam	if (msk) {
156189251Ssam		os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN);
157189251Ssam		wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
158189251Ssam			    msk, EAP_SIM_KEYING_DATA_LEN);
159189251Ssam	}
160189251Ssam	if (emsk) {
161189251Ssam		os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN);
162189251Ssam		wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
163189251Ssam	}
164189251Ssam	os_memset(buf, 0, sizeof(buf));
165189251Ssam
166189251Ssam	return 0;
167189251Ssam}
168189251Ssam
169189251Ssam
170189251Ssamint eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
171189251Ssam		       const u8 *mac, const u8 *extra, size_t extra_len)
172189251Ssam{
173189251Ssam	unsigned char hmac[SHA1_MAC_LEN];
174189251Ssam	const u8 *addr[2];
175189251Ssam	size_t len[2];
176189251Ssam	u8 *tmp;
177189251Ssam
178189251Ssam	if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
179189251Ssam	    mac < wpabuf_head_u8(req) ||
180189251Ssam	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
181189251Ssam		return -1;
182189251Ssam
183189251Ssam	tmp = os_malloc(wpabuf_len(req));
184189251Ssam	if (tmp == NULL)
185189251Ssam		return -1;
186189251Ssam
187189251Ssam	addr[0] = tmp;
188189251Ssam	len[0] = wpabuf_len(req);
189189251Ssam	addr[1] = extra;
190189251Ssam	len[1] = extra_len;
191189251Ssam
192189251Ssam	/* HMAC-SHA1-128 */
193189251Ssam	os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
194189251Ssam	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
195189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
196189251Ssam		    tmp, wpabuf_len(req));
197189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data",
198189251Ssam		    extra, extra_len);
199189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut",
200189251Ssam			k_aut, EAP_SIM_K_AUT_LEN);
201189251Ssam	hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
202189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC",
203189251Ssam		    hmac, EAP_SIM_MAC_LEN);
204189251Ssam	os_free(tmp);
205189251Ssam
206189251Ssam	return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
207189251Ssam}
208189251Ssam
209189251Ssam
210189251Ssamvoid eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
211189251Ssam		     const u8 *extra, size_t extra_len)
212189251Ssam{
213189251Ssam	unsigned char hmac[SHA1_MAC_LEN];
214189251Ssam	const u8 *addr[2];
215189251Ssam	size_t len[2];
216189251Ssam
217189251Ssam	addr[0] = msg;
218189251Ssam	len[0] = msg_len;
219189251Ssam	addr[1] = extra;
220189251Ssam	len[1] = extra_len;
221189251Ssam
222189251Ssam	/* HMAC-SHA1-128 */
223189251Ssam	os_memset(mac, 0, EAP_SIM_MAC_LEN);
224189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len);
225189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data",
226189251Ssam		    extra, extra_len);
227189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut",
228189251Ssam			k_aut, EAP_SIM_K_AUT_LEN);
229189251Ssam	hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
230189251Ssam	os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
231189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC",
232189251Ssam		    mac, EAP_SIM_MAC_LEN);
233189251Ssam}
234189251Ssam
235189251Ssam
236214734Srpaulo#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
237189251Ssamstatic void prf_prime(const u8 *k, const char *seed1,
238189251Ssam		      const u8 *seed2, size_t seed2_len,
239189251Ssam		      const u8 *seed3, size_t seed3_len,
240189251Ssam		      u8 *res, size_t res_len)
241189251Ssam{
242189251Ssam	const u8 *addr[5];
243189251Ssam	size_t len[5];
244189251Ssam	u8 hash[SHA256_MAC_LEN];
245189251Ssam	u8 iter;
246189251Ssam
247189251Ssam	/*
248189251Ssam	 * PRF'(K,S) = T1 | T2 | T3 | T4 | ...
249189251Ssam	 * T1 = HMAC-SHA-256 (K, S | 0x01)
250189251Ssam	 * T2 = HMAC-SHA-256 (K, T1 | S | 0x02)
251189251Ssam	 * T3 = HMAC-SHA-256 (K, T2 | S | 0x03)
252189251Ssam	 * T4 = HMAC-SHA-256 (K, T3 | S | 0x04)
253189251Ssam	 * ...
254189251Ssam	 */
255189251Ssam
256189251Ssam	addr[0] = hash;
257189251Ssam	len[0] = 0;
258189251Ssam	addr[1] = (const u8 *) seed1;
259189251Ssam	len[1] = os_strlen(seed1);
260189251Ssam	addr[2] = seed2;
261189251Ssam	len[2] = seed2_len;
262189251Ssam	addr[3] = seed3;
263189251Ssam	len[3] = seed3_len;
264189251Ssam	addr[4] = &iter;
265189251Ssam	len[4] = 1;
266189251Ssam
267189251Ssam	iter = 0;
268189251Ssam	while (res_len) {
269189251Ssam		size_t hlen;
270189251Ssam		iter++;
271189251Ssam		hmac_sha256_vector(k, 32, 5, addr, len, hash);
272189251Ssam		len[0] = SHA256_MAC_LEN;
273189251Ssam		hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len;
274189251Ssam		os_memcpy(res, hash, hlen);
275189251Ssam		res += hlen;
276189251Ssam		res_len -= hlen;
277189251Ssam	}
278189251Ssam}
279189251Ssam
280189251Ssam
281189251Ssamvoid eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
282189251Ssam			       const u8 *ik, const u8 *ck, u8 *k_encr,
283189251Ssam			       u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk)
284189251Ssam{
285189251Ssam	u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN];
286189251Ssam	u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN +
287189251Ssam		EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN];
288189251Ssam	u8 *pos;
289189251Ssam
290189251Ssam	/*
291189251Ssam	 * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity)
292189251Ssam	 * K_encr = MK[0..127]
293189251Ssam	 * K_aut  = MK[128..383]
294189251Ssam	 * K_re   = MK[384..639]
295189251Ssam	 * MSK    = MK[640..1151]
296189251Ssam	 * EMSK   = MK[1152..1663]
297189251Ssam	 */
298189251Ssam
299189251Ssam	os_memcpy(key, ik, EAP_AKA_IK_LEN);
300189251Ssam	os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN);
301189251Ssam
302189251Ssam	prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0,
303189251Ssam		  keys, sizeof(keys));
304189251Ssam
305189251Ssam	pos = keys;
306189251Ssam	os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
307189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr",
308189251Ssam			k_encr, EAP_SIM_K_ENCR_LEN);
309189251Ssam	pos += EAP_SIM_K_ENCR_LEN;
310189251Ssam
311189251Ssam	os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN);
312189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut",
313189251Ssam			k_aut, EAP_AKA_PRIME_K_AUT_LEN);
314189251Ssam	pos += EAP_AKA_PRIME_K_AUT_LEN;
315189251Ssam
316189251Ssam	os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN);
317189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re",
318189251Ssam			k_re, EAP_AKA_PRIME_K_RE_LEN);
319189251Ssam	pos += EAP_AKA_PRIME_K_RE_LEN;
320189251Ssam
321189251Ssam	os_memcpy(msk, pos, EAP_MSK_LEN);
322189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
323189251Ssam	pos += EAP_MSK_LEN;
324189251Ssam
325189251Ssam	os_memcpy(emsk, pos, EAP_EMSK_LEN);
326189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
327189251Ssam}
328189251Ssam
329189251Ssam
330189251Ssamint eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
331189251Ssam				     const u8 *identity, size_t identity_len,
332189251Ssam				     const u8 *nonce_s, u8 *msk, u8 *emsk)
333189251Ssam{
334189251Ssam	u8 seed3[2 + EAP_SIM_NONCE_S_LEN];
335189251Ssam	u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN];
336189251Ssam	u8 *pos;
337189251Ssam
338189251Ssam	/*
339189251Ssam	 * MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S)
340189251Ssam	 * MSK  = MK[0..511]
341189251Ssam	 * EMSK = MK[512..1023]
342189251Ssam	 */
343189251Ssam
344189251Ssam	WPA_PUT_BE16(seed3, counter);
345189251Ssam	os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN);
346189251Ssam
347189251Ssam	prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len,
348189251Ssam		  seed3, sizeof(seed3),
349189251Ssam		  keys, sizeof(keys));
350189251Ssam
351189251Ssam	pos = keys;
352189251Ssam	os_memcpy(msk, pos, EAP_MSK_LEN);
353189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
354189251Ssam	pos += EAP_MSK_LEN;
355189251Ssam
356189251Ssam	os_memcpy(emsk, pos, EAP_EMSK_LEN);
357189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
358189251Ssam
359189251Ssam	os_memset(keys, 0, sizeof(keys));
360189251Ssam
361189251Ssam	return 0;
362189251Ssam}
363189251Ssam
364189251Ssam
365189251Ssamint eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
366189251Ssam			      const u8 *mac, const u8 *extra, size_t extra_len)
367189251Ssam{
368189251Ssam	unsigned char hmac[SHA256_MAC_LEN];
369189251Ssam	const u8 *addr[2];
370189251Ssam	size_t len[2];
371189251Ssam	u8 *tmp;
372189251Ssam
373189251Ssam	if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
374189251Ssam	    mac < wpabuf_head_u8(req) ||
375189251Ssam	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
376189251Ssam		return -1;
377189251Ssam
378189251Ssam	tmp = os_malloc(wpabuf_len(req));
379189251Ssam	if (tmp == NULL)
380189251Ssam		return -1;
381189251Ssam
382189251Ssam	addr[0] = tmp;
383189251Ssam	len[0] = wpabuf_len(req);
384189251Ssam	addr[1] = extra;
385189251Ssam	len[1] = extra_len;
386189251Ssam
387189251Ssam	/* HMAC-SHA-256-128 */
388189251Ssam	os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
389189251Ssam	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
390189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg",
391189251Ssam		    tmp, wpabuf_len(req));
392189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data",
393189251Ssam		    extra, extra_len);
394189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut",
395189251Ssam			k_aut, EAP_AKA_PRIME_K_AUT_LEN);
396189251Ssam	hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
397189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC",
398189251Ssam		    hmac, EAP_SIM_MAC_LEN);
399189251Ssam	os_free(tmp);
400189251Ssam
401189251Ssam	return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
402189251Ssam}
403189251Ssam
404189251Ssam
405189251Ssamvoid eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
406189251Ssam			    u8 *mac, const u8 *extra, size_t extra_len)
407189251Ssam{
408189251Ssam	unsigned char hmac[SHA256_MAC_LEN];
409189251Ssam	const u8 *addr[2];
410189251Ssam	size_t len[2];
411189251Ssam
412189251Ssam	addr[0] = msg;
413189251Ssam	len[0] = msg_len;
414189251Ssam	addr[1] = extra;
415189251Ssam	len[1] = extra_len;
416189251Ssam
417189251Ssam	/* HMAC-SHA-256-128 */
418189251Ssam	os_memset(mac, 0, EAP_SIM_MAC_LEN);
419189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len);
420189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data",
421189251Ssam		    extra, extra_len);
422189251Ssam	wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut",
423189251Ssam			k_aut, EAP_AKA_PRIME_K_AUT_LEN);
424189251Ssam	hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
425189251Ssam	os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
426189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC",
427189251Ssam		    mac, EAP_SIM_MAC_LEN);
428189251Ssam}
429189251Ssam
430189251Ssam
431189251Ssamvoid eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
432189251Ssam				      const u8 *network_name,
433189251Ssam				      size_t network_name_len)
434189251Ssam{
435189251Ssam	u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN];
436189251Ssam	u8 hash[SHA256_MAC_LEN];
437189251Ssam	const u8 *addr[5];
438189251Ssam	size_t len[5];
439189251Ssam	u8 fc;
440189251Ssam	u8 l0[2], l1[2];
441189251Ssam
442189251Ssam	/* 3GPP TS 33.402 V8.0.0
443189251Ssam	 * (CK', IK') = F(CK, IK, <access network identity>)
444189251Ssam	 */
445189251Ssam	/* TODO: CK', IK' generation should really be moved into the actual
446189251Ssam	 * AKA procedure with network name passed in there and option to use
447189251Ssam	 * AMF separation bit = 1 (3GPP TS 33.401). */
448189251Ssam
449189251Ssam	/* Change Request 33.402 CR 0033 to version 8.1.1 from
450189251Ssam	 * 3GPP TSG-SA WG3 Meeting #53 in September 2008:
451189251Ssam	 *
452189251Ssam	 * CK' || IK' = HMAC-SHA-256(Key, S)
453189251Ssam	 * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln
454189251Ssam	 * Key = CK || IK
455189251Ssam	 * FC = 0x20
456189251Ssam	 * P0 = access network identity (3GPP TS 24.302)
457189251Ssam	 * L0 = length of acceess network identity (2 octets, big endian)
458189251Ssam	 * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0
459189251Ssam	 * L1 = 0x00 0x06
460189251Ssam	 */
461189251Ssam
462189251Ssam	fc = 0x20;
463189251Ssam
464189251Ssam	wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)");
465189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN);
466189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN);
467189251Ssam	wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc);
468189251Ssam	wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity",
469189251Ssam			  network_name, network_name_len);
470189251Ssam	wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6);
471189251Ssam
472189251Ssam	os_memcpy(key, ck, EAP_AKA_CK_LEN);
473189251Ssam	os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN);
474189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK",
475189251Ssam			key, sizeof(key));
476189251Ssam
477189251Ssam	addr[0] = &fc;
478189251Ssam	len[0] = 1;
479189251Ssam	addr[1] = network_name;
480189251Ssam	len[1] = network_name_len;
481189251Ssam	WPA_PUT_BE16(l0, network_name_len);
482189251Ssam	addr[2] = l0;
483189251Ssam	len[2] = 2;
484189251Ssam	addr[3] = sqn_ak;
485189251Ssam	len[3] = 6;
486189251Ssam	WPA_PUT_BE16(l1, 6);
487189251Ssam	addr[4] = l1;
488189251Ssam	len[4] = 2;
489189251Ssam
490189251Ssam	hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash);
491189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')",
492189251Ssam			hash, sizeof(hash));
493189251Ssam
494189251Ssam	os_memcpy(ck, hash, EAP_AKA_CK_LEN);
495189251Ssam	os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN);
496189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN);
497189251Ssam	wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN);
498189251Ssam}
499214734Srpaulo#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
500189251Ssam
501189251Ssam
502189251Ssamint eap_sim_parse_attr(const u8 *start, const u8 *end,
503189251Ssam		       struct eap_sim_attrs *attr, int aka, int encr)
504189251Ssam{
505189251Ssam	const u8 *pos = start, *apos;
506189251Ssam	size_t alen, plen, i, list_len;
507189251Ssam
508189251Ssam	os_memset(attr, 0, sizeof(*attr));
509189251Ssam	attr->id_req = NO_ID_REQ;
510189251Ssam	attr->notification = -1;
511189251Ssam	attr->counter = -1;
512189251Ssam	attr->selected_version = -1;
513189251Ssam	attr->client_error_code = -1;
514189251Ssam
515189251Ssam	while (pos < end) {
516189251Ssam		if (pos + 2 > end) {
517189251Ssam			wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
518189251Ssam			return -1;
519189251Ssam		}
520189251Ssam		wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
521189251Ssam			   pos[0], pos[1] * 4);
522189251Ssam		if (pos + pos[1] * 4 > end) {
523189251Ssam			wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
524189251Ssam				   "(pos=%p len=%d end=%p)",
525189251Ssam				   pos, pos[1] * 4, end);
526189251Ssam			return -1;
527189251Ssam		}
528189251Ssam		if (pos[1] == 0) {
529189251Ssam			wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow");
530189251Ssam			return -1;
531189251Ssam		}
532189251Ssam		apos = pos + 2;
533189251Ssam		alen = pos[1] * 4 - 2;
534189251Ssam		wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
535189251Ssam			    apos, alen);
536189251Ssam
537189251Ssam		switch (pos[0]) {
538189251Ssam		case EAP_SIM_AT_RAND:
539189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
540189251Ssam			apos += 2;
541189251Ssam			alen -= 2;
542189251Ssam			if ((!aka && (alen % GSM_RAND_LEN)) ||
543189251Ssam			    (aka && alen != EAP_AKA_RAND_LEN)) {
544189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
545189251Ssam					   " (len %lu)",
546189251Ssam					   (unsigned long) alen);
547189251Ssam				return -1;
548189251Ssam			}
549189251Ssam			attr->rand = apos;
550189251Ssam			attr->num_chal = alen / GSM_RAND_LEN;
551189251Ssam			break;
552189251Ssam		case EAP_SIM_AT_AUTN:
553189251Ssam			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
554189251Ssam			if (!aka) {
555189251Ssam				wpa_printf(MSG_DEBUG, "EAP-SIM: "
556189251Ssam					   "Unexpected AT_AUTN");
557189251Ssam				return -1;
558189251Ssam			}
559189251Ssam			apos += 2;
560189251Ssam			alen -= 2;
561189251Ssam			if (alen != EAP_AKA_AUTN_LEN) {
562189251Ssam				wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
563189251Ssam					   " (len %lu)",
564189251Ssam					   (unsigned long) alen);
565189251Ssam				return -1;
566189251Ssam			}
567189251Ssam			attr->autn = apos;
568189251Ssam			break;
569189251Ssam		case EAP_SIM_AT_PADDING:
570189251Ssam			if (!encr) {
571189251Ssam				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
572189251Ssam					   "AT_PADDING");
573189251Ssam				return -1;
574189251Ssam			}
575189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
576189251Ssam			for (i = 2; i < alen; i++) {
577189251Ssam				if (apos[i] != 0) {
578189251Ssam					wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
579189251Ssam						   "AT_PADDING used a non-zero"
580189251Ssam						   " padding byte");
581189251Ssam					wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
582189251Ssam						    "(encr) padding bytes",
583189251Ssam						    apos + 2, alen - 2);
584189251Ssam					return -1;
585189251Ssam				}
586189251Ssam			}
587189251Ssam			break;
588189251Ssam		case EAP_SIM_AT_NONCE_MT:
589189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
590189251Ssam			if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
591189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
592189251Ssam					   "AT_NONCE_MT length");
593189251Ssam				return -1;
594189251Ssam			}
595189251Ssam			attr->nonce_mt = apos + 2;
596189251Ssam			break;
597189251Ssam		case EAP_SIM_AT_PERMANENT_ID_REQ:
598189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
599189251Ssam			attr->id_req = PERMANENT_ID;
600189251Ssam			break;
601189251Ssam		case EAP_SIM_AT_MAC:
602189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
603189251Ssam			if (alen != 2 + EAP_SIM_MAC_LEN) {
604189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
605189251Ssam					   "length");
606189251Ssam				return -1;
607189251Ssam			}
608189251Ssam			attr->mac = apos + 2;
609189251Ssam			break;
610189251Ssam		case EAP_SIM_AT_NOTIFICATION:
611189251Ssam			if (alen != 2) {
612189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
613189251Ssam					   "AT_NOTIFICATION length %lu",
614189251Ssam					   (unsigned long) alen);
615189251Ssam				return -1;
616189251Ssam			}
617189251Ssam			attr->notification = apos[0] * 256 + apos[1];
618189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
619189251Ssam				   attr->notification);
620189251Ssam			break;
621189251Ssam		case EAP_SIM_AT_ANY_ID_REQ:
622189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
623189251Ssam			attr->id_req = ANY_ID;
624189251Ssam			break;
625189251Ssam		case EAP_SIM_AT_IDENTITY:
626189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
627189251Ssam			plen = WPA_GET_BE16(apos);
628189251Ssam			apos += 2;
629189251Ssam			alen -= 2;
630189251Ssam			if (plen > alen) {
631189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
632189251Ssam					   "AT_IDENTITY (Actual Length %lu, "
633189251Ssam					   "remaining length %lu)",
634189251Ssam					   (unsigned long) plen,
635189251Ssam					   (unsigned long) alen);
636189251Ssam				return -1;
637189251Ssam			}
638189251Ssam
639189251Ssam			attr->identity = apos;
640189251Ssam			attr->identity_len = plen;
641189251Ssam			break;
642189251Ssam		case EAP_SIM_AT_VERSION_LIST:
643189251Ssam			if (aka) {
644189251Ssam				wpa_printf(MSG_DEBUG, "EAP-AKA: "
645189251Ssam					   "Unexpected AT_VERSION_LIST");
646189251Ssam				return -1;
647189251Ssam			}
648189251Ssam			list_len = apos[0] * 256 + apos[1];
649189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
650189251Ssam			if (list_len < 2 || list_len > alen - 2) {
651189251Ssam				wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
652189251Ssam					   "AT_VERSION_LIST (list_len=%lu "
653189251Ssam					   "attr_len=%lu)",
654189251Ssam					   (unsigned long) list_len,
655189251Ssam					   (unsigned long) alen);
656189251Ssam				return -1;
657189251Ssam			}
658189251Ssam			attr->version_list = apos + 2;
659189251Ssam			attr->version_list_len = list_len;
660189251Ssam			break;
661189251Ssam		case EAP_SIM_AT_SELECTED_VERSION:
662189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
663189251Ssam			if (alen != 2) {
664189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
665189251Ssam					   "AT_SELECTED_VERSION length %lu",
666189251Ssam					   (unsigned long) alen);
667189251Ssam				return -1;
668189251Ssam			}
669189251Ssam			attr->selected_version = apos[0] * 256 + apos[1];
670189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
671189251Ssam				   "%d", attr->selected_version);
672189251Ssam			break;
673189251Ssam		case EAP_SIM_AT_FULLAUTH_ID_REQ:
674189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
675189251Ssam			attr->id_req = FULLAUTH_ID;
676189251Ssam			break;
677189251Ssam		case EAP_SIM_AT_COUNTER:
678189251Ssam			if (!encr) {
679189251Ssam				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
680189251Ssam					   "AT_COUNTER");
681189251Ssam				return -1;
682189251Ssam			}
683189251Ssam			if (alen != 2) {
684189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
685189251Ssam					   "AT_COUNTER (alen=%lu)",
686189251Ssam					   (unsigned long) alen);
687189251Ssam				return -1;
688189251Ssam			}
689189251Ssam			attr->counter = apos[0] * 256 + apos[1];
690189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
691189251Ssam				   attr->counter);
692189251Ssam			break;
693189251Ssam		case EAP_SIM_AT_COUNTER_TOO_SMALL:
694189251Ssam			if (!encr) {
695189251Ssam				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
696189251Ssam					   "AT_COUNTER_TOO_SMALL");
697189251Ssam				return -1;
698189251Ssam			}
699189251Ssam			if (alen != 2) {
700189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
701189251Ssam					   "AT_COUNTER_TOO_SMALL (alen=%lu)",
702189251Ssam					   (unsigned long) alen);
703189251Ssam				return -1;
704189251Ssam			}
705189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
706189251Ssam				   "AT_COUNTER_TOO_SMALL");
707189251Ssam			attr->counter_too_small = 1;
708189251Ssam			break;
709189251Ssam		case EAP_SIM_AT_NONCE_S:
710189251Ssam			if (!encr) {
711189251Ssam				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
712189251Ssam					   "AT_NONCE_S");
713189251Ssam				return -1;
714189251Ssam			}
715189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
716189251Ssam				   "AT_NONCE_S");
717189251Ssam			if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
718189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
719189251Ssam					   "AT_NONCE_S (alen=%lu)",
720189251Ssam					   (unsigned long) alen);
721189251Ssam				return -1;
722189251Ssam			}
723189251Ssam			attr->nonce_s = apos + 2;
724189251Ssam			break;
725189251Ssam		case EAP_SIM_AT_CLIENT_ERROR_CODE:
726189251Ssam			if (alen != 2) {
727189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
728189251Ssam					   "AT_CLIENT_ERROR_CODE length %lu",
729189251Ssam					   (unsigned long) alen);
730189251Ssam				return -1;
731189251Ssam			}
732189251Ssam			attr->client_error_code = apos[0] * 256 + apos[1];
733189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
734189251Ssam				   "%d", attr->client_error_code);
735189251Ssam			break;
736189251Ssam		case EAP_SIM_AT_IV:
737189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
738189251Ssam			if (alen != 2 + EAP_SIM_MAC_LEN) {
739189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
740189251Ssam					   "length %lu", (unsigned long) alen);
741189251Ssam				return -1;
742189251Ssam			}
743189251Ssam			attr->iv = apos + 2;
744189251Ssam			break;
745189251Ssam		case EAP_SIM_AT_ENCR_DATA:
746189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
747189251Ssam			attr->encr_data = apos + 2;
748189251Ssam			attr->encr_data_len = alen - 2;
749189251Ssam			if (attr->encr_data_len % 16) {
750189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
751189251Ssam					   "AT_ENCR_DATA length %lu",
752189251Ssam					   (unsigned long)
753189251Ssam					   attr->encr_data_len);
754189251Ssam				return -1;
755189251Ssam			}
756189251Ssam			break;
757189251Ssam		case EAP_SIM_AT_NEXT_PSEUDONYM:
758189251Ssam			if (!encr) {
759189251Ssam				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
760189251Ssam					   "AT_NEXT_PSEUDONYM");
761189251Ssam				return -1;
762189251Ssam			}
763189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
764189251Ssam				   "AT_NEXT_PSEUDONYM");
765189251Ssam			plen = apos[0] * 256 + apos[1];
766189251Ssam			if (plen > alen - 2) {
767189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
768189251Ssam					   " AT_NEXT_PSEUDONYM (actual"
769189251Ssam					   " len %lu, attr len %lu)",
770189251Ssam					   (unsigned long) plen,
771189251Ssam					   (unsigned long) alen);
772189251Ssam				return -1;
773189251Ssam			}
774189251Ssam			attr->next_pseudonym = pos + 4;
775189251Ssam			attr->next_pseudonym_len = plen;
776189251Ssam			break;
777189251Ssam		case EAP_SIM_AT_NEXT_REAUTH_ID:
778189251Ssam			if (!encr) {
779189251Ssam				wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
780189251Ssam					   "AT_NEXT_REAUTH_ID");
781189251Ssam				return -1;
782189251Ssam			}
783189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
784189251Ssam				   "AT_NEXT_REAUTH_ID");
785189251Ssam			plen = apos[0] * 256 + apos[1];
786189251Ssam			if (plen > alen - 2) {
787189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
788189251Ssam					   " AT_NEXT_REAUTH_ID (actual"
789189251Ssam					   " len %lu, attr len %lu)",
790189251Ssam					   (unsigned long) plen,
791189251Ssam					   (unsigned long) alen);
792189251Ssam				return -1;
793189251Ssam			}
794189251Ssam			attr->next_reauth_id = pos + 4;
795189251Ssam			attr->next_reauth_id_len = plen;
796189251Ssam			break;
797189251Ssam		case EAP_SIM_AT_RES:
798189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES");
799189251Ssam			attr->res_len_bits = WPA_GET_BE16(apos);
800189251Ssam			apos += 2;
801189251Ssam			alen -= 2;
802189251Ssam			if (!aka || alen < EAP_AKA_MIN_RES_LEN ||
803189251Ssam			    alen > EAP_AKA_MAX_RES_LEN) {
804189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES "
805189251Ssam					   "(len %lu)",
806189251Ssam					   (unsigned long) alen);
807189251Ssam				return -1;
808189251Ssam			}
809189251Ssam			attr->res = apos;
810189251Ssam			attr->res_len = alen;
811189251Ssam			break;
812189251Ssam		case EAP_SIM_AT_AUTS:
813189251Ssam			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS");
814189251Ssam			if (!aka) {
815189251Ssam				wpa_printf(MSG_DEBUG, "EAP-SIM: "
816189251Ssam					   "Unexpected AT_AUTS");
817189251Ssam				return -1;
818189251Ssam			}
819189251Ssam			if (alen != EAP_AKA_AUTS_LEN) {
820189251Ssam				wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS"
821189251Ssam					   " (len %lu)",
822189251Ssam					   (unsigned long) alen);
823189251Ssam				return -1;
824189251Ssam			}
825189251Ssam			attr->auts = apos;
826189251Ssam			break;
827189251Ssam		case EAP_SIM_AT_CHECKCODE:
828189251Ssam			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE");
829189251Ssam			if (!aka) {
830189251Ssam				wpa_printf(MSG_DEBUG, "EAP-SIM: "
831189251Ssam					   "Unexpected AT_CHECKCODE");
832189251Ssam				return -1;
833189251Ssam			}
834189251Ssam			apos += 2;
835189251Ssam			alen -= 2;
836189251Ssam			if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN &&
837189251Ssam			    alen != EAP_AKA_PRIME_CHECKCODE_LEN) {
838189251Ssam				wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
839189251Ssam					   "AT_CHECKCODE (len %lu)",
840189251Ssam					   (unsigned long) alen);
841189251Ssam				return -1;
842189251Ssam			}
843189251Ssam			attr->checkcode = apos;
844189251Ssam			attr->checkcode_len = alen;
845189251Ssam			break;
846189251Ssam		case EAP_SIM_AT_RESULT_IND:
847189251Ssam			if (encr) {
848189251Ssam				wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted "
849189251Ssam					   "AT_RESULT_IND");
850189251Ssam				return -1;
851189251Ssam			}
852189251Ssam			if (alen != 2) {
853189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
854189251Ssam					   "AT_RESULT_IND (alen=%lu)",
855189251Ssam					   (unsigned long) alen);
856189251Ssam				return -1;
857189251Ssam			}
858189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND");
859189251Ssam			attr->result_ind = 1;
860189251Ssam			break;
861214734Srpaulo#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
862189251Ssam		case EAP_SIM_AT_KDF_INPUT:
863189251Ssam			if (aka != 2) {
864189251Ssam				wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
865189251Ssam					   "AT_KDF_INPUT");
866189251Ssam				return -1;
867189251Ssam			}
868189251Ssam
869189251Ssam			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT");
870189251Ssam			plen = WPA_GET_BE16(apos);
871189251Ssam			apos += 2;
872189251Ssam			alen -= 2;
873189251Ssam			if (plen > alen) {
874189251Ssam				wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
875189251Ssam					   "AT_KDF_INPUT (Actual Length %lu, "
876189251Ssam					   "remaining length %lu)",
877189251Ssam					   (unsigned long) plen,
878189251Ssam					   (unsigned long) alen);
879189251Ssam				return -1;
880189251Ssam			}
881189251Ssam			attr->kdf_input = apos;
882189251Ssam			attr->kdf_input_len = plen;
883189251Ssam			break;
884189251Ssam		case EAP_SIM_AT_KDF:
885189251Ssam			if (aka != 2) {
886189251Ssam				wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
887189251Ssam					   "AT_KDF");
888189251Ssam				return -1;
889189251Ssam			}
890189251Ssam
891189251Ssam			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF");
892189251Ssam			if (alen != 2) {
893189251Ssam				wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
894189251Ssam					   "AT_KDF (len %lu)",
895189251Ssam					   (unsigned long) alen);
896189251Ssam				return -1;
897189251Ssam			}
898189251Ssam			if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
899189251Ssam				wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
900189251Ssam					   "AT_KDF attributes - ignore this");
901189251Ssam				continue;
902189251Ssam			}
903189251Ssam			attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
904189251Ssam			attr->kdf_count++;
905189251Ssam			break;
906189251Ssam		case EAP_SIM_AT_BIDDING:
907189251Ssam			wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING");
908189251Ssam			if (alen != 2) {
909189251Ssam				wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
910189251Ssam					   "AT_BIDDING (len %lu)",
911189251Ssam					   (unsigned long) alen);
912189251Ssam				return -1;
913189251Ssam			}
914189251Ssam			attr->bidding = apos;
915189251Ssam			break;
916214734Srpaulo#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
917189251Ssam		default:
918189251Ssam			if (pos[0] < 128) {
919189251Ssam				wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
920189251Ssam					   "non-skippable attribute %d",
921189251Ssam					   pos[0]);
922189251Ssam				return -1;
923189251Ssam			}
924189251Ssam
925189251Ssam			wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
926189251Ssam				   " attribute %d ignored", pos[0]);
927189251Ssam			break;
928189251Ssam		}
929189251Ssam
930189251Ssam		pos += pos[1] * 4;
931189251Ssam	}
932189251Ssam
933189251Ssam	wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
934189251Ssam		   "(aka=%d encr=%d)", aka, encr);
935189251Ssam
936189251Ssam	return 0;
937189251Ssam}
938189251Ssam
939189251Ssam
940189251Ssamu8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
941189251Ssam			size_t encr_data_len, const u8 *iv,
942189251Ssam			struct eap_sim_attrs *attr, int aka)
943189251Ssam{
944189251Ssam	u8 *decrypted;
945189251Ssam
946189251Ssam	if (!iv) {
947189251Ssam		wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
948189251Ssam		return NULL;
949189251Ssam	}
950189251Ssam
951189251Ssam	decrypted = os_malloc(encr_data_len);
952189251Ssam	if (decrypted == NULL)
953189251Ssam		return NULL;
954189251Ssam	os_memcpy(decrypted, encr_data, encr_data_len);
955189251Ssam
956189251Ssam	if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
957189251Ssam		os_free(decrypted);
958189251Ssam		return NULL;
959189251Ssam	}
960189251Ssam	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
961189251Ssam		    decrypted, encr_data_len);
962189251Ssam
963189251Ssam	if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr,
964189251Ssam			       aka, 1)) {
965189251Ssam		wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
966189251Ssam			   "decrypted AT_ENCR_DATA");
967189251Ssam		os_free(decrypted);
968189251Ssam		return NULL;
969189251Ssam	}
970189251Ssam
971189251Ssam	return decrypted;
972189251Ssam}
973189251Ssam
974189251Ssam
975189251Ssam#define EAP_SIM_INIT_LEN 128
976189251Ssam
977189251Ssamstruct eap_sim_msg {
978189251Ssam	struct wpabuf *buf;
979189251Ssam	size_t mac, iv, encr; /* index from buf */
980189251Ssam	int type;
981189251Ssam};
982189251Ssam
983189251Ssam
984189251Ssamstruct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
985189251Ssam{
986189251Ssam	struct eap_sim_msg *msg;
987189251Ssam	struct eap_hdr *eap;
988189251Ssam	u8 *pos;
989189251Ssam
990189251Ssam	msg = os_zalloc(sizeof(*msg));
991189251Ssam	if (msg == NULL)
992189251Ssam		return NULL;
993189251Ssam
994189251Ssam	msg->type = type;
995189251Ssam	msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
996189251Ssam	if (msg->buf == NULL) {
997189251Ssam		os_free(msg);
998189251Ssam		return NULL;
999189251Ssam	}
1000189251Ssam	eap = wpabuf_put(msg->buf, sizeof(*eap));
1001189251Ssam	eap->code = code;
1002189251Ssam	eap->identifier = id;
1003189251Ssam
1004189251Ssam	pos = wpabuf_put(msg->buf, 4);
1005189251Ssam	*pos++ = type;
1006189251Ssam	*pos++ = subtype;
1007189251Ssam	*pos++ = 0; /* Reserved */
1008189251Ssam	*pos++ = 0; /* Reserved */
1009189251Ssam
1010189251Ssam	return msg;
1011189251Ssam}
1012189251Ssam
1013189251Ssam
1014189251Ssamstruct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
1015189251Ssam				   const u8 *extra, size_t extra_len)
1016189251Ssam{
1017189251Ssam	struct eap_hdr *eap;
1018189251Ssam	struct wpabuf *buf;
1019189251Ssam
1020189251Ssam	if (msg == NULL)
1021189251Ssam		return NULL;
1022189251Ssam
1023189251Ssam	eap = wpabuf_mhead(msg->buf);
1024189251Ssam	eap->length = host_to_be16(wpabuf_len(msg->buf));
1025189251Ssam
1026214734Srpaulo#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
1027189251Ssam	if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) {
1028189251Ssam		eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf),
1029189251Ssam				       wpabuf_len(msg->buf),
1030189251Ssam				       (u8 *) wpabuf_mhead(msg->buf) +
1031189251Ssam				       msg->mac, extra, extra_len);
1032189251Ssam	} else
1033214734Srpaulo#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
1034189251Ssam	if (k_aut && msg->mac) {
1035189251Ssam		eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf),
1036189251Ssam				wpabuf_len(msg->buf),
1037189251Ssam				(u8 *) wpabuf_mhead(msg->buf) + msg->mac,
1038189251Ssam				extra, extra_len);
1039189251Ssam	}
1040189251Ssam
1041189251Ssam	buf = msg->buf;
1042189251Ssam	os_free(msg);
1043189251Ssam	return buf;
1044189251Ssam}
1045189251Ssam
1046189251Ssam
1047189251Ssamvoid eap_sim_msg_free(struct eap_sim_msg *msg)
1048189251Ssam{
1049189251Ssam	if (msg) {
1050189251Ssam		wpabuf_free(msg->buf);
1051189251Ssam		os_free(msg);
1052189251Ssam	}
1053189251Ssam}
1054189251Ssam
1055189251Ssam
1056189251Ssamu8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
1057189251Ssam			  const u8 *data, size_t len)
1058189251Ssam{
1059189251Ssam	int attr_len = 2 + len;
1060189251Ssam	int pad_len;
1061189251Ssam	u8 *start;
1062189251Ssam
1063189251Ssam	if (msg == NULL)
1064189251Ssam		return NULL;
1065189251Ssam
1066189251Ssam	pad_len = (4 - attr_len % 4) % 4;
1067189251Ssam	attr_len += pad_len;
1068189251Ssam	if (wpabuf_resize(&msg->buf, attr_len))
1069189251Ssam		return NULL;
1070189251Ssam	start = wpabuf_put(msg->buf, 0);
1071189251Ssam	wpabuf_put_u8(msg->buf, attr);
1072189251Ssam	wpabuf_put_u8(msg->buf, attr_len / 4);
1073189251Ssam	wpabuf_put_data(msg->buf, data, len);
1074189251Ssam	if (pad_len)
1075189251Ssam		os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
1076189251Ssam	return start;
1077189251Ssam}
1078189251Ssam
1079189251Ssam
1080189251Ssamu8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
1081189251Ssam		     const u8 *data, size_t len)
1082189251Ssam{
1083189251Ssam	int attr_len = 4 + len;
1084189251Ssam	int pad_len;
1085189251Ssam	u8 *start;
1086189251Ssam
1087189251Ssam	if (msg == NULL)
1088189251Ssam		return NULL;
1089189251Ssam
1090189251Ssam	pad_len = (4 - attr_len % 4) % 4;
1091189251Ssam	attr_len += pad_len;
1092189251Ssam	if (wpabuf_resize(&msg->buf, attr_len))
1093189251Ssam		return NULL;
1094189251Ssam	start = wpabuf_put(msg->buf, 0);
1095189251Ssam	wpabuf_put_u8(msg->buf, attr);
1096189251Ssam	wpabuf_put_u8(msg->buf, attr_len / 4);
1097189251Ssam	wpabuf_put_be16(msg->buf, value);
1098189251Ssam	if (data)
1099189251Ssam		wpabuf_put_data(msg->buf, data, len);
1100189251Ssam	else
1101189251Ssam		wpabuf_put(msg->buf, len);
1102189251Ssam	if (pad_len)
1103189251Ssam		os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
1104189251Ssam	return start;
1105189251Ssam}
1106189251Ssam
1107189251Ssam
1108189251Ssamu8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
1109189251Ssam{
1110189251Ssam	u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
1111189251Ssam	if (pos)
1112189251Ssam		msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4;
1113189251Ssam	return pos;
1114189251Ssam}
1115189251Ssam
1116189251Ssam
1117189251Ssamint eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
1118189251Ssam			       u8 attr_encr)
1119189251Ssam{
1120189251Ssam	u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
1121189251Ssam	if (pos == NULL)
1122189251Ssam		return -1;
1123189251Ssam	msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
1124189251Ssam	if (os_get_random(wpabuf_mhead_u8(msg->buf) + msg->iv,
1125189251Ssam			  EAP_SIM_IV_LEN)) {
1126189251Ssam		msg->iv = 0;
1127189251Ssam		return -1;
1128189251Ssam	}
1129189251Ssam
1130189251Ssam	pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0);
1131189251Ssam	if (pos == NULL) {
1132189251Ssam		msg->iv = 0;
1133189251Ssam		return -1;
1134189251Ssam	}
1135189251Ssam	msg->encr = pos - wpabuf_head_u8(msg->buf);
1136189251Ssam
1137189251Ssam	return 0;
1138189251Ssam}
1139189251Ssam
1140189251Ssam
1141189251Ssamint eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad)
1142189251Ssam{
1143189251Ssam	size_t encr_len;
1144189251Ssam
1145189251Ssam	if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0)
1146189251Ssam		return -1;
1147189251Ssam
1148189251Ssam	encr_len = wpabuf_len(msg->buf) - msg->encr - 4;
1149189251Ssam	if (encr_len % 16) {
1150189251Ssam		u8 *pos;
1151189251Ssam		int pad_len = 16 - (encr_len % 16);
1152189251Ssam		if (pad_len < 4) {
1153189251Ssam			wpa_printf(MSG_WARNING, "EAP-SIM: "
1154189251Ssam				   "eap_sim_msg_add_encr_end - invalid pad_len"
1155189251Ssam				   " %d", pad_len);
1156189251Ssam			return -1;
1157189251Ssam		}
1158189251Ssam		wpa_printf(MSG_DEBUG, "   *AT_PADDING");
1159189251Ssam		pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4);
1160189251Ssam		if (pos == NULL)
1161189251Ssam			return -1;
1162189251Ssam		os_memset(pos + 4, 0, pad_len - 4);
1163189251Ssam		encr_len += pad_len;
1164189251Ssam	}
1165189251Ssam	wpa_printf(MSG_DEBUG, "   (AT_ENCR_DATA data len %lu)",
1166189251Ssam		   (unsigned long) encr_len);
1167189251Ssam	wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1;
1168189251Ssam	return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv,
1169189251Ssam				   wpabuf_mhead_u8(msg->buf) + msg->encr + 4,
1170189251Ssam				   encr_len);
1171189251Ssam}
1172189251Ssam
1173189251Ssam
1174189251Ssamvoid eap_sim_report_notification(void *msg_ctx, int notification, int aka)
1175189251Ssam{
1176189251Ssam#ifndef CONFIG_NO_STDOUT_DEBUG
1177189251Ssam	const char *type = aka ? "AKA" : "SIM";
1178189251Ssam#endif /* CONFIG_NO_STDOUT_DEBUG */
1179189251Ssam
1180189251Ssam	switch (notification) {
1181189251Ssam	case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH:
1182189251Ssam		wpa_printf(MSG_WARNING, "EAP-%s: General failure "
1183189251Ssam			   "notification (after authentication)", type);
1184189251Ssam		break;
1185189251Ssam	case EAP_SIM_TEMPORARILY_DENIED:
1186189251Ssam		wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
1187189251Ssam			   "User has been temporarily denied access to the "
1188189251Ssam			   "requested service", type);
1189189251Ssam		break;
1190189251Ssam	case EAP_SIM_NOT_SUBSCRIBED:
1191189251Ssam		wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
1192189251Ssam			   "User has not subscribed to the requested service",
1193189251Ssam			   type);
1194189251Ssam		break;
1195189251Ssam	case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH:
1196189251Ssam		wpa_printf(MSG_WARNING, "EAP-%s: General failure "
1197189251Ssam			   "notification (before authentication)", type);
1198189251Ssam		break;
1199189251Ssam	case EAP_SIM_SUCCESS:
1200189251Ssam		wpa_printf(MSG_INFO, "EAP-%s: Successful authentication "
1201189251Ssam			   "notification", type);
1202189251Ssam		break;
1203189251Ssam	default:
1204189251Ssam		if (notification >= 32768) {
1205189251Ssam			wpa_printf(MSG_INFO, "EAP-%s: Unrecognized "
1206189251Ssam				   "non-failure notification %d",
1207189251Ssam				   type, notification);
1208189251Ssam		} else {
1209189251Ssam			wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized "
1210189251Ssam				   "failure notification %d",
1211189251Ssam				   type, notification);
1212189251Ssam		}
1213189251Ssam	}
1214189251Ssam}
1215